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 (EmitContext 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 (EmitContext 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 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
);
66 /// Utility wrapper routine for Error, just to beautify the code
68 public void Error (int error
, string format
, params object[] args
)
70 Error (error
, String
.Format (format
, args
));
73 public void Error (int error
, string s
)
76 Report
.Error (error
, loc
, s
);
78 Report
.Error (error
, s
);
81 public virtual void Emit (EmitContext ec
)
88 // This routine must be overrided in derived classes and make copies
89 // of all the data that might be modified if resolved
91 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
93 public Statement
Clone (CloneContext clonectx
)
95 Statement s
= (Statement
) this.MemberwiseClone ();
96 CloneTo (clonectx
, s
);
100 public virtual Expression
CreateExpressionTree (EmitContext ec
)
102 Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
106 public Statement
PerformClone ()
108 CloneContext clonectx
= new CloneContext ();
110 return Clone (clonectx
);
113 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey
);
117 // This class is used during the Statement.Clone operation
118 // to remap objects that have been cloned.
120 // Since blocks are cloned by Block.Clone, we need a way for
121 // expressions that must reference the block to be cloned
122 // pointing to the new cloned block.
124 public class CloneContext
{
125 Hashtable block_map
= new Hashtable ();
126 Hashtable variable_map
;
128 public void AddBlockMap (Block
from, Block to
)
130 if (block_map
.Contains (from))
132 block_map
[from] = to
;
135 public Block
LookupBlock (Block
from)
137 Block result
= (Block
) block_map
[from];
140 result
= (Block
) from.Clone (this);
141 block_map
[from] = result
;
148 /// Remaps block to cloned copy if one exists.
150 public Block
RemapBlockCopy (Block
from)
152 Block mapped_to
= (Block
)block_map
[from];
153 if (mapped_to
== null)
159 public void AddVariableMap (LocalInfo
from, LocalInfo to
)
161 if (variable_map
== null)
162 variable_map
= new Hashtable ();
164 if (variable_map
.Contains (from))
166 variable_map
[from] = to
;
169 public LocalInfo
LookupVariable (LocalInfo
from)
171 LocalInfo result
= (LocalInfo
) variable_map
[from];
174 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
180 public sealed class EmptyStatement
: Statement
{
182 private EmptyStatement () {}
184 public static readonly EmptyStatement Value
= new EmptyStatement ();
186 public override bool Resolve (EmitContext ec
)
191 public override bool ResolveUnreachable (EmitContext ec
, bool warn
)
196 protected override void DoEmit (EmitContext ec
)
200 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
204 protected override void CloneTo (CloneContext clonectx
, Statement target
)
210 public class If
: Statement
{
212 public Statement TrueStatement
;
213 public Statement FalseStatement
;
217 public If (Expression expr
, Statement true_statement
, Location l
)
220 TrueStatement
= true_statement
;
224 public If (Expression expr
,
225 Statement true_statement
,
226 Statement false_statement
,
230 TrueStatement
= true_statement
;
231 FalseStatement
= false_statement
;
235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
237 expr
.MutateHoistedGenericType (storey
);
238 TrueStatement
.MutateHoistedGenericType (storey
);
239 if (FalseStatement
!= null)
240 FalseStatement
.MutateHoistedGenericType (storey
);
243 public override bool Resolve (EmitContext ec
)
247 Report
.Debug (1, "START IF BLOCK", loc
);
249 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
255 Assign ass
= expr
as Assign
;
256 if (ass
!= null && ass
.Source
is Constant
) {
257 Report
.Warning (665, 3, loc
, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
261 // Dead code elimination
263 if (expr
is Constant
){
264 bool take
= !((Constant
) expr
).IsDefaultValue
;
267 if (!TrueStatement
.Resolve (ec
))
270 if ((FalseStatement
!= null) &&
271 !FalseStatement
.ResolveUnreachable (ec
, true))
273 FalseStatement
= null;
275 if (!TrueStatement
.ResolveUnreachable (ec
, true))
277 TrueStatement
= null;
279 if ((FalseStatement
!= null) &&
280 !FalseStatement
.Resolve (ec
))
287 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
289 ok
&= TrueStatement
.Resolve (ec
);
291 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
293 ec
.CurrentBranching
.CreateSibling ();
295 if (FalseStatement
!= null)
296 ok
&= FalseStatement
.Resolve (ec
);
298 ec
.EndFlowBranching ();
300 Report
.Debug (1, "END IF BLOCK", loc
);
305 protected override void DoEmit (EmitContext ec
)
307 ILGenerator ig
= ec
.ig
;
308 Label false_target
= ig
.DefineLabel ();
312 // If we're a boolean constant, Resolve() already
313 // eliminated dead code for us.
315 Constant c
= expr
as Constant
;
317 c
.EmitSideEffect (ec
);
319 if (!c
.IsDefaultValue
)
320 TrueStatement
.Emit (ec
);
321 else if (FalseStatement
!= null)
322 FalseStatement
.Emit (ec
);
327 expr
.EmitBranchable (ec
, false_target
, false);
329 TrueStatement
.Emit (ec
);
331 if (FalseStatement
!= null){
332 bool branch_emitted
= false;
334 end
= ig
.DefineLabel ();
336 ig
.Emit (OpCodes
.Br
, end
);
337 branch_emitted
= true;
340 ig
.MarkLabel (false_target
);
341 FalseStatement
.Emit (ec
);
346 ig
.MarkLabel (false_target
);
350 protected override void CloneTo (CloneContext clonectx
, Statement t
)
354 target
.expr
= expr
.Clone (clonectx
);
355 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
356 if (FalseStatement
!= null)
357 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
361 public class Do
: Statement
{
362 public Expression expr
;
363 public Statement EmbeddedStatement
;
365 public Do (Statement statement
, Expression bool_expr
, Location l
)
368 EmbeddedStatement
= statement
;
372 public override bool Resolve (EmitContext ec
)
376 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
378 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
380 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
381 if (!EmbeddedStatement
.Resolve (ec
))
383 ec
.EndFlowBranching ();
385 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
&& !was_unreachable
)
386 Report
.Warning (162, 2, expr
.Location
, "Unreachable code detected");
388 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
391 else if (expr
is Constant
){
392 bool infinite
= !((Constant
) expr
).IsDefaultValue
;
394 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
397 ec
.EndFlowBranching ();
402 protected override void DoEmit (EmitContext ec
)
404 ILGenerator ig
= ec
.ig
;
405 Label loop
= ig
.DefineLabel ();
406 Label old_begin
= ec
.LoopBegin
;
407 Label old_end
= ec
.LoopEnd
;
409 ec
.LoopBegin
= ig
.DefineLabel ();
410 ec
.LoopEnd
= ig
.DefineLabel ();
413 EmbeddedStatement
.Emit (ec
);
414 ig
.MarkLabel (ec
.LoopBegin
);
417 // Dead code elimination
419 if (expr
is Constant
){
420 bool res
= !((Constant
) expr
).IsDefaultValue
;
422 expr
.EmitSideEffect (ec
);
424 ec
.ig
.Emit (OpCodes
.Br
, loop
);
426 expr
.EmitBranchable (ec
, loop
, true);
428 ig
.MarkLabel (ec
.LoopEnd
);
430 ec
.LoopBegin
= old_begin
;
431 ec
.LoopEnd
= old_end
;
434 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
436 expr
.MutateHoistedGenericType (storey
);
437 EmbeddedStatement
.MutateHoistedGenericType (storey
);
440 protected override void CloneTo (CloneContext clonectx
, Statement t
)
444 target
.EmbeddedStatement
= EmbeddedStatement
.Clone (clonectx
);
445 target
.expr
= expr
.Clone (clonectx
);
449 public class While
: Statement
{
450 public Expression expr
;
451 public Statement Statement
;
452 bool infinite
, empty
;
454 public While (Expression bool_expr
, Statement statement
, Location l
)
456 this.expr
= bool_expr
;
457 Statement
= statement
;
461 public override bool Resolve (EmitContext ec
)
465 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
470 // Inform whether we are infinite or not
472 if (expr
is Constant
){
473 bool value = !((Constant
) expr
).IsDefaultValue
;
476 if (!Statement
.ResolveUnreachable (ec
, true))
484 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
486 ec
.CurrentBranching
.CreateSibling ();
488 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
489 if (!Statement
.Resolve (ec
))
491 ec
.EndFlowBranching ();
493 // There's no direct control flow from the end of the embedded statement to the end of the loop
494 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
496 ec
.EndFlowBranching ();
501 protected override void DoEmit (EmitContext ec
)
504 expr
.EmitSideEffect (ec
);
508 ILGenerator ig
= ec
.ig
;
509 Label old_begin
= ec
.LoopBegin
;
510 Label old_end
= ec
.LoopEnd
;
512 ec
.LoopBegin
= ig
.DefineLabel ();
513 ec
.LoopEnd
= ig
.DefineLabel ();
516 // Inform whether we are infinite or not
518 if (expr
is Constant
){
519 // expr is 'true', since the 'empty' case above handles the 'false' case
520 ig
.MarkLabel (ec
.LoopBegin
);
521 expr
.EmitSideEffect (ec
);
523 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
526 // Inform that we are infinite (ie, `we return'), only
527 // if we do not `break' inside the code.
529 ig
.MarkLabel (ec
.LoopEnd
);
531 Label while_loop
= ig
.DefineLabel ();
533 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
534 ig
.MarkLabel (while_loop
);
538 ig
.MarkLabel (ec
.LoopBegin
);
541 expr
.EmitBranchable (ec
, while_loop
, true);
543 ig
.MarkLabel (ec
.LoopEnd
);
546 ec
.LoopBegin
= old_begin
;
547 ec
.LoopEnd
= old_end
;
550 public override void Emit (EmitContext ec
)
555 protected override void CloneTo (CloneContext clonectx
, Statement t
)
557 While target
= (While
) t
;
559 target
.expr
= expr
.Clone (clonectx
);
560 target
.Statement
= Statement
.Clone (clonectx
);
563 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
565 expr
.MutateHoistedGenericType (storey
);
566 Statement
.MutateHoistedGenericType (storey
);
570 public class For
: Statement
{
572 Statement InitStatement
;
574 public Statement Statement
;
575 bool infinite
, empty
;
577 public For (Statement init_statement
,
583 InitStatement
= init_statement
;
585 Increment
= increment
;
586 Statement
= statement
;
590 public override bool Resolve (EmitContext ec
)
594 if (InitStatement
!= null){
595 if (!InitStatement
.Resolve (ec
))
600 Test
= Expression
.ResolveBoolean (ec
, Test
, loc
);
603 else if (Test
is Constant
){
604 bool value = !((Constant
) Test
).IsDefaultValue
;
607 if (!Statement
.ResolveUnreachable (ec
, true))
609 if ((Increment
!= null) &&
610 !Increment
.ResolveUnreachable (ec
, false))
620 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
622 ec
.CurrentBranching
.CreateSibling ();
624 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
626 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
627 if (!Statement
.Resolve (ec
))
629 ec
.EndFlowBranching ();
631 if (Increment
!= null){
632 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
) {
633 if (!Increment
.ResolveUnreachable (ec
, !was_unreachable
))
636 if (!Increment
.Resolve (ec
))
641 // There's no direct control flow from the end of the embedded statement to the end of the loop
642 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
644 ec
.EndFlowBranching ();
649 protected override void DoEmit (EmitContext ec
)
651 if (InitStatement
!= null && InitStatement
!= EmptyStatement
.Value
)
652 InitStatement
.Emit (ec
);
655 Test
.EmitSideEffect (ec
);
659 ILGenerator ig
= ec
.ig
;
660 Label old_begin
= ec
.LoopBegin
;
661 Label old_end
= ec
.LoopEnd
;
662 Label loop
= ig
.DefineLabel ();
663 Label test
= ig
.DefineLabel ();
665 ec
.LoopBegin
= ig
.DefineLabel ();
666 ec
.LoopEnd
= ig
.DefineLabel ();
668 ig
.Emit (OpCodes
.Br
, test
);
672 ig
.MarkLabel (ec
.LoopBegin
);
673 if (Increment
!= EmptyStatement
.Value
)
678 // If test is null, there is no test, and we are just
683 // The Resolve code already catches the case for
684 // Test == Constant (false) so we know that
687 if (Test
is Constant
) {
688 Test
.EmitSideEffect (ec
);
689 ig
.Emit (OpCodes
.Br
, loop
);
691 Test
.EmitBranchable (ec
, loop
, true);
695 ig
.Emit (OpCodes
.Br
, loop
);
696 ig
.MarkLabel (ec
.LoopEnd
);
698 ec
.LoopBegin
= old_begin
;
699 ec
.LoopEnd
= old_end
;
702 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
704 if (InitStatement
!= null)
705 InitStatement
.MutateHoistedGenericType (storey
);
707 Test
.MutateHoistedGenericType (storey
);
708 if (Increment
!= null)
709 Increment
.MutateHoistedGenericType (storey
);
711 Statement
.MutateHoistedGenericType (storey
);
714 protected override void CloneTo (CloneContext clonectx
, Statement t
)
716 For target
= (For
) t
;
718 if (InitStatement
!= null)
719 target
.InitStatement
= InitStatement
.Clone (clonectx
);
721 target
.Test
= Test
.Clone (clonectx
);
722 if (Increment
!= null)
723 target
.Increment
= Increment
.Clone (clonectx
);
724 target
.Statement
= Statement
.Clone (clonectx
);
728 public class StatementExpression
: Statement
{
729 ExpressionStatement expr
;
731 public StatementExpression (ExpressionStatement expr
)
737 public override bool Resolve (EmitContext ec
)
740 expr
= expr
.ResolveStatement (ec
);
744 protected override void DoEmit (EmitContext ec
)
746 expr
.EmitStatement (ec
);
749 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
751 expr
.MutateHoistedGenericType (storey
);
754 public override string ToString ()
756 return "StatementExpression (" + expr
+ ")";
759 protected override void CloneTo (CloneContext clonectx
, Statement t
)
761 StatementExpression target
= (StatementExpression
) t
;
763 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
767 // A 'return' or a 'yield break'
768 public abstract class ExitStatement
: Statement
770 protected bool unwind_protect
;
771 protected abstract bool DoResolve (EmitContext ec
);
773 public virtual void Error_FinallyClause ()
775 Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
778 public sealed override bool Resolve (EmitContext ec
)
783 unwind_protect
= ec
.CurrentBranching
.AddReturnOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
785 ec
.NeedReturnLabel ();
786 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
792 /// Implements the return statement
794 public class Return
: ExitStatement
{
795 protected Expression Expr
;
796 public Return (Expression expr
, Location l
)
802 protected override bool DoResolve (EmitContext ec
)
805 if (ec
.ReturnType
== TypeManager
.void_type
)
808 Error (126, "An object of a type convertible to `{0}' is required " +
809 "for the return statement",
810 TypeManager
.CSharpName (ec
.ReturnType
));
814 if (ec
.CurrentBlock
.Toplevel
.IsIterator
) {
815 Report
.Error (1622, loc
, "Cannot return a value from iterators. Use the yield return " +
816 "statement to return a value, or yield break to end the iteration");
819 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
820 if (am
== null && ec
.ReturnType
== TypeManager
.void_type
) {
821 MemberCore mc
= ec
.ResolveContext
as MemberCore
;
822 Report
.Error (127, loc
, "`{0}': A return keyword must not be followed by any expression when method returns void",
823 mc
.GetSignatureForError ());
826 Expr
= Expr
.Resolve (ec
);
830 if (Expr
.Type
!= ec
.ReturnType
) {
831 if (ec
.InferReturnType
) {
833 // void cannot be used in contextual return
835 if (Expr
.Type
== TypeManager
.void_type
)
838 ec
.ReturnType
= Expr
.Type
;
840 Expr
= Convert
.ImplicitConversionRequired (
841 ec
, Expr
, ec
.ReturnType
, loc
);
845 Report
.Error (1662, loc
,
846 "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",
847 am
.ContainerType
, am
.GetSignatureForError ());
857 protected override void DoEmit (EmitContext ec
)
863 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
867 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
869 ec
.ig
.Emit (OpCodes
.Ret
);
872 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
875 Expr
.MutateHoistedGenericType (storey
);
878 protected override void CloneTo (CloneContext clonectx
, Statement t
)
880 Return target
= (Return
) t
;
881 // It's null for simple return;
883 target
.Expr
= Expr
.Clone (clonectx
);
887 public class Goto
: Statement
{
889 LabeledStatement label
;
892 public override bool Resolve (EmitContext ec
)
894 int errors
= Report
.Errors
;
895 unwind_protect
= ec
.CurrentBranching
.AddGotoOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
896 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
897 return errors
== Report
.Errors
;
900 public Goto (string label
, Location l
)
906 public string Target
{
907 get { return target; }
910 public void SetResolvedTarget (LabeledStatement label
)
913 label
.AddReference ();
916 protected override void CloneTo (CloneContext clonectx
, Statement target
)
921 protected override void DoEmit (EmitContext ec
)
924 throw new InternalErrorException ("goto emitted before target resolved");
925 Label l
= label
.LabelTarget (ec
);
926 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
934 public class LabeledStatement
: Statement
{
941 FlowBranching
.UsageVector vectors
;
943 public LabeledStatement (string name
, Location l
)
949 public Label
LabelTarget (EmitContext ec
)
954 label
= ec
.ig
.DefineLabel ();
964 public bool IsDefined
{
965 get { return defined; }
968 public bool HasBeenReferenced
{
969 get { return referenced; }
972 public FlowBranching
.UsageVector JumpOrigins
{
973 get { return vectors; }
976 public void AddUsageVector (FlowBranching
.UsageVector vector
)
978 vector
= vector
.Clone ();
979 vector
.Next
= vectors
;
983 protected override void CloneTo (CloneContext clonectx
, Statement target
)
988 public override bool Resolve (EmitContext ec
)
990 // this flow-branching will be terminated when the surrounding block ends
991 ec
.StartFlowBranching (this);
995 protected override void DoEmit (EmitContext ec
)
997 if (ig
!= null && ig
!= ec
.ig
)
998 throw new InternalErrorException ("cannot happen");
1000 ec
.ig
.MarkLabel (label
);
1003 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1007 public void AddReference ()
1015 /// `goto default' statement
1017 public class GotoDefault
: Statement
{
1019 public GotoDefault (Location l
)
1024 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1029 public override bool Resolve (EmitContext ec
)
1031 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1035 protected override void DoEmit (EmitContext ec
)
1037 if (ec
.Switch
== null){
1038 Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
1042 if (!ec
.Switch
.GotDefault
){
1043 FlowBranchingBlock
.Error_UnknownLabel (loc
, "default");
1046 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
1049 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1055 /// `goto case' statement
1057 public class GotoCase
: Statement
{
1061 public GotoCase (Expression e
, Location l
)
1067 public override bool Resolve (EmitContext ec
)
1069 if (ec
.Switch
== null){
1070 Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
1074 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1076 expr
= expr
.Resolve (ec
);
1080 Constant c
= expr
as Constant
;
1082 Error (150, "A constant value is expected");
1086 Type type
= ec
.Switch
.SwitchType
;
1087 Constant res
= c
.TryReduce (ec
, type
, c
.Location
);
1089 c
.Error_ValueCannotBeConverted (ec
, loc
, type
, true);
1093 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
1094 Report
.Warning (469, 2, loc
,
1095 "The `goto case' value is not implicitly convertible to type `{0}'",
1096 TypeManager
.CSharpName (type
));
1098 object val
= res
.GetValue ();
1100 val
= SwitchLabel
.NullStringCase
;
1102 sl
= (SwitchLabel
) ec
.Switch
.Elements
[val
];
1105 FlowBranchingBlock
.Error_UnknownLabel (loc
, "case " +
1106 (c
.GetValue () == null ? "null" : val
.ToString ()));
1113 protected override void DoEmit (EmitContext ec
)
1115 ec
.ig
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
1118 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1120 expr
.MutateHoistedGenericType (storey
);
1123 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1125 GotoCase target
= (GotoCase
) t
;
1127 target
.expr
= expr
.Clone (clonectx
);
1131 public class Throw
: Statement
{
1134 public Throw (Expression expr
, Location l
)
1140 public override bool Resolve (EmitContext ec
)
1143 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1144 return ec
.CurrentBranching
.CheckRethrow (loc
);
1147 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
1148 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1153 if (Convert
.ImplicitConversionExists (ec
, expr
, TypeManager
.exception_type
))
1154 expr
= Convert
.ImplicitConversion (ec
, expr
, TypeManager
.exception_type
, loc
);
1156 Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
1161 protected override void DoEmit (EmitContext ec
)
1164 ec
.ig
.Emit (OpCodes
.Rethrow
);
1168 ec
.ig
.Emit (OpCodes
.Throw
);
1172 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1175 expr
.MutateHoistedGenericType (storey
);
1178 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1180 Throw target
= (Throw
) t
;
1183 target
.expr
= expr
.Clone (clonectx
);
1187 public class Break
: Statement
{
1189 public Break (Location l
)
1194 bool unwind_protect
;
1196 public override bool Resolve (EmitContext ec
)
1198 int errors
= Report
.Errors
;
1199 unwind_protect
= ec
.CurrentBranching
.AddBreakOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1200 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1201 return errors
== Report
.Errors
;
1204 protected override void DoEmit (EmitContext ec
)
1206 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopEnd
);
1209 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1213 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1219 public class Continue
: Statement
{
1221 public Continue (Location l
)
1226 bool unwind_protect
;
1228 public override bool Resolve (EmitContext ec
)
1230 int errors
= Report
.Errors
;
1231 unwind_protect
= ec
.CurrentBranching
.AddContinueOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1232 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1233 return errors
== Report
.Errors
;
1236 protected override void DoEmit (EmitContext ec
)
1238 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopBegin
);
1241 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1245 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1251 public interface ILocalVariable
1253 void Emit (EmitContext ec
);
1254 void EmitAssign (EmitContext ec
);
1255 void EmitAddressOf (EmitContext ec
);
1258 public interface IKnownVariable
{
1259 Block Block { get; }
1260 Location Location { get; }
1264 // The information about a user-perceived local variable
1266 public class LocalInfo
: IKnownVariable
, ILocalVariable
{
1267 public readonly FullNamedExpression Type
;
1269 public Type VariableType
;
1270 public readonly string Name
;
1271 public readonly Location Location
;
1272 public readonly Block Block
;
1274 public VariableInfo VariableInfo
;
1275 public HoistedVariable HoistedVariableReference
;
1284 CompilerGenerated
= 64,
1288 public enum ReadOnlyContext
: byte {
1295 ReadOnlyContext ro_context
;
1296 LocalBuilder builder
;
1298 public LocalInfo (FullNamedExpression type
, string name
, Block block
, Location l
)
1306 public LocalInfo (DeclSpace ds
, Block block
, Location l
)
1308 VariableType
= ds
.IsGeneric
? ds
.CurrentType
: ds
.TypeBuilder
;
1313 public void ResolveVariable (EmitContext ec
)
1315 if (HoistedVariableReference
!= null)
1318 if (builder
== null) {
1321 // This is needed to compile on both .NET 1.x and .NET 2.x
1322 // the later introduced `DeclareLocal (Type t, bool pinned)'
1324 builder
= TypeManager
.DeclareLocalPinned (ec
.ig
, VariableType
);
1326 builder
= ec
.ig
.DeclareLocal (TypeManager
.TypeToReflectionType (VariableType
));
1330 public void Emit (EmitContext ec
)
1332 ec
.ig
.Emit (OpCodes
.Ldloc
, builder
);
1335 public void EmitAssign (EmitContext ec
)
1337 ec
.ig
.Emit (OpCodes
.Stloc
, builder
);
1340 public void EmitAddressOf (EmitContext ec
)
1342 ec
.ig
.Emit (OpCodes
.Ldloca
, builder
);
1345 public void EmitSymbolInfo (EmitContext ec
)
1347 if (builder
!= null)
1348 ec
.DefineLocalVariable (Name
, builder
);
1351 public bool IsThisAssigned (EmitContext ec
)
1353 if (VariableInfo
== null)
1354 throw new Exception ();
1356 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1359 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
.CurrentBranching
, VariableInfo
, ec
.loc
);
1362 public bool IsAssigned (EmitContext ec
)
1364 if (VariableInfo
== null)
1365 throw new Exception ();
1367 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1370 public bool Resolve (EmitContext ec
)
1372 if (VariableType
!= null)
1375 TypeExpr texpr
= Type
.ResolveAsContextualType (ec
, false);
1379 VariableType
= texpr
.Type
;
1381 if (TypeManager
.IsGenericParameter (VariableType
))
1384 if (VariableType
.IsAbstract
&& VariableType
.IsSealed
) {
1385 FieldBase
.Error_VariableOfStaticClass (Location
, Name
, VariableType
);
1389 if (VariableType
.IsPointer
&& !ec
.InUnsafe
)
1390 Expression
.UnsafeError (Location
);
1395 public bool IsConstant
{
1396 get { return (flags & Flags.IsConstant) != 0; }
1397 set { flags |= Flags.IsConstant; }
1400 public bool AddressTaken
{
1401 get { return (flags & Flags.AddressTaken) != 0; }
1402 set { flags |= Flags.AddressTaken; }
1405 public bool CompilerGenerated
{
1406 get { return (flags & Flags.CompilerGenerated) != 0; }
1407 set { flags |= Flags.CompilerGenerated; }
1410 public override string ToString ()
1412 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1413 Name
, Type
, VariableInfo
, Location
);
1417 get { return (flags & Flags.Used) != 0; }
1418 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1421 public bool ReadOnly
{
1422 get { return (flags & Flags.ReadOnly) != 0; }
1425 public void SetReadOnlyContext (ReadOnlyContext context
)
1427 flags
|= Flags
.ReadOnly
;
1428 ro_context
= context
;
1431 public string GetReadOnlyContext ()
1434 throw new InternalErrorException ("Variable is not readonly");
1436 switch (ro_context
) {
1437 case ReadOnlyContext
.Fixed
:
1438 return "fixed variable";
1439 case ReadOnlyContext
.Foreach
:
1440 return "foreach iteration variable";
1441 case ReadOnlyContext
.Using
:
1442 return "using variable";
1444 throw new NotImplementedException ();
1448 // Whether the variable is pinned, if Pinned the variable has been
1449 // allocated in a pinned slot with DeclareLocal.
1451 public bool Pinned
{
1452 get { return (flags & Flags.Pinned) != 0; }
1453 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1456 public bool IsThis
{
1457 get { return (flags & Flags.IsThis) != 0; }
1458 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1461 Block IKnownVariable
.Block
{
1462 get { return Block; }
1465 Location IKnownVariable
.Location
{
1466 get { return Location; }
1469 public LocalInfo
Clone (CloneContext clonectx
)
1472 // Variables in anonymous block are not resolved yet
1474 if (VariableType
== null)
1475 return new LocalInfo ((FullNamedExpression
) Type
.Clone (clonectx
), Name
, clonectx
.LookupBlock (Block
), Location
);
1478 // Variables in method block are resolved
1480 LocalInfo li
= new LocalInfo (null, Name
, clonectx
.LookupBlock (Block
), Location
);
1481 li
.VariableType
= VariableType
;
1487 /// Block represents a C# block.
1491 /// This class is used in a number of places: either to represent
1492 /// explicit blocks that the programmer places or implicit blocks.
1494 /// Implicit blocks are used as labels or to introduce variable
1497 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1498 /// they contain extra information that is not necessary on normal blocks.
1500 public class Block
: Statement
{
1501 public Block Parent
;
1502 public Location StartLocation
;
1503 public Location EndLocation
= Location
.Null
;
1505 public ExplicitBlock Explicit
;
1506 public ToplevelBlock Toplevel
; // TODO: Use Explicit
1509 public enum Flags
: byte {
1512 VariablesInitialized
= 4,
1516 HasCapturedVariable
= 64,
1517 HasCapturedThis
= 128
1519 protected Flags flags
;
1521 public bool Unchecked
{
1522 get { return (flags & Flags.Unchecked) != 0; }
1523 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1526 public bool Unsafe
{
1527 get { return (flags & Flags.Unsafe) != 0; }
1528 set { flags |= Flags.Unsafe; }
1532 // The statements in this block
1534 protected ArrayList statements
;
1537 // An array of Blocks. We keep track of children just
1538 // to generate the local variable declarations.
1540 // Statements and child statements are handled through the
1546 // Labels. (label, block) pairs.
1548 protected HybridDictionary labels
;
1551 // Keeps track of (name, type) pairs
1553 IDictionary variables
;
1556 // Keeps track of constants
1557 HybridDictionary constants
;
1560 // Temporary variables.
1562 ArrayList temporary_variables
;
1565 // If this is a switch section, the enclosing switch block.
1569 protected ArrayList scope_initializers
;
1571 ArrayList anonymous_children
;
1573 protected static int id
;
1577 int assignable_slots
;
1578 bool unreachable_shown
;
1581 public Block (Block parent
)
1582 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1585 public Block (Block parent
, Flags flags
)
1586 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1589 public Block (Block parent
, Location start
, Location end
)
1590 : this (parent
, (Flags
) 0, start
, end
)
1594 // Useful when TopLevel block is downgraded to normal block
1596 public Block (ToplevelBlock parent
, ToplevelBlock source
)
1597 : this (parent
, source
.flags
, source
.StartLocation
, source
.EndLocation
)
1599 statements
= source
.statements
;
1600 children
= source
.children
;
1601 labels
= source
.labels
;
1602 variables
= source
.variables
;
1603 constants
= source
.constants
;
1604 switch_block
= source
.switch_block
;
1607 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1609 if (parent
!= null) {
1610 parent
.AddChild (this);
1612 // the appropriate constructors will fixup these fields
1613 Toplevel
= parent
.Toplevel
;
1614 Explicit
= parent
.Explicit
;
1617 this.Parent
= parent
;
1619 this.StartLocation
= start
;
1620 this.EndLocation
= end
;
1623 statements
= new ArrayList (4);
1626 public Block
CreateSwitchBlock (Location start
)
1628 // FIXME: should this be implicit?
1629 Block new_block
= new ExplicitBlock (this, start
, start
);
1630 new_block
.switch_block
= this;
1635 get { return this_id; }
1638 public IDictionary Variables
{
1640 if (variables
== null)
1641 variables
= new ListDictionary ();
1646 void AddChild (Block b
)
1648 if (children
== null)
1649 children
= new ArrayList (1);
1654 public void SetEndLocation (Location loc
)
1659 protected static void Error_158 (string name
, Location loc
)
1661 Report
.Error (158, loc
, "The label `{0}' shadows another label " +
1662 "by the same name in a contained scope", name
);
1666 /// Adds a label to the current block.
1670 /// false if the name already exists in this block. true
1674 public bool AddLabel (LabeledStatement target
)
1676 if (switch_block
!= null)
1677 return switch_block
.AddLabel (target
);
1679 string name
= target
.Name
;
1682 while (cur
!= null) {
1683 LabeledStatement s
= cur
.DoLookupLabel (name
);
1685 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1686 Report
.Error (140, target
.loc
, "The label `{0}' is a duplicate", name
);
1690 if (this == Explicit
)
1696 while (cur
!= null) {
1697 if (cur
.DoLookupLabel (name
) != null) {
1698 Error_158 (name
, target
.loc
);
1702 if (children
!= null) {
1703 foreach (Block b
in children
) {
1704 LabeledStatement s
= b
.DoLookupLabel (name
);
1708 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1709 Error_158 (name
, target
.loc
);
1717 Toplevel
.CheckError158 (name
, target
.loc
);
1720 labels
= new HybridDictionary();
1722 labels
.Add (name
, target
);
1726 public LabeledStatement
LookupLabel (string name
)
1728 LabeledStatement s
= DoLookupLabel (name
);
1732 if (children
== null)
1735 foreach (Block child
in children
) {
1736 if (Explicit
!= child
.Explicit
)
1739 s
= child
.LookupLabel (name
);
1747 LabeledStatement
DoLookupLabel (string name
)
1749 if (switch_block
!= null)
1750 return switch_block
.LookupLabel (name
);
1753 if (labels
.Contains (name
))
1754 return ((LabeledStatement
) labels
[name
]);
1759 public bool CheckInvariantMeaningInBlock (string name
, Expression e
, Location loc
)
1762 IKnownVariable kvi
= b
.Explicit
.GetKnownVariable (name
);
1763 while (kvi
== null) {
1764 b
= b
.Explicit
.Parent
;
1767 kvi
= b
.Explicit
.GetKnownVariable (name
);
1773 // Is kvi.Block nested inside 'b'
1774 if (b
.Explicit
!= kvi
.Block
.Explicit
) {
1776 // If a variable by the same name it defined in a nested block of this
1777 // block, we violate the invariant meaning in a block.
1780 Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1781 Report
.Error (135, loc
, "`{0}' conflicts with a declaration in a child block", name
);
1786 // It's ok if the definition is in a nested subblock of b, but not
1787 // nested inside this block -- a definition in a sibling block
1788 // should not affect us.
1794 // Block 'b' and kvi.Block are the same textual block.
1795 // However, different variables are extant.
1797 // Check if the variable is in scope in both blocks. We use
1798 // an indirect check that depends on AddVariable doing its
1799 // part in maintaining the invariant-meaning-in-block property.
1801 if (e
is VariableReference
|| (e
is Constant
&& b
.GetLocalInfo (name
) != null))
1804 if (this is ToplevelBlock
) {
1805 Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1806 e
.Error_VariableIsUsedBeforeItIsDeclared (name
);
1811 // Even though we detected the error when the name is used, we
1812 // treat it as if the variable declaration was in error.
1814 Report
.SymbolRelatedToPreviousError (loc
, name
);
1815 Error_AlreadyDeclared (kvi
.Location
, name
, "parent or current");
1819 protected virtual bool CheckParentConflictName (ToplevelBlock block
, string name
, Location l
)
1821 LocalInfo vi
= GetLocalInfo (name
);
1823 Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1824 if (Explicit
== vi
.Block
.Explicit
) {
1825 Error_AlreadyDeclared (l
, name
, null);
1827 Error_AlreadyDeclared (l
, name
, this is ToplevelBlock
?
1828 "parent or current" : "parent");
1833 if (block
!= null) {
1834 Expression e
= block
.GetParameterReference (name
, Location
.Null
);
1836 ParameterReference pr
= e
as ParameterReference
;
1837 if (this is Linq
.QueryBlock
&& (pr
!= null && pr
.Parameter
is Linq
.QueryBlock
.ImplicitQueryParameter
|| e
is MemberAccess
))
1838 Error_AlreadyDeclared (loc
, name
);
1840 Error_AlreadyDeclared (loc
, name
, "parent or current");
1848 public LocalInfo
AddVariable (Expression type
, string name
, Location l
)
1850 if (!CheckParentConflictName (Toplevel
, name
, l
))
1853 if (Toplevel
.GenericMethod
!= null) {
1854 foreach (TypeParameter tp
in Toplevel
.GenericMethod
.CurrentTypeParameters
) {
1855 if (tp
.Name
== name
) {
1856 Report
.SymbolRelatedToPreviousError (tp
);
1857 Error_AlreadyDeclaredTypeParameter (loc
, name
, "local variable");
1863 IKnownVariable kvi
= Explicit
.GetKnownVariable (name
);
1865 Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1866 Error_AlreadyDeclared (l
, name
, "child");
1870 LocalInfo vi
= new LocalInfo ((FullNamedExpression
) type
, name
, this, l
);
1873 if ((flags
& Flags
.VariablesInitialized
) != 0)
1874 throw new InternalErrorException ("block has already been resolved");
1879 protected virtual void AddVariable (LocalInfo li
)
1881 Variables
.Add (li
.Name
, li
);
1882 Explicit
.AddKnownVariable (li
.Name
, li
);
1885 protected virtual void Error_AlreadyDeclared (Location loc
, string var, string reason
)
1887 if (reason
== null) {
1888 Error_AlreadyDeclared (loc
, var);
1892 Report
.Error (136, loc
, "A local variable named `{0}' cannot be declared " +
1893 "in this scope because it would give a different meaning " +
1894 "to `{0}', which is already used in a `{1}' scope " +
1895 "to denote something else", var, reason
);
1898 protected virtual void Error_AlreadyDeclared (Location loc
, string name
)
1900 Report
.Error (128, loc
,
1901 "A local variable named `{0}' is already defined in this scope", name
);
1904 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc
, string name
, string conflict
)
1906 Report
.Error (412, loc
, "The type parameter name `{0}' is the same as `{1}'",
1910 public bool AddConstant (Expression type
, string name
, Expression
value, Location l
)
1912 if (AddVariable (type
, name
, l
) == null)
1915 if (constants
== null)
1916 constants
= new HybridDictionary();
1918 constants
.Add (name
, value);
1920 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1925 static int next_temp_id
= 0;
1927 public LocalInfo
AddTemporaryVariable (TypeExpr te
, Location loc
)
1929 Report
.Debug (64, "ADD TEMPORARY", this, Toplevel
, loc
);
1931 if (temporary_variables
== null)
1932 temporary_variables
= new ArrayList ();
1934 int id
= ++next_temp_id
;
1935 string name
= "$s_" + id
.ToString ();
1937 LocalInfo li
= new LocalInfo (te
, name
, this, loc
);
1938 li
.CompilerGenerated
= true;
1939 temporary_variables
.Add (li
);
1943 public LocalInfo
GetLocalInfo (string name
)
1946 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1947 if (b
.variables
!= null) {
1948 ret
= (LocalInfo
) b
.variables
[name
];
1957 public Expression
GetVariableType (string name
)
1959 LocalInfo vi
= GetLocalInfo (name
);
1960 return vi
== null ? null : vi
.Type
;
1963 public Expression
GetConstantExpression (string name
)
1965 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1966 if (b
.constants
!= null) {
1967 Expression ret
= b
.constants
[name
] as Expression
;
1976 // It should be used by expressions which require to
1977 // register a statement during resolve process.
1979 public void AddScopeStatement (Statement s
)
1981 if (scope_initializers
== null)
1982 scope_initializers
= new ArrayList ();
1984 scope_initializers
.Add (s
);
1987 public void AddStatement (Statement s
)
1990 flags
|= Flags
.BlockUsed
;
1994 get { return (flags & Flags.BlockUsed) != 0; }
1999 flags
|= Flags
.BlockUsed
;
2002 public bool HasRet
{
2003 get { return (flags & Flags.HasRet) != 0; }
2006 public int AssignableSlots
{
2009 // if ((flags & Flags.VariablesInitialized) == 0)
2010 // throw new Exception ("Variables have not been initialized yet");
2011 return assignable_slots
;
2015 public ArrayList AnonymousChildren
{
2016 get { return anonymous_children; }
2019 public void AddAnonymousChild (ToplevelBlock b
)
2021 if (anonymous_children
== null)
2022 anonymous_children
= new ArrayList ();
2024 anonymous_children
.Add (b
);
2027 void DoResolveConstants (EmitContext ec
)
2029 if (constants
== null)
2032 if (variables
== null)
2033 throw new InternalErrorException ("cannot happen");
2035 foreach (DictionaryEntry de
in variables
) {
2036 string name
= (string) de
.Key
;
2037 LocalInfo vi
= (LocalInfo
) de
.Value
;
2038 Type variable_type
= vi
.VariableType
;
2040 if (variable_type
== null) {
2041 if (vi
.Type
is VarExpr
)
2042 Report
.Error (822, vi
.Type
.Location
, "An implicitly typed local variable cannot be a constant");
2047 Expression cv
= (Expression
) constants
[name
];
2051 // Don't let 'const int Foo = Foo;' succeed.
2052 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2053 // which in turn causes the 'must be constant' error to be triggered.
2054 constants
.Remove (name
);
2056 if (!Const
.IsConstantTypeValid (variable_type
)) {
2057 Const
.Error_InvalidConstantType (variable_type
, loc
);
2061 ec
.CurrentBlock
= this;
2063 using (ec
.With (EmitContext
.Flags
.ConstantCheckState
, (flags
& Flags
.Unchecked
) == 0)) {
2064 e
= cv
.Resolve (ec
);
2069 Constant ce
= e
as Constant
;
2071 Const
.Error_ExpressionMustBeConstant (vi
.Location
, name
);
2075 e
= ce
.ConvertImplicitly (variable_type
);
2077 if (TypeManager
.IsReferenceType (variable_type
))
2078 Const
.Error_ConstantCanBeInitializedWithNullOnly (variable_type
, vi
.Location
, vi
.Name
);
2080 ce
.Error_ValueCannotBeConverted (ec
, vi
.Location
, variable_type
, false);
2084 constants
.Add (name
, e
);
2085 vi
.IsConstant
= true;
2089 protected void ResolveMeta (EmitContext ec
, int offset
)
2091 Report
.Debug (64, "BLOCK RESOLVE META", this, Parent
);
2093 // If some parent block was unsafe, we remain unsafe even if this block
2094 // isn't explicitly marked as such.
2095 using (ec
.With (EmitContext
.Flags
.InUnsafe
, ec
.InUnsafe
| Unsafe
)) {
2096 flags
|= Flags
.VariablesInitialized
;
2098 if (variables
!= null) {
2099 foreach (LocalInfo li
in variables
.Values
) {
2100 if (!li
.Resolve (ec
))
2102 li
.VariableInfo
= new VariableInfo (li
, offset
);
2103 offset
+= li
.VariableInfo
.Length
;
2106 assignable_slots
= offset
;
2108 DoResolveConstants (ec
);
2110 if (children
== null)
2112 foreach (Block b
in children
)
2113 b
.ResolveMeta (ec
, offset
);
2118 // Emits the local variable declarations for a block
2120 public virtual void EmitMeta (EmitContext ec
)
2122 if (variables
!= null){
2123 foreach (LocalInfo vi
in variables
.Values
)
2124 vi
.ResolveVariable (ec
);
2127 if (temporary_variables
!= null) {
2128 for (int i
= 0; i
< temporary_variables
.Count
; i
++)
2129 ((LocalInfo
)temporary_variables
[i
]).ResolveVariable(ec
);
2132 if (children
!= null) {
2133 for (int i
= 0; i
< children
.Count
; i
++)
2134 ((Block
)children
[i
]).EmitMeta(ec
);
2138 void UsageWarning ()
2140 if (variables
== null || Report
.WarningLevel
< 3)
2143 foreach (DictionaryEntry de
in variables
) {
2144 LocalInfo vi
= (LocalInfo
) de
.Value
;
2147 string name
= (string) de
.Key
;
2149 // vi.VariableInfo can be null for 'catch' variables
2150 if (vi
.VariableInfo
!= null && vi
.VariableInfo
.IsEverAssigned
)
2151 Report
.Warning (219, 3, vi
.Location
, "The variable `{0}' is assigned but its value is never used", name
);
2153 Report
.Warning (168, 3, vi
.Location
, "The variable `{0}' is declared but never used", name
);
2158 static void CheckPossibleMistakenEmptyStatement (Statement s
)
2162 // Some statements are wrapped by a Block. Since
2163 // others' internal could be changed, here I treat
2164 // them as possibly wrapped by Block equally.
2165 Block b
= s
as Block
;
2166 if (b
!= null && b
.statements
.Count
== 1)
2167 s
= (Statement
) b
.statements
[0];
2170 body
= ((Lock
) s
).Statement
;
2172 body
= ((For
) s
).Statement
;
2173 else if (s
is Foreach
)
2174 body
= ((Foreach
) s
).Statement
;
2175 else if (s
is While
)
2176 body
= ((While
) s
).Statement
;
2177 else if (s
is Fixed
)
2178 body
= ((Fixed
) s
).Statement
;
2179 else if (s
is Using
)
2180 body
= ((Using
) s
).EmbeddedStatement
;
2181 else if (s
is UsingTemporary
)
2182 body
= ((UsingTemporary
) s
).Statement
;
2186 if (body
== null || body
is EmptyStatement
)
2187 Report
.Warning (642, 3, s
.loc
, "Possible mistaken empty statement");
2190 public override bool Resolve (EmitContext ec
)
2192 Block prev_block
= ec
.CurrentBlock
;
2195 int errors
= Report
.Errors
;
2197 ec
.CurrentBlock
= this;
2198 ec
.StartFlowBranching (this);
2200 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
2203 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2204 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2205 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2206 // responsible for handling the situation.
2208 int statement_count
= statements
.Count
;
2209 for (int ix
= 0; ix
< statement_count
; ix
++){
2210 Statement s
= (Statement
) statements
[ix
];
2211 // Check possible empty statement (CS0642)
2212 if (Report
.WarningLevel
>= 3 &&
2213 ix
+ 1 < statement_count
&&
2214 statements
[ix
+ 1] is ExplicitBlock
)
2215 CheckPossibleMistakenEmptyStatement (s
);
2218 // Warn if we detect unreachable code.
2221 if (s
is EmptyStatement
)
2224 if (!unreachable_shown
&& !(s
is LabeledStatement
)) {
2225 Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
2226 unreachable_shown
= true;
2229 Block c_block
= s
as Block
;
2230 if (c_block
!= null)
2231 c_block
.unreachable
= c_block
.unreachable_shown
= true;
2235 // Note that we're not using ResolveUnreachable() for unreachable
2236 // statements here. ResolveUnreachable() creates a temporary
2237 // flow branching and kills it afterwards. This leads to problems
2238 // if you have two unreachable statements where the first one
2239 // assigns a variable and the second one tries to access it.
2242 if (!s
.Resolve (ec
)) {
2244 if (ec
.IsInProbingMode
)
2247 statements
[ix
] = EmptyStatement
.Value
;
2251 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
2252 statements
[ix
] = EmptyStatement
.Value
;
2254 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
2255 if (unreachable
&& s
is LabeledStatement
)
2256 throw new InternalErrorException ("should not happen");
2259 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
2260 ec
.CurrentBranching
, statement_count
);
2262 while (ec
.CurrentBranching
is FlowBranchingLabeled
)
2263 ec
.EndFlowBranching ();
2265 bool flow_unreachable
= ec
.EndFlowBranching ();
2267 ec
.CurrentBlock
= prev_block
;
2269 if (flow_unreachable
)
2270 flags
|= Flags
.HasRet
;
2272 // If we're a non-static `struct' constructor which doesn't have an
2273 // initializer, then we must initialize all of the struct's fields.
2274 if (this == Toplevel
&& !Toplevel
.IsThisAssigned (ec
) && !flow_unreachable
)
2277 if ((labels
!= null) && (Report
.WarningLevel
>= 2)) {
2278 foreach (LabeledStatement label
in labels
.Values
)
2279 if (!label
.HasBeenReferenced
)
2280 Report
.Warning (164, 2, label
.loc
, "This label has not been referenced");
2283 if (ok
&& errors
== Report
.Errors
)
2289 public override bool ResolveUnreachable (EmitContext ec
, bool warn
)
2291 unreachable_shown
= true;
2295 Report
.Warning (162, 2, loc
, "Unreachable code detected");
2297 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2298 bool ok
= Resolve (ec
);
2299 ec
.KillFlowBranching ();
2304 protected override void DoEmit (EmitContext ec
)
2306 for (int ix
= 0; ix
< statements
.Count
; ix
++){
2307 Statement s
= (Statement
) statements
[ix
];
2312 public override void Emit (EmitContext ec
)
2314 Block prev_block
= ec
.CurrentBlock
;
2315 ec
.CurrentBlock
= this;
2317 if (scope_initializers
!= null)
2318 EmitScopeInitializers (ec
);
2320 ec
.Mark (StartLocation
);
2323 if (SymbolWriter
.HasSymbolWriter
)
2324 EmitSymbolInfo (ec
);
2326 ec
.CurrentBlock
= prev_block
;
2329 protected void EmitScopeInitializers (EmitContext ec
)
2331 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
2333 using (ec
.Set (EmitContext
.Flags
.OmitDebuggingInfo
)) {
2334 foreach (Statement s
in scope_initializers
)
2338 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
2341 protected virtual void EmitSymbolInfo (EmitContext ec
)
2343 if (variables
!= null) {
2344 foreach (LocalInfo vi
in variables
.Values
) {
2345 vi
.EmitSymbolInfo (ec
);
2350 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2352 MutateVariables (storey
);
2354 if (scope_initializers
!= null) {
2355 foreach (Statement s
in scope_initializers
)
2356 s
.MutateHoistedGenericType (storey
);
2359 foreach (Statement s
in statements
)
2360 s
.MutateHoistedGenericType (storey
);
2363 void MutateVariables (AnonymousMethodStorey storey
)
2365 if (variables
!= null) {
2366 foreach (LocalInfo vi
in variables
.Values
) {
2367 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2371 if (temporary_variables
!= null) {
2372 foreach (LocalInfo vi
in temporary_variables
)
2373 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2377 public override string ToString ()
2379 return String
.Format ("{0} ({1}:{2})", GetType (),ID
, StartLocation
);
2382 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2384 Block target
= (Block
) t
;
2386 clonectx
.AddBlockMap (this, target
);
2388 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2389 target
.Explicit
= (ExplicitBlock
) clonectx
.LookupBlock (Explicit
);
2391 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
2393 if (variables
!= null){
2394 target
.variables
= new Hashtable ();
2396 foreach (DictionaryEntry de
in variables
){
2397 LocalInfo newlocal
= ((LocalInfo
) de
.Value
).Clone (clonectx
);
2398 target
.variables
[de
.Key
] = newlocal
;
2399 clonectx
.AddVariableMap ((LocalInfo
) de
.Value
, newlocal
);
2403 target
.statements
= new ArrayList (statements
.Count
);
2404 foreach (Statement s
in statements
)
2405 target
.statements
.Add (s
.Clone (clonectx
));
2407 if (target
.children
!= null){
2408 target
.children
= new ArrayList (children
.Count
);
2409 foreach (Block b
in children
){
2410 target
.children
.Add (clonectx
.LookupBlock (b
));
2415 // TODO: labels, switch_block, constants (?), anonymous_children
2420 public class ExplicitBlock
: Block
{
2421 HybridDictionary known_variables
;
2422 protected AnonymousMethodStorey am_storey
;
2424 public ExplicitBlock (Block parent
, Location start
, Location end
)
2425 : this (parent
, (Flags
) 0, start
, end
)
2429 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
2430 : base (parent
, flags
, start
, end
)
2432 this.Explicit
= this;
2436 // Marks a variable with name @name as being used in this or a child block.
2437 // If a variable name has been used in a child block, it's illegal to
2438 // declare a variable with the same name in the current block.
2440 internal void AddKnownVariable (string name
, IKnownVariable info
)
2442 if (known_variables
== null)
2443 known_variables
= new HybridDictionary();
2445 known_variables
[name
] = info
;
2448 Parent
.Explicit
.AddKnownVariable (name
, info
);
2451 public AnonymousMethodStorey AnonymousMethodStorey
{
2452 get { return am_storey; }
2456 // Creates anonymous method storey in current block
2458 public AnonymousMethodStorey
CreateAnonymousMethodStorey (EmitContext ec
)
2461 // When referencing a variable in iterator storey from children anonymous method
2463 if (Toplevel
.am_storey
is IteratorStorey
) {
2464 return Toplevel
.am_storey
;
2468 // An iterator has only 1 storey block
2470 if (ec
.CurrentIterator
!= null)
2471 return ec
.CurrentIterator
.Storey
;
2473 if (am_storey
== null) {
2474 MemberBase mc
= ec
.ResolveContext
as MemberBase
;
2475 GenericMethod gm
= mc
== null ? null : mc
.GenericMethod
;
2478 // Creates anonymous method storey for this block
2480 am_storey
= new AnonymousMethodStorey (this, ec
.TypeContainer
, mc
, gm
, "AnonStorey");
2486 public override void Emit (EmitContext ec
)
2488 if (am_storey
!= null)
2489 am_storey
.EmitStoreyInstantiation (ec
);
2491 bool emit_debug_info
= SymbolWriter
.HasSymbolWriter
&& Parent
!= null && !(am_storey
is IteratorStorey
);
2492 if (emit_debug_info
)
2497 if (emit_debug_info
)
2501 public override void EmitMeta (EmitContext ec
)
2504 // Creates anonymous method storey
2506 if (am_storey
!= null) {
2507 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
2509 // Creates parent storey reference when hoisted this is accessible
2511 if (am_storey
.OriginalSourceBlock
.Explicit
.HasCapturedThis
) {
2512 ExplicitBlock parent
= Toplevel
.Parent
.Explicit
;
2515 // Hoisted this exists in top-level parent storey only
2517 while (parent
.am_storey
== null || parent
.am_storey
.Parent
is AnonymousMethodStorey
)
2518 parent
= parent
.Parent
.Explicit
;
2520 am_storey
.AddParentStoreyReference (parent
.am_storey
);
2523 am_storey
.ChangeParentStorey (ec
.CurrentAnonymousMethod
.Storey
);
2526 am_storey
.DefineType ();
2527 am_storey
.ResolveType ();
2528 am_storey
.Define ();
2529 am_storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (am_storey
);
2531 ArrayList ref_blocks
= am_storey
.ReferencesFromChildrenBlock
;
2532 if (ref_blocks
!= null) {
2533 foreach (ExplicitBlock ref_block
in ref_blocks
) {
2534 for (ExplicitBlock b
= ref_block
.Explicit
; b
!= this; b
= b
.Parent
.Explicit
) {
2535 if (b
.am_storey
!= null) {
2536 b
.am_storey
.AddParentStoreyReference (am_storey
);
2538 // Stop propagation inside same top block
2539 if (b
.Toplevel
== Toplevel
)
2544 b
.HasCapturedVariable
= true;
2553 internal IKnownVariable
GetKnownVariable (string name
)
2555 return known_variables
== null ? null : (IKnownVariable
) known_variables
[name
];
2558 public bool HasCapturedThis
2560 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2561 get { return (flags & Flags.HasCapturedThis) != 0; }
2564 public bool HasCapturedVariable
2566 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2567 get { return (flags & Flags.HasCapturedVariable) != 0; }
2570 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2572 ExplicitBlock target
= (ExplicitBlock
) t
;
2573 target
.known_variables
= null;
2574 base.CloneTo (clonectx
, t
);
2578 public class ToplevelParameterInfo
: IKnownVariable
{
2579 public readonly ToplevelBlock Block
;
2580 public readonly int Index
;
2581 public VariableInfo VariableInfo
;
2583 Block IKnownVariable
.Block
{
2584 get { return Block; }
2586 public Parameter Parameter
{
2587 get { return Block.Parameters [Index]; }
2590 public Type ParameterType
{
2591 get { return Block.Parameters.Types [Index]; }
2594 public Location Location
{
2595 get { return Parameter.Location; }
2598 public ToplevelParameterInfo (ToplevelBlock block
, int idx
)
2606 // A toplevel block contains extra information, the split is done
2607 // only to separate information that would otherwise bloat the more
2608 // lightweight Block.
2610 // In particular, this was introduced when the support for Anonymous
2611 // Methods was implemented.
2613 public class ToplevelBlock
: ExplicitBlock
2616 // Block is converted to an expression
2618 sealed class BlockScopeExpression
: Expression
2621 readonly ToplevelBlock block
;
2623 public BlockScopeExpression (Expression child
, ToplevelBlock block
)
2629 public override Expression
CreateExpressionTree (EmitContext ec
)
2631 throw new NotSupportedException ();
2634 public override Expression
DoResolve (EmitContext ec
)
2639 block
.ResolveMeta (ec
, ParametersCompiled
.EmptyReadOnlyParameters
);
2640 child
= child
.Resolve (ec
);
2644 eclass
= child
.eclass
;
2649 public override void Emit (EmitContext ec
)
2651 block
.EmitMeta (ec
);
2652 block
.EmitScopeInitializers (ec
);
2656 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2658 type
= storey
.MutateType (type
);
2659 child
.MutateHoistedGenericType (storey
);
2660 block
.MutateHoistedGenericType (storey
);
2664 GenericMethod generic
;
2665 FlowBranchingToplevel top_level_branching
;
2666 protected ParametersCompiled parameters
;
2667 ToplevelParameterInfo
[] parameter_info
;
2668 LocalInfo this_variable
;
2670 public HoistedVariable HoistedThisVariable
;
2673 // The parameters for the block.
2675 public ParametersCompiled Parameters
{
2676 get { return parameters; }
2679 public GenericMethod GenericMethod
{
2680 get { return generic; }
2683 public ToplevelBlock Container
{
2684 get { return Parent == null ? null : Parent.Toplevel; }
2687 public ToplevelBlock (Block parent
, ParametersCompiled parameters
, Location start
) :
2688 this (parent
, (Flags
) 0, parameters
, start
)
2692 public ToplevelBlock (Block parent
, ParametersCompiled parameters
, GenericMethod generic
, Location start
) :
2693 this (parent
, parameters
, start
)
2695 this.generic
= generic
;
2698 public ToplevelBlock (ParametersCompiled parameters
, Location start
) :
2699 this (null, (Flags
) 0, parameters
, start
)
2703 ToplevelBlock (Flags flags
, ParametersCompiled parameters
, Location start
) :
2704 this (null, flags
, parameters
, start
)
2708 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2709 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2710 public ToplevelBlock (Block parent
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2711 base (null, flags
, start
, Location
.Null
)
2713 this.Toplevel
= this;
2715 this.parameters
= parameters
;
2716 this.Parent
= parent
;
2718 parent
.AddAnonymousChild (this);
2720 if (!this.parameters
.IsEmpty
)
2721 ProcessParameters ();
2724 public ToplevelBlock (Location loc
)
2725 : this (null, (Flags
) 0, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
2729 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2731 ToplevelBlock target
= (ToplevelBlock
) t
;
2732 base.CloneTo (clonectx
, t
);
2734 if (parameters
.Count
!= 0)
2735 target
.parameter_info
= new ToplevelParameterInfo
[parameters
.Count
];
2736 for (int i
= 0; i
< parameters
.Count
; ++i
)
2737 target
.parameter_info
[i
] = new ToplevelParameterInfo (target
, i
);
2740 public bool CheckError158 (string name
, Location loc
)
2742 if (AnonymousChildren
!= null) {
2743 foreach (ToplevelBlock child
in AnonymousChildren
) {
2744 if (!child
.CheckError158 (name
, loc
))
2749 for (ToplevelBlock c
= Container
; c
!= null; c
= c
.Container
) {
2750 if (!c
.DoCheckError158 (name
, loc
))
2757 void ProcessParameters ()
2759 int n
= parameters
.Count
;
2760 parameter_info
= new ToplevelParameterInfo
[n
];
2761 ToplevelBlock top_parent
= Parent
== null ? null : Parent
.Toplevel
;
2762 for (int i
= 0; i
< n
; ++i
) {
2763 parameter_info
[i
] = new ToplevelParameterInfo (this, i
);
2765 Parameter p
= parameters
[i
];
2769 string name
= p
.Name
;
2770 if (CheckParentConflictName (top_parent
, name
, loc
))
2771 AddKnownVariable (name
, parameter_info
[i
]);
2774 // mark this block as "used" so that we create local declarations in a sub-block
2775 // FIXME: This appears to uncover a lot of bugs
2779 bool DoCheckError158 (string name
, Location loc
)
2781 LabeledStatement s
= LookupLabel (name
);
2783 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
2784 Error_158 (name
, loc
);
2791 public override Expression
CreateExpressionTree (EmitContext ec
)
2793 if (statements
.Count
== 1) {
2794 Expression expr
= ((Statement
) statements
[0]).CreateExpressionTree (ec
);
2795 if (scope_initializers
!= null)
2796 expr
= new BlockScopeExpression (expr
, this);
2801 return base.CreateExpressionTree (ec
);
2805 // Reformats this block to be top-level iterator block
2807 public IteratorStorey
ChangeToIterator (Iterator iterator
, ToplevelBlock source
)
2811 // Creates block with original statements
2812 AddStatement (new IteratorStatement (iterator
, new Block (this, source
)));
2814 source
.statements
= new ArrayList (1);
2815 source
.AddStatement (new Return (iterator
, iterator
.Location
));
2816 source
.IsIterator
= false;
2818 IteratorStorey iterator_storey
= new IteratorStorey (iterator
);
2819 source
.am_storey
= iterator_storey
;
2820 return iterator_storey
;
2823 public FlowBranchingToplevel TopLevelBranching
{
2824 get { return top_level_branching; }
2828 // Returns a parameter reference expression for the given name,
2829 // or null if there is no such parameter
2831 public Expression
GetParameterReference (string name
, Location loc
)
2833 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2834 Expression expr
= t
.GetParameterReferenceExpression (name
, loc
);
2842 protected virtual Expression
GetParameterReferenceExpression (string name
, Location loc
)
2844 int idx
= parameters
.GetParameterIndexByName (name
);
2846 null : new ParameterReference (parameter_info
[idx
], loc
);
2850 // Returns the "this" instance variable of this block.
2851 // See AddThisVariable() for more information.
2853 public LocalInfo ThisVariable
{
2854 get { return this_variable; }
2858 // This is used by non-static `struct' constructors which do not have an
2859 // initializer - in this case, the constructor must initialize all of the
2860 // struct's fields. To do this, we add a "this" variable and use the flow
2861 // analysis code to ensure that it's been fully initialized before control
2862 // leaves the constructor.
2864 public LocalInfo
AddThisVariable (DeclSpace ds
, Location l
)
2866 if (this_variable
== null) {
2867 this_variable
= new LocalInfo (ds
, this, l
);
2868 this_variable
.Used
= true;
2869 this_variable
.IsThis
= true;
2871 Variables
.Add ("this", this_variable
);
2874 return this_variable
;
2877 public bool IsIterator
{
2878 get { return (flags & Flags.IsIterator) != 0; }
2879 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2882 public bool IsThisAssigned (EmitContext ec
)
2884 return this_variable
== null || this_variable
.IsThisAssigned (ec
);
2887 public bool ResolveMeta (EmitContext ec
, ParametersCompiled ip
)
2889 int errors
= Report
.Errors
;
2890 int orig_count
= parameters
.Count
;
2892 if (top_level_branching
!= null)
2898 // Assert: orig_count != parameter.Count => orig_count == 0
2899 if (orig_count
!= 0 && orig_count
!= parameters
.Count
)
2900 throw new InternalErrorException ("parameter information mismatch");
2902 int offset
= Parent
== null ? 0 : Parent
.AssignableSlots
;
2904 for (int i
= 0; i
< orig_count
; ++i
) {
2905 Parameter
.Modifier mod
= parameters
.FixedParameters
[i
].ModFlags
;
2907 if ((mod
& Parameter
.Modifier
.OUT
) != Parameter
.Modifier
.OUT
)
2910 VariableInfo vi
= new VariableInfo (ip
, i
, offset
);
2911 parameter_info
[i
].VariableInfo
= vi
;
2912 offset
+= vi
.Length
;
2915 ResolveMeta (ec
, offset
);
2917 top_level_branching
= ec
.StartFlowBranching (this);
2919 return Report
.Errors
== errors
;
2923 // Check whether all `out' parameters have been assigned.
2925 public void CheckOutParameters (FlowBranching
.UsageVector vector
, Location loc
)
2927 if (vector
.IsUnreachable
)
2930 int n
= parameter_info
== null ? 0 : parameter_info
.Length
;
2932 for (int i
= 0; i
< n
; i
++) {
2933 VariableInfo
var = parameter_info
[i
].VariableInfo
;
2938 if (vector
.IsAssigned (var, false))
2941 Report
.Error (177, loc
, "The out parameter `{0}' must be assigned to before control leaves the current method",
2946 public override void EmitMeta (EmitContext ec
)
2948 parameters
.ResolveVariable ();
2950 // Avoid declaring an IL variable for this_variable since it is not accessed
2951 // from the generated IL
2952 if (this_variable
!= null)
2953 Variables
.Remove ("this");
2957 protected override void EmitSymbolInfo (EmitContext ec
)
2959 AnonymousExpression ae
= ec
.CurrentAnonymousMethod
;
2960 if ((ae
!= null) && (ae
.Storey
!= null))
2961 SymbolWriter
.DefineScopeVariable (ae
.Storey
.ID
);
2963 base.EmitSymbolInfo (ec
);
2966 public override void Emit (EmitContext ec
)
2969 ec
.Mark (EndLocation
);
2973 public class SwitchLabel
{
2980 Label il_label_code
;
2981 bool il_label_code_set
;
2983 public static readonly object NullStringCase
= new object ();
2986 // if expr == null, then it is the default case.
2988 public SwitchLabel (Expression expr
, Location l
)
2994 public Expression Label
{
3000 public Location Location
{
3004 public object Converted
{
3010 public Label
GetILLabel (EmitContext ec
)
3013 il_label
= ec
.ig
.DefineLabel ();
3014 il_label_set
= true;
3019 public Label
GetILLabelCode (EmitContext ec
)
3021 if (!il_label_code_set
){
3022 il_label_code
= ec
.ig
.DefineLabel ();
3023 il_label_code_set
= true;
3025 return il_label_code
;
3029 // Resolves the expression, reduces it to a literal if possible
3030 // and then converts it to the requested type.
3032 public bool ResolveAndReduce (EmitContext ec
, Type required_type
, bool allow_nullable
)
3034 Expression e
= label
.Resolve (ec
);
3039 Constant c
= e
as Constant
;
3041 Report
.Error (150, loc
, "A constant value is expected");
3045 if (required_type
== TypeManager
.string_type
&& c
.GetValue () == null) {
3046 converted
= NullStringCase
;
3050 if (allow_nullable
&& c
.GetValue () == null) {
3051 converted
= NullStringCase
;
3055 c
= c
.ImplicitConversionRequired (ec
, required_type
, loc
);
3059 converted
= c
.GetValue ();
3063 public void Error_AlreadyOccurs (Type switch_type
, SwitchLabel collision_with
)
3066 if (converted
== null)
3068 else if (converted
== NullStringCase
)
3071 label
= converted
.ToString ();
3073 Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
3074 Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
3077 public SwitchLabel
Clone (CloneContext clonectx
)
3079 return new SwitchLabel (label
.Clone (clonectx
), loc
);
3083 public class SwitchSection
{
3084 // An array of SwitchLabels.
3085 public readonly ArrayList Labels
;
3086 public readonly Block Block
;
3088 public SwitchSection (ArrayList labels
, Block block
)
3094 public SwitchSection
Clone (CloneContext clonectx
)
3096 ArrayList cloned_labels
= new ArrayList ();
3098 foreach (SwitchLabel sl
in cloned_labels
)
3099 cloned_labels
.Add (sl
.Clone (clonectx
));
3101 return new SwitchSection (cloned_labels
, clonectx
.LookupBlock (Block
));
3105 public class Switch
: Statement
{
3106 public ArrayList Sections
;
3107 public Expression Expr
;
3110 /// Maps constants whose type type SwitchType to their SwitchLabels.
3112 public IDictionary Elements
;
3115 /// The governing switch type
3117 public Type SwitchType
;
3122 Label default_target
;
3124 Expression new_expr
;
3127 SwitchSection constant_section
;
3128 SwitchSection default_section
;
3130 ExpressionStatement string_dictionary
;
3131 FieldExpr switch_cache_field
;
3132 static int unique_counter
;
3135 // Nullable Types support
3137 Nullable
.Unwrap unwrap
;
3139 protected bool HaveUnwrap
{
3140 get { return unwrap != null; }
3144 // The types allowed to be implicitly cast from
3145 // on the governing type
3147 static Type
[] allowed_types
;
3149 public Switch (Expression e
, ArrayList sects
, Location l
)
3156 public bool GotDefault
{
3158 return default_section
!= null;
3162 public Label DefaultTarget
{
3164 return default_target
;
3169 // Determines the governing type for a switch. The returned
3170 // expression might be the expression from the switch, or an
3171 // expression that includes any potential conversions to the
3172 // integral types or to string.
3174 Expression
SwitchGoverningType (EmitContext ec
, Expression expr
)
3178 if (t
== TypeManager
.byte_type
||
3179 t
== TypeManager
.sbyte_type
||
3180 t
== TypeManager
.ushort_type
||
3181 t
== TypeManager
.short_type
||
3182 t
== TypeManager
.uint32_type
||
3183 t
== TypeManager
.int32_type
||
3184 t
== TypeManager
.uint64_type
||
3185 t
== TypeManager
.int64_type
||
3186 t
== TypeManager
.char_type
||
3187 t
== TypeManager
.string_type
||
3188 t
== TypeManager
.bool_type
||
3189 TypeManager
.IsEnumType (t
))
3192 if (allowed_types
== null){
3193 allowed_types
= new Type
[] {
3194 TypeManager
.sbyte_type
,
3195 TypeManager
.byte_type
,
3196 TypeManager
.short_type
,
3197 TypeManager
.ushort_type
,
3198 TypeManager
.int32_type
,
3199 TypeManager
.uint32_type
,
3200 TypeManager
.int64_type
,
3201 TypeManager
.uint64_type
,
3202 TypeManager
.char_type
,
3203 TypeManager
.string_type
3208 // Try to find a *user* defined implicit conversion.
3210 // If there is no implicit conversion, or if there are multiple
3211 // conversions, we have to report an error
3213 Expression converted
= null;
3214 foreach (Type tt
in allowed_types
){
3217 e
= Convert
.ImplicitUserConversion (ec
, expr
, tt
, loc
);
3222 // Ignore over-worked ImplicitUserConversions that do
3223 // an implicit conversion in addition to the user conversion.
3225 if (!(e
is UserCast
))
3228 if (converted
!= null){
3229 Report
.ExtraInformation (loc
, "(Ambiguous implicit user defined conversion in previous ");
3239 // Performs the basic sanity checks on the switch statement
3240 // (looks for duplicate keys and non-constant expressions).
3242 // It also returns a hashtable with the keys that we will later
3243 // use to compute the switch tables
3245 bool CheckSwitch (EmitContext ec
)
3248 Elements
= Sections
.Count
> 10 ?
3249 (IDictionary
)new Hashtable () :
3250 (IDictionary
)new ListDictionary ();
3252 foreach (SwitchSection ss
in Sections
){
3253 foreach (SwitchLabel sl
in ss
.Labels
){
3254 if (sl
.Label
== null){
3255 if (default_section
!= null){
3256 sl
.Error_AlreadyOccurs (SwitchType
, (SwitchLabel
)default_section
.Labels
[0]);
3259 default_section
= ss
;
3263 if (!sl
.ResolveAndReduce (ec
, SwitchType
, HaveUnwrap
)) {
3268 object key
= sl
.Converted
;
3269 if (key
== SwitchLabel
.NullStringCase
)
3270 has_null_case
= true;
3273 Elements
.Add (key
, sl
);
3274 } catch (ArgumentException
) {
3275 sl
.Error_AlreadyOccurs (SwitchType
, (SwitchLabel
)Elements
[key
]);
3283 void EmitObjectInteger (ILGenerator ig
, object k
)
3286 IntConstant
.EmitInt (ig
, (int) k
);
3287 else if (k
is Constant
) {
3288 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
3291 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
3294 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
3296 IntConstant
.EmitInt (ig
, (int) (long) k
);
3297 ig
.Emit (OpCodes
.Conv_I8
);
3300 LongConstant
.EmitLong (ig
, (long) k
);
3302 else if (k
is ulong)
3304 ulong ul
= (ulong) k
;
3307 IntConstant
.EmitInt (ig
, unchecked ((int) ul
));
3308 ig
.Emit (OpCodes
.Conv_U8
);
3312 LongConstant
.EmitLong (ig
, unchecked ((long) ul
));
3316 IntConstant
.EmitInt (ig
, (int) ((char) k
));
3317 else if (k
is sbyte)
3318 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
3320 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
3321 else if (k
is short)
3322 IntConstant
.EmitInt (ig
, (int) ((short) k
));
3323 else if (k
is ushort)
3324 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
3326 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
3328 throw new Exception ("Unhandled case");
3331 // structure used to hold blocks of keys while calculating table switch
3332 class KeyBlock
: IComparable
3334 public KeyBlock (long _first
)
3336 first
= last
= _first
;
3340 public ArrayList element_keys
= null;
3341 // how many items are in the bucket
3342 public int Size
= 1;
3345 get { return (int) (last - first + 1); }
3347 public static long TotalLength (KeyBlock kb_first
, KeyBlock kb_last
)
3349 return kb_last
.last
- kb_first
.first
+ 1;
3351 public int CompareTo (object obj
)
3353 KeyBlock kb
= (KeyBlock
) obj
;
3354 int nLength
= Length
;
3355 int nLengthOther
= kb
.Length
;
3356 if (nLengthOther
== nLength
)
3357 return (int) (kb
.first
- first
);
3358 return nLength
- nLengthOther
;
3363 /// This method emits code for a lookup-based switch statement (non-string)
3364 /// Basically it groups the cases into blocks that are at least half full,
3365 /// and then spits out individual lookup opcodes for each block.
3366 /// It emits the longest blocks first, and short blocks are just
3367 /// handled with direct compares.
3369 /// <param name="ec"></param>
3370 /// <param name="val"></param>
3371 /// <returns></returns>
3372 void TableSwitchEmit (EmitContext ec
, Expression val
)
3374 int element_count
= Elements
.Count
;
3375 object [] element_keys
= new object [element_count
];
3376 Elements
.Keys
.CopyTo (element_keys
, 0);
3377 Array
.Sort (element_keys
);
3379 // initialize the block list with one element per key
3380 ArrayList key_blocks
= new ArrayList (element_count
);
3381 foreach (object key
in element_keys
)
3382 key_blocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
3384 KeyBlock current_kb
;
3385 // iteratively merge the blocks while they are at least half full
3386 // there's probably a really cool way to do this with a tree...
3387 while (key_blocks
.Count
> 1)
3389 ArrayList key_blocks_new
= new ArrayList ();
3390 current_kb
= (KeyBlock
) key_blocks
[0];
3391 for (int ikb
= 1; ikb
< key_blocks
.Count
; ikb
++)
3393 KeyBlock kb
= (KeyBlock
) key_blocks
[ikb
];
3394 if ((current_kb
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (current_kb
, kb
))
3397 current_kb
.last
= kb
.last
;
3398 current_kb
.Size
+= kb
.Size
;
3402 // start a new block
3403 key_blocks_new
.Add (current_kb
);
3407 key_blocks_new
.Add (current_kb
);
3408 if (key_blocks
.Count
== key_blocks_new
.Count
)
3410 key_blocks
= key_blocks_new
;
3413 // initialize the key lists
3414 foreach (KeyBlock kb
in key_blocks
)
3415 kb
.element_keys
= new ArrayList ();
3417 // fill the key lists
3419 if (key_blocks
.Count
> 0) {
3420 current_kb
= (KeyBlock
) key_blocks
[0];
3421 foreach (object key
in element_keys
)
3423 bool next_block
= (key
is UInt64
) ? (ulong) key
> (ulong) current_kb
.last
:
3424 System
.Convert
.ToInt64 (key
) > current_kb
.last
;
3426 current_kb
= (KeyBlock
) key_blocks
[++iBlockCurr
];
3427 current_kb
.element_keys
.Add (key
);
3431 // sort the blocks so we can tackle the largest ones first
3434 // okay now we can start...
3435 ILGenerator ig
= ec
.ig
;
3436 Label lbl_end
= ig
.DefineLabel (); // at the end ;-)
3437 Label lbl_default
= default_target
;
3439 Type type_keys
= null;
3440 if (element_keys
.Length
> 0)
3441 type_keys
= element_keys
[0].GetType (); // used for conversions
3445 if (TypeManager
.IsEnumType (SwitchType
))
3446 compare_type
= TypeManager
.GetEnumUnderlyingType (SwitchType
);
3448 compare_type
= SwitchType
;
3450 for (int iBlock
= key_blocks
.Count
- 1; iBlock
>= 0; --iBlock
)
3452 KeyBlock kb
= ((KeyBlock
) key_blocks
[iBlock
]);
3453 lbl_default
= (iBlock
== 0) ? default_target
: ig
.DefineLabel ();
3456 foreach (object key
in kb
.element_keys
) {
3457 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3458 if (key
is int && (int) key
== 0) {
3459 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
3462 EmitObjectInteger (ig
, key
);
3463 ig
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
3469 // TODO: if all the keys in the block are the same and there are
3470 // no gaps/defaults then just use a range-check.
3471 if (compare_type
== TypeManager
.int64_type
||
3472 compare_type
== TypeManager
.uint64_type
)
3474 // TODO: optimize constant/I4 cases
3476 // check block range (could be > 2^31)
3478 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3479 ig
.Emit (OpCodes
.Blt
, lbl_default
);
3481 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.last
, type_keys
));
3482 ig
.Emit (OpCodes
.Bgt
, lbl_default
);
3488 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3489 ig
.Emit (OpCodes
.Sub
);
3491 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3497 int first
= (int) kb
.first
;
3500 IntConstant
.EmitInt (ig
, first
);
3501 ig
.Emit (OpCodes
.Sub
);
3505 IntConstant
.EmitInt (ig
, -first
);
3506 ig
.Emit (OpCodes
.Add
);
3510 // first, build the list of labels for the switch
3512 int cJumps
= kb
.Length
;
3513 Label
[] switch_labels
= new Label
[cJumps
];
3514 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
3516 object key
= kb
.element_keys
[iKey
];
3517 if (System
.Convert
.ToInt64 (key
) == kb
.first
+ iJump
)
3519 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3520 switch_labels
[iJump
] = sl
.GetILLabel (ec
);
3524 switch_labels
[iJump
] = lbl_default
;
3526 // emit the switch opcode
3527 ig
.Emit (OpCodes
.Switch
, switch_labels
);
3530 // mark the default for this block
3532 ig
.MarkLabel (lbl_default
);
3535 // TODO: find the default case and emit it here,
3536 // to prevent having to do the following jump.
3537 // make sure to mark other labels in the default section
3539 // the last default just goes to the end
3540 if (element_keys
.Length
> 0)
3541 ig
.Emit (OpCodes
.Br
, lbl_default
);
3543 // now emit the code for the sections
3544 bool found_default
= false;
3546 foreach (SwitchSection ss
in Sections
) {
3547 foreach (SwitchLabel sl
in ss
.Labels
) {
3548 if (sl
.Converted
== SwitchLabel
.NullStringCase
) {
3549 ig
.MarkLabel (null_target
);
3550 } else if (sl
.Label
== null) {
3551 ig
.MarkLabel (lbl_default
);
3552 found_default
= true;
3554 ig
.MarkLabel (null_target
);
3556 ig
.MarkLabel (sl
.GetILLabel (ec
));
3557 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
3562 if (!found_default
) {
3563 ig
.MarkLabel (lbl_default
);
3564 if (!has_null_case
) {
3565 ig
.MarkLabel (null_target
);
3569 ig
.MarkLabel (lbl_end
);
3572 SwitchSection
FindSection (SwitchLabel label
)
3574 foreach (SwitchSection ss
in Sections
){
3575 foreach (SwitchLabel sl
in ss
.Labels
){
3584 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
3586 foreach (SwitchSection ss
in Sections
)
3587 ss
.Block
.MutateHoistedGenericType (storey
);
3590 public static void Reset ()
3593 allowed_types
= null;
3596 public override bool Resolve (EmitContext ec
)
3598 Expr
= Expr
.Resolve (ec
);
3602 new_expr
= SwitchGoverningType (ec
, Expr
);
3604 if ((new_expr
== null) && TypeManager
.IsNullableType (Expr
.Type
)) {
3605 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
3609 new_expr
= SwitchGoverningType (ec
, unwrap
);
3612 if (new_expr
== null){
3613 Report
.Error (151, loc
,
3614 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3615 TypeManager
.CSharpName (Expr
.Type
));
3620 SwitchType
= new_expr
.Type
;
3622 if (RootContext
.Version
== LanguageVersion
.ISO_1
&& SwitchType
== TypeManager
.bool_type
) {
3623 Report
.FeatureIsNotAvailable (loc
, "switch expression of boolean type");
3627 if (!CheckSwitch (ec
))
3631 Elements
.Remove (SwitchLabel
.NullStringCase
);
3633 Switch old_switch
= ec
.Switch
;
3635 ec
.Switch
.SwitchType
= SwitchType
;
3637 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3638 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
3640 is_constant
= new_expr
is Constant
;
3642 object key
= ((Constant
) new_expr
).GetValue ();
3643 SwitchLabel label
= (SwitchLabel
) Elements
[key
];
3645 constant_section
= FindSection (label
);
3646 if (constant_section
== null)
3647 constant_section
= default_section
;
3652 foreach (SwitchSection ss
in Sections
){
3654 ec
.CurrentBranching
.CreateSibling (
3655 null, FlowBranching
.SiblingType
.SwitchSection
);
3659 if (is_constant
&& (ss
!= constant_section
)) {
3660 // If we're a constant switch, we're only emitting
3661 // one single section - mark all the others as
3663 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3664 if (!ss
.Block
.ResolveUnreachable (ec
, true)) {
3668 if (!ss
.Block
.Resolve (ec
))
3673 if (default_section
== null)
3674 ec
.CurrentBranching
.CreateSibling (
3675 null, FlowBranching
.SiblingType
.SwitchSection
);
3677 ec
.EndFlowBranching ();
3678 ec
.Switch
= old_switch
;
3680 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3685 if (SwitchType
== TypeManager
.string_type
&& !is_constant
) {
3686 // TODO: Optimize single case, and single+default case
3687 ResolveStringSwitchMap (ec
);
3693 void ResolveStringSwitchMap (EmitContext ec
)
3695 FullNamedExpression string_dictionary_type
;
3696 if (TypeManager
.generic_ienumerable_type
!= null) {
3697 MemberAccess system_collections_generic
= new MemberAccess (new MemberAccess (
3698 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
), "Generic", loc
);
3700 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Dictionary",
3702 new TypeExpression (TypeManager
.string_type
, loc
),
3703 new TypeExpression (TypeManager
.int32_type
, loc
)), loc
);
3705 MemberAccess system_collections_generic
= new MemberAccess (
3706 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
);
3708 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Hashtable", loc
);
3711 Field field
= new Field (ec
.TypeContainer
, string_dictionary_type
,
3712 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
3713 new MemberName (CompilerGeneratedClass
.MakeName (null, "f", "switch$map", unique_counter
++), loc
), null);
3714 if (!field
.Define ())
3716 ec
.TypeContainer
.PartialContainer
.AddField (field
);
3718 ArrayList init
= new ArrayList ();
3721 string value = null;
3722 foreach (SwitchSection section
in Sections
) {
3723 int last_count
= init
.Count
;
3724 foreach (SwitchLabel sl
in section
.Labels
) {
3725 if (sl
.Label
== null || sl
.Converted
== SwitchLabel
.NullStringCase
)
3728 value = (string) sl
.Converted
;
3729 ArrayList init_args
= new ArrayList (2);
3730 init_args
.Add (new StringLiteral (value, sl
.Location
));
3731 init_args
.Add (new IntConstant (counter
, loc
));
3732 init
.Add (new CollectionElementInitializer (init_args
, loc
));
3736 // Don't add empty sections
3738 if (last_count
== init
.Count
)
3741 Elements
.Add (counter
, section
.Labels
[0]);
3745 Arguments args
= new Arguments (1);
3746 args
.Add (new Argument (new IntConstant (init
.Count
, loc
)));
3747 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
3748 new CollectionOrObjectInitializers (init
, loc
), loc
);
3750 switch_cache_field
= new FieldExpr (field
.FieldBuilder
, loc
);
3751 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
3754 void DoEmitStringSwitch (LocalTemporary
value, EmitContext ec
)
3756 ILGenerator ig
= ec
.ig
;
3757 Label l_initialized
= ig
.DefineLabel ();
3760 // Skip initialization when value is null
3762 value.EmitBranchable (ec
, null_target
, false);
3765 // Check if string dictionary is initialized and initialize
3767 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
3768 string_dictionary
.EmitStatement (ec
);
3769 ig
.MarkLabel (l_initialized
);
3771 LocalTemporary string_switch_variable
= new LocalTemporary (TypeManager
.int32_type
);
3773 if (TypeManager
.generic_ienumerable_type
!= null) {
3774 Arguments get_value_args
= new Arguments (2);
3775 get_value_args
.Add (new Argument (value));
3776 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
3777 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (ec
);
3778 if (get_item
== null)
3782 // A value was not found, go to default case
3784 get_item
.EmitBranchable (ec
, default_target
, false);
3786 Arguments get_value_args
= new Arguments (1);
3787 get_value_args
.Add (new Argument (value));
3789 Expression get_item
= new IndexerAccess (new ElementAccess (switch_cache_field
, get_value_args
), loc
).Resolve (ec
);
3790 if (get_item
== null)
3793 LocalTemporary get_item_object
= new LocalTemporary (TypeManager
.object_type
);
3794 get_item_object
.EmitAssign (ec
, get_item
, true, false);
3795 ec
.ig
.Emit (OpCodes
.Brfalse
, default_target
);
3797 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
3798 new Cast (new TypeExpression (TypeManager
.int32_type
, loc
), get_item_object
, loc
)).Resolve (ec
);
3800 get_item_int
.EmitStatement (ec
);
3801 get_item_object
.Release (ec
);
3804 TableSwitchEmit (ec
, string_switch_variable
);
3805 string_switch_variable
.Release (ec
);
3808 protected override void DoEmit (EmitContext ec
)
3810 ILGenerator ig
= ec
.ig
;
3812 default_target
= ig
.DefineLabel ();
3813 null_target
= ig
.DefineLabel ();
3815 // Store variable for comparission purposes
3816 // TODO: Don't duplicate non-captured VariableReference
3817 LocalTemporary
value;
3819 value = new LocalTemporary (SwitchType
);
3820 unwrap
.EmitCheck (ec
);
3821 ig
.Emit (OpCodes
.Brfalse
, null_target
);
3824 } else if (!is_constant
) {
3825 value = new LocalTemporary (SwitchType
);
3832 // Setup the codegen context
3834 Label old_end
= ec
.LoopEnd
;
3835 Switch old_switch
= ec
.Switch
;
3837 ec
.LoopEnd
= ig
.DefineLabel ();
3842 if (constant_section
!= null)
3843 constant_section
.Block
.Emit (ec
);
3844 } else if (string_dictionary
!= null) {
3845 DoEmitStringSwitch (value, ec
);
3847 TableSwitchEmit (ec
, value);
3853 // Restore context state.
3854 ig
.MarkLabel (ec
.LoopEnd
);
3857 // Restore the previous context
3859 ec
.LoopEnd
= old_end
;
3860 ec
.Switch
= old_switch
;
3863 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3865 Switch target
= (Switch
) t
;
3867 target
.Expr
= Expr
.Clone (clonectx
);
3868 target
.Sections
= new ArrayList ();
3869 foreach (SwitchSection ss
in Sections
){
3870 target
.Sections
.Add (ss
.Clone (clonectx
));
3875 // A place where execution can restart in an iterator
3876 public abstract class ResumableStatement
: Statement
3879 protected Label resume_point
;
3881 public Label
PrepareForEmit (EmitContext ec
)
3885 resume_point
= ec
.ig
.DefineLabel ();
3887 return resume_point
;
3890 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
3894 public virtual void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3899 // Base class for statements that are implemented in terms of try...finally
3900 public abstract class ExceptionStatement
: ResumableStatement
3904 protected abstract void EmitPreTryBody (EmitContext ec
);
3905 protected abstract void EmitTryBody (EmitContext ec
);
3906 protected abstract void EmitFinallyBody (EmitContext ec
);
3908 protected sealed override void DoEmit (EmitContext ec
)
3910 ILGenerator ig
= ec
.ig
;
3912 EmitPreTryBody (ec
);
3914 if (resume_points
!= null) {
3915 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Running
);
3916 ig
.Emit (OpCodes
.Stloc
, ec
.CurrentIterator
.CurrentPC
);
3919 ig
.BeginExceptionBlock ();
3921 if (resume_points
!= null) {
3922 ig
.MarkLabel (resume_point
);
3924 // For normal control flow, we want to fall-through the Switch
3925 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3926 ig
.Emit (OpCodes
.Ldloc
, ec
.CurrentIterator
.CurrentPC
);
3927 IntConstant
.EmitInt (ig
, first_resume_pc
);
3928 ig
.Emit (OpCodes
.Sub
);
3930 Label
[] labels
= new Label
[resume_points
.Count
];
3931 for (int i
= 0; i
< resume_points
.Count
; ++i
)
3932 labels
[i
] = ((ResumableStatement
) resume_points
[i
]).PrepareForEmit (ec
);
3933 ig
.Emit (OpCodes
.Switch
, labels
);
3938 ig
.BeginFinallyBlock ();
3940 Label start_finally
= ec
.ig
.DefineLabel ();
3941 if (resume_points
!= null) {
3942 ig
.Emit (OpCodes
.Ldloc
, ec
.CurrentIterator
.SkipFinally
);
3943 ig
.Emit (OpCodes
.Brfalse_S
, start_finally
);
3944 ig
.Emit (OpCodes
.Endfinally
);
3947 ig
.MarkLabel (start_finally
);
3948 EmitFinallyBody (ec
);
3950 ig
.EndExceptionBlock ();
3953 public void SomeCodeFollows ()
3955 code_follows
= true;
3958 protected void ResolveReachability (EmitContext ec
)
3960 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3961 // So, ensure there's some IL code after this statement.
3962 if (!code_follows
&& resume_points
== null && ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
3963 ec
.NeedReturnLabel ();
3967 ArrayList resume_points
;
3968 int first_resume_pc
;
3969 public void AddResumePoint (ResumableStatement stmt
, int pc
)
3971 if (resume_points
== null) {
3972 resume_points
= new ArrayList ();
3973 first_resume_pc
= pc
;
3976 if (pc
!= first_resume_pc
+ resume_points
.Count
)
3977 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3979 resume_points
.Add (stmt
);
3982 Label dispose_try_block
;
3983 bool prepared_for_dispose
, emitted_dispose
;
3984 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
3986 if (!prepared_for_dispose
) {
3987 prepared_for_dispose
= true;
3988 dispose_try_block
= ec
.ig
.DefineLabel ();
3990 return dispose_try_block
;
3993 public override void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3995 if (emitted_dispose
)
3998 emitted_dispose
= true;
4000 ILGenerator ig
= ec
.ig
;
4002 Label end_of_try
= ig
.DefineLabel ();
4004 // Ensure that the only way we can get into this code is through a dispatcher
4005 if (have_dispatcher
)
4006 ig
.Emit (OpCodes
.Br
, end
);
4008 ig
.BeginExceptionBlock ();
4010 ig
.MarkLabel (dispose_try_block
);
4012 Label
[] labels
= null;
4013 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
4014 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
4015 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
4016 if (ret
.Equals (end_of_try
) && labels
== null)
4018 if (labels
== null) {
4019 labels
= new Label
[resume_points
.Count
];
4020 for (int j
= 0; j
< i
; ++j
)
4021 labels
[j
] = end_of_try
;
4026 if (labels
!= null) {
4028 for (j
= 1; j
< labels
.Length
; ++j
)
4029 if (!labels
[0].Equals (labels
[j
]))
4031 bool emit_dispatcher
= j
< labels
.Length
;
4033 if (emit_dispatcher
) {
4034 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4035 ig
.Emit (OpCodes
.Ldloc
, iterator
.CurrentPC
);
4036 IntConstant
.EmitInt (ig
, first_resume_pc
);
4037 ig
.Emit (OpCodes
.Sub
);
4038 ig
.Emit (OpCodes
.Switch
, labels
);
4039 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4042 foreach (ResumableStatement s
in resume_points
)
4043 s
.EmitForDispose (ec
, iterator
, end_of_try
, emit_dispatcher
);
4046 ig
.MarkLabel (end_of_try
);
4048 ig
.BeginFinallyBlock ();
4050 EmitFinallyBody (ec
);
4052 ig
.EndExceptionBlock ();
4056 public class Lock
: ExceptionStatement
{
4058 public Statement Statement
;
4059 TemporaryVariable temp
;
4061 public Lock (Expression expr
, Statement stmt
, Location l
)
4068 public override bool Resolve (EmitContext ec
)
4070 expr
= expr
.Resolve (ec
);
4074 if (!TypeManager
.IsReferenceType (expr
.Type
)){
4075 Report
.Error (185, loc
,
4076 "`{0}' is not a reference type as required by the lock statement",
4077 TypeManager
.CSharpName (expr
.Type
));
4081 ec
.StartFlowBranching (this);
4082 bool ok
= Statement
.Resolve (ec
);
4083 ec
.EndFlowBranching ();
4085 ResolveReachability (ec
);
4087 // Avoid creating libraries that reference the internal
4090 if (t
== TypeManager
.null_type
)
4091 t
= TypeManager
.object_type
;
4093 temp
= new TemporaryVariable (t
, loc
);
4096 if (TypeManager
.void_monitor_enter_object
== null || TypeManager
.void_monitor_exit_object
== null) {
4097 Type monitor_type
= TypeManager
.CoreLookupType ("System.Threading", "Monitor", Kind
.Class
, true);
4098 TypeManager
.void_monitor_enter_object
= TypeManager
.GetPredefinedMethod (
4099 monitor_type
, "Enter", loc
, TypeManager
.object_type
);
4100 TypeManager
.void_monitor_exit_object
= TypeManager
.GetPredefinedMethod (
4101 monitor_type
, "Exit", loc
, TypeManager
.object_type
);
4107 protected override void EmitPreTryBody (EmitContext ec
)
4109 ILGenerator ig
= ec
.ig
;
4111 temp
.EmitAssign (ec
, expr
);
4113 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
4116 protected override void EmitTryBody (EmitContext ec
)
4118 Statement
.Emit (ec
);
4121 protected override void EmitFinallyBody (EmitContext ec
)
4124 ec
.ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
4127 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4129 expr
.MutateHoistedGenericType (storey
);
4130 temp
.MutateHoistedGenericType (storey
);
4131 Statement
.MutateHoistedGenericType (storey
);
4134 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4136 Lock target
= (Lock
) t
;
4138 target
.expr
= expr
.Clone (clonectx
);
4139 target
.Statement
= Statement
.Clone (clonectx
);
4143 public class Unchecked
: Statement
{
4146 public Unchecked (Block b
)
4152 public override bool Resolve (EmitContext ec
)
4154 using (ec
.With (EmitContext
.Flags
.AllCheckStateFlags
, false))
4155 return Block
.Resolve (ec
);
4158 protected override void DoEmit (EmitContext ec
)
4160 using (ec
.With (EmitContext
.Flags
.AllCheckStateFlags
, false))
4164 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4166 Block
.MutateHoistedGenericType (storey
);
4169 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4171 Unchecked target
= (Unchecked
) t
;
4173 target
.Block
= clonectx
.LookupBlock (Block
);
4177 public class Checked
: Statement
{
4180 public Checked (Block b
)
4183 b
.Unchecked
= false;
4186 public override bool Resolve (EmitContext ec
)
4188 using (ec
.With (EmitContext
.Flags
.AllCheckStateFlags
, true))
4189 return Block
.Resolve (ec
);
4192 protected override void DoEmit (EmitContext ec
)
4194 using (ec
.With (EmitContext
.Flags
.AllCheckStateFlags
, true))
4198 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4200 Block
.MutateHoistedGenericType (storey
);
4203 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4205 Checked target
= (Checked
) t
;
4207 target
.Block
= clonectx
.LookupBlock (Block
);
4211 public class Unsafe
: Statement
{
4214 public Unsafe (Block b
)
4217 Block
.Unsafe
= true;
4220 public override bool Resolve (EmitContext ec
)
4222 using (ec
.With (EmitContext
.Flags
.InUnsafe
, true))
4223 return Block
.Resolve (ec
);
4226 protected override void DoEmit (EmitContext ec
)
4228 using (ec
.With (EmitContext
.Flags
.InUnsafe
, true))
4232 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4234 Block
.MutateHoistedGenericType (storey
);
4237 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4239 Unsafe target
= (Unsafe
) t
;
4241 target
.Block
= clonectx
.LookupBlock (Block
);
4248 public class Fixed
: Statement
{
4250 ArrayList declarators
;
4251 Statement statement
;
4256 abstract class Emitter
4258 protected LocalInfo vi
;
4259 protected Expression converted
;
4261 protected Emitter (Expression expr
, LocalInfo li
)
4267 public abstract void Emit (EmitContext ec
);
4268 public abstract void EmitExit (EmitContext ec
);
4271 class ExpressionEmitter
: Emitter
{
4272 public ExpressionEmitter (Expression converted
, LocalInfo li
) :
4273 base (converted
, li
)
4277 public override void Emit (EmitContext ec
) {
4279 // Store pointer in pinned location
4281 converted
.Emit (ec
);
4285 public override void EmitExit (EmitContext ec
)
4287 ec
.ig
.Emit (OpCodes
.Ldc_I4_0
);
4288 ec
.ig
.Emit (OpCodes
.Conv_U
);
4293 class StringEmitter
: Emitter
4295 LocalInfo pinned_string
;
4297 public StringEmitter (Expression expr
, LocalInfo li
, Location loc
):
4300 pinned_string
= new LocalInfo (new TypeExpression (TypeManager
.string_type
, loc
), null, null, loc
);
4301 pinned_string
.Pinned
= true;
4304 public override void Emit (EmitContext ec
)
4306 pinned_string
.Resolve (ec
);
4307 pinned_string
.ResolveVariable (ec
);
4309 converted
.Emit (ec
);
4310 pinned_string
.EmitAssign (ec
);
4312 PropertyInfo p
= TypeManager
.int_get_offset_to_string_data
;
4314 // TODO: Move to resolve
4315 p
= TypeManager
.int_get_offset_to_string_data
= TypeManager
.GetPredefinedProperty (
4316 TypeManager
.runtime_helpers_type
, "OffsetToStringData", pinned_string
.Location
, TypeManager
.int32_type
);
4322 // TODO: Should use Binary::Add
4323 pinned_string
.Emit (ec
);
4324 ec
.ig
.Emit (OpCodes
.Conv_I
);
4326 PropertyExpr pe
= new PropertyExpr (pinned_string
.VariableType
, p
, pinned_string
.Location
);
4327 //pe.InstanceExpression = pinned_string;
4328 pe
.Resolve (ec
).Emit (ec
);
4330 ec
.ig
.Emit (OpCodes
.Add
);
4334 public override void EmitExit (EmitContext ec
)
4336 ec
.ig
.Emit (OpCodes
.Ldnull
);
4337 pinned_string
.EmitAssign (ec
);
4341 public Fixed (Expression type
, ArrayList decls
, Statement stmt
, Location l
)
4344 declarators
= decls
;
4349 public Statement Statement
{
4350 get { return statement; }
4353 public override bool Resolve (EmitContext ec
)
4356 Expression
.UnsafeError (loc
);
4360 TypeExpr texpr
= type
.ResolveAsContextualType (ec
, false);
4361 if (texpr
== null) {
4362 if (type
is VarExpr
)
4363 Report
.Error (821, type
.Location
, "A fixed statement cannot use an implicitly typed local variable");
4368 expr_type
= texpr
.Type
;
4370 data
= new Emitter
[declarators
.Count
];
4372 if (!expr_type
.IsPointer
){
4373 Report
.Error (209, loc
, "The type of locals declared in a fixed statement must be a pointer type");
4378 foreach (Pair p
in declarators
){
4379 LocalInfo vi
= (LocalInfo
) p
.First
;
4380 Expression e
= (Expression
) p
.Second
;
4382 vi
.VariableInfo
.SetAssigned (ec
);
4383 vi
.SetReadOnlyContext (LocalInfo
.ReadOnlyContext
.Fixed
);
4386 // The rules for the possible declarators are pretty wise,
4387 // but the production on the grammar is more concise.
4389 // So we have to enforce these rules here.
4391 // We do not resolve before doing the case 1 test,
4392 // because the grammar is explicit in that the token &
4393 // is present, so we need to test for this particular case.
4397 Report
.Error (254, loc
, "The right hand side of a fixed statement assignment may not be a cast expression");
4401 ec
.InFixedInitializer
= true;
4403 ec
.InFixedInitializer
= false;
4410 if (e
.Type
.IsArray
){
4411 Type array_type
= TypeManager
.GetElementType (e
.Type
);
4414 // Provided that array_type is unmanaged,
4416 if (!TypeManager
.VerifyUnManaged (array_type
, loc
))
4420 // and T* is implicitly convertible to the
4421 // pointer type given in the fixed statement.
4423 ArrayPtr array_ptr
= new ArrayPtr (e
, array_type
, loc
);
4425 Expression converted
= Convert
.ImplicitConversionRequired (
4426 ec
, array_ptr
, vi
.VariableType
, loc
);
4427 if (converted
== null)
4431 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4433 converted
= new Conditional (new Binary (Binary
.Operator
.LogicalOr
,
4434 new Binary (Binary
.Operator
.Equality
, e
, new NullLiteral (loc
)),
4435 new Binary (Binary
.Operator
.Equality
, new MemberAccess (e
, "Length"), new IntConstant (0, loc
))),
4436 new NullPointer (loc
),
4439 converted
= converted
.Resolve (ec
);
4441 data
[i
] = new ExpressionEmitter (converted
, vi
);
4450 if (e
.Type
== TypeManager
.string_type
){
4451 data
[i
] = new StringEmitter (e
, vi
, loc
);
4456 // Case 4: fixed buffer
4457 if (e
is FixedBufferPtr
) {
4458 data
[i
++] = new ExpressionEmitter (e
, vi
);
4463 // Case 1: & object.
4465 Unary u
= e
as Unary
;
4466 if (u
!= null && u
.Oper
== Unary
.Operator
.AddressOf
) {
4467 IVariableReference vr
= u
.Expr
as IVariableReference
;
4468 if (vr
== null || !vr
.IsFixed
) {
4469 data
[i
] = new ExpressionEmitter (e
, vi
);
4473 if (data
[i
++] == null)
4474 Report
.Error (213, vi
.Location
, "You cannot use the fixed statement to take the address of an already fixed expression");
4476 e
= Convert
.ImplicitConversionRequired (ec
, e
, expr_type
, loc
);
4479 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
4480 bool ok
= statement
.Resolve (ec
);
4481 bool flow_unreachable
= ec
.EndFlowBranching ();
4482 has_ret
= flow_unreachable
;
4487 protected override void DoEmit (EmitContext ec
)
4489 for (int i
= 0; i
< data
.Length
; i
++) {
4493 statement
.Emit (ec
);
4499 // Clear the pinned variable
4501 for (int i
= 0; i
< data
.Length
; i
++) {
4502 data
[i
].EmitExit (ec
);
4506 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4508 // Fixed statement cannot be used inside anonymous methods or lambdas
4509 throw new NotSupportedException ();
4512 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4514 Fixed target
= (Fixed
) t
;
4516 target
.type
= type
.Clone (clonectx
);
4517 target
.declarators
= new ArrayList (declarators
.Count
);
4518 foreach (Pair p
in declarators
) {
4519 LocalInfo vi
= (LocalInfo
) p
.First
;
4520 Expression e
= (Expression
) p
.Second
;
4522 target
.declarators
.Add (
4523 new Pair (clonectx
.LookupVariable (vi
), e
.Clone (clonectx
)));
4526 target
.statement
= statement
.Clone (clonectx
);
4530 public class Catch
: Statement
{
4531 public readonly string Name
;
4533 public Block VarBlock
;
4535 Expression type_expr
;
4538 public Catch (Expression type
, string name
, Block block
, Block var_block
, Location l
)
4543 VarBlock
= var_block
;
4547 public Type CatchType
{
4553 public bool IsGeneral
{
4555 return type_expr
== null;
4559 protected override void DoEmit (EmitContext ec
)
4561 ILGenerator ig
= ec
.ig
;
4563 if (CatchType
!= null)
4564 ig
.BeginCatchBlock (CatchType
);
4566 ig
.BeginCatchBlock (TypeManager
.object_type
);
4568 if (VarBlock
!= null)
4572 // TODO: Move to resolve
4573 LocalVariableReference lvr
= new LocalVariableReference (Block
, Name
, loc
);
4577 // Only to make verifier happy
4578 if (TypeManager
.IsGenericParameter (lvr
.Type
))
4579 ig
.Emit (OpCodes
.Unbox_Any
, lvr
.Type
);
4583 if (lvr
.IsHoisted
) {
4584 LocalTemporary lt
= new LocalTemporary (lvr
.Type
);
4588 // Variable is at the top of the stack
4589 source
= EmptyExpression
.Null
;
4592 lvr
.EmitAssign (ec
, source
, false, false);
4594 ig
.Emit (OpCodes
.Pop
);
4599 public override bool Resolve (EmitContext ec
)
4601 using (ec
.With (EmitContext
.Flags
.InCatch
, true)) {
4602 if (type_expr
!= null) {
4603 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
, false);
4609 if (type
!= TypeManager
.exception_type
&& !TypeManager
.IsSubclassOf (type
, TypeManager
.exception_type
)){
4610 Error (155, "The type caught or thrown must be derived from System.Exception");
4616 if (!Block
.Resolve (ec
))
4619 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4620 // emit the "unused variable" warnings.
4621 if (VarBlock
!= null)
4622 return VarBlock
.Resolve (ec
);
4628 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4631 type
= storey
.MutateType (type
);
4632 if (VarBlock
!= null)
4633 VarBlock
.MutateHoistedGenericType (storey
);
4634 Block
.MutateHoistedGenericType (storey
);
4637 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4639 Catch target
= (Catch
) t
;
4641 if (type_expr
!= null)
4642 target
.type_expr
= type_expr
.Clone (clonectx
);
4643 if (VarBlock
!= null)
4644 target
.VarBlock
= clonectx
.LookupBlock (VarBlock
);
4645 target
.Block
= clonectx
.LookupBlock (Block
);
4649 public class TryFinally
: ExceptionStatement
{
4653 public TryFinally (Statement stmt
, Block fini
, Location l
)
4660 public override bool Resolve (EmitContext ec
)
4664 ec
.StartFlowBranching (this);
4666 if (!stmt
.Resolve (ec
))
4670 ec
.CurrentBranching
.CreateSibling (fini
, FlowBranching
.SiblingType
.Finally
);
4671 using (ec
.With (EmitContext
.Flags
.InFinally
, true)) {
4672 if (!fini
.Resolve (ec
))
4676 ec
.EndFlowBranching ();
4678 ResolveReachability (ec
);
4683 protected override void EmitPreTryBody (EmitContext ec
)
4687 protected override void EmitTryBody (EmitContext ec
)
4692 protected override void EmitFinallyBody (EmitContext ec
)
4697 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4699 stmt
.MutateHoistedGenericType (storey
);
4700 fini
.MutateHoistedGenericType (storey
);
4703 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4705 TryFinally target
= (TryFinally
) t
;
4707 target
.stmt
= (Statement
) stmt
.Clone (clonectx
);
4709 target
.fini
= clonectx
.LookupBlock (fini
);
4713 public class TryCatch
: Statement
{
4715 public ArrayList Specific
;
4716 public Catch General
;
4717 bool inside_try_finally
, code_follows
;
4719 public TryCatch (Block block
, ArrayList catch_clauses
, Location l
, bool inside_try_finally
)
4722 this.Specific
= catch_clauses
;
4723 this.General
= null;
4724 this.inside_try_finally
= inside_try_finally
;
4726 for (int i
= 0; i
< catch_clauses
.Count
; ++i
) {
4727 Catch c
= (Catch
) catch_clauses
[i
];
4729 if (i
!= catch_clauses
.Count
- 1)
4730 Report
.Error (1017, c
.loc
, "Try statement already has an empty catch block");
4732 catch_clauses
.RemoveAt (i
);
4740 public override bool Resolve (EmitContext ec
)
4744 ec
.StartFlowBranching (this);
4746 if (!Block
.Resolve (ec
))
4749 Type
[] prev_catches
= new Type
[Specific
.Count
];
4751 foreach (Catch c
in Specific
){
4752 ec
.CurrentBranching
.CreateSibling (c
.Block
, FlowBranching
.SiblingType
.Catch
);
4754 if (c
.Name
!= null) {
4755 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
4757 throw new Exception ();
4759 vi
.VariableInfo
= null;
4762 if (!c
.Resolve (ec
)) {
4767 Type resolved_type
= c
.CatchType
;
4768 for (int ii
= 0; ii
< last_index
; ++ii
) {
4769 if (resolved_type
== prev_catches
[ii
] || TypeManager
.IsSubclassOf (resolved_type
, prev_catches
[ii
])) {
4770 Report
.Error (160, c
.loc
,
4771 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4772 TypeManager
.CSharpName (prev_catches
[ii
]));
4777 prev_catches
[last_index
++] = resolved_type
;
4780 if (General
!= null) {
4781 if (CodeGen
.Assembly
.WrapNonExceptionThrows
) {
4782 foreach (Catch c
in Specific
){
4783 if (c
.CatchType
== TypeManager
.exception_type
&& PredefinedAttributes
.Get
.RuntimeCompatibility
.IsDefined
) {
4784 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'");
4789 ec
.CurrentBranching
.CreateSibling (General
.Block
, FlowBranching
.SiblingType
.Catch
);
4791 if (!General
.Resolve (ec
))
4795 ec
.EndFlowBranching ();
4797 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4798 // So, ensure there's some IL code after this statement
4799 if (!inside_try_finally
&& !code_follows
&& ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
4800 ec
.NeedReturnLabel ();
4805 public void SomeCodeFollows ()
4807 code_follows
= true;
4810 protected override void DoEmit (EmitContext ec
)
4812 ILGenerator ig
= ec
.ig
;
4814 if (!inside_try_finally
)
4815 ig
.BeginExceptionBlock ();
4819 foreach (Catch c
in Specific
)
4822 if (General
!= null)
4825 if (!inside_try_finally
)
4826 ig
.EndExceptionBlock ();
4829 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4831 Block
.MutateHoistedGenericType (storey
);
4833 if (General
!= null)
4834 General
.MutateHoistedGenericType (storey
);
4835 if (Specific
!= null) {
4836 foreach (Catch c
in Specific
)
4837 c
.MutateHoistedGenericType (storey
);
4841 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4843 TryCatch target
= (TryCatch
) t
;
4845 target
.Block
= clonectx
.LookupBlock (Block
);
4846 if (General
!= null)
4847 target
.General
= (Catch
) General
.Clone (clonectx
);
4848 if (Specific
!= null){
4849 target
.Specific
= new ArrayList ();
4850 foreach (Catch c
in Specific
)
4851 target
.Specific
.Add (c
.Clone (clonectx
));
4856 // FIXME: Why is it almost exact copy of Using ??
4857 public class UsingTemporary
: ExceptionStatement
{
4858 TemporaryVariable local_copy
;
4859 public Statement Statement
;
4863 public UsingTemporary (Expression expr
, Statement stmt
, Location l
)
4870 public override bool Resolve (EmitContext ec
)
4872 expr
= expr
.Resolve (ec
);
4876 expr_type
= expr
.Type
;
4878 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
)) {
4879 if (Convert
.ImplicitConversion (ec
, expr
, TypeManager
.idisposable_type
, loc
) == null) {
4880 Using
.Error_IsNotConvertibleToIDisposable (expr
);
4885 local_copy
= new TemporaryVariable (expr_type
, loc
);
4886 local_copy
.Resolve (ec
);
4888 ec
.StartFlowBranching (this);
4890 bool ok
= Statement
.Resolve (ec
);
4892 ec
.EndFlowBranching ();
4894 ResolveReachability (ec
);
4896 if (TypeManager
.void_dispose_void
== null) {
4897 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
4898 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
4904 protected override void EmitPreTryBody (EmitContext ec
)
4906 local_copy
.EmitAssign (ec
, expr
);
4909 protected override void EmitTryBody (EmitContext ec
)
4911 Statement
.Emit (ec
);
4914 protected override void EmitFinallyBody (EmitContext ec
)
4916 ILGenerator ig
= ec
.ig
;
4917 if (!TypeManager
.IsStruct (expr_type
)) {
4918 Label skip
= ig
.DefineLabel ();
4919 local_copy
.Emit (ec
);
4920 ig
.Emit (OpCodes
.Brfalse
, skip
);
4921 local_copy
.Emit (ec
);
4922 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4923 ig
.MarkLabel (skip
);
4927 Expression ml
= Expression
.MemberLookup (
4928 ec
.ContainerType
, TypeManager
.idisposable_type
, expr_type
,
4929 "Dispose", Location
.Null
);
4931 if (!(ml
is MethodGroupExpr
)) {
4932 local_copy
.Emit (ec
);
4933 ig
.Emit (OpCodes
.Box
, expr_type
);
4934 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4938 MethodInfo mi
= null;
4940 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
4941 if (TypeManager
.GetParameterData (mk
).Count
== 0) {
4948 Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
4952 local_copy
.AddressOf (ec
, AddressOp
.Load
);
4953 ig
.Emit (OpCodes
.Call
, mi
);
4956 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4958 expr_type
= storey
.MutateType (expr_type
);
4959 local_copy
.MutateHoistedGenericType (storey
);
4960 Statement
.MutateHoistedGenericType (storey
);
4963 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4965 UsingTemporary target
= (UsingTemporary
) t
;
4967 target
.expr
= expr
.Clone (clonectx
);
4968 target
.Statement
= Statement
.Clone (clonectx
);
4972 public class Using
: ExceptionStatement
{
4974 public Statement EmbeddedStatement
{
4975 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4981 ExpressionStatement assign
;
4983 public Using (Expression
var, Expression init
, Statement stmt
, Location l
)
4991 static public void Error_IsNotConvertibleToIDisposable (Expression expr
)
4993 Report
.SymbolRelatedToPreviousError (expr
.Type
);
4994 Report
.Error (1674, expr
.Location
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4995 expr
.GetSignatureForError ());
4998 protected override void EmitPreTryBody (EmitContext ec
)
5000 assign
.EmitStatement (ec
);
5003 protected override void EmitTryBody (EmitContext ec
)
5008 protected override void EmitFinallyBody (EmitContext ec
)
5010 ILGenerator ig
= ec
.ig
;
5011 Label skip
= ig
.DefineLabel ();
5013 bool emit_null_check
= !TypeManager
.IsValueType (var.Type
);
5014 if (emit_null_check
) {
5016 ig
.Emit (OpCodes
.Brfalse
, skip
);
5019 Invocation
.EmitCall (ec
, false, var, TypeManager
.void_dispose_void
, null, loc
);
5021 if (emit_null_check
)
5022 ig
.MarkLabel (skip
);
5025 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5027 assign
.MutateHoistedGenericType (storey
);
5028 var.MutateHoistedGenericType (storey
);
5029 stmt
.MutateHoistedGenericType (storey
);
5032 public override bool Resolve (EmitContext ec
)
5034 if (!ResolveVariable (ec
))
5037 ec
.StartFlowBranching (this);
5039 bool ok
= stmt
.Resolve (ec
);
5041 ec
.EndFlowBranching ();
5043 ResolveReachability (ec
);
5045 if (TypeManager
.void_dispose_void
== null) {
5046 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5047 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5053 bool ResolveVariable (EmitContext ec
)
5055 assign
= new SimpleAssign (var, init
, loc
);
5056 assign
= assign
.ResolveStatement (ec
);
5060 if (assign
.Type
== TypeManager
.idisposable_type
||
5061 TypeManager
.ImplementsInterface (assign
.Type
, TypeManager
.idisposable_type
)) {
5065 Expression e
= Convert
.ImplicitConversionStandard (ec
, assign
, TypeManager
.idisposable_type
, var.Location
);
5067 Error_IsNotConvertibleToIDisposable (var);
5071 throw new NotImplementedException ("covariance?");
5074 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5076 Using target
= (Using
) t
;
5078 target
.var = var.Clone (clonectx
);
5079 target
.init
= init
.Clone (clonectx
);
5080 target
.stmt
= stmt
.Clone (clonectx
);
5085 /// Implementation of the foreach C# statement
5087 public class Foreach
: Statement
{
5089 sealed class ArrayForeach
: Statement
5091 class ArrayCounter
: TemporaryVariable
5093 StatementExpression increment
;
5095 public ArrayCounter (Location loc
)
5096 : base (TypeManager
.int32_type
, loc
)
5100 public void ResolveIncrement (EmitContext ec
)
5102 increment
= new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, this));
5103 increment
.Resolve (ec
);
5106 public void EmitIncrement (EmitContext ec
)
5108 increment
.Emit (ec
);
5112 readonly Foreach for_each
;
5113 readonly Statement statement
;
5116 TemporaryVariable
[] lengths
;
5117 Expression
[] length_exprs
;
5118 ArrayCounter
[] counter
;
5120 TemporaryVariable copy
;
5123 public ArrayForeach (Foreach
@foreach, int rank
)
5125 for_each
= @foreach;
5126 statement
= for_each
.statement
;
5129 counter
= new ArrayCounter
[rank
];
5130 length_exprs
= new Expression
[rank
];
5133 // Only use temporary length variables when dealing with
5134 // multi-dimensional arrays
5137 lengths
= new TemporaryVariable
[rank
];
5140 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5142 throw new NotImplementedException ();
5145 public override bool Resolve (EmitContext ec
)
5147 copy
= new TemporaryVariable (for_each
.expr
.Type
, loc
);
5150 int rank
= length_exprs
.Length
;
5151 Arguments list
= new Arguments (rank
);
5152 for (int i
= 0; i
< rank
; i
++) {
5153 counter
[i
] = new ArrayCounter (loc
);
5154 counter
[i
].ResolveIncrement (ec
);
5157 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
5159 lengths
[i
] = new TemporaryVariable (TypeManager
.int32_type
, loc
);
5160 lengths
[i
].Resolve (ec
);
5162 Arguments args
= new Arguments (1);
5163 args
.Add (new Argument (new IntConstant (i
, loc
)));
5164 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
5167 list
.Add (new Argument (counter
[i
]));
5170 access
= new ElementAccess (copy
, list
).Resolve (ec
);
5174 Expression var_type
= for_each
.type
;
5175 VarExpr ve
= var_type
as VarExpr
;
5177 // Infer implicitly typed local variable from foreach array type
5178 var_type
= new TypeExpression (access
.Type
, ve
.Location
);
5181 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5182 if (var_type
== null)
5185 conv
= Convert
.ExplicitConversion (ec
, access
, var_type
.Type
, loc
);
5191 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
5192 ec
.CurrentBranching
.CreateSibling ();
5194 for_each
.variable
= for_each
.variable
.ResolveLValue (ec
, conv
);
5195 if (for_each
.variable
== null)
5198 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
5199 if (!statement
.Resolve (ec
))
5201 ec
.EndFlowBranching ();
5203 // There's no direct control flow from the end of the embedded statement to the end of the loop
5204 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
5206 ec
.EndFlowBranching ();
5211 protected override void DoEmit (EmitContext ec
)
5213 ILGenerator ig
= ec
.ig
;
5215 copy
.EmitAssign (ec
, for_each
.expr
);
5217 int rank
= length_exprs
.Length
;
5218 Label
[] test
= new Label
[rank
];
5219 Label
[] loop
= new Label
[rank
];
5221 for (int i
= 0; i
< rank
; i
++) {
5222 test
[i
] = ig
.DefineLabel ();
5223 loop
[i
] = ig
.DefineLabel ();
5225 if (lengths
!= null)
5226 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
5229 IntConstant zero
= new IntConstant (0, loc
);
5230 for (int i
= 0; i
< rank
; i
++) {
5231 counter
[i
].EmitAssign (ec
, zero
);
5233 ig
.Emit (OpCodes
.Br
, test
[i
]);
5234 ig
.MarkLabel (loop
[i
]);
5237 ((IAssignMethod
) for_each
.variable
).EmitAssign (ec
, conv
, false, false);
5239 statement
.Emit (ec
);
5241 ig
.MarkLabel (ec
.LoopBegin
);
5243 for (int i
= rank
- 1; i
>= 0; i
--){
5244 counter
[i
].EmitIncrement (ec
);
5246 ig
.MarkLabel (test
[i
]);
5247 counter
[i
].Emit (ec
);
5249 if (lengths
!= null)
5250 lengths
[i
].Emit (ec
);
5252 length_exprs
[i
].Emit (ec
);
5254 ig
.Emit (OpCodes
.Blt
, loop
[i
]);
5257 ig
.MarkLabel (ec
.LoopEnd
);
5260 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5262 for_each
.expr
.MutateHoistedGenericType (storey
);
5264 copy
.MutateHoistedGenericType (storey
);
5265 conv
.MutateHoistedGenericType (storey
);
5266 statement
.MutateHoistedGenericType (storey
);
5268 for (int i
= 0; i
< counter
.Length
; i
++) {
5269 counter
[i
].MutateHoistedGenericType (storey
);
5270 if (lengths
!= null)
5271 lengths
[i
].MutateHoistedGenericType (storey
);
5276 sealed class CollectionForeach
: Statement
5278 class CollectionForeachStatement
: Statement
5281 Expression variable
, current
, conv
;
5282 Statement statement
;
5285 public CollectionForeachStatement (Type type
, Expression variable
,
5286 Expression current
, Statement statement
,
5290 this.variable
= variable
;
5291 this.current
= current
;
5292 this.statement
= statement
;
5296 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5298 throw new NotImplementedException ();
5301 public override bool Resolve (EmitContext ec
)
5303 current
= current
.Resolve (ec
);
5304 if (current
== null)
5307 conv
= Convert
.ExplicitConversion (ec
, current
, type
, loc
);
5311 assign
= new SimpleAssign (variable
, conv
, loc
);
5312 if (assign
.Resolve (ec
) == null)
5315 if (!statement
.Resolve (ec
))
5321 protected override void DoEmit (EmitContext ec
)
5323 assign
.EmitStatement (ec
);
5324 statement
.Emit (ec
);
5327 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5329 assign
.MutateHoistedGenericType (storey
);
5330 statement
.MutateHoistedGenericType (storey
);
5334 Expression variable
, expr
;
5335 Statement statement
;
5337 TemporaryVariable enumerator
;
5342 MethodGroupExpr get_enumerator
;
5343 PropertyExpr get_current
;
5344 MethodInfo move_next
;
5345 Expression var_type
;
5346 Type enumerator_type
;
5347 bool enumerator_found
;
5349 public CollectionForeach (Expression var_type
, Expression
var,
5350 Expression expr
, Statement stmt
, Location l
)
5352 this.var_type
= var_type
;
5353 this.variable
= var;
5359 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5361 throw new NotImplementedException ();
5364 bool GetEnumeratorFilter (EmitContext ec
, MethodInfo mi
)
5366 Type return_type
= mi
.ReturnType
;
5369 // Ok, we can access it, now make sure that we can do something
5370 // with this `GetEnumerator'
5373 if (return_type
== TypeManager
.ienumerator_type
||
5374 TypeManager
.ImplementsInterface (return_type
, TypeManager
.ienumerator_type
)) {
5376 // If it is not an interface, lets try to find the methods ourselves.
5377 // For example, if we have:
5378 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5379 // We can avoid the iface call. This is a runtime perf boost.
5380 // even bigger if we have a ValueType, because we avoid the cost
5383 // We have to make sure that both methods exist for us to take
5384 // this path. If one of the methods does not exist, we will just
5385 // use the interface. Sadly, this complex if statement is the only
5386 // way I could do this without a goto
5389 if (TypeManager
.bool_movenext_void
== null) {
5390 TypeManager
.bool_movenext_void
= TypeManager
.GetPredefinedMethod (
5391 TypeManager
.ienumerator_type
, "MoveNext", loc
, Type
.EmptyTypes
);
5394 if (TypeManager
.ienumerator_getcurrent
== null) {
5395 TypeManager
.ienumerator_getcurrent
= TypeManager
.GetPredefinedProperty (
5396 TypeManager
.ienumerator_type
, "Current", loc
, TypeManager
.object_type
);
5400 // Prefer a generic enumerator over a non-generic one.
5402 if (return_type
.IsInterface
&& TypeManager
.IsGenericType (return_type
)) {
5403 enumerator_type
= return_type
;
5404 if (!FetchGetCurrent (ec
, return_type
))
5405 get_current
= new PropertyExpr (
5406 ec
.ContainerType
, TypeManager
.ienumerator_getcurrent
, loc
);
5407 if (!FetchMoveNext (return_type
))
5408 move_next
= TypeManager
.bool_movenext_void
;
5412 if (return_type
.IsInterface
||
5413 !FetchMoveNext (return_type
) ||
5414 !FetchGetCurrent (ec
, return_type
)) {
5415 enumerator_type
= return_type
;
5416 move_next
= TypeManager
.bool_movenext_void
;
5417 get_current
= new PropertyExpr (
5418 ec
.ContainerType
, TypeManager
.ienumerator_getcurrent
, loc
);
5423 // Ok, so they dont return an IEnumerable, we will have to
5424 // find if they support the GetEnumerator pattern.
5427 if (TypeManager
.HasElementType (return_type
) || !FetchMoveNext (return_type
) || !FetchGetCurrent (ec
, return_type
)) {
5428 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",
5429 TypeManager
.CSharpName (return_type
), TypeManager
.CSharpSignature (mi
));
5434 enumerator_type
= return_type
;
5440 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5442 bool FetchMoveNext (Type t
)
5444 MemberInfo
[] move_next_list
= TypeManager
.MemberLookup (null, null, t
,
5446 BindingFlags
.Public
| BindingFlags
.Instance
,
5449 foreach (MemberInfo m
in move_next_list
){
5450 MethodInfo mi
= (MethodInfo
) m
;
5452 if ((TypeManager
.GetParameterData (mi
).Count
== 0) &&
5453 TypeManager
.TypeToCoreType (mi
.ReturnType
) == TypeManager
.bool_type
) {
5463 // Retrieves a `public T get_Current ()' method from the Type `t'
5465 bool FetchGetCurrent (EmitContext ec
, Type t
)
5467 PropertyExpr pe
= Expression
.MemberLookup (
5468 ec
.ContainerType
, t
, "Current", MemberTypes
.Property
,
5469 Expression
.AllBindingFlags
, loc
) as PropertyExpr
;
5477 void Error_Enumerator ()
5479 if (enumerator_found
) {
5483 Report
.Error (1579, loc
,
5484 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5485 TypeManager
.CSharpName (expr
.Type
));
5488 bool IsOverride (MethodInfo m
)
5490 m
= (MethodInfo
) TypeManager
.DropGenericMethodArguments (m
);
5492 if (!m
.IsVirtual
|| ((m
.Attributes
& MethodAttributes
.NewSlot
) != 0))
5494 if (m
is MethodBuilder
)
5497 MethodInfo base_method
= m
.GetBaseDefinition ();
5498 return base_method
!= m
;
5501 bool TryType (EmitContext ec
, Type t
)
5503 MethodGroupExpr mg
= Expression
.MemberLookup (
5504 ec
.ContainerType
, t
, "GetEnumerator", MemberTypes
.Method
,
5505 Expression
.AllBindingFlags
, loc
) as MethodGroupExpr
;
5509 MethodInfo result
= null;
5510 MethodInfo tmp_move_next
= null;
5511 PropertyExpr tmp_get_cur
= null;
5512 Type tmp_enumerator_type
= enumerator_type
;
5513 foreach (MethodInfo mi
in mg
.Methods
) {
5514 if (TypeManager
.GetParameterData (mi
).Count
!= 0)
5517 // Check whether GetEnumerator is public
5518 if ((mi
.Attributes
& MethodAttributes
.Public
) != MethodAttributes
.Public
)
5521 if (IsOverride (mi
))
5524 enumerator_found
= true;
5526 if (!GetEnumeratorFilter (ec
, mi
))
5529 if (result
!= null) {
5530 if (TypeManager
.IsGenericType (result
.ReturnType
)) {
5531 if (!TypeManager
.IsGenericType (mi
.ReturnType
))
5534 MethodBase mb
= TypeManager
.DropGenericMethodArguments (mi
);
5535 Report
.SymbolRelatedToPreviousError (t
);
5536 Report
.Error(1640, loc
, "foreach statement cannot operate on variables of type `{0}' " +
5537 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5538 TypeManager
.CSharpName (t
), TypeManager
.CSharpSignature (mb
));
5542 // Always prefer generics enumerators
5543 if (!TypeManager
.IsGenericType (mi
.ReturnType
)) {
5544 if (TypeManager
.ImplementsInterface (mi
.DeclaringType
, result
.DeclaringType
) ||
5545 TypeManager
.ImplementsInterface (result
.DeclaringType
, mi
.DeclaringType
))
5548 Report
.SymbolRelatedToPreviousError (result
);
5549 Report
.SymbolRelatedToPreviousError (mi
);
5550 Report
.Warning (278, 2, loc
, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5551 TypeManager
.CSharpName (t
), "enumerable", TypeManager
.CSharpSignature (result
), TypeManager
.CSharpSignature (mi
));
5556 tmp_move_next
= move_next
;
5557 tmp_get_cur
= get_current
;
5558 tmp_enumerator_type
= enumerator_type
;
5559 if (mi
.DeclaringType
== t
)
5563 if (result
!= null) {
5564 move_next
= tmp_move_next
;
5565 get_current
= tmp_get_cur
;
5566 enumerator_type
= tmp_enumerator_type
;
5567 MethodInfo
[] mi
= new MethodInfo
[] { (MethodInfo) result }
;
5568 get_enumerator
= new MethodGroupExpr (mi
, enumerator_type
, loc
);
5570 if (t
!= expr
.Type
) {
5571 expr
= Convert
.ExplicitConversion (
5574 throw new InternalErrorException ();
5577 get_enumerator
.InstanceExpression
= expr
;
5578 get_enumerator
.IsBase
= t
!= expr
.Type
;
5586 bool ProbeCollectionType (EmitContext ec
, Type t
)
5588 int errors
= Report
.Errors
;
5589 for (Type tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
5590 if (TryType (ec
, tt
))
5595 if (Report
.Errors
> errors
)
5599 // Now try to find the method in the interfaces
5601 Type
[] ifaces
= TypeManager
.GetInterfaces (t
);
5602 foreach (Type i
in ifaces
){
5603 if (TryType (ec
, i
))
5610 public override bool Resolve (EmitContext ec
)
5612 enumerator_type
= TypeManager
.ienumerator_type
;
5614 if (!ProbeCollectionType (ec
, expr
.Type
)) {
5615 Error_Enumerator ();
5619 VarExpr ve
= var_type
as VarExpr
;
5621 // Infer implicitly typed local variable from foreach enumerable type
5622 var_type
= new TypeExpression (get_current
.PropertyInfo
.PropertyType
, var_type
.Location
);
5625 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5626 if (var_type
== null)
5629 enumerator
= new TemporaryVariable (enumerator_type
, loc
);
5630 enumerator
.Resolve (ec
);
5632 init
= new Invocation (get_enumerator
, null);
5633 init
= init
.Resolve (ec
);
5637 Expression move_next_expr
;
5639 MemberInfo
[] mi
= new MemberInfo
[] { move_next }
;
5640 MethodGroupExpr mg
= new MethodGroupExpr (mi
, var_type
.Type
, loc
);
5641 mg
.InstanceExpression
= enumerator
;
5643 move_next_expr
= new Invocation (mg
, null);
5646 get_current
.InstanceExpression
= enumerator
;
5648 Statement block
= new CollectionForeachStatement (
5649 var_type
.Type
, variable
, get_current
, statement
, loc
);
5651 loop
= new While (move_next_expr
, block
, loc
);
5654 bool implements_idisposable
= TypeManager
.ImplementsInterface (enumerator_type
, TypeManager
.idisposable_type
);
5655 if (implements_idisposable
|| !enumerator_type
.IsSealed
) {
5656 wrapper
= new DisposableWrapper (this, implements_idisposable
);
5658 wrapper
= new NonDisposableWrapper (this);
5661 return wrapper
.Resolve (ec
);
5664 protected override void DoEmit (EmitContext ec
)
5669 class NonDisposableWrapper
: Statement
{
5670 CollectionForeach parent
;
5672 internal NonDisposableWrapper (CollectionForeach parent
)
5674 this.parent
= parent
;
5677 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5679 throw new NotSupportedException ();
5682 public override bool Resolve (EmitContext ec
)
5684 return parent
.ResolveLoop (ec
);
5687 protected override void DoEmit (EmitContext ec
)
5689 parent
.EmitLoopInit (ec
);
5690 parent
.EmitLoopBody (ec
);
5693 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5695 throw new NotSupportedException ();
5699 sealed class DisposableWrapper
: ExceptionStatement
5701 CollectionForeach parent
;
5702 bool implements_idisposable
;
5704 internal DisposableWrapper (CollectionForeach parent
, bool implements
)
5706 this.parent
= parent
;
5707 this.implements_idisposable
= implements
;
5710 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5712 throw new NotSupportedException ();
5715 public override bool Resolve (EmitContext ec
)
5719 ec
.StartFlowBranching (this);
5721 if (!parent
.ResolveLoop (ec
))
5724 ec
.EndFlowBranching ();
5726 ResolveReachability (ec
);
5728 if (TypeManager
.void_dispose_void
== null) {
5729 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5730 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5735 protected override void EmitPreTryBody (EmitContext ec
)
5737 parent
.EmitLoopInit (ec
);
5740 protected override void EmitTryBody (EmitContext ec
)
5742 parent
.EmitLoopBody (ec
);
5745 protected override void EmitFinallyBody (EmitContext ec
)
5747 Expression instance
= parent
.enumerator
;
5748 if (!TypeManager
.IsValueType (parent
.enumerator_type
)) {
5749 ILGenerator ig
= ec
.ig
;
5751 parent
.enumerator
.Emit (ec
);
5753 Label call_dispose
= ig
.DefineLabel ();
5755 if (!implements_idisposable
) {
5756 ec
.ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
5757 LocalTemporary temp
= new LocalTemporary (TypeManager
.idisposable_type
);
5763 ig
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
5765 // using 'endfinally' to empty the evaluation stack
5766 ig
.Emit (OpCodes
.Endfinally
);
5767 ig
.MarkLabel (call_dispose
);
5770 Invocation
.EmitCall (ec
, false, instance
, TypeManager
.void_dispose_void
, null, loc
);
5773 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5775 throw new NotSupportedException ();
5779 bool ResolveLoop (EmitContext ec
)
5781 return loop
.Resolve (ec
);
5784 void EmitLoopInit (EmitContext ec
)
5786 enumerator
.EmitAssign (ec
, init
);
5789 void EmitLoopBody (EmitContext ec
)
5794 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5796 enumerator_type
= storey
.MutateType (enumerator_type
);
5797 init
.MutateHoistedGenericType (storey
);
5798 loop
.MutateHoistedGenericType (storey
);
5803 Expression variable
;
5805 Statement statement
;
5807 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
5808 Statement stmt
, Location l
)
5811 this.variable
= var;
5817 public Statement Statement
{
5818 get { return statement; }
5821 public override bool Resolve (EmitContext ec
)
5823 expr
= expr
.Resolve (ec
);
5828 Report
.Error (186, loc
, "Use of null is not valid in this context");
5832 if (expr
.Type
== TypeManager
.string_type
) {
5833 statement
= new ArrayForeach (this, 1);
5834 } else if (expr
.Type
.IsArray
) {
5835 statement
= new ArrayForeach (this, expr
.Type
.GetArrayRank ());
5837 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
5838 Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
5839 expr
.ExprClassName
);
5843 statement
= new CollectionForeach (type
, variable
, expr
, statement
, loc
);
5846 return statement
.Resolve (ec
);
5849 protected override void DoEmit (EmitContext ec
)
5851 ILGenerator ig
= ec
.ig
;
5853 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
5854 ec
.LoopBegin
= ig
.DefineLabel ();
5855 ec
.LoopEnd
= ig
.DefineLabel ();
5857 statement
.Emit (ec
);
5859 ec
.LoopBegin
= old_begin
;
5860 ec
.LoopEnd
= old_end
;
5863 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5865 Foreach target
= (Foreach
) t
;
5867 target
.type
= type
.Clone (clonectx
);
5868 target
.variable
= variable
.Clone (clonectx
);
5869 target
.expr
= expr
.Clone (clonectx
);
5870 target
.statement
= statement
.Clone (clonectx
);
5873 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5875 statement
.MutateHoistedGenericType (storey
);