PageRenderTime 76ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/mcs/statement.cs

https://bitbucket.org/danipen/mono
C# | 6461 lines | 4695 code | 1203 blank | 563 comment | 1052 complexity | d1889fc01f02f011c13a627ec1b68019 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // statement.cs: Statement representation for the IL tree.
  3. //
  4. // Authors:
  5. // Miguel de Icaza (miguel@ximian.com)
  6. // Martin Baulig (martin@ximian.com)
  7. // Marek Safar (marek.safar@gmail.com)
  8. //
  9. // Copyright 2001, 2002, 2003 Ximian, Inc.
  10. // Copyright 2003, 2004 Novell, Inc.
  11. // Copyright 2011 Xamarin Inc.
  12. //
  13. using System;
  14. using System.Collections.Generic;
  15. #if STATIC
  16. using IKVM.Reflection.Emit;
  17. #else
  18. using System.Reflection.Emit;
  19. #endif
  20. namespace Mono.CSharp {
  21. public abstract class Statement {
  22. public Location loc;
  23. /// <summary>
  24. /// Resolves the statement, true means that all sub-statements
  25. /// did resolve ok.
  26. // </summary>
  27. public virtual bool Resolve (BlockContext bc)
  28. {
  29. return true;
  30. }
  31. /// <summary>
  32. /// We already know that the statement is unreachable, but we still
  33. /// need to resolve it to catch errors.
  34. /// </summary>
  35. public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
  36. {
  37. //
  38. // This conflicts with csc's way of doing this, but IMHO it's
  39. // the right thing to do.
  40. //
  41. // If something is unreachable, we still check whether it's
  42. // correct. This means that you cannot use unassigned variables
  43. // in unreachable code, for instance.
  44. //
  45. bool unreachable = false;
  46. if (warn && !ec.UnreachableReported) {
  47. ec.UnreachableReported = true;
  48. unreachable = true;
  49. ec.Report.Warning (162, 2, loc, "Unreachable code detected");
  50. }
  51. ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
  52. bool ok = Resolve (ec);
  53. ec.KillFlowBranching ();
  54. if (unreachable) {
  55. ec.UnreachableReported = false;
  56. }
  57. return ok;
  58. }
  59. /// <summary>
  60. /// Return value indicates whether all code paths emitted return.
  61. /// </summary>
  62. protected abstract void DoEmit (EmitContext ec);
  63. public virtual void Emit (EmitContext ec)
  64. {
  65. ec.Mark (loc);
  66. DoEmit (ec);
  67. if (ec.StatementEpilogue != null) {
  68. ec.EmitEpilogue ();
  69. }
  70. }
  71. //
  72. // This routine must be overrided in derived classes and make copies
  73. // of all the data that might be modified if resolved
  74. //
  75. protected abstract void CloneTo (CloneContext clonectx, Statement target);
  76. public Statement Clone (CloneContext clonectx)
  77. {
  78. Statement s = (Statement) this.MemberwiseClone ();
  79. CloneTo (clonectx, s);
  80. return s;
  81. }
  82. public virtual Expression CreateExpressionTree (ResolveContext ec)
  83. {
  84. ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
  85. return null;
  86. }
  87. public virtual object Accept (StructuralVisitor visitor)
  88. {
  89. return visitor.Visit (this);
  90. }
  91. }
  92. public sealed class EmptyStatement : Statement
  93. {
  94. public EmptyStatement (Location loc)
  95. {
  96. this.loc = loc;
  97. }
  98. public override bool Resolve (BlockContext ec)
  99. {
  100. return true;
  101. }
  102. public override bool ResolveUnreachable (BlockContext ec, bool warn)
  103. {
  104. return true;
  105. }
  106. public override void Emit (EmitContext ec)
  107. {
  108. }
  109. protected override void DoEmit (EmitContext ec)
  110. {
  111. throw new NotSupportedException ();
  112. }
  113. protected override void CloneTo (CloneContext clonectx, Statement target)
  114. {
  115. // nothing needed.
  116. }
  117. public override object Accept (StructuralVisitor visitor)
  118. {
  119. return visitor.Visit (this);
  120. }
  121. }
  122. public class If : Statement {
  123. Expression expr;
  124. public Statement TrueStatement;
  125. public Statement FalseStatement;
  126. bool is_true_ret;
  127. public If (Expression bool_expr, Statement true_statement, Location l)
  128. : this (bool_expr, true_statement, null, l)
  129. {
  130. }
  131. public If (Expression bool_expr,
  132. Statement true_statement,
  133. Statement false_statement,
  134. Location l)
  135. {
  136. this.expr = bool_expr;
  137. TrueStatement = true_statement;
  138. FalseStatement = false_statement;
  139. loc = l;
  140. }
  141. public Expression Expr {
  142. get {
  143. return this.expr;
  144. }
  145. }
  146. public override bool Resolve (BlockContext ec)
  147. {
  148. bool ok = true;
  149. expr = expr.Resolve (ec);
  150. if (expr == null) {
  151. ok = false;
  152. } else {
  153. //
  154. // Dead code elimination
  155. //
  156. if (expr is Constant) {
  157. bool take = !((Constant) expr).IsDefaultValue;
  158. if (take) {
  159. if (!TrueStatement.Resolve (ec))
  160. return false;
  161. if ((FalseStatement != null) &&
  162. !FalseStatement.ResolveUnreachable (ec, true))
  163. return false;
  164. FalseStatement = null;
  165. } else {
  166. if (!TrueStatement.ResolveUnreachable (ec, true))
  167. return false;
  168. TrueStatement = null;
  169. if ((FalseStatement != null) &&
  170. !FalseStatement.Resolve (ec))
  171. return false;
  172. }
  173. return true;
  174. }
  175. }
  176. ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
  177. ok &= TrueStatement.Resolve (ec);
  178. is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
  179. ec.CurrentBranching.CreateSibling ();
  180. if (FalseStatement != null)
  181. ok &= FalseStatement.Resolve (ec);
  182. ec.EndFlowBranching ();
  183. return ok;
  184. }
  185. protected override void DoEmit (EmitContext ec)
  186. {
  187. Label false_target = ec.DefineLabel ();
  188. Label end;
  189. //
  190. // If we're a boolean constant, Resolve() already
  191. // eliminated dead code for us.
  192. //
  193. Constant c = expr as Constant;
  194. if (c != null){
  195. c.EmitSideEffect (ec);
  196. if (!c.IsDefaultValue)
  197. TrueStatement.Emit (ec);
  198. else if (FalseStatement != null)
  199. FalseStatement.Emit (ec);
  200. return;
  201. }
  202. expr.EmitBranchable (ec, false_target, false);
  203. TrueStatement.Emit (ec);
  204. if (FalseStatement != null){
  205. bool branch_emitted = false;
  206. end = ec.DefineLabel ();
  207. if (!is_true_ret){
  208. ec.Emit (OpCodes.Br, end);
  209. branch_emitted = true;
  210. }
  211. ec.MarkLabel (false_target);
  212. FalseStatement.Emit (ec);
  213. if (branch_emitted)
  214. ec.MarkLabel (end);
  215. } else {
  216. ec.MarkLabel (false_target);
  217. }
  218. }
  219. protected override void CloneTo (CloneContext clonectx, Statement t)
  220. {
  221. If target = (If) t;
  222. target.expr = expr.Clone (clonectx);
  223. target.TrueStatement = TrueStatement.Clone (clonectx);
  224. if (FalseStatement != null)
  225. target.FalseStatement = FalseStatement.Clone (clonectx);
  226. }
  227. public override object Accept (StructuralVisitor visitor)
  228. {
  229. return visitor.Visit (this);
  230. }
  231. }
  232. public class Do : Statement {
  233. public Expression expr;
  234. public Statement EmbeddedStatement;
  235. public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
  236. {
  237. expr = bool_expr;
  238. EmbeddedStatement = statement;
  239. loc = doLocation;
  240. WhileLocation = whileLocation;
  241. }
  242. public Location WhileLocation {
  243. get; private set;
  244. }
  245. public override bool Resolve (BlockContext ec)
  246. {
  247. bool ok = true;
  248. ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
  249. bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
  250. ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
  251. if (!EmbeddedStatement.Resolve (ec))
  252. ok = false;
  253. ec.EndFlowBranching ();
  254. if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
  255. ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
  256. expr = expr.Resolve (ec);
  257. if (expr == null)
  258. ok = false;
  259. else if (expr is Constant){
  260. bool infinite = !((Constant) expr).IsDefaultValue;
  261. if (infinite)
  262. ec.CurrentBranching.CurrentUsageVector.Goto ();
  263. }
  264. ec.EndFlowBranching ();
  265. return ok;
  266. }
  267. protected override void DoEmit (EmitContext ec)
  268. {
  269. Label loop = ec.DefineLabel ();
  270. Label old_begin = ec.LoopBegin;
  271. Label old_end = ec.LoopEnd;
  272. ec.LoopBegin = ec.DefineLabel ();
  273. ec.LoopEnd = ec.DefineLabel ();
  274. ec.MarkLabel (loop);
  275. EmbeddedStatement.Emit (ec);
  276. ec.MarkLabel (ec.LoopBegin);
  277. // Mark start of while condition
  278. ec.Mark (WhileLocation);
  279. //
  280. // Dead code elimination
  281. //
  282. if (expr is Constant) {
  283. bool res = !((Constant) expr).IsDefaultValue;
  284. expr.EmitSideEffect (ec);
  285. if (res)
  286. ec.Emit (OpCodes.Br, loop);
  287. } else {
  288. expr.EmitBranchable (ec, loop, true);
  289. }
  290. ec.MarkLabel (ec.LoopEnd);
  291. ec.LoopBegin = old_begin;
  292. ec.LoopEnd = old_end;
  293. }
  294. protected override void CloneTo (CloneContext clonectx, Statement t)
  295. {
  296. Do target = (Do) t;
  297. target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
  298. target.expr = expr.Clone (clonectx);
  299. }
  300. public override object Accept (StructuralVisitor visitor)
  301. {
  302. return visitor.Visit (this);
  303. }
  304. }
  305. public class While : Statement {
  306. public Expression expr;
  307. public Statement Statement;
  308. bool infinite, empty;
  309. public While (BooleanExpression bool_expr, Statement statement, Location l)
  310. {
  311. this.expr = bool_expr;
  312. Statement = statement;
  313. loc = l;
  314. }
  315. public override bool Resolve (BlockContext ec)
  316. {
  317. bool ok = true;
  318. expr = expr.Resolve (ec);
  319. if (expr == null)
  320. ok = false;
  321. //
  322. // Inform whether we are infinite or not
  323. //
  324. if (expr is Constant){
  325. bool value = !((Constant) expr).IsDefaultValue;
  326. if (value == false){
  327. if (!Statement.ResolveUnreachable (ec, true))
  328. return false;
  329. empty = true;
  330. return true;
  331. } else
  332. infinite = true;
  333. }
  334. ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
  335. if (!infinite)
  336. ec.CurrentBranching.CreateSibling ();
  337. ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
  338. if (!Statement.Resolve (ec))
  339. ok = false;
  340. ec.EndFlowBranching ();
  341. // There's no direct control flow from the end of the embedded statement to the end of the loop
  342. ec.CurrentBranching.CurrentUsageVector.Goto ();
  343. ec.EndFlowBranching ();
  344. return ok;
  345. }
  346. protected override void DoEmit (EmitContext ec)
  347. {
  348. if (empty) {
  349. expr.EmitSideEffect (ec);
  350. return;
  351. }
  352. Label old_begin = ec.LoopBegin;
  353. Label old_end = ec.LoopEnd;
  354. ec.LoopBegin = ec.DefineLabel ();
  355. ec.LoopEnd = ec.DefineLabel ();
  356. //
  357. // Inform whether we are infinite or not
  358. //
  359. if (expr is Constant) {
  360. // expr is 'true', since the 'empty' case above handles the 'false' case
  361. ec.MarkLabel (ec.LoopBegin);
  362. if (ec.EmitAccurateDebugInfo)
  363. ec.Emit (OpCodes.Nop);
  364. expr.EmitSideEffect (ec);
  365. Statement.Emit (ec);
  366. ec.Emit (OpCodes.Br, ec.LoopBegin);
  367. //
  368. // Inform that we are infinite (ie, `we return'), only
  369. // if we do not `break' inside the code.
  370. //
  371. ec.MarkLabel (ec.LoopEnd);
  372. } else {
  373. Label while_loop = ec.DefineLabel ();
  374. ec.Emit (OpCodes.Br, ec.LoopBegin);
  375. ec.MarkLabel (while_loop);
  376. Statement.Emit (ec);
  377. ec.MarkLabel (ec.LoopBegin);
  378. ec.Mark (loc);
  379. expr.EmitBranchable (ec, while_loop, true);
  380. ec.MarkLabel (ec.LoopEnd);
  381. }
  382. ec.LoopBegin = old_begin;
  383. ec.LoopEnd = old_end;
  384. }
  385. protected override void CloneTo (CloneContext clonectx, Statement t)
  386. {
  387. While target = (While) t;
  388. target.expr = expr.Clone (clonectx);
  389. target.Statement = Statement.Clone (clonectx);
  390. }
  391. public override object Accept (StructuralVisitor visitor)
  392. {
  393. return visitor.Visit (this);
  394. }
  395. }
  396. public class For : Statement
  397. {
  398. bool infinite, empty;
  399. public For (Location l)
  400. {
  401. loc = l;
  402. }
  403. public Statement Initializer {
  404. get; set;
  405. }
  406. public Expression Condition {
  407. get; set;
  408. }
  409. public Statement Iterator {
  410. get; set;
  411. }
  412. public Statement Statement {
  413. get; set;
  414. }
  415. public override bool Resolve (BlockContext ec)
  416. {
  417. bool ok = true;
  418. if (Initializer != null) {
  419. if (!Initializer.Resolve (ec))
  420. ok = false;
  421. }
  422. if (Condition != null) {
  423. Condition = Condition.Resolve (ec);
  424. if (Condition == null)
  425. ok = false;
  426. else if (Condition is Constant) {
  427. bool value = !((Constant) Condition).IsDefaultValue;
  428. if (value == false){
  429. if (!Statement.ResolveUnreachable (ec, true))
  430. return false;
  431. if ((Iterator != null) &&
  432. !Iterator.ResolveUnreachable (ec, false))
  433. return false;
  434. empty = true;
  435. return true;
  436. } else
  437. infinite = true;
  438. }
  439. } else
  440. infinite = true;
  441. ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
  442. if (!infinite)
  443. ec.CurrentBranching.CreateSibling ();
  444. bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
  445. ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
  446. if (!Statement.Resolve (ec))
  447. ok = false;
  448. ec.EndFlowBranching ();
  449. if (Iterator != null){
  450. if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
  451. if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
  452. ok = false;
  453. } else {
  454. if (!Iterator.Resolve (ec))
  455. ok = false;
  456. }
  457. }
  458. // There's no direct control flow from the end of the embedded statement to the end of the loop
  459. ec.CurrentBranching.CurrentUsageVector.Goto ();
  460. ec.EndFlowBranching ();
  461. return ok;
  462. }
  463. protected override void DoEmit (EmitContext ec)
  464. {
  465. if (Initializer != null)
  466. Initializer.Emit (ec);
  467. if (empty) {
  468. Condition.EmitSideEffect (ec);
  469. return;
  470. }
  471. Label old_begin = ec.LoopBegin;
  472. Label old_end = ec.LoopEnd;
  473. Label loop = ec.DefineLabel ();
  474. Label test = ec.DefineLabel ();
  475. ec.LoopBegin = ec.DefineLabel ();
  476. ec.LoopEnd = ec.DefineLabel ();
  477. ec.Emit (OpCodes.Br, test);
  478. ec.MarkLabel (loop);
  479. Statement.Emit (ec);
  480. ec.MarkLabel (ec.LoopBegin);
  481. Iterator.Emit (ec);
  482. ec.MarkLabel (test);
  483. //
  484. // If test is null, there is no test, and we are just
  485. // an infinite loop
  486. //
  487. if (Condition != null) {
  488. ec.Mark (Condition.Location);
  489. //
  490. // The Resolve code already catches the case for
  491. // Test == Constant (false) so we know that
  492. // this is true
  493. //
  494. if (Condition is Constant) {
  495. Condition.EmitSideEffect (ec);
  496. ec.Emit (OpCodes.Br, loop);
  497. } else {
  498. Condition.EmitBranchable (ec, loop, true);
  499. }
  500. } else
  501. ec.Emit (OpCodes.Br, loop);
  502. ec.MarkLabel (ec.LoopEnd);
  503. ec.LoopBegin = old_begin;
  504. ec.LoopEnd = old_end;
  505. }
  506. protected override void CloneTo (CloneContext clonectx, Statement t)
  507. {
  508. For target = (For) t;
  509. if (Initializer != null)
  510. target.Initializer = Initializer.Clone (clonectx);
  511. if (Condition != null)
  512. target.Condition = Condition.Clone (clonectx);
  513. if (Iterator != null)
  514. target.Iterator = Iterator.Clone (clonectx);
  515. target.Statement = Statement.Clone (clonectx);
  516. }
  517. public override object Accept (StructuralVisitor visitor)
  518. {
  519. return visitor.Visit (this);
  520. }
  521. }
  522. public class StatementExpression : Statement
  523. {
  524. ExpressionStatement expr;
  525. public StatementExpression (ExpressionStatement expr)
  526. {
  527. this.expr = expr;
  528. loc = expr.Location;
  529. }
  530. public StatementExpression (ExpressionStatement expr, Location loc)
  531. {
  532. this.expr = expr;
  533. this.loc = loc;
  534. }
  535. public ExpressionStatement Expr {
  536. get {
  537. return this.expr;
  538. }
  539. }
  540. protected override void CloneTo (CloneContext clonectx, Statement t)
  541. {
  542. StatementExpression target = (StatementExpression) t;
  543. target.expr = (ExpressionStatement) expr.Clone (clonectx);
  544. }
  545. protected override void DoEmit (EmitContext ec)
  546. {
  547. expr.EmitStatement (ec);
  548. }
  549. public override bool Resolve (BlockContext ec)
  550. {
  551. expr = expr.ResolveStatement (ec);
  552. return expr != null;
  553. }
  554. public override object Accept (StructuralVisitor visitor)
  555. {
  556. return visitor.Visit (this);
  557. }
  558. }
  559. public class StatementErrorExpression : Statement
  560. {
  561. readonly Expression expr;
  562. public StatementErrorExpression (Expression expr)
  563. {
  564. this.expr = expr;
  565. }
  566. public Expression Expr {
  567. get {
  568. return expr;
  569. }
  570. }
  571. protected override void DoEmit (EmitContext ec)
  572. {
  573. throw new NotSupportedException ();
  574. }
  575. protected override void CloneTo (CloneContext clonectx, Statement target)
  576. {
  577. throw new NotImplementedException ();
  578. }
  579. public override object Accept (StructuralVisitor visitor)
  580. {
  581. return visitor.Visit (this);
  582. }
  583. }
  584. //
  585. // Simple version of statement list not requiring a block
  586. //
  587. public class StatementList : Statement
  588. {
  589. List<Statement> statements;
  590. public StatementList (Statement first, Statement second)
  591. {
  592. statements = new List<Statement> () { first, second };
  593. }
  594. #region Properties
  595. public IList<Statement> Statements {
  596. get {
  597. return statements;
  598. }
  599. }
  600. #endregion
  601. public void Add (Statement statement)
  602. {
  603. statements.Add (statement);
  604. }
  605. public override bool Resolve (BlockContext ec)
  606. {
  607. foreach (var s in statements)
  608. s.Resolve (ec);
  609. return true;
  610. }
  611. protected override void DoEmit (EmitContext ec)
  612. {
  613. foreach (var s in statements)
  614. s.Emit (ec);
  615. }
  616. protected override void CloneTo (CloneContext clonectx, Statement target)
  617. {
  618. StatementList t = (StatementList) target;
  619. t.statements = new List<Statement> (statements.Count);
  620. foreach (Statement s in statements)
  621. t.statements.Add (s.Clone (clonectx));
  622. }
  623. public override object Accept (StructuralVisitor visitor)
  624. {
  625. return visitor.Visit (this);
  626. }
  627. }
  628. // A 'return' or a 'yield break'
  629. public abstract class ExitStatement : Statement
  630. {
  631. protected bool unwind_protect;
  632. protected abstract bool DoResolve (BlockContext ec);
  633. public virtual void Error_FinallyClause (Report Report)
  634. {
  635. Report.Error (157, loc, "Control cannot leave the body of a finally clause");
  636. }
  637. public sealed override bool Resolve (BlockContext ec)
  638. {
  639. var res = DoResolve (ec);
  640. unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
  641. ec.CurrentBranching.CurrentUsageVector.Goto ();
  642. return res;
  643. }
  644. }
  645. /// <summary>
  646. /// Implements the return statement
  647. /// </summary>
  648. public class Return : ExitStatement
  649. {
  650. Expression expr;
  651. public Return (Expression expr, Location l)
  652. {
  653. this.expr = expr;
  654. loc = l;
  655. }
  656. #region Properties
  657. public Expression Expr {
  658. get {
  659. return expr;
  660. }
  661. protected set {
  662. expr = value;
  663. }
  664. }
  665. #endregion
  666. protected override bool DoResolve (BlockContext ec)
  667. {
  668. if (expr == null) {
  669. if (ec.ReturnType.Kind == MemberKind.Void)
  670. return true;
  671. //
  672. // Return must not be followed by an expression when
  673. // the method return type is Task
  674. //
  675. if (ec.CurrentAnonymousMethod is AsyncInitializer) {
  676. var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
  677. if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
  678. //
  679. // Extra trick not to emit ret/leave inside awaiter body
  680. //
  681. expr = EmptyExpression.Null;
  682. return true;
  683. }
  684. }
  685. if (ec.CurrentIterator != null) {
  686. Error_ReturnFromIterator (ec);
  687. } else if (ec.ReturnType != InternalType.ErrorType) {
  688. ec.Report.Error (126, loc,
  689. "An object of a type convertible to `{0}' is required for the return statement",
  690. ec.ReturnType.GetSignatureForError ());
  691. }
  692. return false;
  693. }
  694. expr = expr.Resolve (ec);
  695. TypeSpec block_return_type = ec.ReturnType;
  696. AnonymousExpression am = ec.CurrentAnonymousMethod;
  697. if (am == null) {
  698. if (block_return_type.Kind == MemberKind.Void) {
  699. ec.Report.Error (127, loc,
  700. "`{0}': A return keyword must not be followed by any expression when method returns void",
  701. ec.GetSignatureForError ());
  702. return false;
  703. }
  704. } else {
  705. if (am.IsIterator) {
  706. Error_ReturnFromIterator (ec);
  707. return false;
  708. }
  709. var async_block = am as AsyncInitializer;
  710. if (async_block != null) {
  711. if (expr != null) {
  712. var storey = (AsyncTaskStorey) am.Storey;
  713. var async_type = storey.ReturnType;
  714. if (async_type == null && async_block.ReturnTypeInference != null) {
  715. async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
  716. return true;
  717. }
  718. if (async_type.Kind == MemberKind.Void) {
  719. ec.Report.Error (127, loc,
  720. "`{0}': A return keyword must not be followed by any expression when method returns void",
  721. ec.GetSignatureForError ());
  722. return false;
  723. }
  724. if (!async_type.IsGenericTask) {
  725. if (this is ContextualReturn)
  726. return true;
  727. ec.Report.Error (1997, loc,
  728. "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
  729. ec.GetSignatureForError ());
  730. return false;
  731. }
  732. //
  733. // The return type is actually Task<T> type argument
  734. //
  735. if (expr.Type == async_type) {
  736. ec.Report.Error (4016, loc,
  737. "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
  738. ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
  739. } else {
  740. block_return_type = async_type.TypeArguments[0];
  741. }
  742. }
  743. } else {
  744. // Same error code as .NET but better error message
  745. if (block_return_type.Kind == MemberKind.Void) {
  746. ec.Report.Error (127, loc,
  747. "`{0}': A return keyword must not be followed by any expression when delegate returns void",
  748. am.GetSignatureForError ());
  749. return false;
  750. }
  751. var l = am as AnonymousMethodBody;
  752. if (l != null && l.ReturnTypeInference != null && expr != null) {
  753. l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
  754. return true;
  755. }
  756. }
  757. }
  758. if (expr == null)
  759. return false;
  760. if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
  761. expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
  762. if (expr == null) {
  763. if (am != null && block_return_type == ec.ReturnType) {
  764. ec.Report.Error (1662, loc,
  765. "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",
  766. am.ContainerType, am.GetSignatureForError ());
  767. }
  768. return false;
  769. }
  770. }
  771. return true;
  772. }
  773. protected override void DoEmit (EmitContext ec)
  774. {
  775. if (expr != null) {
  776. expr.Emit (ec);
  777. var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
  778. if (async_body != null) {
  779. var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
  780. // It's null for await without async
  781. if (async_return != null) {
  782. async_return.EmitAssign (ec);
  783. ec.EmitEpilogue ();
  784. }
  785. ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
  786. return;
  787. }
  788. ec.EmitEpilogue ();
  789. if (unwind_protect || ec.EmitAccurateDebugInfo)
  790. ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
  791. }
  792. if (unwind_protect) {
  793. ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
  794. } else if (ec.EmitAccurateDebugInfo) {
  795. ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
  796. } else {
  797. ec.Emit (OpCodes.Ret);
  798. }
  799. }
  800. void Error_ReturnFromIterator (ResolveContext rc)
  801. {
  802. rc.Report.Error (1622, loc,
  803. "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
  804. }
  805. protected override void CloneTo (CloneContext clonectx, Statement t)
  806. {
  807. Return target = (Return) t;
  808. // It's null for simple return;
  809. if (expr != null)
  810. target.expr = expr.Clone (clonectx);
  811. }
  812. public override object Accept (StructuralVisitor visitor)
  813. {
  814. return visitor.Visit (this);
  815. }
  816. }
  817. public class Goto : Statement {
  818. string target;
  819. LabeledStatement label;
  820. bool unwind_protect;
  821. public override bool Resolve (BlockContext ec)
  822. {
  823. unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
  824. ec.CurrentBranching.CurrentUsageVector.Goto ();
  825. return true;
  826. }
  827. public Goto (string label, Location l)
  828. {
  829. loc = l;
  830. target = label;
  831. }
  832. public string Target {
  833. get { return target; }
  834. }
  835. public void SetResolvedTarget (LabeledStatement label)
  836. {
  837. this.label = label;
  838. label.AddReference ();
  839. }
  840. protected override void CloneTo (CloneContext clonectx, Statement target)
  841. {
  842. // Nothing to clone
  843. }
  844. protected override void DoEmit (EmitContext ec)
  845. {
  846. if (label == null)
  847. throw new InternalErrorException ("goto emitted before target resolved");
  848. Label l = label.LabelTarget (ec);
  849. ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
  850. }
  851. public override object Accept (StructuralVisitor visitor)
  852. {
  853. return visitor.Visit (this);
  854. }
  855. }
  856. public class LabeledStatement : Statement {
  857. string name;
  858. bool defined;
  859. bool referenced;
  860. Label label;
  861. Block block;
  862. FlowBranching.UsageVector vectors;
  863. public LabeledStatement (string name, Block block, Location l)
  864. {
  865. this.name = name;
  866. this.block = block;
  867. this.loc = l;
  868. }
  869. public Label LabelTarget (EmitContext ec)
  870. {
  871. if (defined)
  872. return label;
  873. label = ec.DefineLabel ();
  874. defined = true;
  875. return label;
  876. }
  877. public Block Block {
  878. get {
  879. return block;
  880. }
  881. }
  882. public string Name {
  883. get { return name; }
  884. }
  885. public bool IsDefined {
  886. get { return defined; }
  887. }
  888. public bool HasBeenReferenced {
  889. get { return referenced; }
  890. }
  891. public FlowBranching.UsageVector JumpOrigins {
  892. get { return vectors; }
  893. }
  894. public void AddUsageVector (FlowBranching.UsageVector vector)
  895. {
  896. vector = vector.Clone ();
  897. vector.Next = vectors;
  898. vectors = vector;
  899. }
  900. protected override void CloneTo (CloneContext clonectx, Statement target)
  901. {
  902. // nothing to clone
  903. }
  904. public override bool Resolve (BlockContext ec)
  905. {
  906. // this flow-branching will be terminated when the surrounding block ends
  907. ec.StartFlowBranching (this);
  908. return true;
  909. }
  910. protected override void DoEmit (EmitContext ec)
  911. {
  912. if (!HasBeenReferenced)
  913. ec.Report.Warning (164, 2, loc, "This label has not been referenced");
  914. LabelTarget (ec);
  915. ec.MarkLabel (label);
  916. }
  917. public void AddReference ()
  918. {
  919. referenced = true;
  920. }
  921. public override object Accept (StructuralVisitor visitor)
  922. {
  923. return visitor.Visit (this);
  924. }
  925. }
  926. /// <summary>
  927. /// `goto default' statement
  928. /// </summary>
  929. public class GotoDefault : Statement {
  930. public GotoDefault (Location l)
  931. {
  932. loc = l;
  933. }
  934. protected override void CloneTo (CloneContext clonectx, Statement target)
  935. {
  936. // nothing to clone
  937. }
  938. public override bool Resolve (BlockContext ec)
  939. {
  940. ec.CurrentBranching.CurrentUsageVector.Goto ();
  941. if (ec.Switch == null) {
  942. ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
  943. return false;
  944. }
  945. if (!ec.Switch.GotDefault) {
  946. FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
  947. return false;
  948. }
  949. return true;
  950. }
  951. protected override void DoEmit (EmitContext ec)
  952. {
  953. ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
  954. }
  955. public override object Accept (StructuralVisitor visitor)
  956. {
  957. return visitor.Visit (this);
  958. }
  959. }
  960. /// <summary>
  961. /// `goto case' statement
  962. /// </summary>
  963. public class GotoCase : Statement {
  964. Expression expr;
  965. SwitchLabel sl;
  966. public GotoCase (Expression e, Location l)
  967. {
  968. expr = e;
  969. loc = l;
  970. }
  971. public Expression Expr {
  972. get {
  973. return this.expr;
  974. }
  975. }
  976. public override bool Resolve (BlockContext ec)
  977. {
  978. if (ec.Switch == null){
  979. ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
  980. return false;
  981. }
  982. ec.CurrentBranching.CurrentUsageVector.Goto ();
  983. expr = expr.Resolve (ec);
  984. if (expr == null)
  985. return false;
  986. Constant c = expr as Constant;
  987. if (c == null) {
  988. ec.Report.Error (150, expr.Location, "A constant value is expected");
  989. return false;
  990. }
  991. Constant res;
  992. if (ec.Switch.IsNullable && c is NullLiteral) {
  993. res = c;
  994. } else {
  995. TypeSpec type = ec.Switch.SwitchType;
  996. res = c.Reduce (ec, type);
  997. if (res == null) {
  998. c.Error_ValueCannotBeConverted (ec, type, true);
  999. return false;
  1000. }
  1001. if (!Convert.ImplicitStandardConversionExists (c, type))
  1002. ec.Report.Warning (469, 2, loc,
  1003. "The `goto case' value is not implicitly convertible to type `{0}'",
  1004. TypeManager.CSharpName (type));
  1005. }
  1006. sl = ec.Switch.ResolveGotoCase (ec, res);
  1007. return true;
  1008. }
  1009. protected override void DoEmit (EmitContext ec)
  1010. {
  1011. ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
  1012. }
  1013. protected override void CloneTo (CloneContext clonectx, Statement t)
  1014. {
  1015. GotoCase target = (GotoCase) t;
  1016. target.expr = expr.Clone (clonectx);
  1017. }
  1018. public override object Accept (StructuralVisitor visitor)
  1019. {
  1020. return visitor.Visit (this);
  1021. }
  1022. }
  1023. public class Throw : Statement {
  1024. Expression expr;
  1025. public Throw (Expression expr, Location l)
  1026. {
  1027. this.expr = expr;
  1028. loc = l;
  1029. }
  1030. public Expression Expr {
  1031. get {
  1032. return this.expr;
  1033. }
  1034. }
  1035. public override bool Resolve (BlockContext ec)
  1036. {
  1037. if (expr == null) {
  1038. ec.CurrentBranching.CurrentUsageVector.Goto ();
  1039. return ec.CurrentBranching.CheckRethrow (loc);
  1040. }
  1041. expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
  1042. ec.CurrentBranching.CurrentUsageVector.Goto ();
  1043. if (expr == null)
  1044. return false;
  1045. var et = ec.BuiltinTypes.Exception;
  1046. if (Convert.ImplicitConversionExists (ec, expr, et))
  1047. expr = Convert.ImplicitConversion (ec, expr, et, loc);
  1048. else
  1049. ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
  1050. return true;
  1051. }
  1052. protected override void DoEmit (EmitContext ec)
  1053. {
  1054. if (expr == null)
  1055. ec.Emit (OpCodes.Rethrow);
  1056. else {
  1057. expr.Emit (ec);
  1058. ec.Emit (OpCodes.Throw);
  1059. }
  1060. }
  1061. protected override void CloneTo (CloneContext clonectx, Statement t)
  1062. {
  1063. Throw target = (Throw) t;
  1064. if (expr != null)
  1065. target.expr = expr.Clone (clonectx);
  1066. }
  1067. public override object Accept (StructuralVisitor visitor)
  1068. {
  1069. return visitor.Visit (this);
  1070. }
  1071. }
  1072. public class Break : Statement {
  1073. public Break (Location l)
  1074. {
  1075. loc = l;
  1076. }
  1077. bool unwind_protect;
  1078. public override bool Resolve (BlockContext ec)
  1079. {
  1080. unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
  1081. ec.CurrentBranching.CurrentUsageVector.Goto ();
  1082. return true;
  1083. }
  1084. protected override void DoEmit (EmitContext ec)
  1085. {
  1086. ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
  1087. }
  1088. protected override void CloneTo (CloneContext clonectx, Statement t)
  1089. {
  1090. // nothing needed
  1091. }
  1092. public override object Accept (StructuralVisitor visitor)
  1093. {
  1094. return visitor.Visit (this);
  1095. }
  1096. }
  1097. public class Continue : Statement {
  1098. public Continue (Location l)
  1099. {
  1100. loc = l;
  1101. }
  1102. bool unwind_protect;
  1103. public override bool Resolve (BlockContext ec)
  1104. {
  1105. unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
  1106. ec.CurrentBranching.CurrentUsageVector.Goto ();
  1107. return true;
  1108. }
  1109. protected override void DoEmit (EmitContext ec)
  1110. {
  1111. ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
  1112. }
  1113. protected override void CloneTo (CloneContext clonectx, Statement t)
  1114. {
  1115. // nothing needed.
  1116. }
  1117. public override object Accept (StructuralVisitor visitor)
  1118. {
  1119. return visitor.Visit (this);
  1120. }
  1121. }
  1122. public interface ILocalVariable
  1123. {
  1124. void Emit (EmitContext ec);
  1125. void EmitAssign (EmitContext ec);
  1126. void EmitAddressOf (EmitContext ec);
  1127. }
  1128. public interface INamedBlockVariable
  1129. {
  1130. Block Block { get; }
  1131. Expression CreateReferenceExpression (ResolveContext rc, Location loc);
  1132. bool IsDeclared { get; }
  1133. bool IsParameter { get; }
  1134. Location Location { get; }
  1135. }
  1136. public class BlockVariableDeclaration : Statement
  1137. {
  1138. public class Declarator
  1139. {
  1140. LocalVariable li;
  1141. Expression initializer;
  1142. public Declarator (LocalVariable li, Expression initializer)
  1143. {
  1144. if (li.Type != null)
  1145. throw new ArgumentException ("Expected null variable type");
  1146. this.li = li;
  1147. this.initializer = initializer;
  1148. }
  1149. public Declarator (Declarator clone, Expression initializer)
  1150. {
  1151. this.li = clone.li;
  1152. this.initializer = initializer;
  1153. }
  1154. #region Properties
  1155. public LocalVariable Variable {
  1156. get {
  1157. return li;
  1158. }
  1159. }
  1160. public Expression Initializer {
  1161. get {
  1162. return initializer;
  1163. }
  1164. set {
  1165. initializer = value;
  1166. }
  1167. }
  1168. #endregion
  1169. }
  1170. Expression initializer;
  1171. protected FullNamedExpression type_expr;
  1172. protected LocalVariable li;
  1173. protected List<Declarator> declarators;
  1174. TypeSpec type;
  1175. public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
  1176. {
  1177. this.type_expr = type;
  1178. this.li = li;
  1179. this.loc = type_expr.Location;
  1180. }
  1181. protected BlockVariableDeclaration (LocalVariable li)
  1182. {
  1183. this.li = li;
  1184. }
  1185. #region Properties
  1186. public List<Declarator> Declarators {
  1187. get {
  1188. return declarators;
  1189. }
  1190. }
  1191. public Expression Initializer {
  1192. get {
  1193. return initializer;
  1194. }
  1195. set {
  1196. initializer = value;
  1197. }
  1198. }
  1199. public FullNamedExpression TypeExpression {
  1200. get {
  1201. return type_expr;
  1202. }
  1203. }
  1204. public LocalVariable Variable {
  1205. get {
  1206. return li;
  1207. }
  1208. }
  1209. #endregion
  1210. public void AddDeclarator (Declarator decl)
  1211. {
  1212. if (declarators == null)
  1213. declarators = new List<Declarator> ();
  1214. declarators.Add (decl);
  1215. }
  1216. static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
  1217. {
  1218. if (bc.Report.Errors != 0)
  1219. return;
  1220. var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
  1221. Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
  1222. new MemberName (li.Name, li.Location), null);
  1223. container.AddField (f);
  1224. f.Define ();
  1225. li.HoistedVariant = new HoistedEvaluatorVariable (f);
  1226. li.SetIsUsed ();
  1227. }
  1228. public override bool Resolve (BlockContext bc)
  1229. {
  1230. return Resolve (bc, true);
  1231. }
  1232. public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
  1233. {
  1234. if (type == null && !li.IsCompilerGenerated) {
  1235. var vexpr = type_expr as VarExpr;
  1236. //
  1237. // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
  1238. // same name exists or as a keyword when no type was found
  1239. //
  1240. if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
  1241. if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
  1242. bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
  1243. if (li.IsFixed) {
  1244. bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
  1245. return false;
  1246. }
  1247. if (li.IsConstant) {
  1248. bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
  1249. return false;
  1250. }
  1251. if (Initializer == null) {
  1252. bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
  1253. return false;
  1254. }
  1255. if (declarators != null) {
  1256. bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
  1257. declarators = null;
  1258. }
  1259. Initializer = Initializer.Resolve (bc);
  1260. if (Initializer != null) {
  1261. ((VarExpr) type_expr).InferType (bc, Initializer);
  1262. type = type_expr.Type;
  1263. } else {
  1264. // Set error type to indicate the var was placed correctly but could
  1265. // not be infered
  1266. //
  1267. // var a = missing ();
  1268. //
  1269. type = InternalType.ErrorType;
  1270. }
  1271. }
  1272. if (type == null) {
  1273. type = type_expr.ResolveAsType (bc);
  1274. if (type == null)
  1275. return false;
  1276. if (li.IsConstant && !type.IsConstantCompatible) {
  1277. Const.Error_InvalidConstantType (type, loc, bc.Report);
  1278. }
  1279. }
  1280. if (type.IsStatic)
  1281. FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
  1282. li.Type = type;
  1283. }
  1284. bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
  1285. if (eval_global) {
  1286. CreateEvaluatorVariable (bc, li);
  1287. } else {
  1288. li.PrepareForFlowAnalysis (bc);
  1289. }
  1290. if (initializer != null) {
  1291. initializer = ResolveInitializer (bc, li, initializer);
  1292. // li.Variable.DefinitelyAssigned
  1293. }
  1294. if (declarators != null) {
  1295. foreach (var d in declarators) {
  1296. d.Variable.Type = li.Type;
  1297. if (eval_global) {
  1298. CreateEvaluatorVariable (bc, d.Variable);
  1299. } else {
  1300. d.Variable.PrepareForFlowAnalysis (bc);
  1301. }
  1302. if (d.Initializer != null && resolveDeclaratorInitializers) {
  1303. d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
  1304. // d.Variable.DefinitelyAssigned
  1305. }
  1306. }
  1307. }
  1308. return true;
  1309. }
  1310. protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
  1311. {
  1312. var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
  1313. return a.ResolveStatement (bc);
  1314. }
  1315. protected override void DoEmit (EmitContext ec)
  1316. {
  1317. li.CreateBuilder (ec);
  1318. if (Initializer != null)
  1319. ((ExpressionStatement) Initializer).EmitStatement (ec);
  1320. if (declarators != null) {
  1321. foreach (var d in declarators) {
  1322. d.Variable.CreateBuilder (ec);
  1323. if (d.Initializer != null) {
  1324. ec.Mark (d.Variable.Location);
  1325. ((ExpressionStatement) d.Initializer).EmitStatement (ec);
  1326. }
  1327. }
  1328. }
  1329. }
  1330. protected override void CloneTo (CloneContext clonectx, Statement target)
  1331. {
  1332. BlockVariableDeclaration t = (BlockVariableDeclaration) target;
  1333. if (type_expr != null)
  1334. t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
  1335. if (initializer != null)
  1336. t.initializer = initializer.Clone (clonectx);
  1337. if (declarators != null) {
  1338. t.declarators = null;
  1339. foreach (var d in declarators)
  1340. t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
  1341. }
  1342. }
  1343. public override object Accept (StructuralVisitor visitor)
  1344. {
  1345. return visitor.Visit (this);
  1346. }
  1347. }
  1348. public class BlockConstantDeclaration : BlockVariableDeclaration
  1349. {
  1350. public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
  1351. : base (type, li)
  1352. {
  1353. }
  1354. public override void Emit (EmitContext ec)
  1355. {
  1356. // Nothing to emit, not even sequence point
  1357. }
  1358. protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
  1359. {
  1360. initializer = initializer.Resolve (bc);
  1361. if (initializer == null)
  1362. return null;
  1363. var c = initializer as Constant;
  1364. if (c == null) {
  1365. initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
  1366. return null;
  1367. }
  1368. c = c.ConvertImplicitly (li.Type);
  1369. if (c == null) {
  1370. if (TypeSpec.IsReferenceType (li.Type))
  1371. initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
  1372. else
  1373. initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
  1374. return null;
  1375. }
  1376. li.ConstantValue = c;
  1377. return initializer;
  1378. }
  1379. public override object Accept (StructuralVisitor visitor)
  1380. {
  1381. return visitor.Visit (this);
  1382. }
  1383. }
  1384. //
  1385. // The information about a user-perceived local variable
  1386. //
  1387. public class LocalVariable : INamedBlockVariable, ILocalVariable
  1388. {
  1389. [Flags]
  1390. public enum Flags
  1391. {
  1392. Used = 1,
  1393. IsThis = 1 << 1,
  1394. AddressTaken = 1 << 2,
  1395. CompilerGenerated = 1 << 3,
  1396. Constant = 1 << 4,
  1397. ForeachVariable = 1 << 5,
  1398. FixedVariable = 1 << 6,
  1399. UsingVariable = 1 << 7,
  1400. // DefinitelyAssigned = 1 << 8,
  1401. IsLocked = 1 << 9,
  1402. ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
  1403. }
  1404. TypeSpec type;
  1405. readonly string name;
  1406. readonly Location loc;
  1407. readonly Block block;
  1408. Flags flags;
  1409. Constant const_value;
  1410. public VariableInfo VariableInfo;
  1411. HoistedVariable hoisted_variant;
  1412. LocalBuilder builder;
  1413. public LocalVariable (Block block, string name, Location loc)
  1414. {
  1415. this.block = block;
  1416. this.name = name;
  1417. this.loc = loc;
  1418. }
  1419. public LocalVariable (Block block, string name, Flags flags, Location loc)
  1420. : this (block, name, loc)
  1421. {
  1422. this.flags = flags;
  1423. }
  1424. //
  1425. // Used by variable declarators
  1426. //
  1427. public LocalVariable (LocalVariable li, string name, Location loc)
  1428. : this (li.block, name, li.flags, loc)
  1429. {
  1430. }
  1431. #region Properties
  1432. public bool AddressTaken {
  1433. get {
  1434. return (flags & Flags.AddressTaken) != 0;
  1435. }
  1436. }
  1437. public Block Block {
  1438. get {
  1439. return block;
  1440. }
  1441. }
  1442. public Constant ConstantValue {
  1443. get {
  1444. return const_value;
  1445. }
  1446. set {
  1447. const_value = value;
  1448. }
  1449. }
  1450. //
  1451. // Hoisted local variable variant
  1452. //
  1453. public HoistedVariable HoistedVariant {
  1454. get {
  1455. return hoisted_variant;
  1456. }
  1457. set {
  1458. hoisted_variant = value;
  1459. }
  1460. }
  1461. public bool IsDeclared {
  1462. get {
  1463. return type != null;
  1464. }
  1465. }
  1466. public bool IsCompilerGenerated {
  1467. get {
  1468. return (flags & Flags.CompilerGenerated) != 0;
  1469. }
  1470. }
  1471. public bool IsConstant {
  1472. get {
  1473. return (flags & Flags.Constant) != 0;
  1474. }
  1475. }
  1476. public bool IsLocked {
  1477. get {
  1478. return (flags & Flags.IsLocked) != 0;
  1479. }
  1480. set {
  1481. flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
  1482. }
  1483. }
  1484. public bool IsThis {
  1485. get {
  1486. return (flags & Flags.IsThis) != 0;
  1487. }
  1488. }
  1489. public bool IsFixed {
  1490. get {
  1491. return (flags & Flags.FixedVariable) != 0;
  1492. }
  1493. }
  1494. bool INamedBlockVariable.IsParameter {
  1495. get {
  1496. return false;
  1497. }
  1498. }
  1499. public bool IsReadonly {
  1500. get {
  1501. return (flags & Flags.ReadonlyMask) != 0;
  1502. }
  1503. }
  1504. public Location Location {
  1505. get {
  1506. return loc;
  1507. }
  1508. }
  1509. public string Name {
  1510. get {
  1511. return name;
  1512. }
  1513. }
  1514. public TypeSpec Type {
  1515. get {
  1516. return type;
  1517. }
  1518. set {
  1519. type = value;
  1520. }
  1521. }
  1522. #endregion
  1523. public void CreateBuilder (EmitContext ec)
  1524. {
  1525. if ((flags & Flags.Used) == 0) {
  1526. if (VariableInfo == null) {
  1527. // Missing flow analysis or wrong variable flags
  1528. throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
  1529. }
  1530. if (VariableInfo.IsEverAssigned)
  1531. ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
  1532. else
  1533. ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
  1534. }
  1535. if (HoistedVariant != null)
  1536. return;
  1537. if (builder != null) {
  1538. if ((flags & Flags.CompilerGenerated) != 0)
  1539. return;
  1540. // To avoid Used warning duplicates
  1541. throw new InternalErrorException ("Already created variable `{0}'", name);
  1542. }
  1543. //
  1544. // All fixed variabled are pinned, a slot has to be alocated
  1545. //
  1546. builder = ec.DeclareLocal (Type, IsFixed);
  1547. if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
  1548. ec.DefineLocalVariable (name, builder);
  1549. }
  1550. public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
  1551. {
  1552. LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
  1553. li.Type = type;
  1554. return li;
  1555. }
  1556. public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
  1557. {
  1558. if (IsConstant && const_value != null)
  1559. return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
  1560. return new LocalVariableReference (this, loc);
  1561. }
  1562. public void Emit (EmitContext ec)
  1563. {
  1564. // TODO: Need something better for temporary variables
  1565. if ((flags & Flags.CompilerGenerated) != 0)
  1566. CreateBuilder (ec);
  1567. ec.Emit (OpCodes.Ldloc, builder);
  1568. }
  1569. public void EmitAssign (EmitContext ec)
  1570. {
  1571. // TODO: Need something better for temporary variables
  1572. if ((flags & Flags.CompilerGenerated) != 0)
  1573. CreateBuilder (ec);
  1574. ec.Emit (OpCodes.Stloc, builder);
  1575. }
  1576. public void EmitAddressOf (EmitContext ec)
  1577. {
  1578. ec.Emit (OpCodes.Ldloca, builder);
  1579. }
  1580. public static string GetCompilerGeneratedName (Block block)
  1581. {
  1582. // HACK: Debugger depends on the name semantics
  1583. return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
  1584. }
  1585. public string GetReadOnlyContext ()
  1586. {
  1587. switch (flags & Flags.ReadonlyMask) {
  1588. case Flags.FixedVariable:
  1589. return "fixed variable";
  1590. case Flags.ForeachVariable:
  1591. return "foreach iteration variable";
  1592. case Flags.UsingVariable:
  1593. return "using variable";
  1594. }
  1595. throw new InternalErrorException ("Variable is not readonly");
  1596. }
  1597. public bool IsThisAssigned (BlockContext ec, Block block)
  1598. {
  1599. if (VariableInfo == null)
  1600. throw new Exception ();
  1601. if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
  1602. return true;
  1603. return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
  1604. }
  1605. public bool IsAssigned (BlockContext ec)
  1606. {
  1607. if (VariableInfo == null)
  1608. throw new Exception ();
  1609. return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
  1610. }
  1611. public void PrepareForFlowAnalysis (BlockContext bc)
  1612. {
  1613. //
  1614. // No need for definitely assigned check for these guys
  1615. //
  1616. if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
  1617. return;
  1618. VariableInfo = new VariableInfo (this, bc.FlowOffset);
  1619. bc.FlowOffset += VariableInfo.Length;
  1620. }
  1621. //
  1622. // Mark the variables as referenced in the user code
  1623. //
  1624. public void SetIsUsed ()
  1625. {
  1626. flags |= Flags.Used;
  1627. }
  1628. public void SetHasAddressTaken ()
  1629. {
  1630. flags |= (Flags.AddressTaken | Flags.Used);
  1631. }
  1632. public override string ToString ()
  1633. {
  1634. return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
  1635. }
  1636. }
  1637. /// <summary>
  1638. /// Block represents a C# block.
  1639. /// </summary>
  1640. ///
  1641. /// <remarks>
  1642. /// This class is used in a number of places: either to represent
  1643. /// explicit blocks that the programmer places or implicit blocks.
  1644. ///
  1645. /// Implicit blocks are used as labels or to introduce variable
  1646. /// declarations.
  1647. ///
  1648. /// Top-level blocks derive from Block, and they are called ToplevelBlock
  1649. /// they contain extra information that is not necessary on normal blocks.
  1650. /// </remarks>
  1651. public class Block : Statement {
  1652. [Flags]
  1653. public enum Flags
  1654. {
  1655. Unchecked = 1,
  1656. HasRet = 8,
  1657. Unsafe = 16,
  1658. HasCapturedVariable = 64,
  1659. HasCapturedThis = 1 << 7,
  1660. IsExpressionTree = 1 << 8,
  1661. CompilerGenerated = 1 << 9,
  1662. HasAsyncModifier = 1 << 10,
  1663. Resolved = 1 << 11,
  1664. YieldBlock = 1 << 12,
  1665. AwaitBlock = 1 << 13
  1666. }
  1667. public Block Parent;
  1668. public Location StartLocation;
  1669. public Location EndLocation;
  1670. public ExplicitBlock Explicit;
  1671. public ParametersBlock ParametersBlock;
  1672. protected Flags flags;
  1673. //
  1674. // The statements in this block
  1675. //
  1676. protected List<Statement> statements;
  1677. protected List<Statement> scope_initializers;
  1678. int? resolving_init_idx;
  1679. Block original;
  1680. #if DEBUG
  1681. static int id;
  1682. public int ID = id++;
  1683. static int clone_id_counter;
  1684. int clone_id;
  1685. #endif
  1686. // int assignable_slots;
  1687. public Block (Block parent, Location start, Location end)
  1688. : this (parent, 0, start, end)
  1689. {
  1690. }
  1691. public Block (Block parent, Flags flags, Location start, Location end)
  1692. {
  1693. if (parent != null) {
  1694. // the appropriate constructors will fixup these fields
  1695. ParametersBlock = parent.ParametersBlock;
  1696. Explicit = parent.Explicit;
  1697. }
  1698. this.Parent = parent;
  1699. this.flags = flags;
  1700. this.StartLocation = start;
  1701. this.EndLocation = end;
  1702. this.loc = start;
  1703. statements = new List<Statement> (4);
  1704. this.original = this;
  1705. }
  1706. #region Properties
  1707. public bool HasUnreachableClosingBrace {
  1708. get {
  1709. return (flags & Flags.HasRet) != 0;
  1710. }
  1711. set {
  1712. flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
  1713. }
  1714. }
  1715. public Block Original {
  1716. get {
  1717. return original;
  1718. }
  1719. protected set {
  1720. original = value;
  1721. }
  1722. }
  1723. public bool IsCompilerGenerated {
  1724. get { return (flags & Flags.CompilerGenerated) != 0; }
  1725. set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
  1726. }
  1727. public bool Unchecked {
  1728. get { return (flags & Flags.Unchecked) != 0; }
  1729. set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
  1730. }
  1731. public bool Unsafe {
  1732. get { return (flags & Flags.Unsafe) != 0; }
  1733. set { flags |= Flags.Unsafe; }
  1734. }
  1735. public List<Statement> Statements {
  1736. get { return statements; }
  1737. }
  1738. #endregion
  1739. public Block CreateSwitchBlock (Location start)
  1740. {
  1741. // FIXME: Only explicit block should be created
  1742. var new_block = new Block (this, start, start);
  1743. new_block.IsCompilerGenerated = true;
  1744. return new_block;
  1745. }
  1746. public void SetEndLocation (Location loc)
  1747. {
  1748. EndLocation = loc;
  1749. }
  1750. public void AddLabel (LabeledStatement target)
  1751. {
  1752. ParametersBlock.TopBlock.AddLabel (target.Name, target);
  1753. }
  1754. public void AddLocalName (LocalVariable li)
  1755. {
  1756. AddLocalName (li.Name, li);
  1757. }
  1758. public void AddLocalName (string name, INamedBlockVariable li)
  1759. {
  1760. ParametersBlock.TopBlock.AddLocalName (name, li, false);
  1761. }
  1762. public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
  1763. {
  1764. if (reason == null) {
  1765. Error_AlreadyDeclared (name, variable);
  1766. return;
  1767. }
  1768. ParametersBlock.TopBlock.Report.Error (136, variable.Location,
  1769. "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
  1770. "to `{0}', which is already used in a `{1}' scope to denote something else",
  1771. name, reason);
  1772. }
  1773. public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
  1774. {
  1775. var pi = variable as ParametersBlock.ParameterInfo;
  1776. if (pi != null) {
  1777. pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
  1778. } else {
  1779. ParametersBlock.TopBlock.Report.Error (128, variable.Location,
  1780. "A local variable named `{0}' is already defined in this scope", name);
  1781. }
  1782. }
  1783. public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
  1784. {
  1785. ParametersBlock.TopBlock.Report.Error (412, loc,
  1786. "The type parameter name `{0}' is the same as local variable or parameter name",
  1787. name);
  1788. }
  1789. //
  1790. // It should be used by expressions which require to
  1791. // register a statement during resolve process.
  1792. //
  1793. public void AddScopeStatement (Statement s)
  1794. {
  1795. if (scope_initializers == null)
  1796. scope_initializers = new List<Statement> ();
  1797. //
  1798. // Simple recursive helper, when resolve scope initializer another
  1799. // new scope initializer can be added, this ensures it's initialized
  1800. // before existing one. For now this can happen with expression trees
  1801. // in base ctor initializer only
  1802. //
  1803. if (resolving_init_idx.HasValue) {
  1804. scope_initializers.Insert (resolving_init_idx.Value, s);
  1805. ++resolving_init_idx;
  1806. } else {
  1807. scope_initializers.Add (s);
  1808. }
  1809. }
  1810. public void AddStatement (Statement s)
  1811. {
  1812. statements.Add (s);
  1813. }
  1814. public int AssignableSlots {
  1815. get {
  1816. // FIXME: HACK, we don't know the block available variables count now, so set this high enough
  1817. return 4096;
  1818. // return assignable_slots;
  1819. }
  1820. }
  1821. public LabeledStatement LookupLabel (string name)
  1822. {
  1823. return ParametersBlock.TopBlock.GetLabel (name, this);
  1824. }
  1825. public override bool Resolve (BlockContext ec)
  1826. {
  1827. if ((flags & Flags.Resolved) != 0)
  1828. return true;
  1829. Block prev_block = ec.CurrentBlock;
  1830. bool ok = true;
  1831. bool unreachable = ec.IsUnreachable;
  1832. bool prev_unreachable = unreachable;
  1833. ec.CurrentBlock = this;
  1834. ec.StartFlowBranching (this);
  1835. //
  1836. // Compiler generated scope statements
  1837. //
  1838. if (scope_initializers != null) {
  1839. for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
  1840. scope_initializers[resolving_init_idx.Value].Resolve (ec);
  1841. }
  1842. resolving_init_idx = null;
  1843. }
  1844. //
  1845. // This flag is used to notate nested statements as unreachable from the beginning of this block.
  1846. // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
  1847. // from the beginning of the function. The outer Resolve() that detected the unreachability is
  1848. // responsible for handling the situation.
  1849. //
  1850. int statement_count = statements.Count;
  1851. for (int ix = 0; ix < statement_count; ix++){
  1852. Statement s = statements [ix];
  1853. //
  1854. // Warn if we detect unreachable code.
  1855. //
  1856. if (unreachable) {
  1857. if (s is EmptyStatement)
  1858. continue;
  1859. if (!ec.UnreachableReported && !(s is LabeledStatement)) {
  1860. ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
  1861. ec.UnreachableReported = true;
  1862. }
  1863. }
  1864. //
  1865. // Note that we're not using ResolveUnreachable() for unreachable
  1866. // statements here. ResolveUnreachable() creates a temporary
  1867. // flow branching and kills it afterwards. This leads to problems
  1868. // if you have two unreachable statements where the first one
  1869. // assigns a variable and the second one tries to access it.
  1870. //
  1871. if (!s.Resolve (ec)) {
  1872. ok = false;
  1873. if (!ec.IsInProbingMode)
  1874. statements [ix] = new EmptyStatement (s.loc);
  1875. continue;
  1876. }
  1877. if (unreachable && !(s is LabeledStatement) && !(s is Block))
  1878. statements [ix] = new EmptyStatement (s.loc);
  1879. unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
  1880. if (unreachable) {
  1881. ec.IsUnreachable = true;
  1882. } else if (ec.IsUnreachable)
  1883. ec.IsUnreachable = false;
  1884. }
  1885. if (unreachable != prev_unreachable) {
  1886. ec.IsUnreachable = prev_unreachable;
  1887. ec.UnreachableReported = false;
  1888. }
  1889. while (ec.CurrentBranching is FlowBranchingLabeled)
  1890. ec.EndFlowBranching ();
  1891. bool flow_unreachable = ec.EndFlowBranching ();
  1892. ec.CurrentBlock = prev_block;
  1893. if (flow_unreachable)
  1894. flags |= Flags.HasRet;
  1895. // If we're a non-static `struct' constructor which doesn't have an
  1896. // initializer, then we must initialize all of the struct's fields.
  1897. if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
  1898. ok = false;
  1899. flags |= Flags.Resolved;
  1900. return ok;
  1901. }
  1902. public override bool ResolveUnreachable (BlockContext ec, bool warn)
  1903. {
  1904. bool unreachable = false;
  1905. if (warn && !ec.UnreachableReported) {
  1906. ec.UnreachableReported = true;
  1907. unreachable = true;
  1908. ec.Report.Warning (162, 2, loc, "Unreachable code detected");
  1909. }
  1910. var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
  1911. fb.CurrentUsageVector.IsUnreachable = true;
  1912. bool ok = Resolve (ec);
  1913. ec.KillFlowBranching ();
  1914. if (unreachable)
  1915. ec.UnreachableReported = false;
  1916. return ok;
  1917. }
  1918. protected override void DoEmit (EmitContext ec)
  1919. {
  1920. for (int ix = 0; ix < statements.Count; ix++){
  1921. statements [ix].Emit (ec);
  1922. }
  1923. }
  1924. public override void Emit (EmitContext ec)
  1925. {
  1926. if (scope_initializers != null)
  1927. EmitScopeInitializers (ec);
  1928. DoEmit (ec);
  1929. }
  1930. protected void EmitScopeInitializers (EmitContext ec)
  1931. {
  1932. foreach (Statement s in scope_initializers)
  1933. s.Emit (ec);
  1934. }
  1935. #if DEBUG
  1936. public override string ToString ()
  1937. {
  1938. return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
  1939. }
  1940. #endif
  1941. protected override void CloneTo (CloneContext clonectx, Statement t)
  1942. {
  1943. Block target = (Block) t;
  1944. #if DEBUG
  1945. target.clone_id = clone_id_counter++;
  1946. #endif
  1947. clonectx.AddBlockMap (this, target);
  1948. if (original != this)
  1949. clonectx.AddBlockMap (original, target);
  1950. target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
  1951. target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
  1952. if (Parent != null)
  1953. target.Parent = clonectx.RemapBlockCopy (Parent);
  1954. target.statements = new List<Statement> (statements.Count);
  1955. foreach (Statement s in statements)
  1956. target.statements.Add (s.Clone (clonectx));
  1957. }
  1958. public override object Accept (StructuralVisitor visitor)
  1959. {
  1960. return visitor.Visit (this);
  1961. }
  1962. }
  1963. public class ExplicitBlock : Block
  1964. {
  1965. protected AnonymousMethodStorey am_storey;
  1966. public ExplicitBlock (Block parent, Location start, Location end)
  1967. : this (parent, (Flags) 0, start, end)
  1968. {
  1969. }
  1970. public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
  1971. : base (parent, flags, start, end)
  1972. {
  1973. this.Explicit = this;
  1974. }
  1975. #region Properties
  1976. public AnonymousMethodStorey AnonymousMethodStorey {
  1977. get {
  1978. return am_storey;
  1979. }
  1980. }
  1981. public bool HasAwait {
  1982. get {
  1983. return (flags & Flags.AwaitBlock) != 0;
  1984. }
  1985. }
  1986. public bool HasCapturedThis {
  1987. set {
  1988. flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
  1989. }
  1990. get {
  1991. return (flags & Flags.HasCapturedThis) != 0;
  1992. }
  1993. }
  1994. //
  1995. // Used to indicate that the block has reference to parent
  1996. // block and cannot be made static when defining anonymous method
  1997. //
  1998. public bool HasCapturedVariable {
  1999. set {
  2000. flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
  2001. }
  2002. get {
  2003. return (flags & Flags.HasCapturedVariable) != 0;
  2004. }
  2005. }
  2006. public bool HasYield {
  2007. get {
  2008. return (flags & Flags.YieldBlock) != 0;
  2009. }
  2010. }
  2011. #endregion
  2012. //
  2013. // Creates anonymous method storey in current block
  2014. //
  2015. public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
  2016. {
  2017. //
  2018. // Return same story for iterator and async blocks unless we are
  2019. // in nested anonymous method
  2020. //
  2021. if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
  2022. return ec.CurrentAnonymousMethod.Storey;
  2023. if (am_storey == null) {
  2024. MemberBase mc = ec.MemberContext as MemberBase;
  2025. //
  2026. // Creates anonymous method storey for this block
  2027. //
  2028. am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
  2029. }
  2030. return am_storey;
  2031. }
  2032. public override void Emit (EmitContext ec)
  2033. {
  2034. if (am_storey != null) {
  2035. DefineStoreyContainer (ec, am_storey);
  2036. am_storey.EmitStoreyInstantiation (ec, this);
  2037. }
  2038. if (scope_initializers != null)
  2039. EmitScopeInitializers (ec);
  2040. if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
  2041. ec.Emit (OpCodes.Nop);
  2042. }
  2043. if (Parent != null)
  2044. ec.BeginScope ();
  2045. DoEmit (ec);
  2046. if (Parent != null)
  2047. ec.EndScope ();
  2048. if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
  2049. ec.Emit (OpCodes.Nop);
  2050. }
  2051. }
  2052. protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
  2053. {
  2054. if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
  2055. storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
  2056. storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
  2057. }
  2058. //
  2059. // Creates anonymous method storey
  2060. //
  2061. storey.CreateContainer ();
  2062. storey.DefineContainer ();
  2063. if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
  2064. //
  2065. // Only first storey in path will hold this reference. All children blocks will
  2066. // reference it indirectly using $ref field
  2067. //
  2068. for (Block b = Original.Explicit; b != null; b = b.Parent) {
  2069. if (b.Parent != null) {
  2070. var s = b.Parent.Explicit.AnonymousMethodStorey;
  2071. if (s != null) {
  2072. storey.HoistedThis = s.HoistedThis;
  2073. break;
  2074. }
  2075. }
  2076. if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
  2077. storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
  2078. if (storey.HoistedThis != null)
  2079. break;
  2080. }
  2081. }
  2082. //
  2083. // We are the first storey on path and 'this' has to be hoisted
  2084. //
  2085. if (storey.HoistedThis == null) {
  2086. foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
  2087. //
  2088. // ThisReferencesFromChildrenBlock holds all reference even if they
  2089. // are not on this path. It saves some memory otherwise it'd have to
  2090. // be in every explicit block. We run this check to see if the reference
  2091. // is valid for this storey
  2092. //
  2093. Block block_on_path = ref_block;
  2094. for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
  2095. if (block_on_path == null)
  2096. continue;
  2097. if (storey.HoistedThis == null) {
  2098. storey.AddCapturedThisField (ec);
  2099. }
  2100. for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
  2101. if (b.AnonymousMethodStorey != null) {
  2102. b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
  2103. b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
  2104. //
  2105. // Stop propagation inside same top block
  2106. //
  2107. if (b.ParametersBlock == ParametersBlock.Original)
  2108. break;
  2109. b = b.ParametersBlock;
  2110. }
  2111. var pb = b as ParametersBlock;
  2112. if (pb != null && pb.StateMachine != null) {
  2113. if (pb.StateMachine == storey)
  2114. break;
  2115. pb.StateMachine.AddParentStoreyReference (ec, storey);
  2116. }
  2117. b.HasCapturedVariable = true;
  2118. }
  2119. }
  2120. }
  2121. }
  2122. var ref_blocks = storey.ReferencesFromChildrenBlock;
  2123. if (ref_blocks != null) {
  2124. foreach (ExplicitBlock ref_block in ref_blocks) {
  2125. for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
  2126. if (b.AnonymousMethodStorey != null) {
  2127. b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
  2128. //
  2129. // Stop propagation inside same top block
  2130. //
  2131. if (b.ParametersBlock == ParametersBlock.Original)
  2132. break;
  2133. b = b.ParametersBlock;
  2134. }
  2135. var pb = b as ParametersBlock;
  2136. if (pb != null && pb.StateMachine != null) {
  2137. if (pb.StateMachine == storey)
  2138. break;
  2139. pb.StateMachine.AddParentStoreyReference (ec, storey);
  2140. }
  2141. b.HasCapturedVariable = true;
  2142. }
  2143. }
  2144. }
  2145. storey.Define ();
  2146. storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
  2147. }
  2148. public void RegisterAsyncAwait ()
  2149. {
  2150. var block = this;
  2151. while ((block.flags & Flags.AwaitBlock) == 0) {
  2152. block.flags |= Flags.AwaitBlock;
  2153. if (block is ParametersBlock)
  2154. return;
  2155. block = block.Parent.Explicit;
  2156. }
  2157. }
  2158. public void RegisterIteratorYield ()
  2159. {
  2160. var block = this;
  2161. while ((block.flags & Flags.YieldBlock) == 0) {
  2162. block.flags |= Flags.YieldBlock;
  2163. if (block.Parent == null)
  2164. return;
  2165. block = block.Parent.Explicit;
  2166. }
  2167. }
  2168. public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
  2169. {
  2170. tryBlock.statements = statements;
  2171. statements = new List<Statement> (1);
  2172. statements.Add (tf);
  2173. }
  2174. }
  2175. //
  2176. // ParametersBlock was introduced to support anonymous methods
  2177. // and lambda expressions
  2178. //
  2179. public class ParametersBlock : ExplicitBlock
  2180. {
  2181. public class ParameterInfo : INamedBlockVariable
  2182. {
  2183. readonly ParametersBlock block;
  2184. readonly int index;
  2185. public VariableInfo VariableInfo;
  2186. bool is_locked;
  2187. public ParameterInfo (ParametersBlock block, int index)
  2188. {
  2189. this.block = block;
  2190. this.index = index;
  2191. }
  2192. #region Properties
  2193. public ParametersBlock Block {
  2194. get {
  2195. return block;
  2196. }
  2197. }
  2198. Block INamedBlockVariable.Block {
  2199. get {
  2200. return block;
  2201. }
  2202. }
  2203. public bool IsDeclared {
  2204. get {
  2205. return true;
  2206. }
  2207. }
  2208. public bool IsParameter {
  2209. get {
  2210. return true;
  2211. }
  2212. }
  2213. public bool IsLocked {
  2214. get {
  2215. return is_locked;
  2216. }
  2217. set {
  2218. is_locked = value;
  2219. }
  2220. }
  2221. public Location Location {
  2222. get {
  2223. return Parameter.Location;
  2224. }
  2225. }
  2226. public Parameter Parameter {
  2227. get {
  2228. return block.Parameters [index];
  2229. }
  2230. }
  2231. public TypeSpec ParameterType {
  2232. get {
  2233. return Parameter.Type;
  2234. }
  2235. }
  2236. #endregion
  2237. public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
  2238. {
  2239. return new ParameterReference (this, loc);
  2240. }
  2241. }
  2242. //
  2243. // Block is converted into an expression
  2244. //
  2245. sealed class BlockScopeExpression : Expression
  2246. {
  2247. Expression child;
  2248. readonly ParametersBlock block;
  2249. public BlockScopeExpression (Expression child, ParametersBlock block)
  2250. {
  2251. this.child = child;
  2252. this.block = block;
  2253. }
  2254. public override bool ContainsEmitWithAwait ()
  2255. {
  2256. return child.ContainsEmitWithAwait ();
  2257. }
  2258. public override Expression CreateExpressionTree (ResolveContext ec)
  2259. {
  2260. throw new NotSupportedException ();
  2261. }
  2262. protected override Expression DoResolve (ResolveContext ec)
  2263. {
  2264. if (child == null)
  2265. return null;
  2266. child = child.Resolve (ec);
  2267. if (child == null)
  2268. return null;
  2269. eclass = child.eclass;
  2270. type = child.Type;
  2271. return this;
  2272. }
  2273. public override void Emit (EmitContext ec)
  2274. {
  2275. block.EmitScopeInitializers (ec);
  2276. child.Emit (ec);
  2277. }
  2278. }
  2279. protected ParametersCompiled parameters;
  2280. protected ParameterInfo[] parameter_info;
  2281. bool resolved;
  2282. protected bool unreachable;
  2283. protected ToplevelBlock top_block;
  2284. protected StateMachine state_machine;
  2285. public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
  2286. : base (parent, 0, start, start)
  2287. {
  2288. if (parameters == null)
  2289. throw new ArgumentNullException ("parameters");
  2290. this.parameters = parameters;
  2291. ParametersBlock = this;
  2292. flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
  2293. this.top_block = parent.ParametersBlock.top_block;
  2294. ProcessParameters ();
  2295. }
  2296. protected ParametersBlock (ParametersCompiled parameters, Location start)
  2297. : base (null, 0, start, start)
  2298. {
  2299. if (parameters == null)
  2300. throw new ArgumentNullException ("parameters");
  2301. this.parameters = parameters;
  2302. ParametersBlock = this;
  2303. }
  2304. //
  2305. // It's supposed to be used by method body implementation of anonymous methods
  2306. //
  2307. protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
  2308. : base (null, 0, source.StartLocation, source.EndLocation)
  2309. {
  2310. this.parameters = parameters;
  2311. this.statements = source.statements;
  2312. this.scope_initializers = source.scope_initializers;
  2313. this.resolved = true;
  2314. this.unreachable = source.unreachable;
  2315. this.am_storey = source.am_storey;
  2316. this.state_machine = source.state_machine;
  2317. ParametersBlock = this;
  2318. //
  2319. // Overwrite original for comparison purposes when linking cross references
  2320. // between anonymous methods
  2321. //
  2322. Original = source.Original;
  2323. }
  2324. #region Properties
  2325. public bool IsAsync {
  2326. get {
  2327. return (flags & Flags.HasAsyncModifier) != 0;
  2328. }
  2329. set {
  2330. flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
  2331. }
  2332. }
  2333. //
  2334. // Block has been converted to expression tree
  2335. //
  2336. public bool IsExpressionTree {
  2337. get {
  2338. return (flags & Flags.IsExpressionTree) != 0;
  2339. }
  2340. }
  2341. //
  2342. // The parameters for the block.
  2343. //
  2344. public ParametersCompiled Parameters {
  2345. get {
  2346. return parameters;
  2347. }
  2348. }
  2349. public StateMachine StateMachine {
  2350. get {
  2351. return state_machine;
  2352. }
  2353. }
  2354. public ToplevelBlock TopBlock {
  2355. get {
  2356. return top_block;
  2357. }
  2358. }
  2359. public bool Resolved {
  2360. get {
  2361. return (flags & Flags.Resolved) != 0;
  2362. }
  2363. }
  2364. public int TemporaryLocalsCount { get; set; }
  2365. #endregion
  2366. // <summary>
  2367. // Check whether all `out' parameters have been assigned.
  2368. // </summary>
  2369. public void CheckOutParameters (FlowBranching.UsageVector vector)
  2370. {
  2371. if (vector.IsUnreachable)
  2372. return;
  2373. int n = parameter_info == null ? 0 : parameter_info.Length;
  2374. for (int i = 0; i < n; i++) {
  2375. VariableInfo var = parameter_info[i].VariableInfo;
  2376. if (var == null)
  2377. continue;
  2378. if (vector.IsAssigned (var, false))
  2379. continue;
  2380. var p = parameter_info[i].Parameter;
  2381. TopBlock.Report.Error (177, p.Location,
  2382. "The out parameter `{0}' must be assigned to before control leaves the current method",
  2383. p.Name);
  2384. }
  2385. }
  2386. public override Expression CreateExpressionTree (ResolveContext ec)
  2387. {
  2388. if (statements.Count == 1) {
  2389. Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
  2390. if (scope_initializers != null)
  2391. expr = new BlockScopeExpression (expr, this);
  2392. return expr;
  2393. }
  2394. return base.CreateExpressionTree (ec);
  2395. }
  2396. public override void Emit (EmitContext ec)
  2397. {
  2398. if (state_machine != null && state_machine.OriginalSourceBlock != this) {
  2399. DefineStoreyContainer (ec, state_machine);
  2400. state_machine.EmitStoreyInstantiation (ec, this);
  2401. }
  2402. base.Emit (ec);
  2403. }
  2404. public void EmitEmbedded (EmitContext ec)
  2405. {
  2406. if (state_machine != null && state_machine.OriginalSourceBlock != this) {
  2407. DefineStoreyContainer (ec, state_machine);
  2408. state_machine.EmitStoreyInstantiation (ec, this);
  2409. }
  2410. base.Emit (ec);
  2411. }
  2412. public ParameterInfo GetParameterInfo (Parameter p)
  2413. {
  2414. for (int i = 0; i < parameters.Count; ++i) {
  2415. if (parameters[i] == p)
  2416. return parameter_info[i];
  2417. }
  2418. throw new ArgumentException ("Invalid parameter");
  2419. }
  2420. public ParameterReference GetParameterReference (int index, Location loc)
  2421. {
  2422. return new ParameterReference (parameter_info[index], loc);
  2423. }
  2424. public Statement PerformClone ()
  2425. {
  2426. CloneContext clonectx = new CloneContext ();
  2427. return Clone (clonectx);
  2428. }
  2429. protected void ProcessParameters ()
  2430. {
  2431. if (parameters.Count == 0)
  2432. return;
  2433. parameter_info = new ParameterInfo[parameters.Count];
  2434. for (int i = 0; i < parameter_info.Length; ++i) {
  2435. var p = parameters.FixedParameters[i];
  2436. if (p == null)
  2437. continue;
  2438. // TODO: Should use Parameter only and more block there
  2439. parameter_info[i] = new ParameterInfo (this, i);
  2440. if (p.Name != null)
  2441. AddLocalName (p.Name, parameter_info[i]);
  2442. }
  2443. }
  2444. public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
  2445. {
  2446. if (resolved)
  2447. return true;
  2448. resolved = true;
  2449. if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
  2450. flags |= Flags.IsExpressionTree;
  2451. try {
  2452. ResolveMeta (rc);
  2453. using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
  2454. FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
  2455. if (!Resolve (rc))
  2456. return false;
  2457. unreachable = top_level.End ();
  2458. }
  2459. } catch (Exception e) {
  2460. if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
  2461. throw;
  2462. if (rc.CurrentBlock != null) {
  2463. rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
  2464. } else {
  2465. rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
  2466. }
  2467. if (rc.Module.Compiler.Settings.DebugFlags > 0)
  2468. throw;
  2469. }
  2470. if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
  2471. if (rc.CurrentAnonymousMethod == null) {
  2472. // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
  2473. if (md is StateMachineMethod) {
  2474. unreachable = true;
  2475. } else {
  2476. rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
  2477. return false;
  2478. }
  2479. } else {
  2480. //
  2481. // If an asynchronous body of F is either an expression classified as nothing, or a
  2482. // statement block where no return statements have expressions, the inferred return type is Task
  2483. //
  2484. if (IsAsync) {
  2485. var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
  2486. if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
  2487. am.ReturnTypeInference = null;
  2488. am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
  2489. return true;
  2490. }
  2491. }
  2492. rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
  2493. rc.CurrentAnonymousMethod.GetSignatureForError ());
  2494. return false;
  2495. }
  2496. }
  2497. return true;
  2498. }
  2499. void ResolveMeta (BlockContext ec)
  2500. {
  2501. int orig_count = parameters.Count;
  2502. for (int i = 0; i < orig_count; ++i) {
  2503. Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
  2504. if ((mod & Parameter.Modifier.OUT) == 0)
  2505. continue;
  2506. VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
  2507. parameter_info[i].VariableInfo = vi;
  2508. ec.FlowOffset += vi.Length;
  2509. }
  2510. }
  2511. public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
  2512. {
  2513. var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
  2514. var stateMachine = new IteratorStorey (iterator);
  2515. state_machine = stateMachine;
  2516. iterator.SetStateMachine (stateMachine);
  2517. var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
  2518. tlb.Original = this;
  2519. tlb.IsCompilerGenerated = true;
  2520. tlb.state_machine = stateMachine;
  2521. tlb.AddStatement (new Return (iterator, iterator.Location));
  2522. return tlb;
  2523. }
  2524. public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
  2525. {
  2526. for (int i = 0; i < parameters.Count; i++) {
  2527. Parameter p = parameters[i];
  2528. Parameter.Modifier mod = p.ModFlags;
  2529. if ((mod & Parameter.Modifier.RefOutMask) != 0) {
  2530. host.Compiler.Report.Error (1988, p.Location,
  2531. "Async methods cannot have ref or out parameters");
  2532. return this;
  2533. }
  2534. if (p is ArglistParameter) {
  2535. host.Compiler.Report.Error (4006, p.Location,
  2536. "__arglist is not allowed in parameter list of async methods");
  2537. return this;
  2538. }
  2539. if (parameters.Types[i].IsPointer) {
  2540. host.Compiler.Report.Error (4005, p.Location,
  2541. "Async methods cannot have unsafe parameters");
  2542. return this;
  2543. }
  2544. }
  2545. if (!HasAwait) {
  2546. host.Compiler.Report.Warning (1998, 1, loc,
  2547. "Async block lacks `await' operator and will run synchronously");
  2548. }
  2549. var block_type = host.Module.Compiler.BuiltinTypes.Void;
  2550. var initializer = new AsyncInitializer (this, host, block_type);
  2551. initializer.Type = block_type;
  2552. var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
  2553. state_machine = stateMachine;
  2554. initializer.SetStateMachine (stateMachine);
  2555. var b = this is ToplevelBlock ?
  2556. new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
  2557. new ParametersBlock (Parent, parameters, Location.Null) {
  2558. IsAsync = true,
  2559. };
  2560. b.Original = this;
  2561. b.IsCompilerGenerated = true;
  2562. b.state_machine = stateMachine;
  2563. b.AddStatement (new StatementExpression (initializer));
  2564. return b;
  2565. }
  2566. }
  2567. //
  2568. //
  2569. //
  2570. public class ToplevelBlock : ParametersBlock
  2571. {
  2572. LocalVariable this_variable;
  2573. CompilerContext compiler;
  2574. Dictionary<string, object> names;
  2575. Dictionary<string, object> labels;
  2576. List<ExplicitBlock> this_references;
  2577. public ToplevelBlock (CompilerContext ctx, Location loc)
  2578. : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
  2579. {
  2580. }
  2581. public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
  2582. : base (parameters, start)
  2583. {
  2584. this.compiler = ctx;
  2585. top_block = this;
  2586. flags |= Flags.HasRet;
  2587. ProcessParameters ();
  2588. }
  2589. //
  2590. // Recreates a top level block from parameters block. Used for
  2591. // compiler generated methods where the original block comes from
  2592. // explicit child block. This works for already resolved blocks
  2593. // only to ensure we resolve them in the correct flow order
  2594. //
  2595. public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
  2596. : base (source, parameters)
  2597. {
  2598. this.compiler = source.TopBlock.compiler;
  2599. top_block = this;
  2600. flags |= Flags.HasRet;
  2601. }
  2602. public bool IsIterator {
  2603. get {
  2604. return HasYield;
  2605. }
  2606. }
  2607. public Report Report {
  2608. get {
  2609. return compiler.Report;
  2610. }
  2611. }
  2612. //
  2613. // Used by anonymous blocks to track references of `this' variable
  2614. //
  2615. public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
  2616. get {
  2617. return this_references;
  2618. }
  2619. }
  2620. //
  2621. // Returns the "this" instance variable of this block.
  2622. // See AddThisVariable() for more information.
  2623. //
  2624. public LocalVariable ThisVariable {
  2625. get {
  2626. return this_variable;
  2627. }
  2628. }
  2629. public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
  2630. {
  2631. if (names == null)
  2632. names = new Dictionary<string, object> ();
  2633. object value;
  2634. if (!names.TryGetValue (name, out value)) {
  2635. names.Add (name, li);
  2636. return;
  2637. }
  2638. INamedBlockVariable existing = value as INamedBlockVariable;
  2639. List<INamedBlockVariable> existing_list;
  2640. if (existing != null) {
  2641. existing_list = new List<INamedBlockVariable> ();
  2642. existing_list.Add (existing);
  2643. names[name] = existing_list;
  2644. } else {
  2645. existing_list = (List<INamedBlockVariable>) value;
  2646. }
  2647. //
  2648. // A collision checking between local names
  2649. //
  2650. for (int i = 0; i < existing_list.Count; ++i) {
  2651. existing = existing_list[i];
  2652. Block b = existing.Block.Explicit;
  2653. // Collision at same level
  2654. if (li.Block.Explicit == b) {
  2655. li.Block.Error_AlreadyDeclared (name, li);
  2656. break;
  2657. }
  2658. // Collision with parent
  2659. Block parent = li.Block.Explicit;
  2660. while ((parent = parent.Parent) != null) {
  2661. if (parent == b) {
  2662. li.Block.Error_AlreadyDeclared (name, li, "parent or current");
  2663. i = existing_list.Count;
  2664. break;
  2665. }
  2666. }
  2667. if (!ignoreChildrenBlocks) {
  2668. // Collision with children
  2669. while ((b = b.Parent) != null) {
  2670. if (li.Block.Explicit == b) {
  2671. li.Block.Error_AlreadyDeclared (name, li, "child");
  2672. i = existing_list.Count;
  2673. break;
  2674. }
  2675. }
  2676. }
  2677. }
  2678. existing_list.Add (li);
  2679. }
  2680. public void AddLabel (string name, LabeledStatement label)
  2681. {
  2682. if (labels == null)
  2683. labels = new Dictionary<string, object> ();
  2684. object value;
  2685. if (!labels.TryGetValue (name, out value)) {
  2686. labels.Add (name, label);
  2687. return;
  2688. }
  2689. LabeledStatement existing = value as LabeledStatement;
  2690. List<LabeledStatement> existing_list;
  2691. if (existing != null) {
  2692. existing_list = new List<LabeledStatement> ();
  2693. existing_list.Add (existing);
  2694. labels[name] = existing_list;
  2695. } else {
  2696. existing_list = (List<LabeledStatement>) value;
  2697. }
  2698. //
  2699. // A collision checking between labels
  2700. //
  2701. for (int i = 0; i < existing_list.Count; ++i) {
  2702. existing = existing_list[i];
  2703. Block b = existing.Block;
  2704. // Collision at same level
  2705. if (label.Block == b) {
  2706. Report.SymbolRelatedToPreviousError (existing.loc, name);
  2707. Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
  2708. break;
  2709. }
  2710. // Collision with parent
  2711. b = label.Block;
  2712. while ((b = b.Parent) != null) {
  2713. if (existing.Block == b) {
  2714. Report.Error (158, label.loc,
  2715. "The label `{0}' shadows another label by the same name in a contained scope", name);
  2716. i = existing_list.Count;
  2717. break;
  2718. }
  2719. }
  2720. // Collision with with children
  2721. b = existing.Block;
  2722. while ((b = b.Parent) != null) {
  2723. if (label.Block == b) {
  2724. Report.Error (158, label.loc,
  2725. "The label `{0}' shadows another label by the same name in a contained scope", name);
  2726. i = existing_list.Count;
  2727. break;
  2728. }
  2729. }
  2730. }
  2731. existing_list.Add (label);
  2732. }
  2733. public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
  2734. {
  2735. if (this_references == null)
  2736. this_references = new List<ExplicitBlock> ();
  2737. if (!this_references.Contains (block))
  2738. this_references.Add (block);
  2739. }
  2740. public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
  2741. {
  2742. this_references.Remove (block);
  2743. }
  2744. //
  2745. // Creates an arguments set from all parameters, useful for method proxy calls
  2746. //
  2747. public Arguments GetAllParametersArguments ()
  2748. {
  2749. int count = parameters.Count;
  2750. Arguments args = new Arguments (count);
  2751. for (int i = 0; i < count; ++i) {
  2752. var arg_expr = GetParameterReference (i, parameter_info[i].Location);
  2753. args.Add (new Argument (arg_expr));
  2754. }
  2755. return args;
  2756. }
  2757. //
  2758. // Lookup inside a block, the returned value can represent 3 states
  2759. //
  2760. // true+variable: A local name was found and it's valid
  2761. // false+variable: A local name was found in a child block only
  2762. // false+null: No local name was found
  2763. //
  2764. public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
  2765. {
  2766. if (names == null)
  2767. return false;
  2768. object value;
  2769. if (!names.TryGetValue (name, out value))
  2770. return false;
  2771. variable = value as INamedBlockVariable;
  2772. Block b = block;
  2773. if (variable != null) {
  2774. do {
  2775. if (variable.Block == b.Original)
  2776. return true;
  2777. b = b.Parent;
  2778. } while (b != null);
  2779. b = variable.Block;
  2780. do {
  2781. if (block == b)
  2782. return false;
  2783. b = b.Parent;
  2784. } while (b != null);
  2785. } else {
  2786. List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
  2787. for (int i = 0; i < list.Count; ++i) {
  2788. variable = list[i];
  2789. do {
  2790. if (variable.Block == b.Original)
  2791. return true;
  2792. b = b.Parent;
  2793. } while (b != null);
  2794. b = variable.Block;
  2795. do {
  2796. if (block == b)
  2797. return false;
  2798. b = b.Parent;
  2799. } while (b != null);
  2800. b = block;
  2801. }
  2802. }
  2803. variable = null;
  2804. return false;
  2805. }
  2806. public LabeledStatement GetLabel (string name, Block block)
  2807. {
  2808. if (labels == null)
  2809. return null;
  2810. object value;
  2811. if (!labels.TryGetValue (name, out value)) {
  2812. return null;
  2813. }
  2814. var label = value as LabeledStatement;
  2815. Block b = block;
  2816. if (label != null) {
  2817. if (label.Block == b.Original)
  2818. return label;
  2819. // TODO: Temporary workaround for the switch block implicit label block
  2820. if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
  2821. return label;
  2822. } else {
  2823. List<LabeledStatement> list = (List<LabeledStatement>) value;
  2824. for (int i = 0; i < list.Count; ++i) {
  2825. label = list[i];
  2826. if (label.Block == b.Original)
  2827. return label;
  2828. // TODO: Temporary workaround for the switch block implicit label block
  2829. if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
  2830. return label;
  2831. }
  2832. }
  2833. return null;
  2834. }
  2835. // <summary>
  2836. // This is used by non-static `struct' constructors which do not have an
  2837. // initializer - in this case, the constructor must initialize all of the
  2838. // struct's fields. To do this, we add a "this" variable and use the flow
  2839. // analysis code to ensure that it's been fully initialized before control
  2840. // leaves the constructor.
  2841. // </summary>
  2842. public void AddThisVariable (BlockContext bc)
  2843. {
  2844. if (this_variable != null)
  2845. throw new InternalErrorException (StartLocation.ToString ());
  2846. this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
  2847. this_variable.Type = bc.CurrentType;
  2848. this_variable.PrepareForFlowAnalysis (bc);
  2849. }
  2850. public bool IsThisAssigned (BlockContext ec)
  2851. {
  2852. return this_variable == null || this_variable.IsThisAssigned (ec, this);
  2853. }
  2854. public override void Emit (EmitContext ec)
  2855. {
  2856. if (Report.Errors > 0)
  2857. return;
  2858. #if PRODUCTION
  2859. try {
  2860. #endif
  2861. if (IsCompilerGenerated) {
  2862. using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
  2863. base.Emit (ec);
  2864. }
  2865. } else {
  2866. base.Emit (ec);
  2867. }
  2868. //
  2869. // If `HasReturnLabel' is set, then we already emitted a
  2870. // jump to the end of the method, so we must emit a `ret'
  2871. // there.
  2872. //
  2873. // Unfortunately, System.Reflection.Emit automatically emits
  2874. // a leave to the end of a finally block. This is a problem
  2875. // if no code is following the try/finally block since we may
  2876. // jump to a point after the end of the method.
  2877. // As a workaround, we're always creating a return label in
  2878. // this case.
  2879. //
  2880. if (ec.HasReturnLabel || !unreachable) {
  2881. if (ec.HasReturnLabel)
  2882. ec.MarkLabel (ec.ReturnLabel);
  2883. if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
  2884. ec.Mark (EndLocation);
  2885. if (ec.ReturnType.Kind != MemberKind.Void)
  2886. ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
  2887. ec.Emit (OpCodes.Ret);
  2888. }
  2889. #if PRODUCTION
  2890. } catch (Exception e){
  2891. Console.WriteLine ("Exception caught by the compiler while emitting:");
  2892. Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
  2893. Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
  2894. throw;
  2895. }
  2896. #endif
  2897. }
  2898. }
  2899. public class SwitchLabel {
  2900. Expression label;
  2901. Constant converted;
  2902. readonly Location loc;
  2903. Label? il_label;
  2904. //
  2905. // if expr == null, then it is the default case.
  2906. //
  2907. public SwitchLabel (Expression expr, Location l)
  2908. {
  2909. label = expr;
  2910. loc = l;
  2911. }
  2912. public bool IsDefault {
  2913. get {
  2914. return label == null;
  2915. }
  2916. }
  2917. public Expression Label {
  2918. get {
  2919. return label;
  2920. }
  2921. }
  2922. public Location Location {
  2923. get {
  2924. return loc;
  2925. }
  2926. }
  2927. public Constant Converted {
  2928. get {
  2929. return converted;
  2930. }
  2931. set {
  2932. converted = value;
  2933. }
  2934. }
  2935. public Label GetILLabel (EmitContext ec)
  2936. {
  2937. if (il_label == null){
  2938. il_label = ec.DefineLabel ();
  2939. }
  2940. return il_label.Value;
  2941. }
  2942. //
  2943. // Resolves the expression, reduces it to a literal if possible
  2944. // and then converts it to the requested type.
  2945. //
  2946. public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
  2947. {
  2948. Expression e = label.Resolve (ec);
  2949. if (e == null)
  2950. return false;
  2951. Constant c = e as Constant;
  2952. if (c == null){
  2953. ec.Report.Error (150, loc, "A constant value is expected");
  2954. return false;
  2955. }
  2956. if (allow_nullable && c is NullLiteral) {
  2957. converted = c;
  2958. return true;
  2959. }
  2960. converted = c.ImplicitConversionRequired (ec, required_type, loc);
  2961. return converted != null;
  2962. }
  2963. public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
  2964. {
  2965. string label;
  2966. if (converted == null)
  2967. label = "default";
  2968. else
  2969. label = converted.GetValueAsLiteral ();
  2970. ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
  2971. ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
  2972. }
  2973. public SwitchLabel Clone (CloneContext clonectx)
  2974. {
  2975. if (label == null)
  2976. return this;
  2977. return new SwitchLabel (label.Clone (clonectx), loc);
  2978. }
  2979. }
  2980. public class SwitchSection {
  2981. public readonly List<SwitchLabel> Labels;
  2982. public readonly Block Block;
  2983. public SwitchSection (List<SwitchLabel> labels, Block block)
  2984. {
  2985. Labels = labels;
  2986. Block = block;
  2987. }
  2988. public SwitchSection Clone (CloneContext clonectx)
  2989. {
  2990. var cloned_labels = new List<SwitchLabel> ();
  2991. foreach (SwitchLabel sl in Labels)
  2992. cloned_labels.Add (sl.Clone (clonectx));
  2993. return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
  2994. }
  2995. }
  2996. public class Switch : Statement
  2997. {
  2998. // structure used to hold blocks of keys while calculating table switch
  2999. sealed class LabelsRange : IComparable<LabelsRange>
  3000. {
  3001. public readonly long min;
  3002. public long max;
  3003. public readonly List<long> label_values;
  3004. public LabelsRange (long value)
  3005. {
  3006. min = max = value;
  3007. label_values = new List<long> ();
  3008. label_values.Add (value);
  3009. }
  3010. public LabelsRange (long min, long max, ICollection<long> values)
  3011. {
  3012. this.min = min;
  3013. this.max = max;
  3014. this.label_values = new List<long> (values);
  3015. }
  3016. public long Range {
  3017. get {
  3018. return max - min + 1;
  3019. }
  3020. }
  3021. public bool AddValue (long value)
  3022. {
  3023. var gap = value - min + 1;
  3024. // Ensure the range has > 50% occupancy
  3025. if (gap > 2 * (label_values.Count + 1) || gap <= 0)
  3026. return false;
  3027. max = value;
  3028. label_values.Add (value);
  3029. return true;
  3030. }
  3031. public int CompareTo (LabelsRange other)
  3032. {
  3033. int nLength = label_values.Count;
  3034. int nLengthOther = other.label_values.Count;
  3035. if (nLengthOther == nLength)
  3036. return (int) (other.min - min);
  3037. return nLength - nLengthOther;
  3038. }
  3039. }
  3040. sealed class LabelMarker : Statement
  3041. {
  3042. readonly Switch s;
  3043. readonly List<SwitchLabel> labels;
  3044. public LabelMarker (Switch s, List<SwitchLabel> labels)
  3045. {
  3046. this.s = s;
  3047. this.labels = labels;
  3048. }
  3049. protected override void CloneTo (CloneContext clonectx, Statement target)
  3050. {
  3051. }
  3052. protected override void DoEmit (EmitContext ec)
  3053. {
  3054. foreach (var l in labels) {
  3055. if (l.IsDefault)
  3056. ec.MarkLabel (s.DefaultLabel);
  3057. else
  3058. ec.MarkLabel (l.GetILLabel (ec));
  3059. }
  3060. }
  3061. }
  3062. public List<SwitchSection> Sections;
  3063. public Expression Expr;
  3064. //
  3065. // Mapping of all labels to their SwitchLabels
  3066. //
  3067. Dictionary<long, SwitchLabel> labels;
  3068. Dictionary<string, SwitchLabel> string_labels;
  3069. /// <summary>
  3070. /// The governing switch type
  3071. /// </summary>
  3072. public TypeSpec SwitchType;
  3073. //
  3074. // Computed
  3075. //
  3076. Label default_target;
  3077. Label null_target;
  3078. Expression new_expr;
  3079. bool is_constant;
  3080. SwitchSection constant_section;
  3081. SwitchSection default_section;
  3082. SwitchLabel null_section;
  3083. Statement simple_stmt;
  3084. VariableReference value;
  3085. ExpressionStatement string_dictionary;
  3086. FieldExpr switch_cache_field;
  3087. ExplicitBlock block;
  3088. //
  3089. // Nullable Types support
  3090. //
  3091. Nullable.Unwrap unwrap;
  3092. public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
  3093. {
  3094. Expr = e;
  3095. this.block = block;
  3096. Sections = sects;
  3097. loc = l;
  3098. }
  3099. public ExplicitBlock Block {
  3100. get {
  3101. return block;
  3102. }
  3103. }
  3104. public Label DefaultLabel {
  3105. get {
  3106. return default_target;
  3107. }
  3108. }
  3109. public bool GotDefault {
  3110. get {
  3111. return default_section != null;
  3112. }
  3113. }
  3114. public bool IsNullable {
  3115. get {
  3116. return unwrap != null;
  3117. }
  3118. }
  3119. //
  3120. // Determines the governing type for a switch. The returned
  3121. // expression might be the expression from the switch, or an
  3122. // expression that includes any potential conversions to
  3123. //
  3124. Expression SwitchGoverningType (ResolveContext ec, Expression expr)
  3125. {
  3126. switch (expr.Type.BuiltinType) {
  3127. case BuiltinTypeSpec.Type.Byte:
  3128. case BuiltinTypeSpec.Type.SByte:
  3129. case BuiltinTypeSpec.Type.UShort:
  3130. case BuiltinTypeSpec.Type.Short:
  3131. case BuiltinTypeSpec.Type.UInt:
  3132. case BuiltinTypeSpec.Type.Int:
  3133. case BuiltinTypeSpec.Type.ULong:
  3134. case BuiltinTypeSpec.Type.Long:
  3135. case BuiltinTypeSpec.Type.Char:
  3136. case BuiltinTypeSpec.Type.String:
  3137. case BuiltinTypeSpec.Type.Bool:
  3138. return expr;
  3139. }
  3140. if (expr.Type.IsEnum)
  3141. return expr;
  3142. //
  3143. // Try to find a *user* defined implicit conversion.
  3144. //
  3145. // If there is no implicit conversion, or if there are multiple
  3146. // conversions, we have to report an error
  3147. //
  3148. Expression converted = null;
  3149. foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
  3150. Expression e;
  3151. e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
  3152. if (e == null)
  3153. continue;
  3154. //
  3155. // Ignore over-worked ImplicitUserConversions that do
  3156. // an implicit conversion in addition to the user conversion.
  3157. //
  3158. if (!(e is UserCast))
  3159. continue;
  3160. if (converted != null){
  3161. ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
  3162. return null;
  3163. }
  3164. converted = e;
  3165. }
  3166. return converted;
  3167. }
  3168. public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
  3169. {
  3170. // LAMESPEC: For some reason it does not contain bool which looks like csc bug
  3171. return new[] {
  3172. types.SByte,
  3173. types.Byte,
  3174. types.Short,
  3175. types.UShort,
  3176. types.Int,
  3177. types.UInt,
  3178. types.Long,
  3179. types.ULong,
  3180. types.Char,
  3181. types.String
  3182. };
  3183. }
  3184. //
  3185. // Performs the basic sanity checks on the switch statement
  3186. // (looks for duplicate keys and non-constant expressions).
  3187. //
  3188. // It also returns a hashtable with the keys that we will later
  3189. // use to compute the switch tables
  3190. //
  3191. bool CheckSwitch (ResolveContext ec)
  3192. {
  3193. bool error = false;
  3194. if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
  3195. string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
  3196. else
  3197. labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
  3198. foreach (SwitchSection ss in Sections){
  3199. foreach (SwitchLabel sl in ss.Labels){
  3200. if (sl.IsDefault){
  3201. if (default_section != null){
  3202. sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
  3203. error = true;
  3204. }
  3205. default_section = ss;
  3206. continue;
  3207. }
  3208. if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
  3209. error = true;
  3210. continue;
  3211. }
  3212. try {
  3213. if (string_labels != null) {
  3214. string s = sl.Converted.GetValue () as string;
  3215. if (s == null)
  3216. null_section = sl;
  3217. else
  3218. string_labels.Add (s, sl);
  3219. } else {
  3220. if (sl.Converted is NullLiteral) {
  3221. null_section = sl;
  3222. } else {
  3223. labels.Add (sl.Converted.GetValueAsLong (), sl);
  3224. }
  3225. }
  3226. } catch (ArgumentException) {
  3227. if (string_labels != null)
  3228. sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
  3229. else
  3230. sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
  3231. error = true;
  3232. }
  3233. }
  3234. }
  3235. return !error;
  3236. }
  3237. //
  3238. // This method emits code for a lookup-based switch statement (non-string)
  3239. // Basically it groups the cases into blocks that are at least half full,
  3240. // and then spits out individual lookup opcodes for each block.
  3241. // It emits the longest blocks first, and short blocks are just
  3242. // handled with direct compares.
  3243. //
  3244. void EmitTableSwitch (EmitContext ec, Expression val)
  3245. {
  3246. Label lbl_default = default_target;
  3247. if (labels != null && labels.Count > 0) {
  3248. List<LabelsRange> ranges;
  3249. if (string_labels != null) {
  3250. // We have done all hard work for string already
  3251. // setup single range only
  3252. ranges = new List<LabelsRange> (1);
  3253. ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
  3254. } else {
  3255. var element_keys = new long[labels.Count];
  3256. labels.Keys.CopyTo (element_keys, 0);
  3257. Array.Sort (element_keys);
  3258. //
  3259. // Build possible ranges of switch labes to reduce number
  3260. // of comparisons
  3261. //
  3262. ranges = new List<LabelsRange> (element_keys.Length);
  3263. var range = new LabelsRange (element_keys[0]);
  3264. ranges.Add (range);
  3265. for (int i = 1; i < element_keys.Length; ++i) {
  3266. var l = element_keys[i];
  3267. if (range.AddValue (l))
  3268. continue;
  3269. range = new LabelsRange (l);
  3270. ranges.Add (range);
  3271. }
  3272. // sort the blocks so we can tackle the largest ones first
  3273. ranges.Sort ();
  3274. }
  3275. TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
  3276. for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
  3277. LabelsRange kb = ranges[range_index];
  3278. lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
  3279. // Optimize small ranges using simple equality check
  3280. if (kb.Range <= 2) {
  3281. foreach (var key in kb.label_values) {
  3282. SwitchLabel sl = labels[key];
  3283. if (sl.Converted.IsDefaultValue) {
  3284. val.EmitBranchable (ec, sl.GetILLabel (ec), false);
  3285. } else {
  3286. val.Emit (ec);
  3287. sl.Converted.Emit (ec);
  3288. ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
  3289. }
  3290. }
  3291. } else {
  3292. // TODO: if all the keys in the block are the same and there are
  3293. // no gaps/defaults then just use a range-check.
  3294. if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
  3295. // TODO: optimize constant/I4 cases
  3296. // check block range (could be > 2^31)
  3297. val.Emit (ec);
  3298. ec.EmitLong (kb.min);
  3299. ec.Emit (OpCodes.Blt, lbl_default);
  3300. val.Emit (ec);
  3301. ec.EmitLong (kb.max);
  3302. ec.Emit (OpCodes.Bgt, lbl_default);
  3303. // normalize range
  3304. val.Emit (ec);
  3305. if (kb.min != 0) {
  3306. ec.EmitLong (kb.min);
  3307. ec.Emit (OpCodes.Sub);
  3308. }
  3309. ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
  3310. } else {
  3311. // normalize range
  3312. val.Emit (ec);
  3313. int first = (int) kb.min;
  3314. if (first > 0) {
  3315. ec.EmitInt (first);
  3316. ec.Emit (OpCodes.Sub);
  3317. } else if (first < 0) {
  3318. ec.EmitInt (-first);
  3319. ec.Emit (OpCodes.Add);
  3320. }
  3321. }
  3322. // first, build the list of labels for the switch
  3323. int iKey = 0;
  3324. long cJumps = kb.Range;
  3325. Label[] switch_labels = new Label[cJumps];
  3326. for (int iJump = 0; iJump < cJumps; iJump++) {
  3327. var key = kb.label_values[iKey];
  3328. if (key == kb.min + iJump) {
  3329. switch_labels[iJump] = labels[key].GetILLabel (ec);
  3330. iKey++;
  3331. } else {
  3332. switch_labels[iJump] = lbl_default;
  3333. }
  3334. }
  3335. // emit the switch opcode
  3336. ec.Emit (OpCodes.Switch, switch_labels);
  3337. }
  3338. // mark the default for this block
  3339. if (range_index != 0)
  3340. ec.MarkLabel (lbl_default);
  3341. }
  3342. // the last default just goes to the end
  3343. if (ranges.Count > 0)
  3344. ec.Emit (OpCodes.Br, lbl_default);
  3345. }
  3346. // now emit the code for the sections
  3347. bool found_default = false;
  3348. foreach (SwitchSection ss in Sections) {
  3349. foreach (SwitchLabel sl in ss.Labels) {
  3350. if (sl.IsDefault) {
  3351. ec.MarkLabel (lbl_default);
  3352. found_default = true;
  3353. if (null_section == null)
  3354. ec.MarkLabel (null_target);
  3355. } else if (sl.Converted.IsNull) {
  3356. ec.MarkLabel (null_target);
  3357. }
  3358. ec.MarkLabel (sl.GetILLabel (ec));
  3359. }
  3360. ss.Block.Emit (ec);
  3361. }
  3362. if (!found_default) {
  3363. ec.MarkLabel (lbl_default);
  3364. if (null_section == null) {
  3365. ec.MarkLabel (null_target);
  3366. }
  3367. }
  3368. }
  3369. SwitchLabel FindLabel (Constant value)
  3370. {
  3371. SwitchLabel sl = null;
  3372. if (string_labels != null) {
  3373. string s = value.GetValue () as string;
  3374. if (s == null) {
  3375. if (null_section != null)
  3376. sl = null_section;
  3377. else if (default_section != null)
  3378. sl = default_section.Labels[0];
  3379. } else {
  3380. string_labels.TryGetValue (s, out sl);
  3381. }
  3382. } else {
  3383. if (value is NullLiteral) {
  3384. sl = null_section;
  3385. } else {
  3386. labels.TryGetValue (value.GetValueAsLong (), out sl);
  3387. }
  3388. }
  3389. return sl;
  3390. }
  3391. SwitchSection FindSection (SwitchLabel label)
  3392. {
  3393. foreach (SwitchSection ss in Sections){
  3394. foreach (SwitchLabel sl in ss.Labels){
  3395. if (label == sl)
  3396. return ss;
  3397. }
  3398. }
  3399. return null;
  3400. }
  3401. public override bool Resolve (BlockContext ec)
  3402. {
  3403. Expr = Expr.Resolve (ec);
  3404. if (Expr == null)
  3405. return false;
  3406. new_expr = SwitchGoverningType (ec, Expr);
  3407. if (new_expr == null && Expr.Type.IsNullableType) {
  3408. unwrap = Nullable.Unwrap.Create (Expr, false);
  3409. if (unwrap == null)
  3410. return false;
  3411. new_expr = SwitchGoverningType (ec, unwrap);
  3412. }
  3413. if (new_expr == null){
  3414. ec.Report.Error (151, loc,
  3415. "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
  3416. TypeManager.CSharpName (Expr.Type));
  3417. return false;
  3418. }
  3419. // Validate switch.
  3420. SwitchType = new_expr.Type;
  3421. if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
  3422. ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
  3423. return false;
  3424. }
  3425. if (!CheckSwitch (ec))
  3426. return false;
  3427. Switch old_switch = ec.Switch;
  3428. ec.Switch = this;
  3429. ec.Switch.SwitchType = SwitchType;
  3430. ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
  3431. var constant = new_expr as Constant;
  3432. if (constant != null) {
  3433. is_constant = true;
  3434. SwitchLabel label = FindLabel (constant);
  3435. if (label != null)
  3436. constant_section = FindSection (label);
  3437. if (constant_section == null)
  3438. constant_section = default_section;
  3439. } else {
  3440. //
  3441. // Store switch expression for comparison purposes
  3442. //
  3443. value = new_expr as VariableReference;
  3444. if (value == null) {
  3445. // Create temporary variable inside switch scope
  3446. var block = ec.CurrentBlock;
  3447. ec.CurrentBlock = Block;
  3448. value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
  3449. value.Resolve (ec);
  3450. ec.CurrentBlock = block;
  3451. }
  3452. }
  3453. bool first = true;
  3454. bool ok = true;
  3455. foreach (SwitchSection ss in Sections){
  3456. if (!first)
  3457. ec.CurrentBranching.CreateSibling (
  3458. null, FlowBranching.SiblingType.SwitchSection);
  3459. else
  3460. first = false;
  3461. if (is_constant && (ss != constant_section)) {
  3462. // If we're a constant switch, we're only emitting
  3463. // one single section - mark all the others as
  3464. // unreachable.
  3465. ec.CurrentBranching.CurrentUsageVector.Goto ();
  3466. if (!ss.Block.ResolveUnreachable (ec, true)) {
  3467. ok = false;
  3468. }
  3469. } else {
  3470. if (!ss.Block.Resolve (ec))
  3471. ok = false;
  3472. }
  3473. }
  3474. if (default_section == null)
  3475. ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
  3476. ec.EndFlowBranching ();
  3477. ec.Switch = old_switch;
  3478. if (!ok)
  3479. return false;
  3480. if (!is_constant) {
  3481. if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
  3482. if (string_labels.Count < 7)
  3483. ResolveSimpleSwitch (ec);
  3484. else
  3485. ResolveStringSwitchMap (ec);
  3486. } else if (labels.Count < 3 && !IsNullable) {
  3487. ResolveSimpleSwitch (ec);
  3488. }
  3489. }
  3490. return true;
  3491. }
  3492. public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
  3493. {
  3494. var sl = FindLabel (value);
  3495. if (sl == null) {
  3496. FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
  3497. }
  3498. return sl;
  3499. }
  3500. //
  3501. // Prepares switch using simple if/else comparison for small label count (4 + optional default)
  3502. //
  3503. void ResolveSimpleSwitch (BlockContext bc)
  3504. {
  3505. simple_stmt = default_section != null ? default_section.Block : null;
  3506. for (int i = Sections.Count - 1; i >= 0; --i) {
  3507. var s = Sections[i];
  3508. if (s == default_section) {
  3509. s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
  3510. continue;
  3511. }
  3512. s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
  3513. Expression cond = null;
  3514. for (int ci = 0; ci < s.Labels.Count; ++ci) {
  3515. var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted);
  3516. if (ci > 0) {
  3517. cond = new Binary (Binary.Operator.LogicalOr, cond, e);
  3518. } else {
  3519. cond = e;
  3520. }
  3521. }
  3522. //
  3523. // Compiler generated, hide from symbol file
  3524. //
  3525. simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
  3526. }
  3527. // It's null for empty switch
  3528. if (simple_stmt != null)
  3529. simple_stmt.Resolve (bc);
  3530. }
  3531. //
  3532. // Converts string switch into string hashtable
  3533. //
  3534. void ResolveStringSwitchMap (ResolveContext ec)
  3535. {
  3536. FullNamedExpression string_dictionary_type;
  3537. if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
  3538. string_dictionary_type = new TypeExpression (
  3539. ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
  3540. new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
  3541. loc);
  3542. } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
  3543. string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
  3544. } else {
  3545. ec.Module.PredefinedTypes.Dictionary.Resolve ();
  3546. return;
  3547. }
  3548. var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
  3549. Field field = new Field (ctype, string_dictionary_type,
  3550. Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
  3551. new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
  3552. if (!field.Define ())
  3553. return;
  3554. ctype.AddField (field);
  3555. var init = new List<Expression> ();
  3556. int counter = 0;
  3557. labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
  3558. string value = null;
  3559. foreach (SwitchSection section in Sections) {
  3560. bool contains_label = false;
  3561. foreach (SwitchLabel sl in section.Labels) {
  3562. if (sl.IsDefault || sl.Converted.IsNull)
  3563. continue;
  3564. if (!contains_label) {
  3565. labels.Add (counter, sl);
  3566. contains_label = true;
  3567. }
  3568. value = (string) sl.Converted.GetValue ();
  3569. var init_args = new List<Expression> (2);
  3570. init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
  3571. sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
  3572. init_args.Add (sl.Converted);
  3573. init.Add (new CollectionElementInitializer (init_args, loc));
  3574. }
  3575. //
  3576. // Don't add empty sections
  3577. //
  3578. if (contains_label)
  3579. ++counter;
  3580. }
  3581. Arguments args = new Arguments (1);
  3582. args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
  3583. Expression initializer = new NewInitialize (string_dictionary_type, args,
  3584. new CollectionOrObjectInitializers (init, loc), loc);
  3585. switch_cache_field = new FieldExpr (field, loc);
  3586. string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
  3587. }
  3588. void DoEmitStringSwitch (EmitContext ec)
  3589. {
  3590. Label l_initialized = ec.DefineLabel ();
  3591. //
  3592. // Skip initialization when value is null
  3593. //
  3594. value.EmitBranchable (ec, null_target, false);
  3595. //
  3596. // Check if string dictionary is initialized and initialize
  3597. //
  3598. switch_cache_field.EmitBranchable (ec, l_initialized, true);
  3599. using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
  3600. string_dictionary.EmitStatement (ec);
  3601. }
  3602. ec.MarkLabel (l_initialized);
  3603. LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
  3604. ResolveContext rc = new ResolveContext (ec.MemberContext);
  3605. if (switch_cache_field.Type.IsGeneric) {
  3606. Arguments get_value_args = new Arguments (2);
  3607. get_value_args.Add (new Argument (value));
  3608. get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
  3609. Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
  3610. if (get_item == null)
  3611. return;
  3612. //
  3613. // A value was not found, go to default case
  3614. //
  3615. get_item.EmitBranchable (ec, default_target, false);
  3616. } else {
  3617. Arguments get_value_args = new Arguments (1);
  3618. get_value_args.Add (new Argument (value));
  3619. Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
  3620. if (get_item == null)
  3621. return;
  3622. LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
  3623. get_item_object.EmitAssign (ec, get_item, true, false);
  3624. ec.Emit (OpCodes.Brfalse, default_target);
  3625. ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
  3626. new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
  3627. get_item_int.EmitStatement (ec);
  3628. get_item_object.Release (ec);
  3629. }
  3630. EmitTableSwitch (ec, string_switch_variable);
  3631. string_switch_variable.Release (ec);
  3632. }
  3633. protected override void DoEmit (EmitContext ec)
  3634. {
  3635. // Workaround broken flow-analysis
  3636. block.HasUnreachableClosingBrace = true;
  3637. //
  3638. // Needed to emit anonymous storey initialization
  3639. // Otherwise it does not contain any statements for now
  3640. //
  3641. block.Emit (ec);
  3642. default_target = ec.DefineLabel ();
  3643. null_target = ec.DefineLabel ();
  3644. if (IsNullable) {
  3645. unwrap.EmitCheck (ec);
  3646. ec.Emit (OpCodes.Brfalse, null_target);
  3647. value.EmitAssign (ec, new_expr, false, false);
  3648. } else if (new_expr != value && !is_constant) {
  3649. value.EmitAssign (ec, new_expr, false, false);
  3650. }
  3651. //
  3652. // Setup the codegen context
  3653. //
  3654. Label old_end = ec.LoopEnd;
  3655. Switch old_switch = ec.Switch;
  3656. ec.LoopEnd = ec.DefineLabel ();
  3657. ec.Switch = this;
  3658. // Emit Code.
  3659. if (is_constant) {
  3660. if (constant_section != null)
  3661. constant_section.Block.Emit (ec);
  3662. } else if (string_dictionary != null) {
  3663. DoEmitStringSwitch (ec);
  3664. } else if (simple_stmt != null) {
  3665. simple_stmt.Emit (ec);
  3666. } else {
  3667. EmitTableSwitch (ec, value);
  3668. }
  3669. // Restore context state.
  3670. ec.MarkLabel (ec.LoopEnd);
  3671. //
  3672. // Restore the previous context
  3673. //
  3674. ec.LoopEnd = old_end;
  3675. ec.Switch = old_switch;
  3676. }
  3677. protected override void CloneTo (CloneContext clonectx, Statement t)
  3678. {
  3679. Switch target = (Switch) t;
  3680. target.Expr = Expr.Clone (clonectx);
  3681. target.Sections = new List<SwitchSection> ();
  3682. foreach (SwitchSection ss in Sections){
  3683. target.Sections.Add (ss.Clone (clonectx));
  3684. }
  3685. }
  3686. public override object Accept (StructuralVisitor visitor)
  3687. {
  3688. return visitor.Visit (this);
  3689. }
  3690. }
  3691. // A place where execution can restart in an iterator
  3692. public abstract class ResumableStatement : Statement
  3693. {
  3694. bool prepared;
  3695. protected Label resume_point;
  3696. public Label PrepareForEmit (EmitContext ec)
  3697. {
  3698. if (!prepared) {
  3699. prepared = true;
  3700. resume_point = ec.DefineLabel ();
  3701. }
  3702. return resume_point;
  3703. }
  3704. public virtual Label PrepareForDispose (EmitContext ec, Label end)
  3705. {
  3706. return end;
  3707. }
  3708. public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
  3709. {
  3710. }
  3711. }
  3712. public abstract class TryFinallyBlock : ExceptionStatement
  3713. {
  3714. protected Statement stmt;
  3715. Label dispose_try_block;
  3716. bool prepared_for_dispose, emitted_dispose;
  3717. Method finally_host;
  3718. protected TryFinallyBlock (Statement stmt, Location loc)
  3719. : base (loc)
  3720. {
  3721. this.stmt = stmt;
  3722. }
  3723. #region Properties
  3724. public Statement Statement {
  3725. get {
  3726. return stmt;
  3727. }
  3728. }
  3729. #endregion
  3730. protected abstract void EmitTryBody (EmitContext ec);
  3731. public abstract void EmitFinallyBody (EmitContext ec);
  3732. public override Label PrepareForDispose (EmitContext ec, Label end)
  3733. {
  3734. if (!prepared_for_dispose) {
  3735. prepared_for_dispose = true;
  3736. dispose_try_block = ec.DefineLabel ();
  3737. }
  3738. return dispose_try_block;
  3739. }
  3740. protected sealed override void DoEmit (EmitContext ec)
  3741. {
  3742. EmitTryBodyPrepare (ec);
  3743. EmitTryBody (ec);
  3744. ec.BeginFinallyBlock ();
  3745. Label start_finally = ec.DefineLabel ();
  3746. if (resume_points != null) {
  3747. var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
  3748. ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
  3749. ec.Emit (OpCodes.Brfalse_S, start_finally);
  3750. ec.Emit (OpCodes.Endfinally);
  3751. }
  3752. ec.MarkLabel (start_finally);
  3753. if (finally_host != null) {
  3754. finally_host.Define ();
  3755. finally_host.Emit ();
  3756. // Now it's safe to add, to close it properly and emit sequence points
  3757. finally_host.Parent.AddMember (finally_host);
  3758. var ce = new CallEmitter ();
  3759. ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
  3760. ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
  3761. } else {
  3762. EmitFinallyBody (ec);
  3763. }
  3764. ec.EndExceptionBlock ();
  3765. }
  3766. public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
  3767. {
  3768. if (emitted_dispose)
  3769. return;
  3770. emitted_dispose = true;
  3771. Label end_of_try = ec.DefineLabel ();
  3772. // Ensure that the only way we can get into this code is through a dispatcher
  3773. if (have_dispatcher)
  3774. ec.Emit (OpCodes.Br, end);
  3775. ec.BeginExceptionBlock ();
  3776. ec.MarkLabel (dispose_try_block);
  3777. Label[] labels = null;
  3778. for (int i = 0; i < resume_points.Count; ++i) {
  3779. ResumableStatement s = resume_points[i];
  3780. Label ret = s.PrepareForDispose (ec, end_of_try);
  3781. if (ret.Equals (end_of_try) && labels == null)
  3782. continue;
  3783. if (labels == null) {
  3784. labels = new Label[resume_points.Count];
  3785. for (int j = 0; j < i; ++j)
  3786. labels[j] = end_of_try;
  3787. }
  3788. labels[i] = ret;
  3789. }
  3790. if (labels != null) {
  3791. int j;
  3792. for (j = 1; j < labels.Length; ++j)
  3793. if (!labels[0].Equals (labels[j]))
  3794. break;
  3795. bool emit_dispatcher = j < labels.Length;
  3796. if (emit_dispatcher) {
  3797. ec.Emit (OpCodes.Ldloc, pc);
  3798. ec.EmitInt (first_resume_pc);
  3799. ec.Emit (OpCodes.Sub);
  3800. ec.Emit (OpCodes.Switch, labels);
  3801. }
  3802. foreach (ResumableStatement s in resume_points)
  3803. s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
  3804. }
  3805. ec.MarkLabel (end_of_try);
  3806. ec.BeginFinallyBlock ();
  3807. if (finally_host != null) {
  3808. var ce = new CallEmitter ();
  3809. ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
  3810. ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
  3811. } else {
  3812. EmitFinallyBody (ec);
  3813. }
  3814. ec.EndExceptionBlock ();
  3815. }
  3816. public override bool Resolve (BlockContext bc)
  3817. {
  3818. //
  3819. // Finally block inside iterator is called from MoveNext and
  3820. // Dispose methods that means we need to lift the block into
  3821. // newly created host method to emit the body only once. The
  3822. // original block then simply calls the newly generated method.
  3823. //
  3824. if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
  3825. var b = stmt as Block;
  3826. if (b != null && b.Explicit.HasYield) {
  3827. finally_host = bc.CurrentIterator.CreateFinallyHost (this);
  3828. }
  3829. }
  3830. return base.Resolve (bc);
  3831. }
  3832. }
  3833. //
  3834. // Base class for blocks using exception handling
  3835. //
  3836. public abstract class ExceptionStatement : ResumableStatement
  3837. {
  3838. #if !STATIC
  3839. bool code_follows;
  3840. #endif
  3841. protected List<ResumableStatement> resume_points;
  3842. protected int first_resume_pc;
  3843. protected ExceptionStatement (Location loc)
  3844. {
  3845. this.loc = loc;
  3846. }
  3847. protected virtual void EmitTryBodyPrepare (EmitContext ec)
  3848. {
  3849. StateMachineInitializer state_machine = null;
  3850. if (resume_points != null) {
  3851. state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
  3852. ec.EmitInt ((int) IteratorStorey.State.Running);
  3853. ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
  3854. }
  3855. ec.BeginExceptionBlock ();
  3856. if (resume_points != null) {
  3857. ec.MarkLabel (resume_point);
  3858. // For normal control flow, we want to fall-through the Switch
  3859. // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
  3860. ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
  3861. ec.EmitInt (first_resume_pc);
  3862. ec.Emit (OpCodes.Sub);
  3863. Label[] labels = new Label[resume_points.Count];
  3864. for (int i = 0; i < resume_points.Count; ++i)
  3865. labels[i] = resume_points[i].PrepareForEmit (ec);
  3866. ec.Emit (OpCodes.Switch, labels);
  3867. }
  3868. }
  3869. public void SomeCodeFollows ()
  3870. {
  3871. #if !STATIC
  3872. code_follows = true;
  3873. #endif
  3874. }
  3875. public override bool Resolve (BlockContext ec)
  3876. {
  3877. #if !STATIC
  3878. // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
  3879. // So, ensure there's some IL code after this statement.
  3880. if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
  3881. ec.NeedReturnLabel ();
  3882. #endif
  3883. return true;
  3884. }
  3885. public void AddResumePoint (ResumableStatement stmt, int pc)
  3886. {
  3887. if (resume_points == null) {
  3888. resume_points = new List<ResumableStatement> ();
  3889. first_resume_pc = pc;
  3890. }
  3891. if (pc != first_resume_pc + resume_points.Count)
  3892. throw new InternalErrorException ("missed an intervening AddResumePoint?");
  3893. resume_points.Add (stmt);
  3894. }
  3895. }
  3896. public class Lock : TryFinallyBlock
  3897. {
  3898. Expression expr;
  3899. TemporaryVariableReference expr_copy;
  3900. TemporaryVariableReference lock_taken;
  3901. public Lock (Expression expr, Statement stmt, Location loc)
  3902. : base (stmt, loc)
  3903. {
  3904. this.expr = expr;
  3905. }
  3906. public Expression Expr {
  3907. get {
  3908. return this.expr;
  3909. }
  3910. }
  3911. public override bool Resolve (BlockContext ec)
  3912. {
  3913. expr = expr.Resolve (ec);
  3914. if (expr == null)
  3915. return false;
  3916. if (!TypeSpec.IsReferenceType (expr.Type)) {
  3917. ec.Report.Error (185, loc,
  3918. "`{0}' is not a reference type as required by the lock statement",
  3919. expr.Type.GetSignatureForError ());
  3920. }
  3921. if (expr.Type.IsGenericParameter) {
  3922. expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
  3923. }
  3924. VariableReference lv = expr as VariableReference;
  3925. bool locked;
  3926. if (lv != null) {
  3927. locked = lv.IsLockedByStatement;
  3928. lv.IsLockedByStatement = true;
  3929. } else {
  3930. lv = null;
  3931. locked = false;
  3932. }
  3933. //
  3934. // Have to keep original lock value around to unlock same location
  3935. // in the case of original value has changed or is null
  3936. //
  3937. expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
  3938. expr_copy.Resolve (ec);
  3939. //
  3940. // Ensure Monitor methods are available
  3941. //
  3942. if (ResolvePredefinedMethods (ec) > 1) {
  3943. lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
  3944. lock_taken.Resolve (ec);
  3945. }
  3946. using (ec.Set (ResolveContext.Options.LockScope)) {
  3947. ec.StartFlowBranching (this);
  3948. Statement.Resolve (ec);
  3949. ec.EndFlowBranching ();
  3950. }
  3951. if (lv != null) {
  3952. lv.IsLockedByStatement = locked;
  3953. }
  3954. base.Resolve (ec);
  3955. return true;
  3956. }
  3957. protected override void EmitTryBodyPrepare (EmitContext ec)
  3958. {
  3959. expr_copy.EmitAssign (ec, expr);
  3960. if (lock_taken != null) {
  3961. //
  3962. // Initialize ref variable
  3963. //
  3964. lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
  3965. } else {
  3966. //
  3967. // Monitor.Enter (expr_copy)
  3968. //
  3969. expr_copy.Emit (ec);
  3970. ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
  3971. }
  3972. base.EmitTryBodyPrepare (ec);
  3973. }
  3974. protected override void EmitTryBody (EmitContext ec)
  3975. {
  3976. //
  3977. // Monitor.Enter (expr_copy, ref lock_taken)
  3978. //
  3979. if (lock_taken != null) {
  3980. expr_copy.Emit (ec);
  3981. lock_taken.LocalInfo.CreateBuilder (ec);
  3982. lock_taken.AddressOf (ec, AddressOp.Load);
  3983. ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
  3984. }
  3985. Statement.Emit (ec);
  3986. }
  3987. public override void EmitFinallyBody (EmitContext ec)
  3988. {
  3989. //
  3990. // if (lock_taken) Monitor.Exit (expr_copy)
  3991. //
  3992. Label skip = ec.DefineLabel ();
  3993. if (lock_taken != null) {
  3994. lock_taken.Emit (ec);
  3995. ec.Emit (OpCodes.Brfalse_S, skip);
  3996. }
  3997. expr_copy.Emit (ec);
  3998. var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
  3999. if (m != null)
  4000. ec.Emit (OpCodes.Call, m);
  4001. ec.MarkLabel (skip);
  4002. }
  4003. int ResolvePredefinedMethods (ResolveContext rc)
  4004. {
  4005. // Try 4.0 Monitor.Enter (object, ref bool) overload first
  4006. var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
  4007. if (m != null)
  4008. return 4;
  4009. m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
  4010. if (m != null)
  4011. return 1;
  4012. rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
  4013. return 0;
  4014. }
  4015. protected override void CloneTo (CloneContext clonectx, Statement t)
  4016. {
  4017. Lock target = (Lock) t;
  4018. target.expr = expr.Clone (clonectx);
  4019. target.stmt = Statement.Clone (clonectx);
  4020. }
  4021. public override object Accept (StructuralVisitor visitor)
  4022. {
  4023. return visitor.Visit (this);
  4024. }
  4025. }
  4026. public class Unchecked : Statement {
  4027. public Block Block;
  4028. public Unchecked (Block b, Location loc)
  4029. {
  4030. Block = b;
  4031. b.Unchecked = true;
  4032. this.loc = loc;
  4033. }
  4034. public override bool Resolve (BlockContext ec)
  4035. {
  4036. using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
  4037. return Block.Resolve (ec);
  4038. }
  4039. protected override void DoEmit (EmitContext ec)
  4040. {
  4041. using (ec.With (EmitContext.Options.CheckedScope, false))
  4042. Block.Emit (ec);
  4043. }
  4044. protected override void CloneTo (CloneContext clonectx, Statement t)
  4045. {
  4046. Unchecked target = (Unchecked) t;
  4047. target.Block = clonectx.LookupBlock (Block);
  4048. }
  4049. public override object Accept (StructuralVisitor visitor)
  4050. {
  4051. return visitor.Visit (this);
  4052. }
  4053. }
  4054. public class Checked : Statement {
  4055. public Block Block;
  4056. public Checked (Block b, Location loc)
  4057. {
  4058. Block = b;
  4059. b.Unchecked = false;
  4060. this.loc = loc;
  4061. }
  4062. public override bool Resolve (BlockContext ec)
  4063. {
  4064. using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
  4065. return Block.Resolve (ec);
  4066. }
  4067. protected override void DoEmit (EmitContext ec)
  4068. {
  4069. using (ec.With (EmitContext.Options.CheckedScope, true))
  4070. Block.Emit (ec);
  4071. }
  4072. protected override void CloneTo (CloneContext clonectx, Statement t)
  4073. {
  4074. Checked target = (Checked) t;
  4075. target.Block = clonectx.LookupBlock (Block);
  4076. }
  4077. public override object Accept (StructuralVisitor visitor)
  4078. {
  4079. return visitor.Visit (this);
  4080. }
  4081. }
  4082. public class Unsafe : Statement {
  4083. public Block Block;
  4084. public Unsafe (Block b, Location loc)
  4085. {
  4086. Block = b;
  4087. Block.Unsafe = true;
  4088. this.loc = loc;
  4089. }
  4090. public override bool Resolve (BlockContext ec)
  4091. {
  4092. if (ec.CurrentIterator != null)
  4093. ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
  4094. using (ec.Set (ResolveContext.Options.UnsafeScope))
  4095. return Block.Resolve (ec);
  4096. }
  4097. protected override void DoEmit (EmitContext ec)
  4098. {
  4099. Block.Emit (ec);
  4100. }
  4101. protected override void CloneTo (CloneContext clonectx, Statement t)
  4102. {
  4103. Unsafe target = (Unsafe) t;
  4104. target.Block = clonectx.LookupBlock (Block);
  4105. }
  4106. public override object Accept (StructuralVisitor visitor)
  4107. {
  4108. return visitor.Visit (this);
  4109. }
  4110. }
  4111. //
  4112. // Fixed statement
  4113. //
  4114. public class Fixed : Statement
  4115. {
  4116. abstract class Emitter : ShimExpression
  4117. {
  4118. protected LocalVariable vi;
  4119. protected Emitter (Expression expr, LocalVariable li)
  4120. : base (expr)
  4121. {
  4122. vi = li;
  4123. }
  4124. public abstract void EmitExit (EmitContext ec);
  4125. }
  4126. class ExpressionEmitter : Emitter {
  4127. public ExpressionEmitter (Expression converted, LocalVariable li) :
  4128. base (converted, li)
  4129. {
  4130. }
  4131. protected override Expression DoResolve (ResolveContext rc)
  4132. {
  4133. throw new NotImplementedException ();
  4134. }
  4135. public override void Emit (EmitContext ec) {
  4136. //
  4137. // Store pointer in pinned location
  4138. //
  4139. expr.Emit (ec);
  4140. vi.EmitAssign (ec);
  4141. }
  4142. public override void EmitExit (EmitContext ec)
  4143. {
  4144. ec.EmitInt (0);
  4145. ec.Emit (OpCodes.Conv_U);
  4146. vi.EmitAssign (ec);
  4147. }
  4148. }
  4149. class StringEmitter : Emitter
  4150. {
  4151. LocalVariable pinned_string;
  4152. public StringEmitter (Expression expr, LocalVariable li, Location loc)
  4153. : base (expr, li)
  4154. {
  4155. }
  4156. protected override Expression DoResolve (ResolveContext rc)
  4157. {
  4158. pinned_string = new LocalVariable (vi.Block, "$pinned",
  4159. LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
  4160. vi.Location);
  4161. pinned_string.Type = rc.BuiltinTypes.String;
  4162. eclass = ExprClass.Variable;
  4163. type = rc.BuiltinTypes.Int;
  4164. return this;
  4165. }
  4166. public override void Emit (EmitContext ec)
  4167. {
  4168. pinned_string.CreateBuilder (ec);
  4169. expr.Emit (ec);
  4170. pinned_string.EmitAssign (ec);
  4171. // TODO: Should use Binary::Add
  4172. pinned_string.Emit (ec);
  4173. ec.Emit (OpCodes.Conv_I);
  4174. var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
  4175. if (m == null)
  4176. return;
  4177. PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
  4178. //pe.InstanceExpression = pinned_string;
  4179. pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
  4180. ec.Emit (OpCodes.Add);
  4181. vi.EmitAssign (ec);
  4182. }
  4183. public override void EmitExit (EmitContext ec)
  4184. {
  4185. ec.EmitNull ();
  4186. pinned_string.EmitAssign (ec);
  4187. }
  4188. }
  4189. public class VariableDeclaration : BlockVariableDeclaration
  4190. {
  4191. public VariableDeclaration (FullNamedExpression type, LocalVariable li)
  4192. : base (type, li)
  4193. {
  4194. }
  4195. protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
  4196. {
  4197. if (!Variable.Type.IsPointer && li == Variable) {
  4198. bc.Report.Error (209, TypeExpression.Location,
  4199. "The type of locals declared in a fixed statement must be a pointer type");
  4200. return null;
  4201. }
  4202. //
  4203. // The rules for the possible declarators are pretty wise,
  4204. // but the production on the grammar is more concise.
  4205. //
  4206. // So we have to enforce these rules here.
  4207. //
  4208. // We do not resolve before doing the case 1 test,
  4209. // because the grammar is explicit in that the token &
  4210. // is present, so we need to test for this particular case.
  4211. //
  4212. if (initializer is Cast) {
  4213. bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
  4214. return null;
  4215. }
  4216. initializer = initializer.Resolve (bc);
  4217. if (initializer == null)
  4218. return null;
  4219. //
  4220. // Case 1: Array
  4221. //
  4222. if (initializer.Type.IsArray) {
  4223. TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
  4224. //
  4225. // Provided that array_type is unmanaged,
  4226. //
  4227. if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
  4228. return null;
  4229. //
  4230. // and T* is implicitly convertible to the
  4231. // pointer type given in the fixed statement.
  4232. //
  4233. ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
  4234. Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
  4235. if (converted == null)
  4236. return null;
  4237. //
  4238. // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
  4239. //
  4240. converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
  4241. new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
  4242. new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
  4243. new NullLiteral (loc),
  4244. converted, loc);
  4245. converted = converted.Resolve (bc);
  4246. return new ExpressionEmitter (converted, li);
  4247. }
  4248. //
  4249. // Case 2: string
  4250. //
  4251. if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
  4252. return new StringEmitter (initializer, li, loc).Resolve (bc);
  4253. }
  4254. // Case 3: fixed buffer
  4255. if (initializer is FixedBufferPtr) {
  4256. return new ExpressionEmitter (initializer, li);
  4257. }
  4258. //
  4259. // Case 4: & object.
  4260. //
  4261. bool already_fixed = true;
  4262. Unary u = initializer as Unary;
  4263. if (u != null && u.Oper == Unary.Operator.AddressOf) {
  4264. IVariableReference vr = u.Expr as IVariableReference;
  4265. if (vr == null || !vr.IsFixed) {
  4266. already_fixed = false;
  4267. }
  4268. }
  4269. if (already_fixed) {
  4270. bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
  4271. }
  4272. initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
  4273. return new ExpressionEmitter (initializer, li);
  4274. }
  4275. }
  4276. VariableDeclaration decl;
  4277. Statement statement;
  4278. bool has_ret;
  4279. public Fixed (VariableDeclaration decl, Statement stmt, Location l)
  4280. {
  4281. this.decl = decl;
  4282. statement = stmt;
  4283. loc = l;
  4284. }
  4285. #region Properties
  4286. public Statement Statement {
  4287. get {
  4288. return statement;
  4289. }
  4290. }
  4291. public BlockVariableDeclaration Variables {
  4292. get {
  4293. return decl;
  4294. }
  4295. }
  4296. #endregion
  4297. public override bool Resolve (BlockContext ec)
  4298. {
  4299. using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
  4300. if (!decl.Resolve (ec))
  4301. return false;
  4302. }
  4303. ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
  4304. bool ok = statement.Resolve (ec);
  4305. bool flow_unreachable = ec.EndFlowBranching ();
  4306. has_ret = flow_unreachable;
  4307. return ok;
  4308. }
  4309. protected override void DoEmit (EmitContext ec)
  4310. {
  4311. decl.Variable.CreateBuilder (ec);
  4312. decl.Initializer.Emit (ec);
  4313. if (decl.Declarators != null) {
  4314. foreach (var d in decl.Declarators) {
  4315. d.Variable.CreateBuilder (ec);
  4316. d.Initializer.Emit (ec);
  4317. }
  4318. }
  4319. statement.Emit (ec);
  4320. if (has_ret)
  4321. return;
  4322. //
  4323. // Clear the pinned variable
  4324. //
  4325. ((Emitter) decl.Initializer).EmitExit (ec);
  4326. if (decl.Declarators != null) {
  4327. foreach (var d in decl.Declarators) {
  4328. ((Emitter)d.Initializer).EmitExit (ec);
  4329. }
  4330. }
  4331. }
  4332. protected override void CloneTo (CloneContext clonectx, Statement t)
  4333. {
  4334. Fixed target = (Fixed) t;
  4335. target.decl = (VariableDeclaration) decl.Clone (clonectx);
  4336. target.statement = statement.Clone (clonectx);
  4337. }
  4338. public override object Accept (StructuralVisitor visitor)
  4339. {
  4340. return visitor.Visit (this);
  4341. }
  4342. }
  4343. public class Catch : Statement
  4344. {
  4345. Block block;
  4346. LocalVariable li;
  4347. FullNamedExpression type_expr;
  4348. CompilerAssign assign;
  4349. TypeSpec type;
  4350. public Catch (Block block, Location loc)
  4351. {
  4352. this.block = block;
  4353. this.loc = loc;
  4354. }
  4355. #region Properties
  4356. public Block Block {
  4357. get {
  4358. return block;
  4359. }
  4360. }
  4361. public TypeSpec CatchType {
  4362. get {
  4363. return type;
  4364. }
  4365. }
  4366. public bool IsGeneral {
  4367. get {
  4368. return type_expr == null;
  4369. }
  4370. }
  4371. public FullNamedExpression TypeExpression {
  4372. get {
  4373. return type_expr;
  4374. }
  4375. set {
  4376. type_expr = value;
  4377. }
  4378. }
  4379. public LocalVariable Variable {
  4380. get {
  4381. return li;
  4382. }
  4383. set {
  4384. li = value;
  4385. }
  4386. }
  4387. #endregion
  4388. protected override void DoEmit (EmitContext ec)
  4389. {
  4390. if (IsGeneral)
  4391. ec.BeginCatchBlock (ec.BuiltinTypes.Object);
  4392. else
  4393. ec.BeginCatchBlock (CatchType);
  4394. if (li != null) {
  4395. li.CreateBuilder (ec);
  4396. //
  4397. // Special case hoisted catch variable, we have to use a temporary variable
  4398. // to pass via anonymous storey initialization with the value still on top
  4399. // of the stack
  4400. //
  4401. if (li.HoistedVariant != null) {
  4402. LocalTemporary lt = new LocalTemporary (li.Type);
  4403. lt.Store (ec);
  4404. // switch to assigning from the temporary variable and not from top of the stack
  4405. assign.UpdateSource (lt);
  4406. }
  4407. } else {
  4408. ec.Emit (OpCodes.Pop);
  4409. }
  4410. Block.Emit (ec);
  4411. }
  4412. public override bool Resolve (BlockContext ec)
  4413. {
  4414. using (ec.With (ResolveContext.Options.CatchScope, true)) {
  4415. if (type_expr != null) {
  4416. type = type_expr.ResolveAsType (ec);
  4417. if (type == null)
  4418. return false;
  4419. if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
  4420. ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
  4421. } else if (li != null) {
  4422. li.Type = type;
  4423. li.PrepareForFlowAnalysis (ec);
  4424. // source variable is at the top of the stack
  4425. Expression source = new EmptyExpression (li.Type);
  4426. if (li.Type.IsGenericParameter)
  4427. source = new UnboxCast (source, li.Type);
  4428. //
  4429. // Uses Location.Null to hide from symbol file
  4430. //
  4431. assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
  4432. Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
  4433. }
  4434. }
  4435. return Block.Resolve (ec);
  4436. }
  4437. }
  4438. protected override void CloneTo (CloneContext clonectx, Statement t)
  4439. {
  4440. Catch target = (Catch) t;
  4441. if (type_expr != null)
  4442. target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
  4443. target.block = clonectx.LookupBlock (block);
  4444. }
  4445. }
  4446. public class TryFinally : TryFinallyBlock
  4447. {
  4448. Block fini;
  4449. public TryFinally (Statement stmt, Block fini, Location loc)
  4450. : base (stmt, loc)
  4451. {
  4452. this.fini = fini;
  4453. }
  4454. public Block Finallyblock {
  4455. get {
  4456. return fini;
  4457. }
  4458. }
  4459. public override bool Resolve (BlockContext ec)
  4460. {
  4461. bool ok = true;
  4462. ec.StartFlowBranching (this);
  4463. if (!stmt.Resolve (ec))
  4464. ok = false;
  4465. if (ok)
  4466. ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
  4467. using (ec.With (ResolveContext.Options.FinallyScope, true)) {
  4468. if (!fini.Resolve (ec))
  4469. ok = false;
  4470. }
  4471. ec.EndFlowBranching ();
  4472. ok &= base.Resolve (ec);
  4473. return ok;
  4474. }
  4475. protected override void EmitTryBody (EmitContext ec)
  4476. {
  4477. stmt.Emit (ec);
  4478. }
  4479. public override void EmitFinallyBody (EmitContext ec)
  4480. {
  4481. fini.Emit (ec);
  4482. }
  4483. protected override void CloneTo (CloneContext clonectx, Statement t)
  4484. {
  4485. TryFinally target = (TryFinally) t;
  4486. target.stmt = (Statement) stmt.Clone (clonectx);
  4487. if (fini != null)
  4488. target.fini = clonectx.LookupBlock (fini);
  4489. }
  4490. public override object Accept (StructuralVisitor visitor)
  4491. {
  4492. return visitor.Visit (this);
  4493. }
  4494. }
  4495. public class TryCatch : ExceptionStatement
  4496. {
  4497. public Block Block;
  4498. List<Catch> clauses;
  4499. readonly bool inside_try_finally;
  4500. public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
  4501. : base (l)
  4502. {
  4503. this.Block = block;
  4504. this.clauses = catch_clauses;
  4505. this.inside_try_finally = inside_try_finally;
  4506. }
  4507. public List<Catch> Clauses {
  4508. get {
  4509. return clauses;
  4510. }
  4511. }
  4512. public bool IsTryCatchFinally {
  4513. get {
  4514. return inside_try_finally;
  4515. }
  4516. }
  4517. public override bool Resolve (BlockContext ec)
  4518. {
  4519. bool ok = true;
  4520. ec.StartFlowBranching (this);
  4521. if (!Block.Resolve (ec))
  4522. ok = false;
  4523. for (int i = 0; i < clauses.Count; ++i) {
  4524. var c = clauses[i];
  4525. ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
  4526. if (!c.Resolve (ec)) {
  4527. ok = false;
  4528. continue;
  4529. }
  4530. TypeSpec resolved_type = c.CatchType;
  4531. for (int ii = 0; ii < clauses.Count; ++ii) {
  4532. if (ii == i)
  4533. continue;
  4534. if (clauses[ii].IsGeneral) {
  4535. if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
  4536. continue;
  4537. if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
  4538. continue;
  4539. if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
  4540. continue;
  4541. ec.Report.Warning (1058, 1, c.loc,
  4542. "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
  4543. continue;
  4544. }
  4545. if (ii >= i)
  4546. continue;
  4547. var ct = clauses[ii].CatchType;
  4548. if (ct == null)
  4549. continue;
  4550. if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
  4551. ec.Report.Error (160, c.loc,
  4552. "A previous catch clause already catches all exceptions of this or a super type `{0}'",
  4553. ct.GetSignatureForError ());
  4554. ok = false;
  4555. }
  4556. }
  4557. }
  4558. ec.EndFlowBranching ();
  4559. return base.Resolve (ec) && ok;
  4560. }
  4561. protected sealed override void DoEmit (EmitContext ec)
  4562. {
  4563. if (!inside_try_finally)
  4564. EmitTryBodyPrepare (ec);
  4565. Block.Emit (ec);
  4566. foreach (Catch c in clauses)
  4567. c.Emit (ec);
  4568. if (!inside_try_finally)
  4569. ec.EndExceptionBlock ();
  4570. }
  4571. protected override void CloneTo (CloneContext clonectx, Statement t)
  4572. {
  4573. TryCatch target = (TryCatch) t;
  4574. target.Block = clonectx.LookupBlock (Block);
  4575. if (clauses != null){
  4576. target.clauses = new List<Catch> ();
  4577. foreach (Catch c in clauses)
  4578. target.clauses.Add ((Catch) c.Clone (clonectx));
  4579. }
  4580. }
  4581. public override object Accept (StructuralVisitor visitor)
  4582. {
  4583. return visitor.Visit (this);
  4584. }
  4585. }
  4586. public class Using : TryFinallyBlock
  4587. {
  4588. public class VariableDeclaration : BlockVariableDeclaration
  4589. {
  4590. Statement dispose_call;
  4591. public VariableDeclaration (FullNamedExpression type, LocalVariable li)
  4592. : base (type, li)
  4593. {
  4594. }
  4595. public VariableDeclaration (LocalVariable li, Location loc)
  4596. : base (li)
  4597. {
  4598. this.loc = loc;
  4599. }
  4600. public VariableDeclaration (Expression expr)
  4601. : base (null)
  4602. {
  4603. loc = expr.Location;
  4604. Initializer = expr;
  4605. }
  4606. #region Properties
  4607. public bool IsNested { get; private set; }
  4608. #endregion
  4609. public void EmitDispose (EmitContext ec)
  4610. {
  4611. dispose_call.Emit (ec);
  4612. }
  4613. public override bool Resolve (BlockContext bc)
  4614. {
  4615. if (IsNested)
  4616. return true;
  4617. return base.Resolve (bc, false);
  4618. }
  4619. public Expression ResolveExpression (BlockContext bc)
  4620. {
  4621. var e = Initializer.Resolve (bc);
  4622. if (e == null)
  4623. return null;
  4624. li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
  4625. Initializer = ResolveInitializer (bc, Variable, e);
  4626. return e;
  4627. }
  4628. protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
  4629. {
  4630. if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
  4631. initializer = initializer.Resolve (bc);
  4632. if (initializer == null)
  4633. return null;
  4634. // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
  4635. Arguments args = new Arguments (1);
  4636. args.Add (new Argument (initializer));
  4637. initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
  4638. if (initializer == null)
  4639. return null;
  4640. var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
  4641. dispose_call = CreateDisposeCall (bc, var);
  4642. dispose_call.Resolve (bc);
  4643. return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
  4644. }
  4645. if (li == Variable) {
  4646. CheckIDiposableConversion (bc, li, initializer);
  4647. dispose_call = CreateDisposeCall (bc, li);
  4648. dispose_call.Resolve (bc);
  4649. }
  4650. return base.ResolveInitializer (bc, li, initializer);
  4651. }
  4652. protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
  4653. {
  4654. var type = li.Type;
  4655. if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
  4656. if (type.IsNullableType) {
  4657. // it's handled in CreateDisposeCall
  4658. return;
  4659. }
  4660. bc.Report.SymbolRelatedToPreviousError (type);
  4661. var loc = type_expr == null ? initializer.Location : type_expr.Location;
  4662. bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
  4663. type.GetSignatureForError ());
  4664. return;
  4665. }
  4666. }
  4667. protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
  4668. {
  4669. var lvr = lv.CreateReferenceExpression (bc, lv.Location);
  4670. var type = lv.Type;
  4671. var loc = lv.Location;
  4672. var idt = bc.BuiltinTypes.IDisposable;
  4673. var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
  4674. var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
  4675. dispose_mg.InstanceExpression = type.IsNullableType ?
  4676. new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
  4677. lvr;
  4678. //
  4679. // Hide it from symbol file via null location
  4680. //
  4681. Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
  4682. // Add conditional call when disposing possible null variable
  4683. if (!type.IsStruct || type.IsNullableType)
  4684. dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
  4685. return dispose;
  4686. }
  4687. public void ResolveDeclaratorInitializer (BlockContext bc)
  4688. {
  4689. Initializer = base.ResolveInitializer (bc, Variable, Initializer);
  4690. }
  4691. public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
  4692. {
  4693. for (int i = declarators.Count - 1; i >= 0; --i) {
  4694. var d = declarators [i];
  4695. var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
  4696. vd.Initializer = d.Initializer;
  4697. vd.IsNested = true;
  4698. vd.dispose_call = CreateDisposeCall (bc, d.Variable);
  4699. vd.dispose_call.Resolve (bc);
  4700. stmt = new Using (vd, stmt, d.Variable.Location);
  4701. }
  4702. declarators = null;
  4703. return stmt;
  4704. }
  4705. public override object Accept (StructuralVisitor visitor)
  4706. {
  4707. return visitor.Visit (this);
  4708. }
  4709. }
  4710. VariableDeclaration decl;
  4711. public Using (VariableDeclaration decl, Statement stmt, Location loc)
  4712. : base (stmt, loc)
  4713. {
  4714. this.decl = decl;
  4715. }
  4716. public Using (Expression expr, Statement stmt, Location loc)
  4717. : base (stmt, loc)
  4718. {
  4719. this.decl = new VariableDeclaration (expr);
  4720. }
  4721. #region Properties
  4722. public Expression Expr {
  4723. get {
  4724. return decl.Variable == null ? decl.Initializer : null;
  4725. }
  4726. }
  4727. public BlockVariableDeclaration Variables {
  4728. get {
  4729. return decl;
  4730. }
  4731. }
  4732. #endregion
  4733. public override void Emit (EmitContext ec)
  4734. {
  4735. //
  4736. // Don't emit sequence point it will be set on variable declaration
  4737. //
  4738. DoEmit (ec);
  4739. }
  4740. protected override void EmitTryBodyPrepare (EmitContext ec)
  4741. {
  4742. decl.Emit (ec);
  4743. base.EmitTryBodyPrepare (ec);
  4744. }
  4745. protected override void EmitTryBody (EmitContext ec)
  4746. {
  4747. stmt.Emit (ec);
  4748. }
  4749. public override void EmitFinallyBody (EmitContext ec)
  4750. {
  4751. decl.EmitDispose (ec);
  4752. }
  4753. public override bool Resolve (BlockContext ec)
  4754. {
  4755. VariableReference vr;
  4756. bool vr_locked = false;
  4757. using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
  4758. if (decl.Variable == null) {
  4759. vr = decl.ResolveExpression (ec) as VariableReference;
  4760. if (vr != null) {
  4761. vr_locked = vr.IsLockedByStatement;
  4762. vr.IsLockedByStatement = true;
  4763. }
  4764. } else {
  4765. if (decl.IsNested) {
  4766. decl.ResolveDeclaratorInitializer (ec);
  4767. } else {
  4768. if (!decl.Resolve (ec))
  4769. return false;
  4770. if (decl.Declarators != null) {
  4771. stmt = decl.RewriteUsingDeclarators (ec, stmt);
  4772. }
  4773. }
  4774. vr = null;
  4775. }
  4776. }
  4777. ec.StartFlowBranching (this);
  4778. stmt.Resolve (ec);
  4779. ec.EndFlowBranching ();
  4780. if (vr != null)
  4781. vr.IsLockedByStatement = vr_locked;
  4782. base.Resolve (ec);
  4783. return true;
  4784. }
  4785. protected override void CloneTo (CloneContext clonectx, Statement t)
  4786. {
  4787. Using target = (Using) t;
  4788. target.decl = (VariableDeclaration) decl.Clone (clonectx);
  4789. target.stmt = stmt.Clone (clonectx);
  4790. }
  4791. public override object Accept (StructuralVisitor visitor)
  4792. {
  4793. return visitor.Visit (this);
  4794. }
  4795. }
  4796. /// <summary>
  4797. /// Implementation of the foreach C# statement
  4798. /// </summary>
  4799. public class Foreach : Statement
  4800. {
  4801. abstract class IteratorStatement : Statement
  4802. {
  4803. protected readonly Foreach for_each;
  4804. protected IteratorStatement (Foreach @foreach)
  4805. {
  4806. this.for_each = @foreach;
  4807. this.loc = @foreach.expr.Location;
  4808. }
  4809. protected override void CloneTo (CloneContext clonectx, Statement target)
  4810. {
  4811. throw new NotImplementedException ();
  4812. }
  4813. public override void Emit (EmitContext ec)
  4814. {
  4815. if (ec.EmitAccurateDebugInfo) {
  4816. ec.Emit (OpCodes.Nop);
  4817. }
  4818. base.Emit (ec);
  4819. }
  4820. }
  4821. sealed class ArrayForeach : IteratorStatement
  4822. {
  4823. TemporaryVariableReference[] lengths;
  4824. Expression [] length_exprs;
  4825. StatementExpression[] counter;
  4826. TemporaryVariableReference[] variables;
  4827. TemporaryVariableReference copy;
  4828. public ArrayForeach (Foreach @foreach, int rank)
  4829. : base (@foreach)
  4830. {
  4831. counter = new StatementExpression[rank];
  4832. variables = new TemporaryVariableReference[rank];
  4833. length_exprs = new Expression [rank];
  4834. //
  4835. // Only use temporary length variables when dealing with
  4836. // multi-dimensional arrays
  4837. //
  4838. if (rank > 1)
  4839. lengths = new TemporaryVariableReference [rank];
  4840. }
  4841. public override bool Resolve (BlockContext ec)
  4842. {
  4843. Block variables_block = for_each.variable.Block;
  4844. copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
  4845. copy.Resolve (ec);
  4846. int rank = length_exprs.Length;
  4847. Arguments list = new Arguments (rank);
  4848. for (int i = 0; i < rank; i++) {
  4849. var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
  4850. variables[i] = v;
  4851. counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
  4852. counter[i].Resolve (ec);
  4853. if (rank == 1) {
  4854. length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
  4855. } else {
  4856. lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
  4857. lengths[i].Resolve (ec);
  4858. Arguments args = new Arguments (1);
  4859. args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
  4860. length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
  4861. }
  4862. list.Add (new Argument (v));
  4863. }
  4864. var access = new ElementAccess (copy, list, loc).Resolve (ec);
  4865. if (access == null)
  4866. return false;
  4867. TypeSpec var_type;
  4868. if (for_each.type is VarExpr) {
  4869. // Infer implicitly typed local variable from foreach array type
  4870. var_type = access.Type;
  4871. } else {
  4872. var_type = for_each.type.ResolveAsType (ec);
  4873. if (var_type == null)
  4874. return false;
  4875. access = Convert.ExplicitConversion (ec, access, var_type, loc);
  4876. if (access == null)
  4877. return false;
  4878. }
  4879. for_each.variable.Type = var_type;
  4880. var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
  4881. if (variable_ref == null)
  4882. return false;
  4883. for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
  4884. bool ok = true;
  4885. ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
  4886. ec.CurrentBranching.CreateSibling ();
  4887. ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
  4888. if (!for_each.body.Resolve (ec))
  4889. ok = false;
  4890. ec.EndFlowBranching ();
  4891. // There's no direct control flow from the end of the embedded statement to the end of the loop
  4892. ec.CurrentBranching.CurrentUsageVector.Goto ();
  4893. ec.EndFlowBranching ();
  4894. return ok;
  4895. }
  4896. protected override void DoEmit (EmitContext ec)
  4897. {
  4898. copy.EmitAssign (ec, for_each.expr);
  4899. int rank = length_exprs.Length;
  4900. Label[] test = new Label [rank];
  4901. Label[] loop = new Label [rank];
  4902. for (int i = 0; i < rank; i++) {
  4903. test [i] = ec.DefineLabel ();
  4904. loop [i] = ec.DefineLabel ();
  4905. if (lengths != null)
  4906. lengths [i].EmitAssign (ec, length_exprs [i]);
  4907. }
  4908. IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
  4909. for (int i = 0; i < rank; i++) {
  4910. variables [i].EmitAssign (ec, zero);
  4911. ec.Emit (OpCodes.Br, test [i]);
  4912. ec.MarkLabel (loop [i]);
  4913. }
  4914. for_each.body.Emit (ec);
  4915. ec.MarkLabel (ec.LoopBegin);
  4916. ec.Mark (for_each.expr.Location);
  4917. for (int i = rank - 1; i >= 0; i--){
  4918. counter [i].Emit (ec);
  4919. ec.MarkLabel (test [i]);
  4920. variables [i].Emit (ec);
  4921. if (lengths != null)
  4922. lengths [i].Emit (ec);
  4923. else
  4924. length_exprs [i].Emit (ec);
  4925. ec.Emit (OpCodes.Blt, loop [i]);
  4926. }
  4927. ec.MarkLabel (ec.LoopEnd);
  4928. }
  4929. }
  4930. sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
  4931. {
  4932. class RuntimeDispose : Using.VariableDeclaration
  4933. {
  4934. public RuntimeDispose (LocalVariable lv, Location loc)
  4935. : base (lv, loc)
  4936. {
  4937. }
  4938. protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
  4939. {
  4940. // Defered to runtime check
  4941. }
  4942. protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
  4943. {
  4944. var idt = bc.BuiltinTypes.IDisposable;
  4945. //
  4946. // Fabricates code like
  4947. //
  4948. // if ((temp = vr as IDisposable) != null) temp.Dispose ();
  4949. //
  4950. var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
  4951. var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
  4952. dispose_variable.CreateReferenceExpression (bc, loc),
  4953. new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
  4954. loc), new NullLiteral (loc));
  4955. var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
  4956. var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
  4957. dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
  4958. Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
  4959. return new If (idisaposable_test, dispose, loc);
  4960. }
  4961. }
  4962. LocalVariable variable;
  4963. Expression expr;
  4964. Statement statement;
  4965. ExpressionStatement init;
  4966. TemporaryVariableReference enumerator_variable;
  4967. bool ambiguous_getenumerator_name;
  4968. public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
  4969. : base (@foreach)
  4970. {
  4971. this.variable = var;
  4972. this.expr = expr;
  4973. }
  4974. void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
  4975. {
  4976. rc.Report.SymbolRelatedToPreviousError (enumerator);
  4977. rc.Report.Error (202, loc,
  4978. "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
  4979. enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
  4980. }
  4981. MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
  4982. {
  4983. //
  4984. // Option 1: Try to match by name GetEnumerator first
  4985. //
  4986. var mexpr = Expression.MemberLookup (rc, false, expr.Type,
  4987. "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
  4988. var mg = mexpr as MethodGroupExpr;
  4989. if (mg != null) {
  4990. mg.InstanceExpression = expr;
  4991. Arguments args = new Arguments (0);
  4992. mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
  4993. // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
  4994. if (ambiguous_getenumerator_name)
  4995. mg = null;
  4996. if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
  4997. return mg;
  4998. }
  4999. }
  5000. //
  5001. // Option 2: Try to match using IEnumerable interfaces with preference of generic version
  5002. //
  5003. var t = expr.Type;
  5004. PredefinedMember<MethodSpec> iface_candidate = null;
  5005. var ptypes = rc.Module.PredefinedTypes;
  5006. var gen_ienumerable = ptypes.IEnumerableGeneric;
  5007. if (!gen_ienumerable.Define ())
  5008. gen_ienumerable = null;
  5009. var ifaces = t.Interfaces;
  5010. if (ifaces != null) {
  5011. foreach (var iface in ifaces) {
  5012. if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
  5013. if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
  5014. rc.Report.SymbolRelatedToPreviousError (expr.Type);
  5015. rc.Report.Error (1640, loc,
  5016. "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
  5017. expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
  5018. return null;
  5019. }
  5020. // TODO: Cache this somehow
  5021. iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
  5022. MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
  5023. continue;
  5024. }
  5025. if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
  5026. iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
  5027. }
  5028. }
  5029. }
  5030. if (iface_candidate == null) {
  5031. if (expr.Type != InternalType.ErrorType) {
  5032. rc.Report.Error (1579, loc,
  5033. "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
  5034. expr.Type.GetSignatureForError (), "GetEnumerator");
  5035. }
  5036. return null;
  5037. }
  5038. var method = iface_candidate.Resolve (loc);
  5039. if (method == null)
  5040. return null;
  5041. mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
  5042. mg.InstanceExpression = expr;
  5043. return mg;
  5044. }
  5045. MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
  5046. {
  5047. var ms = MemberCache.FindMember (enumerator.ReturnType,
  5048. MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
  5049. BindingRestriction.InstanceOnly) as MethodSpec;
  5050. if (ms == null || !ms.IsPublic) {
  5051. Error_WrongEnumerator (rc, enumerator);
  5052. return null;
  5053. }
  5054. return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
  5055. }
  5056. PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
  5057. {
  5058. var ps = MemberCache.FindMember (enumerator.ReturnType,
  5059. MemberFilter.Property ("Current", null),
  5060. BindingRestriction.InstanceOnly) as PropertySpec;
  5061. if (ps == null || !ps.IsPublic) {
  5062. Error_WrongEnumerator (rc, enumerator);
  5063. return null;
  5064. }
  5065. return ps;
  5066. }
  5067. public override bool Resolve (BlockContext ec)
  5068. {
  5069. bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
  5070. if (is_dynamic) {
  5071. expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
  5072. } else if (expr.Type.IsNullableType) {
  5073. expr = new Nullable.UnwrapCall (expr).Resolve (ec);
  5074. }
  5075. var get_enumerator_mg = ResolveGetEnumerator (ec);
  5076. if (get_enumerator_mg == null) {
  5077. return false;
  5078. }
  5079. var get_enumerator = get_enumerator_mg.BestCandidate;
  5080. enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
  5081. enumerator_variable.Resolve (ec);
  5082. // Prepare bool MoveNext ()
  5083. var move_next_mg = ResolveMoveNext (ec, get_enumerator);
  5084. if (move_next_mg == null) {
  5085. return false;
  5086. }
  5087. move_next_mg.InstanceExpression = enumerator_variable;
  5088. // Prepare ~T~ Current { get; }
  5089. var current_prop = ResolveCurrent (ec, get_enumerator);
  5090. if (current_prop == null) {
  5091. return false;
  5092. }
  5093. var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
  5094. if (current_pe == null)
  5095. return false;
  5096. VarExpr ve = for_each.type as VarExpr;
  5097. if (ve != null) {
  5098. if (is_dynamic) {
  5099. // Source type is dynamic, set element type to dynamic too
  5100. variable.Type = ec.BuiltinTypes.Dynamic;
  5101. } else {
  5102. // Infer implicitly typed local variable from foreach enumerable type
  5103. variable.Type = current_pe.Type;
  5104. }
  5105. } else {
  5106. if (is_dynamic) {
  5107. // Explicit cast of dynamic collection elements has to be done at runtime
  5108. current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
  5109. }
  5110. variable.Type = for_each.type.ResolveAsType (ec);
  5111. if (variable.Type == null)
  5112. return false;
  5113. current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
  5114. if (current_pe == null)
  5115. return false;
  5116. }
  5117. var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
  5118. if (variable_ref == null)
  5119. return false;
  5120. for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
  5121. var init = new Invocation (get_enumerator_mg, null);
  5122. statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
  5123. for_each.body, Location.Null);
  5124. var enum_type = enumerator_variable.Type;
  5125. //
  5126. // Add Dispose method call when enumerator can be IDisposable
  5127. //
  5128. if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
  5129. if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
  5130. //
  5131. // Runtime Dispose check
  5132. //
  5133. var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
  5134. vd.Initializer = init;
  5135. statement = new Using (vd, statement, Location.Null);
  5136. } else {
  5137. //
  5138. // No Dispose call needed
  5139. //
  5140. this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
  5141. this.init.Resolve (ec);
  5142. }
  5143. } else {
  5144. //
  5145. // Static Dispose check
  5146. //
  5147. var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
  5148. vd.Initializer = init;
  5149. statement = new Using (vd, statement, Location.Null);
  5150. }
  5151. return statement.Resolve (ec);
  5152. }
  5153. protected override void DoEmit (EmitContext ec)
  5154. {
  5155. enumerator_variable.LocalInfo.CreateBuilder (ec);
  5156. if (init != null)
  5157. init.EmitStatement (ec);
  5158. statement.Emit (ec);
  5159. }
  5160. #region IErrorHandler Members
  5161. bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
  5162. {
  5163. ec.Report.SymbolRelatedToPreviousError (best);
  5164. ec.Report.Warning (278, 2, expr.Location,
  5165. "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
  5166. expr.Type.GetSignatureForError (), "enumerable",
  5167. best.GetSignatureForError (), ambiguous.GetSignatureForError ());
  5168. ambiguous_getenumerator_name = true;
  5169. return true;
  5170. }
  5171. bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
  5172. {
  5173. return false;
  5174. }
  5175. bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
  5176. {
  5177. return false;
  5178. }
  5179. bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
  5180. {
  5181. return false;
  5182. }
  5183. #endregion
  5184. }
  5185. Expression type;
  5186. LocalVariable variable;
  5187. Expression expr;
  5188. Statement statement;
  5189. Block body;
  5190. public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
  5191. {
  5192. this.type = type;
  5193. this.variable = var;
  5194. this.expr = expr;
  5195. this.statement = stmt;
  5196. this.body = body;
  5197. loc = l;
  5198. }
  5199. public Expression Expr {
  5200. get { return expr; }
  5201. }
  5202. public Statement Statement {
  5203. get { return statement; }
  5204. }
  5205. public Expression TypeExpression {
  5206. get { return type; }
  5207. }
  5208. public LocalVariable Variable {
  5209. get { return variable; }
  5210. }
  5211. public override bool Resolve (BlockContext ec)
  5212. {
  5213. expr = expr.Resolve (ec);
  5214. if (expr == null)
  5215. return false;
  5216. if (expr.IsNull) {
  5217. ec.Report.Error (186, loc, "Use of null is not valid in this context");
  5218. return false;
  5219. }
  5220. body.AddStatement (statement);
  5221. if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
  5222. statement = new ArrayForeach (this, 1);
  5223. } else if (expr.Type is ArrayContainer) {
  5224. statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
  5225. } else {
  5226. if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
  5227. ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
  5228. expr.ExprClassName);
  5229. return false;
  5230. }
  5231. statement = new CollectionForeach (this, variable, expr);
  5232. }
  5233. return statement.Resolve (ec);
  5234. }
  5235. protected override void DoEmit (EmitContext ec)
  5236. {
  5237. variable.CreateBuilder (ec);
  5238. Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
  5239. ec.LoopBegin = ec.DefineLabel ();
  5240. ec.LoopEnd = ec.DefineLabel ();
  5241. statement.Emit (ec);
  5242. ec.LoopBegin = old_begin;
  5243. ec.LoopEnd = old_end;
  5244. }
  5245. protected override void CloneTo (CloneContext clonectx, Statement t)
  5246. {
  5247. Foreach target = (Foreach) t;
  5248. target.type = type.Clone (clonectx);
  5249. target.expr = expr.Clone (clonectx);
  5250. target.body = (Block) body.Clone (clonectx);
  5251. target.statement = statement.Clone (clonectx);
  5252. }
  5253. public override object Accept (StructuralVisitor visitor)
  5254. {
  5255. return visitor.Visit (this);
  5256. }
  5257. }
  5258. }