+ var escapemark = self.break_mark
+ assert escapemark != null
+
+ # Main block that will contain the whole for and will replace `self`
+ var nblock = v.builder.make_block
+
+ # Part before the loop
+ var before = v.builder.make_block
+ nblock.add before
+
+ # The loop
+ var nloop = v.builder.make_loop
+ nloop.break_mark = escapemark
+ nblock.add nloop
+
+ # Part before the body inside the loop
+ var begin = v.builder.make_block
+ nloop.add begin
+
+ # The `do` block with then user code
+ var ndo = v.builder.make_do
+ ndo.break_mark = escapemark.continue_mark
+ nloop.add ndo
+
+ ndo.add self.n_block.as(not null)
+
+ # Fill up each part
+ for g in n_groups do
+ g.transform_in(v, before, begin, nloop, nblock, escapemark)
+ end
+
+ replace_with(nblock)
+ end
+end
+
+redef class AForGroup
+ private fun transform_in(v: TransformVisitor, before, begin, next, finish: AExpr, escapemark: EscapeMark)
+ do
+ var nexpr = n_expr
+
+ # Shortcut on explicit range
+ # Avoid the instantiation of the range and the iterator
+ if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
+ # Before: evaluate bounds
+ var variable = variables.first
+ before.add v.builder.make_var_assign(variable, nexpr.n_expr)
+ var to = nexpr.n_expr2
+ before.add to
+
+ # Begin: check variable
+ var is_ok = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_lt.as(not null), [to.make_var_read])
+ var nif = v.builder.make_if(is_ok, null)
+ begin.add nif
+ nif.n_else.add v.builder.make_break(escapemark)
+
+ # Next: increment one
+ var one = v.builder.make_int(1)
+ var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
+ next.add v.builder.make_var_assign(variable, succ)
+ return
+ end
+
+ # Before: evaluate expr, make the iterator
+ before.add nexpr
+ var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
+ before.add iter
+
+ # Begin: check iterator `is_ok`
+ var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
+ var nif = v.builder.make_if(is_ok, null)
+ begin.add nif
+ nif.n_else.add v.builder.make_break(escapemark)
+
+ # Begin: assign automatic variables
+ if variables.length == 1 then
+ var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
+ begin.add v.builder.make_var_assign(variables.first, item)
+ else if variables.length == 2 then
+ var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
+ begin.add v.builder.make_var_assign(variables[0], key)
+ var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
+ begin.add v.builder.make_var_assign(variables[1], item)
+ else
+ abort
+ end
+
+ # Next: call next
+ next.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
+
+ # Finish: call finish
+ var method_finish = method_finish
+ if method_finish != null then
+ finish.add v.builder.make_call(iter.make_var_read, method_finish, null)
+ end
+ end
+end
+
+redef class AWithExpr
+ # is replaced with a do/end and injected calls to `start` and `finish`
+ #
+ # Basically, the following
+ #
+ # ~~~nitish
+ # with expr do
+ # block
+ # end label l
+ # ~~~
+ #
+ # is transformed into
+ #
+ # ~~~nitish
+ # var x = expr
+ # do
+ # x.start
+ # block
+ # end label l
+ # x.finish
+ # ~~~
+ #
+ # The point is that `finish` is called even if the block is escaped.
+ redef fun accept_transform_visitor(v)
+ do
+ var escapemark = self.break_mark
+ assert escapemark != null
+
+ var nblock = v.builder.make_block
+
+ var nexpr = n_expr
+
+ nblock.add nexpr
+
+ var ndo = v.builder.make_do
+ ndo.break_mark = escapemark
+
+ var start = v.builder.make_call(nexpr.make_var_read, method_start.as(not null), null)
+
+ ndo.add start
+
+ ndo.add self.n_block.as(not null)
+
+ nblock.add ndo
+
+ nblock.add v.builder.make_call(nexpr.make_var_read, method_finish.as(not null), null)
+
+ replace_with(nblock)