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@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System
.Collections
.Generic
;
18 using IKVM
.Reflection
.Emit
;
20 using System
.Reflection
.Emit
;
23 namespace Mono
.CSharp
{
25 public abstract class Statement
{
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc
)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec
, bool warn
)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
55 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
56 bool ok
= Resolve (ec
);
57 ec
.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec
);
67 public virtual void Emit (EmitContext ec
)
72 if (ec
.StatementEpilogue
!= null) {
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
81 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
83 public Statement
Clone (CloneContext clonectx
)
85 Statement s
= (Statement
) this.MemberwiseClone ();
86 CloneTo (clonectx
, s
);
90 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
92 ec
.Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
96 public virtual object Accept (StructuralVisitor visitor
)
98 return visitor
.Visit (this);
102 public sealed class EmptyStatement
: Statement
104 public EmptyStatement (Location loc
)
109 public override bool Resolve (BlockContext ec
)
114 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
119 public override void Emit (EmitContext ec
)
123 protected override void DoEmit (EmitContext ec
)
125 throw new NotSupportedException ();
128 protected override void CloneTo (CloneContext clonectx
, Statement target
)
133 public override object Accept (StructuralVisitor visitor
)
135 return visitor
.Visit (this);
139 public class If
: Statement
{
141 public Statement TrueStatement
;
142 public Statement FalseStatement
;
146 public If (Expression bool_expr
, Statement true_statement
, Location l
)
147 : this (bool_expr
, true_statement
, null, l
)
151 public If (Expression bool_expr
,
152 Statement true_statement
,
153 Statement false_statement
,
156 this.expr
= bool_expr
;
157 TrueStatement
= true_statement
;
158 FalseStatement
= false_statement
;
162 public Expression Expr
{
168 public override bool Resolve (BlockContext ec
)
172 expr
= expr
.Resolve (ec
);
177 // Dead code elimination
179 if (expr
is Constant
) {
180 bool take
= !((Constant
) expr
).IsDefaultValue
;
183 if (!TrueStatement
.Resolve (ec
))
186 if ((FalseStatement
!= null) &&
187 !FalseStatement
.ResolveUnreachable (ec
, true))
189 FalseStatement
= null;
191 if (!TrueStatement
.ResolveUnreachable (ec
, true))
193 TrueStatement
= null;
195 if ((FalseStatement
!= null) &&
196 !FalseStatement
.Resolve (ec
))
204 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
206 ok
&= TrueStatement
.Resolve (ec
);
208 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
210 ec
.CurrentBranching
.CreateSibling ();
212 if (FalseStatement
!= null)
213 ok
&= FalseStatement
.Resolve (ec
);
215 ec
.EndFlowBranching ();
220 protected override void DoEmit (EmitContext ec
)
222 Label false_target
= ec
.DefineLabel ();
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c
= expr
as Constant
;
231 c
.EmitSideEffect (ec
);
233 if (!c
.IsDefaultValue
)
234 TrueStatement
.Emit (ec
);
235 else if (FalseStatement
!= null)
236 FalseStatement
.Emit (ec
);
241 expr
.EmitBranchable (ec
, false_target
, false);
243 TrueStatement
.Emit (ec
);
245 if (FalseStatement
!= null){
246 bool branch_emitted
= false;
248 end
= ec
.DefineLabel ();
250 ec
.Emit (OpCodes
.Br
, end
);
251 branch_emitted
= true;
254 ec
.MarkLabel (false_target
);
255 FalseStatement
.Emit (ec
);
260 ec
.MarkLabel (false_target
);
264 protected override void CloneTo (CloneContext clonectx
, Statement t
)
268 target
.expr
= expr
.Clone (clonectx
);
269 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
270 if (FalseStatement
!= null)
271 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
274 public override object Accept (StructuralVisitor visitor
)
276 return visitor
.Visit (this);
280 public class Do
: Statement
{
281 public Expression expr
;
282 public Statement EmbeddedStatement
;
284 public Do (Statement statement
, BooleanExpression bool_expr
, Location l
)
287 EmbeddedStatement
= statement
;
291 public override bool Resolve (BlockContext ec
)
295 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
297 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
299 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
300 if (!EmbeddedStatement
.Resolve (ec
))
302 ec
.EndFlowBranching ();
304 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
&& !was_unreachable
)
305 ec
.Report
.Warning (162, 2, expr
.Location
, "Unreachable code detected");
307 expr
= expr
.Resolve (ec
);
310 else if (expr
is Constant
){
311 bool infinite
= !((Constant
) expr
).IsDefaultValue
;
313 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
316 ec
.EndFlowBranching ();
321 protected override void DoEmit (EmitContext ec
)
323 Label loop
= ec
.DefineLabel ();
324 Label old_begin
= ec
.LoopBegin
;
325 Label old_end
= ec
.LoopEnd
;
327 ec
.LoopBegin
= ec
.DefineLabel ();
328 ec
.LoopEnd
= ec
.DefineLabel ();
331 EmbeddedStatement
.Emit (ec
);
332 ec
.MarkLabel (ec
.LoopBegin
);
334 // Mark start of while condition
335 ec
.Mark (expr
.Location
);
338 // Dead code elimination
340 if (expr
is Constant
) {
341 bool res
= !((Constant
) expr
).IsDefaultValue
;
343 expr
.EmitSideEffect (ec
);
345 ec
.Emit (OpCodes
.Br
, loop
);
347 expr
.EmitBranchable (ec
, loop
, true);
350 ec
.MarkLabel (ec
.LoopEnd
);
352 ec
.LoopBegin
= old_begin
;
353 ec
.LoopEnd
= old_end
;
356 protected override void CloneTo (CloneContext clonectx
, Statement t
)
360 target
.EmbeddedStatement
= EmbeddedStatement
.Clone (clonectx
);
361 target
.expr
= expr
.Clone (clonectx
);
364 public override object Accept (StructuralVisitor visitor
)
366 return visitor
.Visit (this);
370 public class While
: Statement
{
371 public Expression expr
;
372 public Statement Statement
;
373 bool infinite
, empty
;
375 public While (BooleanExpression bool_expr
, Statement statement
, Location l
)
377 this.expr
= bool_expr
;
378 Statement
= statement
;
382 public override bool Resolve (BlockContext ec
)
386 expr
= expr
.Resolve (ec
);
391 // Inform whether we are infinite or not
393 if (expr
is Constant
){
394 bool value = !((Constant
) expr
).IsDefaultValue
;
397 if (!Statement
.ResolveUnreachable (ec
, true))
405 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
407 ec
.CurrentBranching
.CreateSibling ();
409 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
410 if (!Statement
.Resolve (ec
))
412 ec
.EndFlowBranching ();
414 // There's no direct control flow from the end of the embedded statement to the end of the loop
415 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
417 ec
.EndFlowBranching ();
422 protected override void DoEmit (EmitContext ec
)
425 expr
.EmitSideEffect (ec
);
429 Label old_begin
= ec
.LoopBegin
;
430 Label old_end
= ec
.LoopEnd
;
432 ec
.LoopBegin
= ec
.DefineLabel ();
433 ec
.LoopEnd
= ec
.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr
is Constant
) {
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ec
.MarkLabel (ec
.LoopBegin
);
442 if (ec
.EmitAccurateDebugInfo
)
443 ec
.Emit (OpCodes
.Nop
);
445 expr
.EmitSideEffect (ec
);
447 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
450 // Inform that we are infinite (ie, `we return'), only
451 // if we do not `break' inside the code.
453 ec
.MarkLabel (ec
.LoopEnd
);
455 Label while_loop
= ec
.DefineLabel ();
457 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
458 ec
.MarkLabel (while_loop
);
462 ec
.MarkLabel (ec
.LoopBegin
);
464 ec
.Mark (expr
.Location
);
465 expr
.EmitBranchable (ec
, while_loop
, true);
467 ec
.MarkLabel (ec
.LoopEnd
);
470 ec
.LoopBegin
= old_begin
;
471 ec
.LoopEnd
= old_end
;
474 protected override void CloneTo (CloneContext clonectx
, Statement t
)
476 While target
= (While
) t
;
478 target
.expr
= expr
.Clone (clonectx
);
479 target
.Statement
= Statement
.Clone (clonectx
);
482 public override object Accept (StructuralVisitor visitor
)
484 return visitor
.Visit (this);
488 public class For
: Statement
490 bool infinite
, empty
;
492 public For (Location l
)
497 public Statement Initializer
{
501 public Expression Condition
{
505 public Statement Iterator
{
509 public Statement Statement
{
513 public override bool Resolve (BlockContext ec
)
517 if (Initializer
!= null) {
518 if (!Initializer
.Resolve (ec
))
522 if (Condition
!= null) {
523 Condition
= Condition
.Resolve (ec
);
524 if (Condition
== null)
526 else if (Condition
is Constant
) {
527 bool value = !((Constant
) Condition
).IsDefaultValue
;
530 if (!Statement
.ResolveUnreachable (ec
, true))
532 if ((Iterator
!= null) &&
533 !Iterator
.ResolveUnreachable (ec
, false))
543 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
545 ec
.CurrentBranching
.CreateSibling ();
547 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
549 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
550 if (!Statement
.Resolve (ec
))
552 ec
.EndFlowBranching ();
554 if (Iterator
!= null){
555 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
) {
556 if (!Iterator
.ResolveUnreachable (ec
, !was_unreachable
))
559 if (!Iterator
.Resolve (ec
))
564 // There's no direct control flow from the end of the embedded statement to the end of the loop
565 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
567 ec
.EndFlowBranching ();
572 protected override void DoEmit (EmitContext ec
)
574 if (Initializer
!= null)
575 Initializer
.Emit (ec
);
578 Condition
.EmitSideEffect (ec
);
582 Label old_begin
= ec
.LoopBegin
;
583 Label old_end
= ec
.LoopEnd
;
584 Label loop
= ec
.DefineLabel ();
585 Label test
= ec
.DefineLabel ();
587 ec
.LoopBegin
= ec
.DefineLabel ();
588 ec
.LoopEnd
= ec
.DefineLabel ();
590 ec
.Emit (OpCodes
.Br
, test
);
594 ec
.MarkLabel (ec
.LoopBegin
);
599 // If test is null, there is no test, and we are just
602 if (Condition
!= null) {
603 ec
.Mark (Condition
.Location
);
606 // The Resolve code already catches the case for
607 // Test == Constant (false) so we know that
610 if (Condition
is Constant
) {
611 Condition
.EmitSideEffect (ec
);
612 ec
.Emit (OpCodes
.Br
, loop
);
614 Condition
.EmitBranchable (ec
, loop
, true);
618 ec
.Emit (OpCodes
.Br
, loop
);
619 ec
.MarkLabel (ec
.LoopEnd
);
621 ec
.LoopBegin
= old_begin
;
622 ec
.LoopEnd
= old_end
;
625 protected override void CloneTo (CloneContext clonectx
, Statement t
)
627 For target
= (For
) t
;
629 if (Initializer
!= null)
630 target
.Initializer
= Initializer
.Clone (clonectx
);
631 if (Condition
!= null)
632 target
.Condition
= Condition
.Clone (clonectx
);
633 if (Iterator
!= null)
634 target
.Iterator
= Iterator
.Clone (clonectx
);
635 target
.Statement
= Statement
.Clone (clonectx
);
638 public override object Accept (StructuralVisitor visitor
)
640 return visitor
.Visit (this);
644 public class StatementExpression
: Statement
646 ExpressionStatement expr
;
648 public StatementExpression (ExpressionStatement expr
)
654 public StatementExpression (ExpressionStatement expr
, Location loc
)
660 public ExpressionStatement Expr
{
666 protected override void CloneTo (CloneContext clonectx
, Statement t
)
668 StatementExpression target
= (StatementExpression
) t
;
669 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
672 protected override void DoEmit (EmitContext ec
)
674 expr
.EmitStatement (ec
);
677 public override bool Resolve (BlockContext ec
)
679 expr
= expr
.ResolveStatement (ec
);
683 public override object Accept (StructuralVisitor visitor
)
685 return visitor
.Visit (this);
689 public class StatementErrorExpression
: Statement
691 readonly Expression expr
;
693 public StatementErrorExpression (Expression expr
)
698 public Expression Expr
{
704 protected override void DoEmit (EmitContext ec
)
706 throw new NotSupportedException ();
709 protected override void CloneTo (CloneContext clonectx
, Statement target
)
711 throw new NotImplementedException ();
714 public override object Accept (StructuralVisitor visitor
)
716 return visitor
.Visit (this);
721 // Simple version of statement list not requiring a block
723 public class StatementList
: Statement
725 List
<Statement
> statements
;
727 public StatementList (Statement first
, Statement second
)
729 statements
= new List
<Statement
> () { first, second }
;
733 public IList
<Statement
> Statements
{
740 public void Add (Statement statement
)
742 statements
.Add (statement
);
745 public override bool Resolve (BlockContext ec
)
747 foreach (var s
in statements
)
753 protected override void DoEmit (EmitContext ec
)
755 foreach (var s
in statements
)
759 protected override void CloneTo (CloneContext clonectx
, Statement target
)
761 StatementList t
= (StatementList
) target
;
763 t
.statements
= new List
<Statement
> (statements
.Count
);
764 foreach (Statement s
in statements
)
765 t
.statements
.Add (s
.Clone (clonectx
));
768 public override object Accept (StructuralVisitor visitor
)
770 return visitor
.Visit (this);
774 // A 'return' or a 'yield break'
775 public abstract class ExitStatement
: Statement
777 protected bool unwind_protect
;
778 protected abstract bool DoResolve (BlockContext ec
);
780 public virtual void Error_FinallyClause (Report Report
)
782 Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
785 public sealed override bool Resolve (BlockContext ec
)
790 unwind_protect
= ec
.CurrentBranching
.AddReturnOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
791 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
797 /// Implements the return statement
799 public class Return
: ExitStatement
803 public Return (Expression expr
, Location l
)
811 public Expression Expr
{
822 protected override bool DoResolve (BlockContext ec
)
825 if (ec
.ReturnType
.Kind
== MemberKind
.Void
)
829 // Return must not be followed by an expression when
830 // the method return type is Task
832 if (ec
.CurrentAnonymousMethod
is AsyncInitializer
) {
833 var storey
= (AsyncTaskStorey
) ec
.CurrentAnonymousMethod
.Storey
;
834 if (storey
.ReturnType
== ec
.Module
.PredefinedTypes
.Task
.TypeSpec
) {
836 // Extra trick not to emit ret/leave inside awaiter body
838 expr
= EmptyExpression
.Null
;
843 if (ec
.CurrentIterator
!= null) {
844 Error_ReturnFromIterator (ec
);
846 ec
.Report
.Error (126, loc
,
847 "An object of a type convertible to `{0}' is required for the return statement",
848 ec
.ReturnType
.GetSignatureForError ());
854 expr
= expr
.Resolve (ec
);
855 TypeSpec block_return_type
= ec
.ReturnType
;
857 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
859 if (block_return_type
.Kind
== MemberKind
.Void
) {
860 ec
.Report
.Error (127, loc
,
861 "`{0}': A return keyword must not be followed by any expression when method returns void",
862 ec
.GetSignatureForError ());
868 Error_ReturnFromIterator (ec
);
872 var async_block
= am
as AsyncInitializer
;
873 if (async_block
!= null) {
875 var storey
= (AsyncTaskStorey
) am
.Storey
;
876 var async_type
= storey
.ReturnType
;
878 if (async_type
== null && async_block
.ReturnTypeInference
!= null) {
879 async_block
.ReturnTypeInference
.AddCommonTypeBound (expr
.Type
);
883 // TODO: Better error message
884 if (async_type
.Kind
== MemberKind
.Void
) {
885 ec
.Report
.Error (127, loc
,
886 "`{0}': A return keyword must not be followed by any expression when method returns void",
887 ec
.GetSignatureForError ());
892 if (!async_type
.IsGenericTask
) {
893 if (this is ContextualReturn
)
896 ec
.Report
.Error (1997, loc
,
897 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
898 ec
.GetSignatureForError ());
903 // The return type is actually Task<T> type argument
905 if (expr
.Type
== async_type
) {
906 ec
.Report
.Error (4016, loc
,
907 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
908 ec
.GetSignatureForError (), async_type
.TypeArguments
[0].GetSignatureForError ());
910 block_return_type
= async_type
.TypeArguments
[0];
914 var l
= am
as AnonymousMethodBody
;
915 if (l
!= null && l
.ReturnTypeInference
!= null && expr
!= null) {
916 l
.ReturnTypeInference
.AddCommonTypeBound (expr
.Type
);
925 if (expr
.Type
!= block_return_type
&& expr
.Type
!= InternalType
.ErrorType
) {
926 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, block_return_type
, loc
);
929 if (am
!= null && block_return_type
== ec
.ReturnType
) {
930 ec
.Report
.Error (1662, loc
,
931 "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",
932 am
.ContainerType
, am
.GetSignatureForError ());
941 protected override void DoEmit (EmitContext ec
)
946 var async_body
= ec
.CurrentAnonymousMethod
as AsyncInitializer
;
947 if (async_body
!= null) {
948 var async_return
= ((AsyncTaskStorey
) async_body
.Storey
).HoistedReturn
;
950 // It's null for await without async
951 if (async_return
!= null) {
952 async_return
.EmitAssign (ec
);
956 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, async_body
.BodyEnd
);
964 if (unwind_protect
|| ec
.EmitAccurateDebugInfo
)
965 ec
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
968 if (unwind_protect
) {
969 ec
.Emit (OpCodes
.Leave
, ec
.CreateReturnLabel ());
970 } else if (ec
.EmitAccurateDebugInfo
) {
971 ec
.Emit (OpCodes
.Br
, ec
.CreateReturnLabel ());
973 ec
.Emit (OpCodes
.Ret
);
977 void Error_ReturnFromIterator (ResolveContext rc
)
979 rc
.Report
.Error (1622, loc
,
980 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
983 protected override void CloneTo (CloneContext clonectx
, Statement t
)
985 Return target
= (Return
) t
;
986 // It's null for simple return;
988 target
.expr
= expr
.Clone (clonectx
);
991 public override object Accept (StructuralVisitor visitor
)
993 return visitor
.Visit (this);
997 public class Goto
: Statement
{
999 LabeledStatement label
;
1000 bool unwind_protect
;
1002 public override bool Resolve (BlockContext ec
)
1004 unwind_protect
= ec
.CurrentBranching
.AddGotoOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
1005 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1009 public Goto (string label
, Location l
)
1015 public string Target
{
1016 get { return target; }
1019 public void SetResolvedTarget (LabeledStatement label
)
1022 label
.AddReference ();
1025 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1030 protected override void DoEmit (EmitContext ec
)
1033 throw new InternalErrorException ("goto emitted before target resolved");
1034 Label l
= label
.LabelTarget (ec
);
1035 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
1038 public override object Accept (StructuralVisitor visitor
)
1040 return visitor
.Visit (this);
1044 public class LabeledStatement
: Statement
{
1051 FlowBranching
.UsageVector vectors
;
1053 public LabeledStatement (string name
, Block block
, Location l
)
1060 public Label
LabelTarget (EmitContext ec
)
1065 label
= ec
.DefineLabel ();
1070 public Block Block
{
1076 public string Name
{
1077 get { return name; }
1080 public bool IsDefined
{
1081 get { return defined; }
1084 public bool HasBeenReferenced
{
1085 get { return referenced; }
1088 public FlowBranching
.UsageVector JumpOrigins
{
1089 get { return vectors; }
1092 public void AddUsageVector (FlowBranching
.UsageVector vector
)
1094 vector
= vector
.Clone ();
1095 vector
.Next
= vectors
;
1099 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1104 public override bool Resolve (BlockContext ec
)
1106 // this flow-branching will be terminated when the surrounding block ends
1107 ec
.StartFlowBranching (this);
1111 protected override void DoEmit (EmitContext ec
)
1113 if (!HasBeenReferenced
)
1114 ec
.Report
.Warning (164, 2, loc
, "This label has not been referenced");
1117 ec
.MarkLabel (label
);
1120 public void AddReference ()
1125 public override object Accept (StructuralVisitor visitor
)
1127 return visitor
.Visit (this);
1133 /// `goto default' statement
1135 public class GotoDefault
: Statement
{
1137 public GotoDefault (Location l
)
1142 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1147 public override bool Resolve (BlockContext ec
)
1149 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1151 if (ec
.Switch
== null) {
1152 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
1156 if (!ec
.Switch
.GotDefault
) {
1157 FlowBranchingBlock
.Error_UnknownLabel (loc
, "default", ec
.Report
);
1164 protected override void DoEmit (EmitContext ec
)
1166 ec
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultLabel
);
1169 public override object Accept (StructuralVisitor visitor
)
1171 return visitor
.Visit (this);
1176 /// `goto case' statement
1178 public class GotoCase
: Statement
{
1182 public GotoCase (Expression e
, Location l
)
1188 public Expression Expr
{
1194 public override bool Resolve (BlockContext ec
)
1196 if (ec
.Switch
== null){
1197 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
1201 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1203 expr
= expr
.Resolve (ec
);
1207 Constant c
= expr
as Constant
;
1209 ec
.Report
.Error (150, expr
.Location
, "A constant value is expected");
1214 if (ec
.Switch
.IsNullable
&& c
is NullLiteral
) {
1217 TypeSpec type
= ec
.Switch
.SwitchType
;
1218 res
= c
.TryReduce (ec
, type
, c
.Location
);
1220 c
.Error_ValueCannotBeConverted (ec
, loc
, type
, true);
1224 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
1225 ec
.Report
.Warning (469, 2, loc
,
1226 "The `goto case' value is not implicitly convertible to type `{0}'",
1227 TypeManager
.CSharpName (type
));
1231 sl
= ec
.Switch
.ResolveGotoCase (ec
, res
);
1235 protected override void DoEmit (EmitContext ec
)
1237 ec
.Emit (OpCodes
.Br
, sl
.GetILLabel (ec
));
1240 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1242 GotoCase target
= (GotoCase
) t
;
1244 target
.expr
= expr
.Clone (clonectx
);
1247 public override object Accept (StructuralVisitor visitor
)
1249 return visitor
.Visit (this);
1253 public class Throw
: Statement
{
1256 public Throw (Expression expr
, Location l
)
1262 public Expression Expr
{
1268 public override bool Resolve (BlockContext ec
)
1271 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1272 return ec
.CurrentBranching
.CheckRethrow (loc
);
1275 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
1276 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1281 var et
= ec
.BuiltinTypes
.Exception
;
1282 if (Convert
.ImplicitConversionExists (ec
, expr
, et
))
1283 expr
= Convert
.ImplicitConversion (ec
, expr
, et
, loc
);
1285 ec
.Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
1290 protected override void DoEmit (EmitContext ec
)
1293 ec
.Emit (OpCodes
.Rethrow
);
1297 ec
.Emit (OpCodes
.Throw
);
1301 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1303 Throw target
= (Throw
) t
;
1306 target
.expr
= expr
.Clone (clonectx
);
1309 public override object Accept (StructuralVisitor visitor
)
1311 return visitor
.Visit (this);
1315 public class Break
: Statement
{
1317 public Break (Location l
)
1322 bool unwind_protect
;
1324 public override bool Resolve (BlockContext ec
)
1326 unwind_protect
= ec
.CurrentBranching
.AddBreakOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1327 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1331 protected override void DoEmit (EmitContext ec
)
1333 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopEnd
);
1336 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1341 public override object Accept (StructuralVisitor visitor
)
1343 return visitor
.Visit (this);
1347 public class Continue
: Statement
{
1349 public Continue (Location l
)
1354 bool unwind_protect
;
1356 public override bool Resolve (BlockContext ec
)
1358 unwind_protect
= ec
.CurrentBranching
.AddContinueOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1359 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1363 protected override void DoEmit (EmitContext ec
)
1365 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopBegin
);
1368 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1373 public override object Accept (StructuralVisitor visitor
)
1375 return visitor
.Visit (this);
1379 public interface ILocalVariable
1381 void Emit (EmitContext ec
);
1382 void EmitAssign (EmitContext ec
);
1383 void EmitAddressOf (EmitContext ec
);
1386 public interface INamedBlockVariable
1388 Block Block { get; }
1389 Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
);
1390 bool IsDeclared { get; }
1391 bool IsParameter { get; }
1392 Location Location { get; }
1395 public class BlockVariableDeclaration
: Statement
1397 public class Declarator
1400 Expression initializer
;
1402 public Declarator (LocalVariable li
, Expression initializer
)
1404 if (li
.Type
!= null)
1405 throw new ArgumentException ("Expected null variable type");
1408 this.initializer
= initializer
;
1411 public Declarator (Declarator clone
, Expression initializer
)
1414 this.initializer
= initializer
;
1419 public LocalVariable Variable
{
1425 public Expression Initializer
{
1430 initializer
= value;
1437 Expression initializer
;
1438 protected FullNamedExpression type_expr
;
1439 protected LocalVariable li
;
1440 protected List
<Declarator
> declarators
;
1442 public BlockVariableDeclaration (FullNamedExpression type
, LocalVariable li
)
1444 this.type_expr
= type
;
1446 this.loc
= type_expr
.Location
;
1449 protected BlockVariableDeclaration (LocalVariable li
)
1456 public List
<Declarator
> Declarators
{
1462 public Expression Initializer
{
1467 initializer
= value;
1471 public FullNamedExpression TypeExpression
{
1477 public LocalVariable Variable
{
1485 public void AddDeclarator (Declarator decl
)
1487 if (declarators
== null)
1488 declarators
= new List
<Declarator
> ();
1490 declarators
.Add (decl
);
1493 static void CreateEvaluatorVariable (BlockContext bc
, LocalVariable li
)
1495 if (bc
.Report
.Errors
!= 0)
1498 var container
= bc
.CurrentMemberDefinition
.Parent
.PartialContainer
;
1500 Field f
= new Field (container
, new TypeExpression (li
.Type
, li
.Location
), Modifiers
.PUBLIC
| Modifiers
.STATIC
,
1501 new MemberName (li
.Name
, li
.Location
), null);
1503 container
.AddField (f
);
1506 li
.HoistedVariant
= new HoistedEvaluatorVariable (f
);
1510 public override bool Resolve (BlockContext bc
)
1512 return Resolve (bc
, true);
1515 public bool Resolve (BlockContext bc
, bool resolveDeclaratorInitializers
)
1517 if (li
.Type
== null) {
1518 TypeSpec type
= null;
1519 var vexpr
= type_expr
as VarExpr
;
1522 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1523 // same name exists or as a keyword when no type was found
1525 if (vexpr
!= null && !vexpr
.IsPossibleTypeOrNamespace (bc
)) {
1526 if (bc
.Module
.Compiler
.Settings
.Version
< LanguageVersion
.V_3
)
1527 bc
.Report
.FeatureIsNotAvailable (bc
.Module
.Compiler
, loc
, "implicitly typed local variable");
1530 bc
.Report
.Error (821, loc
, "A fixed statement cannot use an implicitly typed local variable");
1534 if (li
.IsConstant
) {
1535 bc
.Report
.Error (822, loc
, "An implicitly typed local variable cannot be a constant");
1539 if (Initializer
== null) {
1540 bc
.Report
.Error (818, loc
, "An implicitly typed local variable declarator must include an initializer");
1544 if (declarators
!= null) {
1545 bc
.Report
.Error (819, loc
, "An implicitly typed local variable declaration cannot include multiple declarators");
1549 Initializer
= Initializer
.Resolve (bc
);
1550 if (Initializer
!= null) {
1551 ((VarExpr
) type_expr
).InferType (bc
, Initializer
);
1552 type
= type_expr
.Type
;
1554 // Set error type to indicate the var was placed correctly but could
1557 // var a = missing ();
1559 type
= InternalType
.ErrorType
;
1564 type
= type_expr
.ResolveAsType (bc
);
1568 if (li
.IsConstant
&& !type
.IsConstantCompatible
) {
1569 Const
.Error_InvalidConstantType (type
, loc
, bc
.Report
);
1574 FieldBase
.Error_VariableOfStaticClass (loc
, li
.Name
, type
, bc
.Report
);
1579 bool eval_global
= bc
.Module
.Compiler
.Settings
.StatementMode
&& bc
.CurrentBlock
is ToplevelBlock
;
1581 CreateEvaluatorVariable (bc
, li
);
1583 li
.PrepareForFlowAnalysis (bc
);
1586 if (initializer
!= null) {
1587 initializer
= ResolveInitializer (bc
, li
, initializer
);
1588 // li.Variable.DefinitelyAssigned
1591 if (declarators
!= null) {
1592 foreach (var d
in declarators
) {
1593 d
.Variable
.Type
= li
.Type
;
1595 CreateEvaluatorVariable (bc
, d
.Variable
);
1597 d
.Variable
.PrepareForFlowAnalysis (bc
);
1600 if (d
.Initializer
!= null && resolveDeclaratorInitializers
) {
1601 d
.Initializer
= ResolveInitializer (bc
, d
.Variable
, d
.Initializer
);
1602 // d.Variable.DefinitelyAssigned
1610 protected virtual Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
1612 var a
= new SimpleAssign (li
.CreateReferenceExpression (bc
, li
.Location
), initializer
, li
.Location
);
1613 return a
.ResolveStatement (bc
);
1616 protected override void DoEmit (EmitContext ec
)
1618 li
.CreateBuilder (ec
);
1620 if (Initializer
!= null)
1621 ((ExpressionStatement
) Initializer
).EmitStatement (ec
);
1623 if (declarators
!= null) {
1624 foreach (var d
in declarators
) {
1625 d
.Variable
.CreateBuilder (ec
);
1626 if (d
.Initializer
!= null)
1627 ((ExpressionStatement
) d
.Initializer
).EmitStatement (ec
);
1632 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1634 BlockVariableDeclaration t
= (BlockVariableDeclaration
) target
;
1636 if (type_expr
!= null)
1637 t
.type_expr
= (FullNamedExpression
) type_expr
.Clone (clonectx
);
1639 if (initializer
!= null)
1640 t
.initializer
= initializer
.Clone (clonectx
);
1642 if (declarators
!= null) {
1643 t
.declarators
= null;
1644 foreach (var d
in declarators
)
1645 t
.AddDeclarator (new Declarator (d
, d
.Initializer
== null ? null : d
.Initializer
.Clone (clonectx
)));
1649 public override object Accept (StructuralVisitor visitor
)
1651 return visitor
.Visit (this);
1655 public class BlockConstantDeclaration
: BlockVariableDeclaration
1657 public BlockConstantDeclaration (FullNamedExpression type
, LocalVariable li
)
1662 public override void Emit (EmitContext ec
)
1664 // Nothing to emit, not even sequence point
1667 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
1669 initializer
= initializer
.Resolve (bc
);
1670 if (initializer
== null)
1673 var c
= initializer
as Constant
;
1675 initializer
.Error_ExpressionMustBeConstant (bc
, initializer
.Location
, li
.Name
);
1679 c
= c
.ConvertImplicitly (li
.Type
);
1681 if (TypeSpec
.IsReferenceType (li
.Type
))
1682 initializer
.Error_ConstantCanBeInitializedWithNullOnly (bc
, li
.Type
, initializer
.Location
, li
.Name
);
1684 initializer
.Error_ValueCannotBeConverted (bc
, initializer
.Location
, li
.Type
, false);
1689 li
.ConstantValue
= c
;
1693 public override object Accept (StructuralVisitor visitor
)
1695 return visitor
.Visit (this);
1700 // The information about a user-perceived local variable
1702 public class LocalVariable
: INamedBlockVariable
, ILocalVariable
1709 AddressTaken
= 1 << 2,
1710 CompilerGenerated
= 1 << 3,
1712 ForeachVariable
= 1 << 5,
1713 FixedVariable
= 1 << 6,
1714 UsingVariable
= 1 << 7,
1715 // DefinitelyAssigned = 1 << 8,
1718 ReadonlyMask
= ForeachVariable
| FixedVariable
| UsingVariable
1722 readonly string name
;
1723 readonly Location loc
;
1724 readonly Block block
;
1726 Constant const_value
;
1728 public VariableInfo VariableInfo
;
1729 HoistedVariable hoisted_variant
;
1731 LocalBuilder builder
;
1733 public LocalVariable (Block block
, string name
, Location loc
)
1740 public LocalVariable (Block block
, string name
, Flags flags
, Location loc
)
1741 : this (block
, name
, loc
)
1747 // Used by variable declarators
1749 public LocalVariable (LocalVariable li
, string name
, Location loc
)
1750 : this (li
.block
, name
, li
.flags
, loc
)
1756 public bool AddressTaken
{
1758 return (flags
& Flags
.AddressTaken
) != 0;
1762 public Block Block
{
1768 public Constant ConstantValue
{
1773 const_value
= value;
1778 // Hoisted local variable variant
1780 public HoistedVariable HoistedVariant
{
1782 return hoisted_variant
;
1785 hoisted_variant
= value;
1789 public bool IsDeclared
{
1791 return type
!= null;
1795 public bool IsCompilerGenerated
{
1797 return (flags
& Flags
.CompilerGenerated
) != 0;
1801 public bool IsConstant
{
1803 return (flags
& Flags
.Constant
) != 0;
1807 public bool IsLocked
{
1809 return (flags
& Flags
.IsLocked
) != 0;
1812 flags
= value ? flags
| Flags
.IsLocked
: flags
& ~Flags
.IsLocked
;
1816 public bool IsThis
{
1818 return (flags
& Flags
.IsThis
) != 0;
1822 public bool IsFixed
{
1824 return (flags
& Flags
.FixedVariable
) != 0;
1828 bool INamedBlockVariable
.IsParameter
{
1834 public bool IsReadonly
{
1836 return (flags
& Flags
.ReadonlyMask
) != 0;
1840 public Location Location
{
1846 public string Name
{
1852 public TypeSpec Type
{
1863 public void CreateBuilder (EmitContext ec
)
1865 if ((flags
& Flags
.Used
) == 0) {
1866 if (VariableInfo
== null) {
1867 // Missing flow analysis or wrong variable flags
1868 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name
);
1871 if (VariableInfo
.IsEverAssigned
)
1872 ec
.Report
.Warning (219, 3, Location
, "The variable `{0}' is assigned but its value is never used", Name
);
1874 ec
.Report
.Warning (168, 3, Location
, "The variable `{0}' is declared but never used", Name
);
1877 if (HoistedVariant
!= null)
1880 if (builder
!= null) {
1881 if ((flags
& Flags
.CompilerGenerated
) != 0)
1884 // To avoid Used warning duplicates
1885 throw new InternalErrorException ("Already created variable `{0}'", name
);
1889 // All fixed variabled are pinned, a slot has to be alocated
1891 builder
= ec
.DeclareLocal (Type
, IsFixed
);
1892 if (!ec
.HasSet (BuilderContext
.Options
.OmitDebugInfo
) && (flags
& Flags
.CompilerGenerated
) == 0)
1893 ec
.DefineLocalVariable (name
, builder
);
1896 public static LocalVariable
CreateCompilerGenerated (TypeSpec type
, Block block
, Location loc
)
1898 LocalVariable li
= new LocalVariable (block
, GetCompilerGeneratedName (block
), Flags
.CompilerGenerated
| Flags
.Used
, loc
);
1903 public Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
)
1905 if (IsConstant
&& const_value
!= null)
1906 return Constant
.CreateConstantFromValue (Type
, const_value
.GetValue (), loc
);
1908 return new LocalVariableReference (this, loc
);
1911 public void Emit (EmitContext ec
)
1913 // TODO: Need something better for temporary variables
1914 if ((flags
& Flags
.CompilerGenerated
) != 0)
1917 ec
.Emit (OpCodes
.Ldloc
, builder
);
1920 public void EmitAssign (EmitContext ec
)
1922 // TODO: Need something better for temporary variables
1923 if ((flags
& Flags
.CompilerGenerated
) != 0)
1926 ec
.Emit (OpCodes
.Stloc
, builder
);
1929 public void EmitAddressOf (EmitContext ec
)
1931 ec
.Emit (OpCodes
.Ldloca
, builder
);
1934 public static string GetCompilerGeneratedName (Block block
)
1936 // HACK: Debugger depends on the name semantics
1937 return "$locvar" + block
.ParametersBlock
.TemporaryLocalsCount
++.ToString ("X");
1940 public string GetReadOnlyContext ()
1942 switch (flags
& Flags
.ReadonlyMask
) {
1943 case Flags
.FixedVariable
:
1944 return "fixed variable";
1945 case Flags
.ForeachVariable
:
1946 return "foreach iteration variable";
1947 case Flags
.UsingVariable
:
1948 return "using variable";
1951 throw new InternalErrorException ("Variable is not readonly");
1954 public bool IsThisAssigned (BlockContext ec
, Block block
)
1956 if (VariableInfo
== null)
1957 throw new Exception ();
1959 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1962 return VariableInfo
.IsFullyInitialized (ec
, block
.StartLocation
);
1965 public bool IsAssigned (BlockContext ec
)
1967 if (VariableInfo
== null)
1968 throw new Exception ();
1970 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1973 public void PrepareForFlowAnalysis (BlockContext bc
)
1976 // No need for definitely assigned check for these guys
1978 if ((flags
& (Flags
.Constant
| Flags
.ReadonlyMask
| Flags
.CompilerGenerated
)) != 0)
1981 VariableInfo
= new VariableInfo (this, bc
.FlowOffset
);
1982 bc
.FlowOffset
+= VariableInfo
.Length
;
1986 // Mark the variables as referenced in the user code
1988 public void SetIsUsed ()
1990 flags
|= Flags
.Used
;
1993 public void SetHasAddressTaken ()
1995 flags
|= (Flags
.AddressTaken
| Flags
.Used
);
1998 public override string ToString ()
2000 return string.Format ("LocalInfo ({0},{1},{2},{3})", name
, type
, VariableInfo
, Location
);
2005 /// Block represents a C# block.
2009 /// This class is used in a number of places: either to represent
2010 /// explicit blocks that the programmer places or implicit blocks.
2012 /// Implicit blocks are used as labels or to introduce variable
2015 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2016 /// they contain extra information that is not necessary on normal blocks.
2018 public class Block
: Statement
{
2025 HasCapturedVariable
= 64,
2026 HasCapturedThis
= 1 << 7,
2027 IsExpressionTree
= 1 << 8,
2028 CompilerGenerated
= 1 << 9,
2029 HasAsyncModifier
= 1 << 10,
2031 YieldBlock
= 1 << 12,
2032 AwaitBlock
= 1 << 13
2035 public Block Parent
;
2036 public Location StartLocation
;
2037 public Location EndLocation
;
2039 public ExplicitBlock Explicit
;
2040 public ParametersBlock ParametersBlock
;
2042 protected Flags flags
;
2045 // The statements in this block
2047 protected List
<Statement
> statements
;
2049 protected List
<Statement
> scope_initializers
;
2051 int? resolving_init_idx
;
2057 public int ID
= id
++;
2059 static int clone_id_counter
;
2063 // int assignable_slots;
2064 bool unreachable_shown
;
2067 public Block (Block parent
, Location start
, Location end
)
2068 : this (parent
, 0, start
, end
)
2072 public Block (Block parent
, Flags flags
, Location start
, Location end
)
2074 if (parent
!= null) {
2075 // the appropriate constructors will fixup these fields
2076 ParametersBlock
= parent
.ParametersBlock
;
2077 Explicit
= parent
.Explicit
;
2080 this.Parent
= parent
;
2082 this.StartLocation
= start
;
2083 this.EndLocation
= end
;
2085 statements
= new List
<Statement
> (4);
2087 this.original
= this;
2092 public bool HasUnreachableClosingBrace
{
2094 return (flags
& Flags
.HasRet
) != 0;
2097 flags
= value ? flags
| Flags
.HasRet
: flags
& ~Flags
.HasRet
;
2101 public Block Original
{
2110 public bool IsCompilerGenerated
{
2111 get { return (flags & Flags.CompilerGenerated) != 0; }
2112 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2115 public bool Unchecked
{
2116 get { return (flags & Flags.Unchecked) != 0; }
2117 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2120 public bool Unsafe
{
2121 get { return (flags & Flags.Unsafe) != 0; }
2122 set { flags |= Flags.Unsafe; }
2125 public List
<Statement
> Statements
{
2126 get { return statements; }
2131 public Block
CreateSwitchBlock (Location start
)
2133 // FIXME: Only explicit block should be created
2134 var new_block
= new Block (this, start
, start
);
2135 new_block
.IsCompilerGenerated
= true;
2139 public void SetEndLocation (Location loc
)
2144 public void AddLabel (LabeledStatement target
)
2146 ParametersBlock
.TopBlock
.AddLabel (target
.Name
, target
);
2149 public void AddLocalName (LocalVariable li
)
2151 AddLocalName (li
.Name
, li
);
2154 public void AddLocalName (string name
, INamedBlockVariable li
)
2156 ParametersBlock
.TopBlock
.AddLocalName (name
, li
, false);
2159 public virtual void Error_AlreadyDeclared (string name
, INamedBlockVariable variable
, string reason
)
2161 if (reason
== null) {
2162 Error_AlreadyDeclared (name
, variable
);
2166 ParametersBlock
.TopBlock
.Report
.Error (136, variable
.Location
,
2167 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2168 "to `{0}', which is already used in a `{1}' scope to denote something else",
2172 public virtual void Error_AlreadyDeclared (string name
, INamedBlockVariable variable
)
2174 var pi
= variable
as ParametersBlock
.ParameterInfo
;
2176 pi
.Parameter
.Error_DuplicateName (ParametersBlock
.TopBlock
.Report
);
2178 ParametersBlock
.TopBlock
.Report
.Error (128, variable
.Location
,
2179 "A local variable named `{0}' is already defined in this scope", name
);
2183 public virtual void Error_AlreadyDeclaredTypeParameter (string name
, Location loc
)
2185 ParametersBlock
.TopBlock
.Report
.Error (412, loc
,
2186 "The type parameter name `{0}' is the same as local variable or parameter name",
2191 // It should be used by expressions which require to
2192 // register a statement during resolve process.
2194 public void AddScopeStatement (Statement s
)
2196 if (scope_initializers
== null)
2197 scope_initializers
= new List
<Statement
> ();
2200 // Simple recursive helper, when resolve scope initializer another
2201 // new scope initializer can be added, this ensures it's initialized
2202 // before existing one. For now this can happen with expression trees
2203 // in base ctor initializer only
2205 if (resolving_init_idx
.HasValue
) {
2206 scope_initializers
.Insert (resolving_init_idx
.Value
, s
);
2207 ++resolving_init_idx
;
2209 scope_initializers
.Add (s
);
2213 public void AddStatement (Statement s
)
2218 public int AssignableSlots
{
2220 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2222 // return assignable_slots;
2226 public LabeledStatement
LookupLabel (string name
)
2228 return ParametersBlock
.TopBlock
.GetLabel (name
, this);
2231 public override bool Resolve (BlockContext ec
)
2233 if ((flags
& Flags
.Resolved
) != 0)
2236 Block prev_block
= ec
.CurrentBlock
;
2239 ec
.CurrentBlock
= this;
2240 ec
.StartFlowBranching (this);
2243 // Compiler generated scope statements
2245 if (scope_initializers
!= null) {
2246 for (resolving_init_idx
= 0; resolving_init_idx
< scope_initializers
.Count
; ++resolving_init_idx
) {
2247 scope_initializers
[resolving_init_idx
.Value
].Resolve (ec
);
2250 resolving_init_idx
= null;
2254 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2255 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2256 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2257 // responsible for handling the situation.
2259 int statement_count
= statements
.Count
;
2260 for (int ix
= 0; ix
< statement_count
; ix
++){
2261 Statement s
= statements
[ix
];
2264 // Warn if we detect unreachable code.
2267 if (s
is EmptyStatement
)
2270 if (!unreachable_shown
&& !(s
is LabeledStatement
)) {
2271 ec
.Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
2272 unreachable_shown
= true;
2275 Block c_block
= s
as Block
;
2276 if (c_block
!= null)
2277 c_block
.unreachable
= c_block
.unreachable_shown
= true;
2281 // Note that we're not using ResolveUnreachable() for unreachable
2282 // statements here. ResolveUnreachable() creates a temporary
2283 // flow branching and kills it afterwards. This leads to problems
2284 // if you have two unreachable statements where the first one
2285 // assigns a variable and the second one tries to access it.
2288 if (!s
.Resolve (ec
)) {
2290 if (ec
.IsInProbingMode
)
2293 statements
[ix
] = new EmptyStatement (s
.loc
);
2297 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
2298 statements
[ix
] = new EmptyStatement (s
.loc
);
2300 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
2301 if (unreachable
&& s
is LabeledStatement
)
2302 throw new InternalErrorException ("should not happen");
2305 while (ec
.CurrentBranching
is FlowBranchingLabeled
)
2306 ec
.EndFlowBranching ();
2308 bool flow_unreachable
= ec
.EndFlowBranching ();
2310 ec
.CurrentBlock
= prev_block
;
2312 if (flow_unreachable
)
2313 flags
|= Flags
.HasRet
;
2315 // If we're a non-static `struct' constructor which doesn't have an
2316 // initializer, then we must initialize all of the struct's fields.
2317 if (this == ParametersBlock
.TopBlock
&& !ParametersBlock
.TopBlock
.IsThisAssigned (ec
) && !flow_unreachable
)
2320 flags
|= Flags
.Resolved
;
2324 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
2326 unreachable_shown
= true;
2330 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
2332 var fb
= ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2333 fb
.CurrentUsageVector
.IsUnreachable
= true;
2334 bool ok
= Resolve (ec
);
2335 ec
.KillFlowBranching ();
2340 protected override void DoEmit (EmitContext ec
)
2342 for (int ix
= 0; ix
< statements
.Count
; ix
++){
2343 statements
[ix
].Emit (ec
);
2347 public override void Emit (EmitContext ec
)
2349 if (scope_initializers
!= null)
2350 EmitScopeInitializers (ec
);
2355 protected void EmitScopeInitializers (EmitContext ec
)
2357 foreach (Statement s
in scope_initializers
)
2362 public override string ToString ()
2364 return String
.Format ("{0} ({1}:{2})", GetType (), ID
, StartLocation
);
2368 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2370 Block target
= (Block
) t
;
2372 target
.clone_id
= clone_id_counter
++;
2375 clonectx
.AddBlockMap (this, target
);
2376 if (original
!= this)
2377 clonectx
.AddBlockMap (original
, target
);
2379 target
.ParametersBlock
= (ParametersBlock
) (ParametersBlock
== this ? target
: clonectx
.RemapBlockCopy (ParametersBlock
));
2380 target
.Explicit
= (ExplicitBlock
) (Explicit
== this ? target
: clonectx
.LookupBlock (Explicit
));
2383 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
2385 target
.statements
= new List
<Statement
> (statements
.Count
);
2386 foreach (Statement s
in statements
)
2387 target
.statements
.Add (s
.Clone (clonectx
));
2390 public override object Accept (StructuralVisitor visitor
)
2392 return visitor
.Visit (this);
2396 public class ExplicitBlock
: Block
2398 protected AnonymousMethodStorey am_storey
;
2400 public ExplicitBlock (Block parent
, Location start
, Location end
)
2401 : this (parent
, (Flags
) 0, start
, end
)
2405 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
2406 : base (parent
, flags
, start
, end
)
2408 this.Explicit
= this;
2413 public AnonymousMethodStorey AnonymousMethodStorey
{
2419 public bool HasAwait
{
2421 return (flags
& Flags
.AwaitBlock
) != 0;
2425 public bool HasCapturedThis
{
2426 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2428 return (flags
& Flags
.HasCapturedThis
) != 0;
2432 public bool HasCapturedVariable
{
2434 flags
= value ? flags
| Flags
.HasCapturedVariable
: flags
& ~Flags
.HasCapturedVariable
;
2437 return (flags
& Flags
.HasCapturedVariable
) != 0;
2441 public bool HasYield
{
2443 return (flags
& Flags
.YieldBlock
) != 0;
2450 // Creates anonymous method storey in current block
2452 public AnonymousMethodStorey
CreateAnonymousMethodStorey (ResolveContext ec
)
2455 // Return same story for iterator and async blocks unless we are
2456 // in nested anonymous method
2458 if (ec
.CurrentAnonymousMethod
is StateMachineInitializer
&& ParametersBlock
.Original
== ec
.CurrentAnonymousMethod
.Block
.Original
)
2459 return ec
.CurrentAnonymousMethod
.Storey
;
2462 // When referencing a variable in parent iterator/async storey
2463 // from nested anonymous method
2465 if (ParametersBlock
.am_storey
is StateMachine
) {
2466 return ParametersBlock
.am_storey
;
2469 if (am_storey
== null) {
2470 MemberBase mc
= ec
.MemberContext
as MemberBase
;
2473 // Creates anonymous method storey for this block
2475 am_storey
= new AnonymousMethodStorey (this, ec
.CurrentMemberDefinition
.Parent
.PartialContainer
, mc
, ec
.CurrentTypeParameters
, "AnonStorey", MemberKind
.Class
);
2481 public override void Emit (EmitContext ec
)
2483 // TODO: The is check should go once state machine is fully separated
2484 if (am_storey
!= null && !(am_storey
is StateMachine
)) {
2485 DefineStoreyContainer (ec
, am_storey
);
2486 am_storey
.EmitStoreyInstantiation (ec
, this);
2489 if (scope_initializers
!= null)
2490 EmitScopeInitializers (ec
);
2492 if (ec
.EmitAccurateDebugInfo
&& !IsCompilerGenerated
&& ec
.Mark (StartLocation
)) {
2493 ec
.Emit (OpCodes
.Nop
);
2504 if (ec
.EmitAccurateDebugInfo
&& !HasUnreachableClosingBrace
&& !IsCompilerGenerated
&& ec
.Mark (EndLocation
)) {
2505 ec
.Emit (OpCodes
.Nop
);
2509 protected void DefineStoreyContainer (EmitContext ec
, AnonymousMethodStorey storey
)
2512 // Creates anonymous method storey
2514 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
2516 // Creates parent storey reference when hoisted this is accessible
2518 if (storey
.OriginalSourceBlock
.Explicit
.HasCapturedThis
) {
2519 ExplicitBlock parent
= storey
.OriginalSourceBlock
.Explicit
.Parent
.Explicit
;
2522 // Hoisted this exists in top-level parent storey only
2524 while (parent
.AnonymousMethodStorey
== null || parent
.AnonymousMethodStorey
.Parent
is AnonymousMethodStorey
)
2525 parent
= parent
.Parent
.Explicit
;
2527 storey
.AddParentStoreyReference (ec
, parent
.AnonymousMethodStorey
);
2530 storey
.SetNestedStoryParent (ec
.CurrentAnonymousMethod
.Storey
);
2532 // TODO MemberCache: Review
2533 storey
.Mutator
= ec
.CurrentAnonymousMethod
.Storey
.Mutator
;
2536 storey
.CreateContainer ();
2537 storey
.DefineContainer ();
2539 var ref_blocks
= storey
.ReferencesFromChildrenBlock
;
2540 if (ref_blocks
!= null) {
2541 foreach (ExplicitBlock ref_block
in ref_blocks
) {
2542 for (ExplicitBlock b
= ref_block
.Explicit
; b
.AnonymousMethodStorey
!= storey
; b
= b
.Parent
.Explicit
) {
2543 if (b
.AnonymousMethodStorey
!= null) {
2544 b
.AnonymousMethodStorey
.AddParentStoreyReference (ec
, storey
);
2546 // Stop propagation inside same top block
2547 if (b
.ParametersBlock
.Original
== ParametersBlock
.Original
)
2550 b
= b
.ParametersBlock
;
2553 b
.HasCapturedVariable
= true;
2559 storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (storey
);
2562 public void RegisterAsyncAwait ()
2565 while ((block
.flags
& Flags
.AwaitBlock
) == 0) {
2566 block
.flags
|= Flags
.AwaitBlock
;
2568 if (block
is ParametersBlock
)
2571 block
= block
.Parent
.Explicit
;
2575 public void RegisterIteratorYield ()
2578 while ((block
.flags
& Flags
.YieldBlock
) == 0) {
2579 block
.flags
|= Flags
.YieldBlock
;
2581 if (block
.Parent
== null)
2584 block
= block
.Parent
.Explicit
;
2588 public void WrapIntoDestructor (TryFinally tf
, ExplicitBlock tryBlock
)
2590 tryBlock
.statements
= statements
;
2591 statements
= new List
<Statement
> (1);
2592 statements
.Add (tf
);
2597 // ParametersBlock was introduced to support anonymous methods
2598 // and lambda expressions
2600 public class ParametersBlock
: ExplicitBlock
2602 public class ParameterInfo
: INamedBlockVariable
2604 readonly ParametersBlock block
;
2606 public VariableInfo VariableInfo
;
2609 public ParameterInfo (ParametersBlock block
, int index
)
2617 public Block Block
{
2623 public bool IsDeclared
{
2629 public bool IsParameter
{
2635 public bool IsLocked
{
2644 public Location Location
{
2646 return Parameter
.Location
;
2650 public Parameter Parameter
{
2652 return block
.Parameters
[index
];
2656 public TypeSpec ParameterType
{
2658 return Parameter
.Type
;
2664 public Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
)
2666 return new ParameterReference (this, loc
);
2671 // Block is converted into an expression
2673 sealed class BlockScopeExpression
: Expression
2676 readonly ParametersBlock block
;
2678 public BlockScopeExpression (Expression child
, ParametersBlock block
)
2684 public override bool ContainsEmitWithAwait ()
2686 return child
.ContainsEmitWithAwait ();
2689 public override Expression
CreateExpressionTree (ResolveContext ec
)
2691 throw new NotSupportedException ();
2694 protected override Expression
DoResolve (ResolveContext ec
)
2699 child
= child
.Resolve (ec
);
2703 eclass
= child
.eclass
;
2708 public override void Emit (EmitContext ec
)
2710 block
.EmitScopeInitializers (ec
);
2715 protected ParametersCompiled parameters
;
2716 protected ParameterInfo
[] parameter_info
;
2718 protected bool unreachable
;
2719 protected ToplevelBlock top_block
;
2720 protected StateMachine state_machine
;
2722 public ParametersBlock (Block parent
, ParametersCompiled parameters
, Location start
)
2723 : base (parent
, 0, start
, start
)
2725 if (parameters
== null)
2726 throw new ArgumentNullException ("parameters");
2728 this.parameters
= parameters
;
2729 ParametersBlock
= this;
2731 flags
|= (parent
.ParametersBlock
.flags
& (Flags
.YieldBlock
| Flags
.AwaitBlock
));
2733 this.top_block
= parent
.ParametersBlock
.top_block
;
2734 ProcessParameters ();
2737 protected ParametersBlock (ParametersCompiled parameters
, Location start
)
2738 : base (null, 0, start
, start
)
2740 if (parameters
== null)
2741 throw new ArgumentNullException ("parameters");
2743 this.parameters
= parameters
;
2744 ParametersBlock
= this;
2748 // It's supposed to be used by method body implementation of anonymous methods
2750 protected ParametersBlock (ParametersBlock source
, ParametersCompiled parameters
)
2751 : base (null, 0, source
.StartLocation
, source
.EndLocation
)
2753 this.parameters
= parameters
;
2754 this.statements
= source
.statements
;
2755 this.scope_initializers
= source
.scope_initializers
;
2757 this.resolved
= true;
2758 this.unreachable
= source
.unreachable
;
2759 this.am_storey
= source
.am_storey
;
2760 this.state_machine
= source
.state_machine
;
2762 ParametersBlock
= this;
2765 // Overwrite original for comparison purposes when linking cross references
2766 // between anonymous methods
2773 public bool IsAsync
{
2775 return (flags
& Flags
.HasAsyncModifier
) != 0;
2778 flags
= value ? flags
| Flags
.HasAsyncModifier
: flags
& ~Flags
.HasAsyncModifier
;
2783 // Block has been converted to expression tree
2785 public bool IsExpressionTree
{
2787 return (flags
& Flags
.IsExpressionTree
) != 0;
2792 // The parameters for the block.
2794 public ParametersCompiled Parameters
{
2800 public ToplevelBlock TopBlock
{
2806 public bool Resolved
{
2808 return (flags
& Flags
.Resolved
) != 0;
2812 public int TemporaryLocalsCount { get; set; }
2817 // Check whether all `out' parameters have been assigned.
2819 public void CheckOutParameters (FlowBranching
.UsageVector vector
)
2821 if (vector
.IsUnreachable
)
2824 int n
= parameter_info
== null ? 0 : parameter_info
.Length
;
2826 for (int i
= 0; i
< n
; i
++) {
2827 VariableInfo
var = parameter_info
[i
].VariableInfo
;
2832 if (vector
.IsAssigned (var, false))
2835 var p
= parameter_info
[i
].Parameter
;
2836 TopBlock
.Report
.Error (177, p
.Location
,
2837 "The out parameter `{0}' must be assigned to before control leaves the current method",
2842 public override Expression
CreateExpressionTree (ResolveContext ec
)
2844 if (statements
.Count
== 1) {
2845 Expression expr
= ((Statement
) statements
[0]).CreateExpressionTree (ec
);
2846 if (scope_initializers
!= null)
2847 expr
= new BlockScopeExpression (expr
, this);
2852 return base.CreateExpressionTree (ec
);
2855 public override void Emit (EmitContext ec
)
2857 if (state_machine
!= null) {
2858 DefineStoreyContainer (ec
, state_machine
);
2859 state_machine
.EmitStoreyInstantiation (ec
, this);
2865 public void EmitEmbedded (EmitContext ec
)
2867 if (state_machine
!= null) {
2868 DefineStoreyContainer (ec
, state_machine
);
2869 state_machine
.EmitStoreyInstantiation (ec
, this);
2875 public ParameterInfo
GetParameterInfo (Parameter p
)
2877 for (int i
= 0; i
< parameters
.Count
; ++i
) {
2878 if (parameters
[i
] == p
)
2879 return parameter_info
[i
];
2882 throw new ArgumentException ("Invalid parameter");
2885 public ParameterReference
GetParameterReference (int index
, Location loc
)
2887 return new ParameterReference (parameter_info
[index
], loc
);
2890 public Statement
PerformClone ()
2892 CloneContext clonectx
= new CloneContext ();
2893 return Clone (clonectx
);
2896 protected void ProcessParameters ()
2898 if (parameters
.Count
== 0)
2901 parameter_info
= new ParameterInfo
[parameters
.Count
];
2902 for (int i
= 0; i
< parameter_info
.Length
; ++i
) {
2903 var p
= parameters
.FixedParameters
[i
];
2907 // TODO: Should use Parameter only and more block there
2908 parameter_info
[i
] = new ParameterInfo (this, i
);
2910 AddLocalName (p
.Name
, parameter_info
[i
]);
2914 public bool Resolve (FlowBranching parent
, BlockContext rc
, IMethodData md
)
2921 if (rc
.HasSet (ResolveContext
.Options
.ExpressionTreeConversion
))
2922 flags
|= Flags
.IsExpressionTree
;
2927 using (rc
.With (ResolveContext
.Options
.DoFlowAnalysis
, true)) {
2928 FlowBranchingToplevel top_level
= rc
.StartFlowBranching (this, parent
);
2933 unreachable
= top_level
.End ();
2935 } catch (Exception e
) {
2936 if (e
is CompletionResult
|| rc
.Report
.IsDisabled
|| e
is FatalException
)
2939 if (rc
.CurrentBlock
!= null) {
2940 rc
.Report
.Error (584, rc
.CurrentBlock
.StartLocation
, "Internal compiler error: {0}", e
.Message
);
2942 rc
.Report
.Error (587, "Internal compiler error: {0}", e
.Message
);
2945 if (rc
.Module
.Compiler
.Settings
.DebugFlags
> 0)
2949 if (rc
.ReturnType
.Kind
!= MemberKind
.Void
&& !unreachable
) {
2950 if (rc
.CurrentAnonymousMethod
== null) {
2951 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2952 if (md
is StateMachineMethod
) {
2955 rc
.Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
2960 // If an asynchronous body of F is either an expression classified as nothing, or a
2961 // statement block where no return statements have expressions, the inferred return type is Task
2964 var am
= rc
.CurrentAnonymousMethod
as AnonymousMethodBody
;
2965 if (am
!= null && am
.ReturnTypeInference
!= null && !am
.ReturnTypeInference
.HasBounds (0)) {
2966 am
.ReturnTypeInference
= null;
2967 am
.ReturnType
= rc
.Module
.PredefinedTypes
.Task
.TypeSpec
;
2972 rc
.Report
.Error (1643, rc
.CurrentAnonymousMethod
.Location
, "Not all code paths return a value in anonymous method of type `{0}'",
2973 rc
.CurrentAnonymousMethod
.GetSignatureForError ());
2981 void ResolveMeta (BlockContext ec
)
2983 int orig_count
= parameters
.Count
;
2985 for (int i
= 0; i
< orig_count
; ++i
) {
2986 Parameter
.Modifier mod
= parameters
.FixedParameters
[i
].ModFlags
;
2988 if ((mod
& Parameter
.Modifier
.OUT
) == 0)
2991 VariableInfo vi
= new VariableInfo (parameters
, i
, ec
.FlowOffset
);
2992 parameter_info
[i
].VariableInfo
= vi
;
2993 ec
.FlowOffset
+= vi
.Length
;
2997 public ToplevelBlock
ConvertToIterator (IMethodData method
, TypeDefinition host
, TypeSpec iterator_type
, bool is_enumerable
)
2999 var iterator
= new Iterator (this, method
, host
, iterator_type
, is_enumerable
);
3000 var stateMachine
= new IteratorStorey (iterator
);
3002 am_storey
= stateMachine
;
3003 iterator
.SetStateMachine (stateMachine
);
3005 var tlb
= new ToplevelBlock (host
.Compiler
, Parameters
, Location
.Null
);
3006 tlb
.IsCompilerGenerated
= true;
3007 tlb
.state_machine
= stateMachine
;
3008 tlb
.AddStatement (new Return (iterator
, iterator
.Location
));
3012 public ParametersBlock
ConvertToAsyncTask (IMemberContext context
, TypeDefinition host
, ParametersCompiled parameters
, TypeSpec returnType
)
3014 for (int i
= 0; i
< parameters
.Count
; i
++) {
3015 Parameter p
= parameters
[i
];
3016 Parameter
.Modifier mod
= p
.ModFlags
;
3017 if ((mod
& Parameter
.Modifier
.RefOutMask
) != 0) {
3018 host
.Compiler
.Report
.Error (1988, p
.Location
,
3019 "Async methods cannot have ref or out parameters");
3023 if (p
is ArglistParameter
) {
3024 host
.Compiler
.Report
.Error (4006, p
.Location
,
3025 "__arglist is not allowed in parameter list of async methods");
3029 if (parameters
.Types
[i
].IsPointer
) {
3030 host
.Compiler
.Report
.Error (4005, p
.Location
,
3031 "Async methods cannot have unsafe parameters");
3037 host
.Compiler
.Report
.Warning (1998, 1, loc
,
3038 "Async block lacks `await' operator and will run synchronously");
3041 var block_type
= host
.Module
.Compiler
.BuiltinTypes
.Void
;
3042 var initializer
= new AsyncInitializer (this, host
, block_type
);
3043 initializer
.Type
= block_type
;
3045 var stateMachine
= new AsyncTaskStorey (this, context
, initializer
, returnType
);
3047 am_storey
= stateMachine
;
3048 initializer
.SetStateMachine (stateMachine
);
3050 var b
= this is ToplevelBlock
?
3051 new ToplevelBlock (host
.Compiler
, Parameters
, Location
.Null
) :
3052 new ParametersBlock (Parent
, parameters
, Location
.Null
) {
3057 b
.IsCompilerGenerated
= true;
3058 b
.state_machine
= stateMachine
;
3059 b
.AddStatement (new StatementExpression (initializer
));
3067 public class ToplevelBlock
: ParametersBlock
3069 LocalVariable this_variable
;
3070 CompilerContext compiler
;
3071 Dictionary
<string, object> names
;
3072 Dictionary
<string, object> labels
;
3074 public HoistedVariable HoistedThisVariable
;
3076 public Report Report
{
3077 get { return compiler.Report; }
3080 public ToplevelBlock (CompilerContext ctx
, Location loc
)
3081 : this (ctx
, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
3085 public ToplevelBlock (CompilerContext ctx
, ParametersCompiled parameters
, Location start
)
3086 : base (parameters
, start
)
3088 this.compiler
= ctx
;
3090 flags
|= Flags
.HasRet
;
3092 ProcessParameters ();
3096 // Recreates a top level block from parameters block. Used for
3097 // compiler generated methods where the original block comes from
3098 // explicit child block. This works for already resolved blocks
3099 // only to ensure we resolve them in the correct flow order
3101 public ToplevelBlock (ParametersBlock source
, ParametersCompiled parameters
)
3102 : base (source
, parameters
)
3104 this.compiler
= source
.TopBlock
.compiler
;
3106 flags
|= Flags
.HasRet
;
3109 public bool IsIterator
{
3115 public void AddLocalName (string name
, INamedBlockVariable li
, bool ignoreChildrenBlocks
)
3118 names
= new Dictionary
<string, object> ();
3121 if (!names
.TryGetValue (name
, out value)) {
3122 names
.Add (name
, li
);
3126 INamedBlockVariable existing
= value as INamedBlockVariable
;
3127 List
<INamedBlockVariable
> existing_list
;
3128 if (existing
!= null) {
3129 existing_list
= new List
<INamedBlockVariable
> ();
3130 existing_list
.Add (existing
);
3131 names
[name
] = existing_list
;
3133 existing_list
= (List
<INamedBlockVariable
>) value;
3137 // A collision checking between local names
3139 for (int i
= 0; i
< existing_list
.Count
; ++i
) {
3140 existing
= existing_list
[i
];
3141 Block b
= existing
.Block
.Explicit
;
3143 // Collision at same level
3144 if (li
.Block
.Explicit
== b
) {
3145 li
.Block
.Error_AlreadyDeclared (name
, li
);
3149 // Collision with parent
3150 Block parent
= li
.Block
.Explicit
;
3151 while ((parent
= parent
.Parent
) != null) {
3153 li
.Block
.Error_AlreadyDeclared (name
, li
, "parent or current");
3154 i
= existing_list
.Count
;
3159 if (!ignoreChildrenBlocks
) {
3160 // Collision with children
3161 while ((b
= b
.Parent
) != null) {
3162 if (li
.Block
.Explicit
== b
) {
3163 li
.Block
.Error_AlreadyDeclared (name
, li
, "child");
3164 i
= existing_list
.Count
;
3171 existing_list
.Add (li
);
3174 public void AddLabel (string name
, LabeledStatement label
)
3177 labels
= new Dictionary
<string, object> ();
3180 if (!labels
.TryGetValue (name
, out value)) {
3181 labels
.Add (name
, label
);
3185 LabeledStatement existing
= value as LabeledStatement
;
3186 List
<LabeledStatement
> existing_list
;
3187 if (existing
!= null) {
3188 existing_list
= new List
<LabeledStatement
> ();
3189 existing_list
.Add (existing
);
3190 labels
[name
] = existing_list
;
3192 existing_list
= (List
<LabeledStatement
>) value;
3196 // A collision checking between labels
3198 for (int i
= 0; i
< existing_list
.Count
; ++i
) {
3199 existing
= existing_list
[i
];
3200 Block b
= existing
.Block
;
3202 // Collision at same level
3203 if (label
.Block
== b
) {
3204 Report
.SymbolRelatedToPreviousError (existing
.loc
, name
);
3205 Report
.Error (140, label
.loc
, "The label `{0}' is a duplicate", name
);
3209 // Collision with parent
3211 while ((b
= b
.Parent
) != null) {
3212 if (existing
.Block
== b
) {
3213 Report
.Error (158, label
.loc
,
3214 "The label `{0}' shadows another label by the same name in a contained scope", name
);
3215 i
= existing_list
.Count
;
3220 // Collision with with children
3222 while ((b
= b
.Parent
) != null) {
3223 if (label
.Block
== b
) {
3224 Report
.Error (158, label
.loc
,
3225 "The label `{0}' shadows another label by the same name in a contained scope", name
);
3226 i
= existing_list
.Count
;
3232 existing_list
.Add (label
);
3236 // Creates an arguments set from all parameters, useful for method proxy calls
3238 public Arguments
GetAllParametersArguments ()
3240 int count
= parameters
.Count
;
3241 Arguments args
= new Arguments (count
);
3242 for (int i
= 0; i
< count
; ++i
) {
3243 var arg_expr
= GetParameterReference (i
, parameter_info
[i
].Location
);
3244 args
.Add (new Argument (arg_expr
));
3251 // Lookup inside a block, the returned value can represent 3 states
3253 // true+variable: A local name was found and it's valid
3254 // false+variable: A local name was found in a child block only
3255 // false+null: No local name was found
3257 public bool GetLocalName (string name
, Block block
, ref INamedBlockVariable variable
)
3263 if (!names
.TryGetValue (name
, out value))
3266 variable
= value as INamedBlockVariable
;
3268 if (variable
!= null) {
3270 if (variable
.Block
== b
.Original
)
3274 } while (b
!= null);
3282 } while (b
!= null);
3284 List
<INamedBlockVariable
> list
= (List
<INamedBlockVariable
>) value;
3285 for (int i
= 0; i
< list
.Count
; ++i
) {
3288 if (variable
.Block
== b
.Original
)
3292 } while (b
!= null);
3300 } while (b
!= null);
3310 public LabeledStatement
GetLabel (string name
, Block block
)
3316 if (!labels
.TryGetValue (name
, out value)) {
3320 var label
= value as LabeledStatement
;
3322 if (label
!= null) {
3323 if (label
.Block
== b
.Original
)
3326 // TODO: Temporary workaround for the switch block implicit label block
3327 if (label
.Block
.IsCompilerGenerated
&& label
.Block
.Parent
== b
.Original
)
3330 List
<LabeledStatement
> list
= (List
<LabeledStatement
>) value;
3331 for (int i
= 0; i
< list
.Count
; ++i
) {
3333 if (label
.Block
== b
.Original
)
3336 // TODO: Temporary workaround for the switch block implicit label block
3337 if (label
.Block
.IsCompilerGenerated
&& label
.Block
.Parent
== b
.Original
)
3346 // Returns the "this" instance variable of this block.
3347 // See AddThisVariable() for more information.
3349 public LocalVariable ThisVariable
{
3350 get { return this_variable; }
3354 // This is used by non-static `struct' constructors which do not have an
3355 // initializer - in this case, the constructor must initialize all of the
3356 // struct's fields. To do this, we add a "this" variable and use the flow
3357 // analysis code to ensure that it's been fully initialized before control
3358 // leaves the constructor.
3360 public void AddThisVariable (BlockContext bc
)
3362 if (this_variable
!= null)
3363 throw new InternalErrorException (StartLocation
.ToString ());
3365 this_variable
= new LocalVariable (this, "this", LocalVariable
.Flags
.IsThis
| LocalVariable
.Flags
.Used
, StartLocation
);
3366 this_variable
.Type
= bc
.CurrentType
;
3367 this_variable
.PrepareForFlowAnalysis (bc
);
3370 public bool IsThisAssigned (BlockContext ec
)
3372 return this_variable
== null || this_variable
.IsThisAssigned (ec
, this);
3375 public override void Emit (EmitContext ec
)
3377 if (Report
.Errors
> 0)
3383 if (IsCompilerGenerated
) {
3384 using (ec
.With (BuilderContext
.Options
.OmitDebugInfo
, true)) {
3392 // If `HasReturnLabel' is set, then we already emitted a
3393 // jump to the end of the method, so we must emit a `ret'
3396 // Unfortunately, System.Reflection.Emit automatically emits
3397 // a leave to the end of a finally block. This is a problem
3398 // if no code is following the try/finally block since we may
3399 // jump to a point after the end of the method.
3400 // As a workaround, we're always creating a return label in
3403 if (ec
.HasReturnLabel
|| !unreachable
) {
3404 if (ec
.HasReturnLabel
)
3405 ec
.MarkLabel (ec
.ReturnLabel
);
3407 if (ec
.EmitAccurateDebugInfo
&& !IsCompilerGenerated
)
3408 ec
.Mark (EndLocation
);
3410 if (ec
.ReturnType
.Kind
!= MemberKind
.Void
)
3411 ec
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
3413 ec
.Emit (OpCodes
.Ret
);
3417 } catch (Exception e
){
3418 Console
.WriteLine ("Exception caught by the compiler while emitting:");
3419 Console
.WriteLine (" Block that caused the problem begin at: " + block
.loc
);
3421 Console
.WriteLine (e
.GetType ().FullName
+ ": " + e
.Message
);
3428 public class SwitchLabel
{
3431 readonly Location loc
;
3436 // if expr == null, then it is the default case.
3438 public SwitchLabel (Expression expr
, Location l
)
3444 public bool IsDefault
{
3446 return label
== null;
3450 public Expression Label
{
3456 public Location Location
{
3462 public Constant Converted
{
3471 public Label
GetILLabel (EmitContext ec
)
3473 if (il_label
== null){
3474 il_label
= ec
.DefineLabel ();
3477 return il_label
.Value
;
3481 // Resolves the expression, reduces it to a literal if possible
3482 // and then converts it to the requested type.
3484 public bool ResolveAndReduce (ResolveContext ec
, TypeSpec required_type
, bool allow_nullable
)
3486 Expression e
= label
.Resolve (ec
);
3491 Constant c
= e
as Constant
;
3493 ec
.Report
.Error (150, loc
, "A constant value is expected");
3497 if (allow_nullable
&& c
is NullLiteral
) {
3502 converted
= c
.ImplicitConversionRequired (ec
, required_type
, loc
);
3503 return converted
!= null;
3506 public void Error_AlreadyOccurs (ResolveContext ec
, TypeSpec switch_type
, SwitchLabel collision_with
)
3509 if (converted
== null)
3512 label
= converted
.GetValueAsLiteral ();
3514 ec
.Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
3515 ec
.Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
3518 public SwitchLabel
Clone (CloneContext clonectx
)
3523 return new SwitchLabel (label
.Clone (clonectx
), loc
);
3527 public class SwitchSection
{
3528 public readonly List
<SwitchLabel
> Labels
;
3529 public readonly Block Block
;
3531 public SwitchSection (List
<SwitchLabel
> labels
, Block block
)
3537 public SwitchSection
Clone (CloneContext clonectx
)
3539 var cloned_labels
= new List
<SwitchLabel
> ();
3541 foreach (SwitchLabel sl
in Labels
)
3542 cloned_labels
.Add (sl
.Clone (clonectx
));
3544 return new SwitchSection (cloned_labels
, clonectx
.LookupBlock (Block
));
3548 public class Switch
: Statement
3550 // structure used to hold blocks of keys while calculating table switch
3551 sealed class LabelsRange
: IComparable
<LabelsRange
>
3553 public readonly long min
;
3555 public readonly List
<long> label_values
;
3557 public LabelsRange (long value)
3560 label_values
= new List
<long> ();
3561 label_values
.Add (value);
3564 public LabelsRange (long min
, long max
, ICollection
<long> values
)
3568 this.label_values
= new List
<long> (values
);
3573 return max
- min
+ 1;
3577 public bool AddValue (long value)
3579 var gap
= value - min
+ 1;
3580 // Ensure the range has > 50% occupancy
3581 if (gap
> 2 * (label_values
.Count
+ 1) || gap
<= 0)
3585 label_values
.Add (value);
3589 public int CompareTo (LabelsRange other
)
3591 int nLength
= label_values
.Count
;
3592 int nLengthOther
= other
.label_values
.Count
;
3593 if (nLengthOther
== nLength
)
3594 return (int) (other
.min
- min
);
3596 return nLength
- nLengthOther
;
3600 sealed class LabelMarker
: Statement
3603 readonly List
<SwitchLabel
> labels
;
3605 public LabelMarker (Switch s
, List
<SwitchLabel
> labels
)
3608 this.labels
= labels
;
3611 protected override void CloneTo (CloneContext clonectx
, Statement target
)
3615 protected override void DoEmit (EmitContext ec
)
3617 foreach (var l
in labels
) {
3619 ec
.MarkLabel (s
.DefaultLabel
);
3621 ec
.MarkLabel (l
.GetILLabel (ec
));
3626 public List
<SwitchSection
> Sections
;
3627 public Expression Expr
;
3630 // Mapping of all labels to their SwitchLabels
3632 Dictionary
<long, SwitchLabel
> labels
;
3633 Dictionary
<string, SwitchLabel
> string_labels
;
3636 /// The governing switch type
3638 public TypeSpec SwitchType
;
3643 Label default_target
;
3645 Expression new_expr
;
3648 SwitchSection constant_section
;
3649 SwitchSection default_section
;
3650 SwitchLabel null_section
;
3652 Statement simple_stmt
;
3653 VariableReference
value;
3654 ExpressionStatement string_dictionary
;
3655 FieldExpr switch_cache_field
;
3656 ExplicitBlock block
;
3659 // Nullable Types support
3661 Nullable
.Unwrap unwrap
;
3663 public Switch (Expression e
, ExplicitBlock block
, List
<SwitchSection
> sects
, Location l
)
3671 public ExplicitBlock Block
{
3677 public Label DefaultLabel
{
3679 return default_target
;
3683 public bool GotDefault
{
3685 return default_section
!= null;
3689 public bool IsNullable
{
3691 return unwrap
!= null;
3696 // Determines the governing type for a switch. The returned
3697 // expression might be the expression from the switch, or an
3698 // expression that includes any potential conversions to
3700 Expression
SwitchGoverningType (ResolveContext ec
, Expression expr
)
3702 switch (expr
.Type
.BuiltinType
) {
3703 case BuiltinTypeSpec
.Type
.Byte
:
3704 case BuiltinTypeSpec
.Type
.SByte
:
3705 case BuiltinTypeSpec
.Type
.UShort
:
3706 case BuiltinTypeSpec
.Type
.Short
:
3707 case BuiltinTypeSpec
.Type
.UInt
:
3708 case BuiltinTypeSpec
.Type
.Int
:
3709 case BuiltinTypeSpec
.Type
.ULong
:
3710 case BuiltinTypeSpec
.Type
.Long
:
3711 case BuiltinTypeSpec
.Type
.Char
:
3712 case BuiltinTypeSpec
.Type
.String
:
3713 case BuiltinTypeSpec
.Type
.Bool
:
3717 if (expr
.Type
.IsEnum
)
3721 // Try to find a *user* defined implicit conversion.
3723 // If there is no implicit conversion, or if there are multiple
3724 // conversions, we have to report an error
3726 Expression converted
= null;
3727 foreach (TypeSpec tt
in ec
.BuiltinTypes
.SwitchUserTypes
) {
3730 e
= Convert
.ImplicitUserConversion (ec
, expr
, tt
, loc
);
3735 // Ignore over-worked ImplicitUserConversions that do
3736 // an implicit conversion in addition to the user conversion.
3738 if (!(e
is UserCast
))
3741 if (converted
!= null){
3742 ec
.Report
.ExtraInformation (loc
, "(Ambiguous implicit user defined conversion in previous ");
3751 public static TypeSpec
[] CreateSwitchUserTypes (BuiltinTypes types
)
3753 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3769 // Performs the basic sanity checks on the switch statement
3770 // (looks for duplicate keys and non-constant expressions).
3772 // It also returns a hashtable with the keys that we will later
3773 // use to compute the switch tables
3775 bool CheckSwitch (ResolveContext ec
)
3778 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.String
)
3779 string_labels
= new Dictionary
<string, SwitchLabel
> (Sections
.Count
+ 1);
3781 labels
= new Dictionary
<long, SwitchLabel
> (Sections
.Count
+ 1);
3783 foreach (SwitchSection ss
in Sections
){
3784 foreach (SwitchLabel sl
in ss
.Labels
){
3786 if (default_section
!= null){
3787 sl
.Error_AlreadyOccurs (ec
, SwitchType
, default_section
.Labels
[0]);
3790 default_section
= ss
;
3794 if (!sl
.ResolveAndReduce (ec
, SwitchType
, IsNullable
)) {
3800 if (string_labels
!= null) {
3801 string s
= sl
.Converted
.GetValue () as string;
3805 string_labels
.Add (s
, sl
);
3807 if (sl
.Converted
is NullLiteral
) {
3810 labels
.Add (sl
.Converted
.GetValueAsLong (), sl
);
3813 } catch (ArgumentException
) {
3814 if (string_labels
!= null)
3815 sl
.Error_AlreadyOccurs (ec
, SwitchType
, string_labels
[(string) sl
.Converted
.GetValue ()]);
3817 sl
.Error_AlreadyOccurs (ec
, SwitchType
, labels
[sl
.Converted
.GetValueAsLong ()]);
3827 // This method emits code for a lookup-based switch statement (non-string)
3828 // Basically it groups the cases into blocks that are at least half full,
3829 // and then spits out individual lookup opcodes for each block.
3830 // It emits the longest blocks first, and short blocks are just
3831 // handled with direct compares.
3833 void EmitTableSwitch (EmitContext ec
, Expression val
)
3835 Label lbl_default
= default_target
;
3837 if (labels
!= null && labels
.Count
> 0) {
3838 List
<LabelsRange
> ranges
;
3839 if (string_labels
!= null) {
3840 // We have done all hard work for string already
3841 // setup single range only
3842 ranges
= new List
<LabelsRange
> (1);
3843 ranges
.Add (new LabelsRange (0, labels
.Count
- 1, labels
.Keys
));
3845 var element_keys
= new long[labels
.Count
];
3846 labels
.Keys
.CopyTo (element_keys
, 0);
3847 Array
.Sort (element_keys
);
3850 // Build possible ranges of switch labes to reduce number
3853 ranges
= new List
<LabelsRange
> (element_keys
.Length
);
3854 var range
= new LabelsRange (element_keys
[0]);
3856 for (int i
= 1; i
< element_keys
.Length
; ++i
) {
3857 var l
= element_keys
[i
];
3858 if (range
.AddValue (l
))
3861 range
= new LabelsRange (l
);
3865 // sort the blocks so we can tackle the largest ones first
3869 TypeSpec compare_type
= SwitchType
.IsEnum
? EnumSpec
.GetUnderlyingType (SwitchType
) : SwitchType
;
3871 for (int range_index
= ranges
.Count
- 1; range_index
>= 0; --range_index
) {
3872 LabelsRange kb
= ranges
[range_index
];
3873 lbl_default
= (range_index
== 0) ? default_target
: ec
.DefineLabel ();
3875 // Optimize small ranges using simple equality check
3876 if (kb
.Range
<= 2) {
3877 foreach (var key
in kb
.label_values
) {
3878 SwitchLabel sl
= labels
[key
];
3879 if (sl
.Converted
.IsDefaultValue
) {
3880 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
3883 sl
.Converted
.Emit (ec
);
3884 ec
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
3888 // TODO: if all the keys in the block are the same and there are
3889 // no gaps/defaults then just use a range-check.
3890 if (compare_type
.BuiltinType
== BuiltinTypeSpec
.Type
.Long
|| compare_type
.BuiltinType
== BuiltinTypeSpec
.Type
.ULong
) {
3891 // TODO: optimize constant/I4 cases
3893 // check block range (could be > 2^31)
3895 ec
.EmitLong (kb
.min
);
3896 ec
.Emit (OpCodes
.Blt
, lbl_default
);
3899 ec
.EmitLong (kb
.max
);
3900 ec
.Emit (OpCodes
.Bgt
, lbl_default
);
3905 ec
.EmitLong (kb
.min
);
3906 ec
.Emit (OpCodes
.Sub
);
3909 ec
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3913 int first
= (int) kb
.min
;
3916 ec
.Emit (OpCodes
.Sub
);
3917 } else if (first
< 0) {
3918 ec
.EmitInt (-first
);
3919 ec
.Emit (OpCodes
.Add
);
3923 // first, build the list of labels for the switch
3925 long cJumps
= kb
.Range
;
3926 Label
[] switch_labels
= new Label
[cJumps
];
3927 for (int iJump
= 0; iJump
< cJumps
; iJump
++) {
3928 var key
= kb
.label_values
[iKey
];
3929 if (key
== kb
.min
+ iJump
) {
3930 switch_labels
[iJump
] = labels
[key
].GetILLabel (ec
);
3933 switch_labels
[iJump
] = lbl_default
;
3937 // emit the switch opcode
3938 ec
.Emit (OpCodes
.Switch
, switch_labels
);
3941 // mark the default for this block
3942 if (range_index
!= 0)
3943 ec
.MarkLabel (lbl_default
);
3946 // the last default just goes to the end
3947 if (ranges
.Count
> 0)
3948 ec
.Emit (OpCodes
.Br
, lbl_default
);
3951 // now emit the code for the sections
3952 bool found_default
= false;
3954 foreach (SwitchSection ss
in Sections
) {
3955 foreach (SwitchLabel sl
in ss
.Labels
) {
3957 ec
.MarkLabel (lbl_default
);
3958 found_default
= true;
3959 if (null_section
== null)
3960 ec
.MarkLabel (null_target
);
3961 } else if (sl
.Converted
.IsNull
) {
3962 ec
.MarkLabel (null_target
);
3965 ec
.MarkLabel (sl
.GetILLabel (ec
));
3971 if (!found_default
) {
3972 ec
.MarkLabel (lbl_default
);
3973 if (null_section
== null) {
3974 ec
.MarkLabel (null_target
);
3979 SwitchLabel
FindLabel (Constant
value)
3981 SwitchLabel sl
= null;
3983 if (string_labels
!= null) {
3984 string s
= value.GetValue () as string;
3986 if (null_section
!= null)
3988 else if (default_section
!= null)
3989 sl
= default_section
.Labels
[0];
3991 string_labels
.TryGetValue (s
, out sl
);
3994 if (value is NullLiteral
) {
3997 labels
.TryGetValue (value.GetValueAsLong (), out sl
);
4004 SwitchSection
FindSection (SwitchLabel label
)
4006 foreach (SwitchSection ss
in Sections
){
4007 foreach (SwitchLabel sl
in ss
.Labels
){
4016 public override bool Resolve (BlockContext ec
)
4018 Expr
= Expr
.Resolve (ec
);
4022 new_expr
= SwitchGoverningType (ec
, Expr
);
4024 if (new_expr
== null && Expr
.Type
.IsNullableType
) {
4025 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
4029 new_expr
= SwitchGoverningType (ec
, unwrap
);
4032 if (new_expr
== null){
4033 ec
.Report
.Error (151, loc
,
4034 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4035 TypeManager
.CSharpName (Expr
.Type
));
4040 SwitchType
= new_expr
.Type
;
4042 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.Bool
&& ec
.Module
.Compiler
.Settings
.Version
== LanguageVersion
.ISO_1
) {
4043 ec
.Report
.FeatureIsNotAvailable (ec
.Module
.Compiler
, loc
, "switch expression of boolean type");
4047 if (!CheckSwitch (ec
))
4050 Switch old_switch
= ec
.Switch
;
4052 ec
.Switch
.SwitchType
= SwitchType
;
4054 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
4056 var constant
= new_expr
as Constant
;
4057 if (constant
!= null) {
4059 SwitchLabel label
= FindLabel (constant
);
4061 constant_section
= FindSection (label
);
4063 if (constant_section
== null)
4064 constant_section
= default_section
;
4067 // Store switch expression for comparission purposes
4069 value = new_expr
as VariableReference
;
4071 value = TemporaryVariableReference
.Create (SwitchType
, ec
.CurrentBlock
, loc
);
4076 foreach (SwitchSection ss
in Sections
){
4078 ec
.CurrentBranching
.CreateSibling (
4079 null, FlowBranching
.SiblingType
.SwitchSection
);
4083 if (is_constant
&& (ss
!= constant_section
)) {
4084 // If we're a constant switch, we're only emitting
4085 // one single section - mark all the others as
4087 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
4088 if (!ss
.Block
.ResolveUnreachable (ec
, true)) {
4092 if (!ss
.Block
.Resolve (ec
))
4097 if (default_section
== null)
4098 ec
.CurrentBranching
.CreateSibling (null, FlowBranching
.SiblingType
.SwitchSection
);
4100 ec
.EndFlowBranching ();
4101 ec
.Switch
= old_switch
;
4107 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
4108 if (string_labels
.Count
< 7)
4109 ResolveSimpleSwitch (ec
);
4111 ResolveStringSwitchMap (ec
);
4112 } else if (labels
.Count
< 3 && !IsNullable
) {
4113 ResolveSimpleSwitch (ec
);
4120 public SwitchLabel
ResolveGotoCase (ResolveContext rc
, Constant
value)
4122 var sl
= FindLabel (value);
4125 FlowBranchingBlock
.Error_UnknownLabel (loc
, "case " + value.GetValueAsLiteral (), rc
.Report
);
4132 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4134 void ResolveSimpleSwitch (BlockContext bc
)
4136 simple_stmt
= default_section
!= null ? default_section
.Block
: null;
4138 for (int i
= Sections
.Count
- 1; i
>= 0; --i
) {
4139 var s
= Sections
[i
];
4141 if (s
== default_section
) {
4142 s
.Block
.AddScopeStatement (new LabelMarker (this, s
.Labels
));
4146 s
.Block
.AddScopeStatement (new LabelMarker (this, s
.Labels
));
4148 Expression cond
= null;
4149 for (int ci
= 0; ci
< s
.Labels
.Count
; ++ci
) {
4150 var e
= new Binary (Binary
.Operator
.Equality
, value, s
.Labels
[ci
].Converted
, loc
);
4153 cond
= new Binary (Binary
.Operator
.LogicalOr
, cond
, e
, loc
);
4160 // Compiler generated, hide from symbol file
4162 simple_stmt
= new If (cond
, s
.Block
, simple_stmt
, Location
.Null
);
4165 // It's null for empty switch
4166 if (simple_stmt
!= null)
4167 simple_stmt
.Resolve (bc
);
4171 // Converts string switch into string hashtable
4173 void ResolveStringSwitchMap (ResolveContext ec
)
4175 FullNamedExpression string_dictionary_type
;
4176 if (ec
.Module
.PredefinedTypes
.Dictionary
.Define ()) {
4177 string_dictionary_type
= new TypeExpression (
4178 ec
.Module
.PredefinedTypes
.Dictionary
.TypeSpec
.MakeGenericType (ec
,
4179 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }
),
4181 } else if (ec
.Module
.PredefinedTypes
.Hashtable
.Define ()) {
4182 string_dictionary_type
= new TypeExpression (ec
.Module
.PredefinedTypes
.Hashtable
.TypeSpec
, loc
);
4184 ec
.Module
.PredefinedTypes
.Dictionary
.Resolve ();
4188 var ctype
= ec
.CurrentMemberDefinition
.Parent
.PartialContainer
;
4189 Field field
= new Field (ctype
, string_dictionary_type
,
4190 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
4191 new MemberName (CompilerGeneratedContainer
.MakeName (null, "f", "switch$map", ec
.Module
.CounterSwitchTypes
++), loc
), null);
4192 if (!field
.Define ())
4194 ctype
.AddField (field
);
4196 var init
= new List
<Expression
> ();
4198 labels
= new Dictionary
<long, SwitchLabel
> (string_labels
.Count
);
4199 string value = null;
4200 foreach (SwitchSection section
in Sections
) {
4201 bool contains_label
= false;
4202 foreach (SwitchLabel sl
in section
.Labels
) {
4203 if (sl
.IsDefault
|| sl
.Converted
.IsNull
)
4206 if (!contains_label
) {
4207 labels
.Add (counter
, sl
);
4208 contains_label
= true;
4211 value = (string) sl
.Converted
.GetValue ();
4212 var init_args
= new List
<Expression
> (2);
4213 init_args
.Add (new StringLiteral (ec
.BuiltinTypes
, value, sl
.Location
));
4215 sl
.Converted
= new IntConstant (ec
.BuiltinTypes
, counter
, loc
);
4216 init_args
.Add (sl
.Converted
);
4218 init
.Add (new CollectionElementInitializer (init_args
, loc
));
4222 // Don't add empty sections
4228 Arguments args
= new Arguments (1);
4229 args
.Add (new Argument (new IntConstant (ec
.BuiltinTypes
, init
.Count
, loc
)));
4230 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
4231 new CollectionOrObjectInitializers (init
, loc
), loc
);
4233 switch_cache_field
= new FieldExpr (field
, loc
);
4234 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
4237 void DoEmitStringSwitch (EmitContext ec
)
4239 Label l_initialized
= ec
.DefineLabel ();
4242 // Skip initialization when value is null
4244 value.EmitBranchable (ec
, null_target
, false);
4247 // Check if string dictionary is initialized and initialize
4249 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
4250 using (ec
.With (BuilderContext
.Options
.OmitDebugInfo
, true)) {
4251 string_dictionary
.EmitStatement (ec
);
4253 ec
.MarkLabel (l_initialized
);
4255 LocalTemporary string_switch_variable
= new LocalTemporary (ec
.BuiltinTypes
.Int
);
4257 ResolveContext rc
= new ResolveContext (ec
.MemberContext
);
4259 if (switch_cache_field
.Type
.IsGeneric
) {
4260 Arguments get_value_args
= new Arguments (2);
4261 get_value_args
.Add (new Argument (value));
4262 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
4263 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (rc
);
4264 if (get_item
== null)
4268 // A value was not found, go to default case
4270 get_item
.EmitBranchable (ec
, default_target
, false);
4272 Arguments get_value_args
= new Arguments (1);
4273 get_value_args
.Add (new Argument (value));
4275 Expression get_item
= new ElementAccess (switch_cache_field
, get_value_args
, loc
).Resolve (rc
);
4276 if (get_item
== null)
4279 LocalTemporary get_item_object
= new LocalTemporary (ec
.BuiltinTypes
.Object
);
4280 get_item_object
.EmitAssign (ec
, get_item
, true, false);
4281 ec
.Emit (OpCodes
.Brfalse
, default_target
);
4283 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
4284 new Cast (new TypeExpression (ec
.BuiltinTypes
.Int
, loc
), get_item_object
, loc
)).Resolve (rc
);
4286 get_item_int
.EmitStatement (ec
);
4287 get_item_object
.Release (ec
);
4290 EmitTableSwitch (ec
, string_switch_variable
);
4291 string_switch_variable
.Release (ec
);
4294 protected override void DoEmit (EmitContext ec
)
4296 // Workaround broken flow-analysis
4297 block
.HasUnreachableClosingBrace
= true;
4300 // Needed to emit anonymous storey initialization
4301 // Otherwise it does not contain any statements for now
4305 default_target
= ec
.DefineLabel ();
4306 null_target
= ec
.DefineLabel ();
4309 unwrap
.EmitCheck (ec
);
4310 ec
.Emit (OpCodes
.Brfalse
, null_target
);
4311 value.EmitAssign (ec
, new_expr
, false, false);
4312 } else if (new_expr
!= value && !is_constant
) {
4313 value.EmitAssign (ec
, new_expr
, false, false);
4317 // Setup the codegen context
4319 Label old_end
= ec
.LoopEnd
;
4320 Switch old_switch
= ec
.Switch
;
4322 ec
.LoopEnd
= ec
.DefineLabel ();
4327 if (constant_section
!= null)
4328 constant_section
.Block
.Emit (ec
);
4329 } else if (string_dictionary
!= null) {
4330 DoEmitStringSwitch (ec
);
4331 } else if (simple_stmt
!= null) {
4332 simple_stmt
.Emit (ec
);
4334 EmitTableSwitch (ec
, value);
4337 // Restore context state.
4338 ec
.MarkLabel (ec
.LoopEnd
);
4341 // Restore the previous context
4343 ec
.LoopEnd
= old_end
;
4344 ec
.Switch
= old_switch
;
4347 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4349 Switch target
= (Switch
) t
;
4351 target
.Expr
= Expr
.Clone (clonectx
);
4352 target
.Sections
= new List
<SwitchSection
> ();
4353 foreach (SwitchSection ss
in Sections
){
4354 target
.Sections
.Add (ss
.Clone (clonectx
));
4358 public override object Accept (StructuralVisitor visitor
)
4360 return visitor
.Visit (this);
4364 // A place where execution can restart in an iterator
4365 public abstract class ResumableStatement
: Statement
4368 protected Label resume_point
;
4370 public Label
PrepareForEmit (EmitContext ec
)
4374 resume_point
= ec
.DefineLabel ();
4376 return resume_point
;
4379 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
4384 public virtual void EmitForDispose (EmitContext ec
, LocalBuilder pc
, Label end
, bool have_dispatcher
)
4389 public abstract class TryFinallyBlock
: ExceptionStatement
4391 protected Statement stmt
;
4392 Label dispose_try_block
;
4393 bool prepared_for_dispose
, emitted_dispose
;
4395 protected TryFinallyBlock (Statement stmt
, Location loc
)
4403 public Statement Statement
{
4411 protected abstract void EmitTryBody (EmitContext ec
);
4412 protected abstract void EmitFinallyBody (EmitContext ec
);
4414 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
4416 if (!prepared_for_dispose
) {
4417 prepared_for_dispose
= true;
4418 dispose_try_block
= ec
.DefineLabel ();
4420 return dispose_try_block
;
4423 protected sealed override void DoEmit (EmitContext ec
)
4425 EmitTryBodyPrepare (ec
);
4428 ec
.BeginFinallyBlock ();
4430 Label start_finally
= ec
.DefineLabel ();
4431 if (resume_points
!= null) {
4432 var state_machine
= (StateMachineInitializer
) ec
.CurrentAnonymousMethod
;
4434 ec
.Emit (OpCodes
.Ldloc
, state_machine
.SkipFinally
);
4435 ec
.Emit (OpCodes
.Brfalse_S
, start_finally
);
4436 ec
.Emit (OpCodes
.Endfinally
);
4439 ec
.MarkLabel (start_finally
);
4440 EmitFinallyBody (ec
);
4442 ec
.EndExceptionBlock ();
4445 public override void EmitForDispose (EmitContext ec
, LocalBuilder pc
, Label end
, bool have_dispatcher
)
4447 if (emitted_dispose
)
4450 emitted_dispose
= true;
4452 Label end_of_try
= ec
.DefineLabel ();
4454 // Ensure that the only way we can get into this code is through a dispatcher
4455 if (have_dispatcher
)
4456 ec
.Emit (OpCodes
.Br
, end
);
4458 ec
.BeginExceptionBlock ();
4460 ec
.MarkLabel (dispose_try_block
);
4462 Label
[] labels
= null;
4463 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
4464 ResumableStatement s
= resume_points
[i
];
4465 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
4466 if (ret
.Equals (end_of_try
) && labels
== null)
4468 if (labels
== null) {
4469 labels
= new Label
[resume_points
.Count
];
4470 for (int j
= 0; j
< i
; ++j
)
4471 labels
[j
] = end_of_try
;
4476 if (labels
!= null) {
4478 for (j
= 1; j
< labels
.Length
; ++j
)
4479 if (!labels
[0].Equals (labels
[j
]))
4481 bool emit_dispatcher
= j
< labels
.Length
;
4483 if (emit_dispatcher
) {
4484 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4485 ec
.Emit (OpCodes
.Ldloc
, pc
);
4486 ec
.EmitInt (first_resume_pc
);
4487 ec
.Emit (OpCodes
.Sub
);
4488 ec
.Emit (OpCodes
.Switch
, labels
);
4489 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4492 foreach (ResumableStatement s
in resume_points
)
4493 s
.EmitForDispose (ec
, pc
, end_of_try
, emit_dispatcher
);
4496 ec
.MarkLabel (end_of_try
);
4498 ec
.BeginFinallyBlock ();
4500 EmitFinallyBody (ec
);
4502 ec
.EndExceptionBlock ();
4507 // Base class for blocks using exception handling
4509 public abstract class ExceptionStatement
: ResumableStatement
4514 protected List
<ResumableStatement
> resume_points
;
4515 protected int first_resume_pc
;
4517 protected ExceptionStatement (Location loc
)
4522 protected virtual void EmitTryBodyPrepare (EmitContext ec
)
4524 StateMachineInitializer state_machine
= null;
4525 if (resume_points
!= null) {
4526 state_machine
= (StateMachineInitializer
) ec
.CurrentAnonymousMethod
;
4528 ec
.EmitInt ((int) IteratorStorey
.State
.Running
);
4529 ec
.Emit (OpCodes
.Stloc
, state_machine
.CurrentPC
);
4532 ec
.BeginExceptionBlock ();
4534 if (resume_points
!= null) {
4535 ec
.MarkLabel (resume_point
);
4537 // For normal control flow, we want to fall-through the Switch
4538 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4539 ec
.Emit (OpCodes
.Ldloc
, state_machine
.CurrentPC
);
4540 ec
.EmitInt (first_resume_pc
);
4541 ec
.Emit (OpCodes
.Sub
);
4543 Label
[] labels
= new Label
[resume_points
.Count
];
4544 for (int i
= 0; i
< resume_points
.Count
; ++i
)
4545 labels
[i
] = resume_points
[i
].PrepareForEmit (ec
);
4546 ec
.Emit (OpCodes
.Switch
, labels
);
4550 public void SomeCodeFollows ()
4553 code_follows
= true;
4557 public override bool Resolve (BlockContext ec
)
4560 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4561 // So, ensure there's some IL code after this statement.
4562 if (!code_follows
&& resume_points
== null && ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
4563 ec
.NeedReturnLabel ();
4568 public void AddResumePoint (ResumableStatement stmt
, int pc
)
4570 if (resume_points
== null) {
4571 resume_points
= new List
<ResumableStatement
> ();
4572 first_resume_pc
= pc
;
4575 if (pc
!= first_resume_pc
+ resume_points
.Count
)
4576 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4578 resume_points
.Add (stmt
);
4583 public class Lock
: TryFinallyBlock
4586 TemporaryVariableReference expr_copy
;
4587 TemporaryVariableReference lock_taken
;
4589 public Lock (Expression expr
, Statement stmt
, Location loc
)
4595 public Expression Expr
{
4601 public override bool Resolve (BlockContext ec
)
4603 expr
= expr
.Resolve (ec
);
4607 if (!TypeSpec
.IsReferenceType (expr
.Type
)) {
4608 ec
.Report
.Error (185, loc
,
4609 "`{0}' is not a reference type as required by the lock statement",
4610 expr
.Type
.GetSignatureForError ());
4613 if (expr
.Type
.IsGenericParameter
) {
4614 expr
= Convert
.ImplicitTypeParameterConversion (expr
, (TypeParameterSpec
)expr
.Type
, ec
.BuiltinTypes
.Object
);
4617 VariableReference lv
= expr
as VariableReference
;
4620 locked
= lv
.IsLockedByStatement
;
4621 lv
.IsLockedByStatement
= true;
4627 using (ec
.Set (ResolveContext
.Options
.LockScope
)) {
4628 ec
.StartFlowBranching (this);
4629 Statement
.Resolve (ec
);
4630 ec
.EndFlowBranching ();
4634 lv
.IsLockedByStatement
= locked
;
4640 // Have to keep original lock value around to unlock same location
4641 // in the case the original has changed or is null
4643 expr_copy
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Object
, ec
.CurrentBlock
, loc
);
4644 expr_copy
.Resolve (ec
);
4647 // Ensure Monitor methods are available
4649 if (ResolvePredefinedMethods (ec
) > 1) {
4650 lock_taken
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Bool
, ec
.CurrentBlock
, loc
);
4651 lock_taken
.Resolve (ec
);
4657 protected override void EmitTryBodyPrepare (EmitContext ec
)
4659 expr_copy
.EmitAssign (ec
, expr
);
4661 if (lock_taken
!= null) {
4663 // Initialize ref variable
4665 lock_taken
.EmitAssign (ec
, new BoolLiteral (ec
.BuiltinTypes
, false, loc
));
4668 // Monitor.Enter (expr_copy)
4670 expr_copy
.Emit (ec
);
4671 ec
.Emit (OpCodes
.Call
, ec
.Module
.PredefinedMembers
.MonitorEnter
.Get ());
4674 base.EmitTryBodyPrepare (ec
);
4677 protected override void EmitTryBody (EmitContext ec
)
4680 // Monitor.Enter (expr_copy, ref lock_taken)
4682 if (lock_taken
!= null) {
4683 expr_copy
.Emit (ec
);
4684 lock_taken
.LocalInfo
.CreateBuilder (ec
);
4685 lock_taken
.AddressOf (ec
, AddressOp
.Load
);
4686 ec
.Emit (OpCodes
.Call
, ec
.Module
.PredefinedMembers
.MonitorEnter_v4
.Get ());
4689 Statement
.Emit (ec
);
4692 protected override void EmitFinallyBody (EmitContext ec
)
4695 // if (lock_taken) Monitor.Exit (expr_copy)
4697 Label skip
= ec
.DefineLabel ();
4699 if (lock_taken
!= null) {
4700 lock_taken
.Emit (ec
);
4701 ec
.Emit (OpCodes
.Brfalse_S
, skip
);
4704 expr_copy
.Emit (ec
);
4705 var m
= ec
.Module
.PredefinedMembers
.MonitorExit
.Resolve (loc
);
4707 ec
.Emit (OpCodes
.Call
, m
);
4709 ec
.MarkLabel (skip
);
4712 int ResolvePredefinedMethods (ResolveContext rc
)
4714 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4715 var m
= rc
.Module
.PredefinedMembers
.MonitorEnter_v4
.Get ();
4719 m
= rc
.Module
.PredefinedMembers
.MonitorEnter
.Get ();
4723 rc
.Module
.PredefinedMembers
.MonitorEnter_v4
.Resolve (loc
);
4727 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4729 Lock target
= (Lock
) t
;
4731 target
.expr
= expr
.Clone (clonectx
);
4732 target
.stmt
= Statement
.Clone (clonectx
);
4735 public override object Accept (StructuralVisitor visitor
)
4737 return visitor
.Visit (this);
4742 public class Unchecked
: Statement
{
4745 public Unchecked (Block b
, Location loc
)
4752 public override bool Resolve (BlockContext ec
)
4754 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, false))
4755 return Block
.Resolve (ec
);
4758 protected override void DoEmit (EmitContext ec
)
4760 using (ec
.With (EmitContext
.Options
.CheckedScope
, false))
4764 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4766 Unchecked target
= (Unchecked
) t
;
4768 target
.Block
= clonectx
.LookupBlock (Block
);
4771 public override object Accept (StructuralVisitor visitor
)
4773 return visitor
.Visit (this);
4777 public class Checked
: Statement
{
4780 public Checked (Block b
, Location loc
)
4783 b
.Unchecked
= false;
4787 public override bool Resolve (BlockContext ec
)
4789 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, true))
4790 return Block
.Resolve (ec
);
4793 protected override void DoEmit (EmitContext ec
)
4795 using (ec
.With (EmitContext
.Options
.CheckedScope
, true))
4799 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4801 Checked target
= (Checked
) t
;
4803 target
.Block
= clonectx
.LookupBlock (Block
);
4806 public override object Accept (StructuralVisitor visitor
)
4808 return visitor
.Visit (this);
4812 public class Unsafe
: Statement
{
4815 public Unsafe (Block b
, Location loc
)
4818 Block
.Unsafe
= true;
4822 public override bool Resolve (BlockContext ec
)
4824 if (ec
.CurrentIterator
!= null)
4825 ec
.Report
.Error (1629, loc
, "Unsafe code may not appear in iterators");
4827 using (ec
.Set (ResolveContext
.Options
.UnsafeScope
))
4828 return Block
.Resolve (ec
);
4831 protected override void DoEmit (EmitContext ec
)
4836 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4838 Unsafe target
= (Unsafe
) t
;
4840 target
.Block
= clonectx
.LookupBlock (Block
);
4843 public override object Accept (StructuralVisitor visitor
)
4845 return visitor
.Visit (this);
4852 public class Fixed
: Statement
4854 abstract class Emitter
: ShimExpression
4856 protected LocalVariable vi
;
4858 protected Emitter (Expression expr
, LocalVariable li
)
4864 public abstract void EmitExit (EmitContext ec
);
4867 class ExpressionEmitter
: Emitter
{
4868 public ExpressionEmitter (Expression converted
, LocalVariable li
) :
4869 base (converted
, li
)
4873 protected override Expression
DoResolve (ResolveContext rc
)
4875 throw new NotImplementedException ();
4878 public override void Emit (EmitContext ec
) {
4880 // Store pointer in pinned location
4886 public override void EmitExit (EmitContext ec
)
4889 ec
.Emit (OpCodes
.Conv_U
);
4894 class StringEmitter
: Emitter
4896 LocalVariable pinned_string
;
4898 public StringEmitter (Expression expr
, LocalVariable li
, Location loc
)
4903 protected override Expression
DoResolve (ResolveContext rc
)
4905 pinned_string
= new LocalVariable (vi
.Block
, "$pinned",
4906 LocalVariable
.Flags
.FixedVariable
| LocalVariable
.Flags
.CompilerGenerated
| LocalVariable
.Flags
.Used
,
4908 pinned_string
.Type
= rc
.BuiltinTypes
.String
;
4910 eclass
= ExprClass
.Variable
;
4911 type
= rc
.BuiltinTypes
.Int
;
4915 public override void Emit (EmitContext ec
)
4917 pinned_string
.CreateBuilder (ec
);
4920 pinned_string
.EmitAssign (ec
);
4922 // TODO: Should use Binary::Add
4923 pinned_string
.Emit (ec
);
4924 ec
.Emit (OpCodes
.Conv_I
);
4926 var m
= ec
.Module
.PredefinedMembers
.RuntimeHelpersOffsetToStringData
.Resolve (loc
);
4930 PropertyExpr pe
= new PropertyExpr (m
, pinned_string
.Location
);
4931 //pe.InstanceExpression = pinned_string;
4932 pe
.Resolve (new ResolveContext (ec
.MemberContext
)).Emit (ec
);
4934 ec
.Emit (OpCodes
.Add
);
4938 public override void EmitExit (EmitContext ec
)
4941 pinned_string
.EmitAssign (ec
);
4945 public class VariableDeclaration
: BlockVariableDeclaration
4947 public VariableDeclaration (FullNamedExpression type
, LocalVariable li
)
4952 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
4954 if (!Variable
.Type
.IsPointer
&& li
== Variable
) {
4955 bc
.Report
.Error (209, TypeExpression
.Location
,
4956 "The type of locals declared in a fixed statement must be a pointer type");
4961 // The rules for the possible declarators are pretty wise,
4962 // but the production on the grammar is more concise.
4964 // So we have to enforce these rules here.
4966 // We do not resolve before doing the case 1 test,
4967 // because the grammar is explicit in that the token &
4968 // is present, so we need to test for this particular case.
4971 if (initializer
is Cast
) {
4972 bc
.Report
.Error (254, initializer
.Location
, "The right hand side of a fixed statement assignment may not be a cast expression");
4976 initializer
= initializer
.Resolve (bc
);
4978 if (initializer
== null)
4984 if (initializer
.Type
.IsArray
) {
4985 TypeSpec array_type
= TypeManager
.GetElementType (initializer
.Type
);
4988 // Provided that array_type is unmanaged,
4990 if (!TypeManager
.VerifyUnmanaged (bc
.Module
, array_type
, loc
))
4994 // and T* is implicitly convertible to the
4995 // pointer type given in the fixed statement.
4997 ArrayPtr array_ptr
= new ArrayPtr (initializer
, array_type
, loc
);
4999 Expression converted
= Convert
.ImplicitConversionRequired (bc
, array_ptr
.Resolve (bc
), li
.Type
, loc
);
5000 if (converted
== null)
5004 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5006 converted
= new Conditional (new BooleanExpression (new Binary (Binary
.Operator
.LogicalOr
,
5007 new Binary (Binary
.Operator
.Equality
, initializer
, new NullLiteral (loc
), loc
),
5008 new Binary (Binary
.Operator
.Equality
, new MemberAccess (initializer
, "Length"), new IntConstant (bc
.BuiltinTypes
, 0, loc
), loc
), loc
)),
5009 new NullLiteral (loc
),
5012 converted
= converted
.Resolve (bc
);
5014 return new ExpressionEmitter (converted
, li
);
5020 if (initializer
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
5021 return new StringEmitter (initializer
, li
, loc
).Resolve (bc
);
5024 // Case 3: fixed buffer
5025 if (initializer
is FixedBufferPtr
) {
5026 return new ExpressionEmitter (initializer
, li
);
5030 // Case 4: & object.
5032 bool already_fixed
= true;
5033 Unary u
= initializer
as Unary
;
5034 if (u
!= null && u
.Oper
== Unary
.Operator
.AddressOf
) {
5035 IVariableReference vr
= u
.Expr
as IVariableReference
;
5036 if (vr
== null || !vr
.IsFixed
) {
5037 already_fixed
= false;
5041 if (already_fixed
) {
5042 bc
.Report
.Error (213, loc
, "You cannot use the fixed statement to take the address of an already fixed expression");
5045 initializer
= Convert
.ImplicitConversionRequired (bc
, initializer
, li
.Type
, loc
);
5046 return new ExpressionEmitter (initializer
, li
);
5051 VariableDeclaration decl
;
5052 Statement statement
;
5055 public Fixed (VariableDeclaration decl
, Statement stmt
, Location l
)
5064 public Statement Statement
{
5070 public BlockVariableDeclaration Variables
{
5078 public override bool Resolve (BlockContext ec
)
5080 using (ec
.Set (ResolveContext
.Options
.FixedInitializerScope
)) {
5081 if (!decl
.Resolve (ec
))
5085 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
5086 bool ok
= statement
.Resolve (ec
);
5087 bool flow_unreachable
= ec
.EndFlowBranching ();
5088 has_ret
= flow_unreachable
;
5093 protected override void DoEmit (EmitContext ec
)
5095 decl
.Variable
.CreateBuilder (ec
);
5096 decl
.Initializer
.Emit (ec
);
5097 if (decl
.Declarators
!= null) {
5098 foreach (var d
in decl
.Declarators
) {
5099 d
.Variable
.CreateBuilder (ec
);
5100 d
.Initializer
.Emit (ec
);
5104 statement
.Emit (ec
);
5110 // Clear the pinned variable
5112 ((Emitter
) decl
.Initializer
).EmitExit (ec
);
5113 if (decl
.Declarators
!= null) {
5114 foreach (var d
in decl
.Declarators
) {
5115 ((Emitter
)d
.Initializer
).EmitExit (ec
);
5120 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5122 Fixed target
= (Fixed
) t
;
5124 target
.decl
= (VariableDeclaration
) decl
.Clone (clonectx
);
5125 target
.statement
= statement
.Clone (clonectx
);
5128 public override object Accept (StructuralVisitor visitor
)
5130 return visitor
.Visit (this);
5134 public class Catch
: Statement
5138 FullNamedExpression type_expr
;
5139 CompilerAssign assign
;
5142 public Catch (Block block
, Location loc
)
5150 public Block Block
{
5156 public TypeSpec CatchType
{
5162 public bool IsGeneral
{
5164 return type_expr
== null;
5168 public FullNamedExpression TypeExpression
{
5177 public LocalVariable Variable
{
5188 protected override void DoEmit (EmitContext ec
)
5191 ec
.BeginCatchBlock (ec
.BuiltinTypes
.Object
);
5193 ec
.BeginCatchBlock (CatchType
);
5196 li
.CreateBuilder (ec
);
5199 // Special case hoisted catch variable, we have to use a temporary variable
5200 // to pass via anonymous storey initialization with the value still on top
5203 if (li
.HoistedVariant
!= null) {
5204 LocalTemporary lt
= new LocalTemporary (li
.Type
);
5207 // switch to assigning from the temporary variable and not from top of the stack
5208 assign
.UpdateSource (lt
);
5211 ec
.Emit (OpCodes
.Pop
);
5217 public override bool Resolve (BlockContext ec
)
5219 using (ec
.With (ResolveContext
.Options
.CatchScope
, true)) {
5220 if (type_expr
!= null) {
5221 type
= type_expr
.ResolveAsType (ec
);
5225 if (type
.BuiltinType
!= BuiltinTypeSpec
.Type
.Exception
&& !TypeSpec
.IsBaseClass (type
, ec
.BuiltinTypes
.Exception
, false)) {
5226 ec
.Report
.Error (155, loc
, "The type caught or thrown must be derived from System.Exception");
5227 } else if (li
!= null) {
5229 li
.PrepareForFlowAnalysis (ec
);
5231 // source variable is at the top of the stack
5232 Expression source
= new EmptyExpression (li
.Type
);
5233 if (li
.Type
.IsGenericParameter
)
5234 source
= new UnboxCast (source
, li
.Type
);
5237 // Uses Location.Null to hide from symbol file
5239 assign
= new CompilerAssign (new LocalVariableReference (li
, Location
.Null
), source
, Location
.Null
);
5240 Block
.AddScopeStatement (new StatementExpression (assign
, Location
.Null
));
5244 return Block
.Resolve (ec
);
5248 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5250 Catch target
= (Catch
) t
;
5252 if (type_expr
!= null)
5253 target
.type_expr
= (FullNamedExpression
) type_expr
.Clone (clonectx
);
5255 target
.block
= clonectx
.LookupBlock (block
);
5259 public class TryFinally
: TryFinallyBlock
5263 public TryFinally (Statement stmt
, Block fini
, Location loc
)
5269 public Block Finallyblock
{
5275 public override bool Resolve (BlockContext ec
)
5279 ec
.StartFlowBranching (this);
5281 if (!stmt
.Resolve (ec
))
5285 ec
.CurrentBranching
.CreateSibling (fini
, FlowBranching
.SiblingType
.Finally
);
5286 using (ec
.With (ResolveContext
.Options
.FinallyScope
, true)) {
5287 if (!fini
.Resolve (ec
))
5291 ec
.EndFlowBranching ();
5293 ok
&= base.Resolve (ec
);
5298 protected override void EmitTryBody (EmitContext ec
)
5303 protected override void EmitFinallyBody (EmitContext ec
)
5308 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5310 TryFinally target
= (TryFinally
) t
;
5312 target
.stmt
= (Statement
) stmt
.Clone (clonectx
);
5314 target
.fini
= clonectx
.LookupBlock (fini
);
5317 public override object Accept (StructuralVisitor visitor
)
5319 return visitor
.Visit (this);
5323 public class TryCatch
: ExceptionStatement
5326 List
<Catch
> clauses
;
5327 readonly bool inside_try_finally
;
5329 public TryCatch (Block block
, List
<Catch
> catch_clauses
, Location l
, bool inside_try_finally
)
5333 this.clauses
= catch_clauses
;
5334 this.inside_try_finally
= inside_try_finally
;
5337 public List
<Catch
> Clauses
{
5343 public bool IsTryCatchFinally
{
5345 return inside_try_finally
;
5349 public override bool Resolve (BlockContext ec
)
5353 ec
.StartFlowBranching (this);
5355 if (!Block
.Resolve (ec
))
5358 for (int i
= 0; i
< clauses
.Count
; ++i
) {
5360 ec
.CurrentBranching
.CreateSibling (c
.Block
, FlowBranching
.SiblingType
.Catch
);
5362 if (!c
.Resolve (ec
)) {
5367 TypeSpec resolved_type
= c
.CatchType
;
5368 for (int ii
= 0; ii
< clauses
.Count
; ++ii
) {
5372 if (clauses
[ii
].IsGeneral
) {
5373 if (resolved_type
.BuiltinType
!= BuiltinTypeSpec
.Type
.Exception
)
5376 if (!ec
.Module
.DeclaringAssembly
.WrapNonExceptionThrows
)
5379 if (!ec
.Module
.PredefinedAttributes
.RuntimeCompatibility
.IsDefined
)
5382 ec
.Report
.Warning (1058, 1, c
.loc
,
5383 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5391 var ct
= clauses
[ii
].CatchType
;
5395 if (resolved_type
== ct
|| TypeSpec
.IsBaseClass (resolved_type
, ct
, true)) {
5396 ec
.Report
.Error (160, c
.loc
,
5397 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5398 ct
.GetSignatureForError ());
5404 ec
.EndFlowBranching ();
5406 return base.Resolve (ec
) && ok
;
5409 protected sealed override void DoEmit (EmitContext ec
)
5411 if (!inside_try_finally
)
5412 EmitTryBodyPrepare (ec
);
5416 foreach (Catch c
in clauses
)
5419 if (!inside_try_finally
)
5420 ec
.EndExceptionBlock ();
5423 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5425 TryCatch target
= (TryCatch
) t
;
5427 target
.Block
= clonectx
.LookupBlock (Block
);
5428 if (clauses
!= null){
5429 target
.clauses
= new List
<Catch
> ();
5430 foreach (Catch c
in clauses
)
5431 target
.clauses
.Add ((Catch
) c
.Clone (clonectx
));
5435 public override object Accept (StructuralVisitor visitor
)
5437 return visitor
.Visit (this);
5441 public class Using
: TryFinallyBlock
5443 public class VariableDeclaration
: BlockVariableDeclaration
5445 Statement dispose_call
;
5447 public VariableDeclaration (FullNamedExpression type
, LocalVariable li
)
5452 public VariableDeclaration (LocalVariable li
, Location loc
)
5458 public VariableDeclaration (Expression expr
)
5461 loc
= expr
.Location
;
5467 public bool IsNested { get; private set; }
5471 public void EmitDispose (EmitContext ec
)
5473 dispose_call
.Emit (ec
);
5476 public override bool Resolve (BlockContext bc
)
5481 return base.Resolve (bc
, false);
5484 public Expression
ResolveExpression (BlockContext bc
)
5486 var e
= Initializer
.Resolve (bc
);
5490 li
= LocalVariable
.CreateCompilerGenerated (e
.Type
, bc
.CurrentBlock
, loc
);
5491 Initializer
= ResolveInitializer (bc
, Variable
, e
);
5495 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
5497 if (li
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.Dynamic
) {
5498 initializer
= initializer
.Resolve (bc
);
5499 if (initializer
== null)
5502 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5503 Arguments args
= new Arguments (1);
5504 args
.Add (new Argument (initializer
));
5505 initializer
= new DynamicConversion (bc
.BuiltinTypes
.IDisposable
, 0, args
, initializer
.Location
).Resolve (bc
);
5506 if (initializer
== null)
5509 var var = LocalVariable
.CreateCompilerGenerated (initializer
.Type
, bc
.CurrentBlock
, loc
);
5510 dispose_call
= CreateDisposeCall (bc
, var);
5511 dispose_call
.Resolve (bc
);
5513 return base.ResolveInitializer (bc
, li
, new SimpleAssign (var.CreateReferenceExpression (bc
, loc
), initializer
, loc
));
5516 if (li
== Variable
) {
5517 CheckIDiposableConversion (bc
, li
, initializer
);
5518 dispose_call
= CreateDisposeCall (bc
, li
);
5519 dispose_call
.Resolve (bc
);
5522 return base.ResolveInitializer (bc
, li
, initializer
);
5525 protected virtual void CheckIDiposableConversion (BlockContext bc
, LocalVariable li
, Expression initializer
)
5529 if (type
.BuiltinType
!= BuiltinTypeSpec
.Type
.IDisposable
&& !type
.ImplementsInterface (bc
.BuiltinTypes
.IDisposable
, false)) {
5530 if (type
.IsNullableType
) {
5531 // it's handled in CreateDisposeCall
5535 bc
.Report
.SymbolRelatedToPreviousError (type
);
5536 var loc
= type_expr
== null ? initializer
.Location
: type_expr
.Location
;
5537 bc
.Report
.Error (1674, loc
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5538 type
.GetSignatureForError ());
5544 protected virtual Statement
CreateDisposeCall (BlockContext bc
, LocalVariable lv
)
5546 var lvr
= lv
.CreateReferenceExpression (bc
, lv
.Location
);
5548 var loc
= lv
.Location
;
5550 var idt
= bc
.BuiltinTypes
.IDisposable
;
5551 var m
= bc
.Module
.PredefinedMembers
.IDisposableDispose
.Resolve (loc
);
5553 var dispose_mg
= MethodGroupExpr
.CreatePredefined (m
, idt
, loc
);
5554 dispose_mg
.InstanceExpression
= type
.IsNullableType
?
5555 new Cast (new TypeExpression (idt
, loc
), lvr
, loc
).Resolve (bc
) :
5559 // Hide it from symbol file via null location
5561 Statement dispose
= new StatementExpression (new Invocation (dispose_mg
, null), Location
.Null
);
5563 // Add conditional call when disposing possible null variable
5564 if (!type
.IsStruct
|| type
.IsNullableType
)
5565 dispose
= new If (new Binary (Binary
.Operator
.Inequality
, lvr
, new NullLiteral (loc
), loc
), dispose
, dispose
.loc
);
5570 public void ResolveDeclaratorInitializer (BlockContext bc
)
5572 Initializer
= base.ResolveInitializer (bc
, Variable
, Initializer
);
5575 public Statement
RewriteUsingDeclarators (BlockContext bc
, Statement stmt
)
5577 for (int i
= declarators
.Count
- 1; i
>= 0; --i
) {
5578 var d
= declarators
[i
];
5579 var vd
= new VariableDeclaration (d
.Variable
, type_expr
.Location
);
5580 vd
.Initializer
= d
.Initializer
;
5582 vd
.dispose_call
= CreateDisposeCall (bc
, d
.Variable
);
5583 vd
.dispose_call
.Resolve (bc
);
5585 stmt
= new Using (vd
, stmt
, d
.Variable
.Location
);
5592 public override object Accept (StructuralVisitor visitor
)
5594 return visitor
.Visit (this);
5598 VariableDeclaration decl
;
5600 public Using (VariableDeclaration decl
, Statement stmt
, Location loc
)
5606 public Using (Expression expr
, Statement stmt
, Location loc
)
5609 this.decl
= new VariableDeclaration (expr
);
5614 public Expression Expr
{
5616 return decl
.Variable
== null ? decl
.Initializer
: null;
5620 public BlockVariableDeclaration Variables
{
5628 public override void Emit (EmitContext ec
)
5631 // Don't emit sequence point it will be set on variable declaration
5636 protected override void EmitTryBodyPrepare (EmitContext ec
)
5639 base.EmitTryBodyPrepare (ec
);
5642 protected override void EmitTryBody (EmitContext ec
)
5647 protected override void EmitFinallyBody (EmitContext ec
)
5649 decl
.EmitDispose (ec
);
5652 public override bool Resolve (BlockContext ec
)
5654 VariableReference vr
;
5655 bool vr_locked
= false;
5657 using (ec
.Set (ResolveContext
.Options
.UsingInitializerScope
)) {
5658 if (decl
.Variable
== null) {
5659 vr
= decl
.ResolveExpression (ec
) as VariableReference
;
5661 vr_locked
= vr
.IsLockedByStatement
;
5662 vr
.IsLockedByStatement
= true;
5665 if (decl
.IsNested
) {
5666 decl
.ResolveDeclaratorInitializer (ec
);
5668 if (!decl
.Resolve (ec
))
5671 if (decl
.Declarators
!= null) {
5672 stmt
= decl
.RewriteUsingDeclarators (ec
, stmt
);
5680 ec
.StartFlowBranching (this);
5684 ec
.EndFlowBranching ();
5687 vr
.IsLockedByStatement
= vr_locked
;
5694 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5696 Using target
= (Using
) t
;
5698 target
.decl
= (VariableDeclaration
) decl
.Clone (clonectx
);
5699 target
.stmt
= stmt
.Clone (clonectx
);
5702 public override object Accept (StructuralVisitor visitor
)
5704 return visitor
.Visit (this);
5709 /// Implementation of the foreach C# statement
5711 public class Foreach
: Statement
5713 abstract class IteratorStatement
: Statement
5715 protected readonly Foreach for_each
;
5717 protected IteratorStatement (Foreach
@foreach)
5719 this.for_each
= @foreach;
5720 this.loc
= @foreach.expr
.Location
;
5723 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5725 throw new NotImplementedException ();
5728 public override void Emit (EmitContext ec
)
5730 if (ec
.EmitAccurateDebugInfo
) {
5731 ec
.Emit (OpCodes
.Nop
);
5738 sealed class ArrayForeach
: IteratorStatement
5740 TemporaryVariableReference
[] lengths
;
5741 Expression
[] length_exprs
;
5742 StatementExpression
[] counter
;
5743 TemporaryVariableReference
[] variables
;
5745 TemporaryVariableReference copy
;
5747 public ArrayForeach (Foreach
@foreach, int rank
)
5750 counter
= new StatementExpression
[rank
];
5751 variables
= new TemporaryVariableReference
[rank
];
5752 length_exprs
= new Expression
[rank
];
5755 // Only use temporary length variables when dealing with
5756 // multi-dimensional arrays
5759 lengths
= new TemporaryVariableReference
[rank
];
5762 public override bool Resolve (BlockContext ec
)
5764 Block variables_block
= for_each
.variable
.Block
;
5765 copy
= TemporaryVariableReference
.Create (for_each
.expr
.Type
, variables_block
, loc
);
5768 int rank
= length_exprs
.Length
;
5769 Arguments list
= new Arguments (rank
);
5770 for (int i
= 0; i
< rank
; i
++) {
5771 var v
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Int
, variables_block
, loc
);
5773 counter
[i
] = new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, v
, Location
.Null
));
5774 counter
[i
].Resolve (ec
);
5777 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
5779 lengths
[i
] = TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Int
, variables_block
, loc
);
5780 lengths
[i
].Resolve (ec
);
5782 Arguments args
= new Arguments (1);
5783 args
.Add (new Argument (new IntConstant (ec
.BuiltinTypes
, i
, loc
)));
5784 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
5787 list
.Add (new Argument (v
));
5790 var access
= new ElementAccess (copy
, list
, loc
).Resolve (ec
);
5795 if (for_each
.type
is VarExpr
) {
5796 // Infer implicitly typed local variable from foreach array type
5797 var_type
= access
.Type
;
5799 var_type
= for_each
.type
.ResolveAsType (ec
);
5801 if (var_type
== null)
5804 access
= Convert
.ExplicitConversion (ec
, access
, var_type
, loc
);
5809 for_each
.variable
.Type
= var_type
;
5811 var variable_ref
= new LocalVariableReference (for_each
.variable
, loc
).Resolve (ec
);
5812 if (variable_ref
== null)
5815 for_each
.body
.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref
, access
, Location
.Null
), for_each
.variable
.Location
));
5819 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
5820 ec
.CurrentBranching
.CreateSibling ();
5822 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
5823 if (!for_each
.body
.Resolve (ec
))
5825 ec
.EndFlowBranching ();
5827 // There's no direct control flow from the end of the embedded statement to the end of the loop
5828 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
5830 ec
.EndFlowBranching ();
5835 protected override void DoEmit (EmitContext ec
)
5837 copy
.EmitAssign (ec
, for_each
.expr
);
5839 int rank
= length_exprs
.Length
;
5840 Label
[] test
= new Label
[rank
];
5841 Label
[] loop
= new Label
[rank
];
5843 for (int i
= 0; i
< rank
; i
++) {
5844 test
[i
] = ec
.DefineLabel ();
5845 loop
[i
] = ec
.DefineLabel ();
5847 if (lengths
!= null)
5848 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
5851 IntConstant zero
= new IntConstant (ec
.BuiltinTypes
, 0, loc
);
5852 for (int i
= 0; i
< rank
; i
++) {
5853 variables
[i
].EmitAssign (ec
, zero
);
5855 ec
.Emit (OpCodes
.Br
, test
[i
]);
5856 ec
.MarkLabel (loop
[i
]);
5859 for_each
.body
.Emit (ec
);
5861 ec
.MarkLabel (ec
.LoopBegin
);
5862 ec
.Mark (for_each
.expr
.Location
);
5864 for (int i
= rank
- 1; i
>= 0; i
--){
5865 counter
[i
].Emit (ec
);
5867 ec
.MarkLabel (test
[i
]);
5868 variables
[i
].Emit (ec
);
5870 if (lengths
!= null)
5871 lengths
[i
].Emit (ec
);
5873 length_exprs
[i
].Emit (ec
);
5875 ec
.Emit (OpCodes
.Blt
, loop
[i
]);
5878 ec
.MarkLabel (ec
.LoopEnd
);
5882 sealed class CollectionForeach
: IteratorStatement
, OverloadResolver
.IErrorHandler
5884 class RuntimeDispose
: Using
.VariableDeclaration
5886 public RuntimeDispose (LocalVariable lv
, Location loc
)
5891 protected override void CheckIDiposableConversion (BlockContext bc
, LocalVariable li
, Expression initializer
)
5893 // Defered to runtime check
5896 protected override Statement
CreateDisposeCall (BlockContext bc
, LocalVariable lv
)
5898 var idt
= bc
.BuiltinTypes
.IDisposable
;
5901 // Fabricates code like
5903 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5906 var dispose_variable
= LocalVariable
.CreateCompilerGenerated (idt
, bc
.CurrentBlock
, loc
);
5908 var idisaposable_test
= new Binary (Binary
.Operator
.Inequality
, new CompilerAssign (
5909 dispose_variable
.CreateReferenceExpression (bc
, loc
),
5910 new As (lv
.CreateReferenceExpression (bc
, loc
), new TypeExpression (dispose_variable
.Type
, loc
), loc
),
5911 loc
), new NullLiteral (loc
), loc
);
5913 var m
= bc
.Module
.PredefinedMembers
.IDisposableDispose
.Resolve (loc
);
5915 var dispose_mg
= MethodGroupExpr
.CreatePredefined (m
, idt
, loc
);
5916 dispose_mg
.InstanceExpression
= dispose_variable
.CreateReferenceExpression (bc
, loc
);
5918 Statement dispose
= new StatementExpression (new Invocation (dispose_mg
, null));
5919 return new If (idisaposable_test
, dispose
, loc
);
5923 LocalVariable variable
;
5925 Statement statement
;
5926 ExpressionStatement init
;
5927 TemporaryVariableReference enumerator_variable
;
5928 bool ambiguous_getenumerator_name
;
5930 public CollectionForeach (Foreach
@foreach, LocalVariable
var, Expression expr
)
5933 this.variable
= var;
5937 void Error_WrongEnumerator (ResolveContext rc
, MethodSpec enumerator
)
5939 rc
.Report
.SymbolRelatedToPreviousError (enumerator
);
5940 rc
.Report
.Error (202, loc
,
5941 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5942 enumerator
.ReturnType
.GetSignatureForError (), enumerator
.GetSignatureForError ());
5945 MethodGroupExpr
ResolveGetEnumerator (ResolveContext rc
)
5948 // Option 1: Try to match by name GetEnumerator first
5950 var mexpr
= Expression
.MemberLookup (rc
, false, expr
.Type
,
5951 "GetEnumerator", 0, Expression
.MemberLookupRestrictions
.ExactArity
, loc
); // TODO: What if CS0229 ?
5953 var mg
= mexpr
as MethodGroupExpr
;
5955 mg
.InstanceExpression
= expr
;
5956 Arguments args
= new Arguments (0);
5957 mg
= mg
.OverloadResolve (rc
, ref args
, this, OverloadResolver
.Restrictions
.None
);
5959 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5960 if (ambiguous_getenumerator_name
)
5963 if (mg
!= null && args
.Count
== 0 && !mg
.BestCandidate
.IsStatic
&& mg
.BestCandidate
.IsPublic
) {
5969 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5972 PredefinedMember
<MethodSpec
> iface_candidate
= null;
5973 var ptypes
= rc
.Module
.PredefinedTypes
;
5974 var gen_ienumerable
= ptypes
.IEnumerableGeneric
;
5975 if (!gen_ienumerable
.Define ())
5976 gen_ienumerable
= null;
5979 var ifaces
= t
.Interfaces
;
5980 if (ifaces
!= null) {
5981 foreach (var iface
in ifaces
) {
5982 if (gen_ienumerable
!= null && iface
.MemberDefinition
== gen_ienumerable
.TypeSpec
.MemberDefinition
) {
5983 if (iface_candidate
!= null && iface_candidate
!= rc
.Module
.PredefinedMembers
.IEnumerableGetEnumerator
) {
5984 rc
.Report
.SymbolRelatedToPreviousError (expr
.Type
);
5985 rc
.Report
.Error (1640, loc
,
5986 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5987 expr
.Type
.GetSignatureForError (), gen_ienumerable
.TypeSpec
.GetSignatureForError ());
5992 // TODO: Cache this somehow
5993 iface_candidate
= new PredefinedMember
<MethodSpec
> (rc
.Module
, iface
,
5994 MemberFilter
.Method ("GetEnumerator", 0, ParametersCompiled
.EmptyReadOnlyParameters
, null));
5999 if (iface
.BuiltinType
== BuiltinTypeSpec
.Type
.IEnumerable
&& iface_candidate
== null) {
6000 iface_candidate
= rc
.Module
.PredefinedMembers
.IEnumerableGetEnumerator
;
6005 if (t
.IsGenericParameter
)
6010 } while (t
!= null);
6012 if (iface_candidate
== null) {
6013 if (expr
.Type
!= InternalType
.ErrorType
) {
6014 rc
.Report
.Error (1579, loc
,
6015 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6016 expr
.Type
.GetSignatureForError (), "GetEnumerator");
6022 var method
= iface_candidate
.Resolve (loc
);
6026 mg
= MethodGroupExpr
.CreatePredefined (method
, expr
.Type
, loc
);
6027 mg
.InstanceExpression
= expr
;
6031 MethodGroupExpr
ResolveMoveNext (ResolveContext rc
, MethodSpec enumerator
)
6033 var ms
= MemberCache
.FindMember (enumerator
.ReturnType
,
6034 MemberFilter
.Method ("MoveNext", 0, ParametersCompiled
.EmptyReadOnlyParameters
, rc
.BuiltinTypes
.Bool
),
6035 BindingRestriction
.InstanceOnly
) as MethodSpec
;
6037 if (ms
== null || !ms
.IsPublic
) {
6038 Error_WrongEnumerator (rc
, enumerator
);
6042 return MethodGroupExpr
.CreatePredefined (ms
, enumerator
.ReturnType
, expr
.Location
);
6045 PropertySpec
ResolveCurrent (ResolveContext rc
, MethodSpec enumerator
)
6047 var ps
= MemberCache
.FindMember (enumerator
.ReturnType
,
6048 MemberFilter
.Property ("Current", null),
6049 BindingRestriction
.InstanceOnly
) as PropertySpec
;
6051 if (ps
== null || !ps
.IsPublic
) {
6052 Error_WrongEnumerator (rc
, enumerator
);
6059 public override bool Resolve (BlockContext ec
)
6061 bool is_dynamic
= expr
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.Dynamic
;
6064 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, ec
.BuiltinTypes
.IEnumerable
, loc
);
6065 } else if (expr
.Type
.IsNullableType
) {
6066 expr
= new Nullable
.UnwrapCall (expr
).Resolve (ec
);
6069 var get_enumerator_mg
= ResolveGetEnumerator (ec
);
6070 if (get_enumerator_mg
== null) {
6074 var get_enumerator
= get_enumerator_mg
.BestCandidate
;
6075 enumerator_variable
= TemporaryVariableReference
.Create (get_enumerator
.ReturnType
, variable
.Block
, loc
);
6076 enumerator_variable
.Resolve (ec
);
6078 // Prepare bool MoveNext ()
6079 var move_next_mg
= ResolveMoveNext (ec
, get_enumerator
);
6080 if (move_next_mg
== null) {
6084 move_next_mg
.InstanceExpression
= enumerator_variable
;
6086 // Prepare ~T~ Current { get; }
6087 var current_prop
= ResolveCurrent (ec
, get_enumerator
);
6088 if (current_prop
== null) {
6092 var current_pe
= new PropertyExpr (current_prop
, loc
) { InstanceExpression = enumerator_variable }
.Resolve (ec
);
6093 if (current_pe
== null)
6096 VarExpr ve
= for_each
.type
as VarExpr
;
6100 // Source type is dynamic, set element type to dynamic too
6101 variable
.Type
= ec
.BuiltinTypes
.Dynamic
;
6103 // Infer implicitly typed local variable from foreach enumerable type
6104 variable
.Type
= current_pe
.Type
;
6108 // Explicit cast of dynamic collection elements has to be done at runtime
6109 current_pe
= EmptyCast
.Create (current_pe
, ec
.BuiltinTypes
.Dynamic
);
6112 variable
.Type
= for_each
.type
.ResolveAsType (ec
);
6114 if (variable
.Type
== null)
6117 current_pe
= Convert
.ExplicitConversion (ec
, current_pe
, variable
.Type
, loc
);
6118 if (current_pe
== null)
6122 var variable_ref
= new LocalVariableReference (variable
, loc
).Resolve (ec
);
6123 if (variable_ref
== null)
6126 for_each
.body
.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref
, current_pe
, Location
.Null
), variable
.Location
));
6128 var init
= new Invocation (get_enumerator_mg
, null);
6130 statement
= new While (new BooleanExpression (new Invocation (move_next_mg
, null)),
6131 for_each
.body
, Location
.Null
);
6133 var enum_type
= enumerator_variable
.Type
;
6136 // Add Dispose method call when enumerator can be IDisposable
6138 if (!enum_type
.ImplementsInterface (ec
.BuiltinTypes
.IDisposable
, false)) {
6139 if (!enum_type
.IsSealed
&& !TypeSpec
.IsValueType (enum_type
)) {
6141 // Runtime Dispose check
6143 var vd
= new RuntimeDispose (enumerator_variable
.LocalInfo
, Location
.Null
);
6144 vd
.Initializer
= init
;
6145 statement
= new Using (vd
, statement
, Location
.Null
);
6148 // No Dispose call needed
6150 this.init
= new SimpleAssign (enumerator_variable
, init
, Location
.Null
);
6151 this.init
.Resolve (ec
);
6155 // Static Dispose check
6157 var vd
= new Using
.VariableDeclaration (enumerator_variable
.LocalInfo
, Location
.Null
);
6158 vd
.Initializer
= init
;
6159 statement
= new Using (vd
, statement
, Location
.Null
);
6162 return statement
.Resolve (ec
);
6165 protected override void DoEmit (EmitContext ec
)
6167 enumerator_variable
.LocalInfo
.CreateBuilder (ec
);
6170 init
.EmitStatement (ec
);
6172 statement
.Emit (ec
);
6175 #region IErrorHandler Members
6177 bool OverloadResolver
.IErrorHandler
.AmbiguousCandidates (ResolveContext ec
, MemberSpec best
, MemberSpec ambiguous
)
6179 ec
.Report
.SymbolRelatedToPreviousError (best
);
6180 ec
.Report
.Warning (278, 2, expr
.Location
,
6181 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6182 expr
.Type
.GetSignatureForError (), "enumerable",
6183 best
.GetSignatureForError (), ambiguous
.GetSignatureForError ());
6185 ambiguous_getenumerator_name
= true;
6189 bool OverloadResolver
.IErrorHandler
.ArgumentMismatch (ResolveContext rc
, MemberSpec best
, Argument arg
, int index
)
6194 bool OverloadResolver
.IErrorHandler
.NoArgumentMatch (ResolveContext rc
, MemberSpec best
)
6199 bool OverloadResolver
.IErrorHandler
.TypeInferenceFailed (ResolveContext rc
, MemberSpec best
)
6208 LocalVariable variable
;
6210 Statement statement
;
6213 public Foreach (Expression type
, LocalVariable
var, Expression expr
, Statement stmt
, Block body
, Location l
)
6216 this.variable
= var;
6218 this.statement
= stmt
;
6223 public Expression Expr
{
6224 get { return expr; }
6227 public Statement Statement
{
6228 get { return statement; }
6231 public Expression TypeExpression
{
6232 get { return type; }
6235 public LocalVariable Variable
{
6236 get { return variable; }
6239 public override bool Resolve (BlockContext ec
)
6241 expr
= expr
.Resolve (ec
);
6246 ec
.Report
.Error (186, loc
, "Use of null is not valid in this context");
6250 body
.AddStatement (statement
);
6252 if (expr
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
6253 statement
= new ArrayForeach (this, 1);
6254 } else if (expr
.Type
is ArrayContainer
) {
6255 statement
= new ArrayForeach (this, ((ArrayContainer
) expr
.Type
).Rank
);
6257 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
6258 ec
.Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
6259 expr
.ExprClassName
);
6263 statement
= new CollectionForeach (this, variable
, expr
);
6266 return statement
.Resolve (ec
);
6269 protected override void DoEmit (EmitContext ec
)
6271 variable
.CreateBuilder (ec
);
6273 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
6274 ec
.LoopBegin
= ec
.DefineLabel ();
6275 ec
.LoopEnd
= ec
.DefineLabel ();
6277 statement
.Emit (ec
);
6279 ec
.LoopBegin
= old_begin
;
6280 ec
.LoopEnd
= old_end
;
6283 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6285 Foreach target
= (Foreach
) t
;
6287 target
.type
= type
.Clone (clonectx
);
6288 target
.expr
= expr
.Clone (clonectx
);
6289 target
.body
= (Block
) body
.Clone (clonectx
);
6292 public override object Accept (StructuralVisitor visitor
)
6294 return visitor
.Visit (this);