frontend: handle multi-iterators
[nit.git] / src / semantize / flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Intraprocedural static flow.
18 module flow
19
20 import scope
21
22 redef class ToolContext
23 # Run `APropdef::do_flow` on each propdef
24 var flow_phase: Phase = new FlowPhase(self, [scope_phase])
25 end
26
27 private class FlowPhase
28 super Phase
29
30 redef fun process_npropdef(npropdef) do npropdef.do_flow(toolcontext)
31 end
32
33 # The visitor that determine flowcontext for nodes
34 private class FlowVisitor
35 super Visitor
36
37 var current_flow_context = new FlowContext
38
39 var toolcontext: ToolContext
40
41 init
42 do
43 flows.add(current_flow_context)
44 current_flow_context.is_start = true
45 end
46
47 var first: nullable ANode = null
48
49 redef fun visit(node)
50 do
51 if first == null then first = node
52
53 if current_flow_context.node == null then current_flow_context.node = node
54 node.accept_flow_visitor(self)
55 if node isa AExpr then
56 var flow = self.current_flow_context
57 node.after_flow_context = flow
58 # Force the creation of a specific merge after the analysis of the node.
59 if flow.when_true != flow or flow.when_false != flow then
60 self.make_sub_flow
61 self.current_flow_context.name = "AUTOSUB"
62 end
63 end
64
65 if first == node then
66 #self.printflow
67 end
68 end
69
70 fun visit_expr(node: AExpr): FlowContext
71 do
72 self.enter_visit(node)
73 return node.after_flow_context.as(not null)
74 end
75
76 var flows = new Array[FlowContext]
77
78 fun printflow
79 do
80 var file = new FileWriter.open("flow.dot")
81 file.write("digraph \{\nnode[shape=box];")
82 for f in flows do
83 var s = ""
84 if f.node isa AExpr then
85 s = "\\nmain={f.node.as(AExpr).after_flow_context.object_id}"
86 end
87 file.write "F{f.object_id} [label=\"{f.object_id}\\n{f.node.location}\\n{f.node.class_name}\\n{f.name}{s}\"];\n"
88 for p in f.previous do
89 s = ""
90 if f.when_true == p then s = "[label=TRUE, style=dotted]"
91 if f.when_false == p then s = "[label=FALSE, style=dotted]"
92 if f.when_true == p and f.when_false == p then s = "[label=TRUE_FALSE, style=dotted]"
93 file.write "F{p.object_id} -> F{f.object_id}{s};\n"
94 end
95 for p in f.loops do
96 file.write "F{p.object_id} -> F{f.object_id}[label=LOOP, style=dashed, constraint=false];\n"
97 end
98 end
99 file.write("\}\n")
100 file.close
101 end
102
103
104 fun make_sub_flow: FlowContext
105 do
106 var flow = new FlowContext
107 flows.add(flow)
108 flow.node = current_node
109 flow.add_previous(self.current_flow_context)
110 self.current_flow_context = flow
111 return flow
112 end
113
114 fun make_merge_flow(flow1, flow2: FlowContext): FlowContext
115 do
116 var flow = new FlowContext
117 flows.add(flow)
118 flow.node = current_node
119 flow.add_previous(flow1)
120 flow.add_previous(flow2)
121 self.current_flow_context = flow
122 return flow
123 end
124
125 fun make_true_false_flow(true_flow, false_flow: FlowContext): FlowContext
126 do
127 var flow = new FlowContext
128 flows.add(flow)
129 flow.node = current_node
130 flow.add_previous(true_flow)
131 flow.add_previous(false_flow)
132 flow.when_true = true_flow
133 flow.when_false = false_flow
134 self.current_flow_context = flow
135 return flow
136 end
137
138 fun make_sub_true_false_flow: FlowContext
139 do
140 var orig_flow = self.current_flow_context
141 var true_flow = new FlowContext
142 flows.add(true_flow)
143 true_flow.node = current_node
144 true_flow.add_previous(orig_flow)
145 true_flow.name = "TRUE"
146 var false_flow = new FlowContext
147 flows.add(false_flow)
148 false_flow.node = current_node
149 false_flow.add_previous(orig_flow)
150 false_flow.name = "FALSE"
151 return make_true_false_flow(true_flow, false_flow)
152 end
153
154 fun make_unreachable_flow: FlowContext
155 do
156 var flow = new FlowContext
157 flows.add(flow)
158 flow.node = current_node
159 flow.add_previous(self.current_flow_context)
160 flow.is_marked_unreachable = true
161 self.current_flow_context = flow
162 return flow
163 end
164
165 fun merge_continues_to(before_loop: FlowContext, escapemark: nullable EscapeMark)
166 do
167 if escapemark == null then return
168 for b in escapemark.escapes do
169 var before = b.before_flow_context
170 if before == null then continue # Forward error
171 before_loop.add_loop(before)
172 end
173 end
174
175 fun merge_breaks(escapemark: nullable EscapeMark)
176 do
177 if escapemark == null then return
178 for b in escapemark.escapes do
179 var before = b.before_flow_context
180 if before == null then continue # Forward error
181 self.make_merge_flow(self.current_flow_context, before)
182 end
183 end
184 end
185
186 # A Node in the static flow graph.
187 # A same `FlowContext` can be shared by more than one `ANode`.
188 class FlowContext
189 # The reachable previous flow
190 var previous = new Array[FlowContext]
191
192 # Additional reachable flow that loop up to self.
193 # Loops appears in `loop`, `while`, `for`, and with `continue`
194 var loops = new Array[FlowContext]
195
196 private var is_marked_unreachable: Bool = false
197
198 # Is the flow dead?
199 fun is_unreachable: Bool
200 do
201 # Are we explicitly marked unreachable?
202 if self.is_marked_unreachable then return true
203
204 # Are we the starting flow context?
205 if is_start then return false
206
207 # De we have a reachable previous?
208 if previous.length == 0 then return true
209 return false
210 end
211
212 # Flag to avoid repeated errors
213 var is_already_unreachable: Bool = false
214
215 # Mark that self is the starting flow context.
216 # Such a context is reachable even if there is no previous flow
217 var is_start: Bool = false
218
219 # The node that introduce the flow (for debugging)
220 var node: nullable ANode = null
221
222 # Additional information for the flow (for debugging)
223 var name: String = ""
224
225 # The sub-flow to use if the associated expr is true
226 var when_true: FlowContext = self
227
228 # The sub-flow to use if the associated expr is true
229 var when_false: FlowContext = self
230
231 # Add a previous flow (iff it is reachable)
232 private fun add_previous(flow: FlowContext)
233 do
234 if not flow.is_unreachable and not previous.has(flow) then
235 previous.add(flow)
236 end
237 end
238
239 # Add a previous loop flow (iff it is reachable)
240 private fun add_loop(flow: FlowContext)
241 do
242 if not flow.is_unreachable and not previous.has(flow) then
243 loops.add(flow)
244 end
245 end
246
247 end
248
249 redef class ANode
250 private fun accept_flow_visitor(v: FlowVisitor)
251 do
252 self.visit_all(v)
253 end
254 end
255
256 redef class APropdef
257 # The entry point of the whole flow analysis
258 fun do_flow(toolcontext: ToolContext)
259 do
260 var v = new FlowVisitor(toolcontext)
261 v.enter_visit(self)
262 end
263
264
265 # The starting flow
266 var before_flow_context: nullable FlowContext
267
268 # The ending flow
269 var after_flow_context: nullable FlowContext
270
271 redef fun accept_flow_visitor(v)
272 do
273 self.before_flow_context = v.current_flow_context
274 super
275 self.after_flow_context = v.current_flow_context
276 end
277 end
278
279 redef class AExpr
280 # The flow after the full evaluation of the expression/statement
281 var after_flow_context: nullable FlowContext
282 end
283
284 redef class AVarAssignExpr
285 redef fun accept_flow_visitor(v)
286 do
287 super
288 self.after_flow_context = v.make_sub_flow
289 end
290 end
291
292 redef class AReassignFormExpr
293 redef fun accept_flow_visitor(v)
294 do
295 super
296 self.after_flow_context = v.make_sub_flow
297 end
298 end
299
300 redef class ABlockExpr
301 redef fun accept_flow_visitor(v)
302 do
303 for e in n_expr do
304 if not v.current_flow_context.is_unreachable then
305 v.enter_visit(e)
306 else if not v.current_flow_context.is_already_unreachable then
307 v.current_flow_context.is_already_unreachable = true
308 v.toolcontext.error(e.hot_location, "Error: unreachable statement.")
309 end
310 end
311 end
312 end
313
314 redef class AReturnExpr
315 redef fun accept_flow_visitor(v)
316 do
317 super
318 v.make_unreachable_flow
319 end
320 end
321
322 redef class AEscapeExpr
323 # The flow just before it become unreachable
324 fun before_flow_context: nullable FlowContext
325 do
326 var after = self.after_flow_context
327 if after == null then return null
328 return after.previous.first
329 end
330 redef fun accept_flow_visitor(v)
331 do
332 super
333 v.make_unreachable_flow
334 end
335 end
336
337 redef class AAbortExpr
338 redef fun accept_flow_visitor(v)
339 do
340 super
341 v.make_unreachable_flow
342 end
343 end
344
345 redef class ADoExpr
346 redef fun accept_flow_visitor(v)
347 do
348 super
349 v.merge_breaks(self.break_mark)
350 end
351 end
352
353 redef class AIfExpr
354 redef fun accept_flow_visitor(v)
355 do
356 var after_expr = v.visit_expr(self.n_expr)
357
358 v.current_flow_context = after_expr.when_true
359 v.enter_visit(self.n_then)
360 var after_then = v.current_flow_context
361
362 v.current_flow_context = after_expr.when_false
363 v.enter_visit(self.n_else)
364 var after_else = v.current_flow_context
365
366 v.make_merge_flow(after_then, after_else)
367 end
368 end
369
370 redef class AIfexprExpr
371 redef fun accept_flow_visitor(v)
372 do
373 var after_expr = v.visit_expr(self.n_expr)
374
375 v.current_flow_context = after_expr.when_true
376 v.enter_visit(self.n_then)
377 var after_then = v.current_flow_context
378
379 v.current_flow_context = after_expr.when_false
380 v.enter_visit(self.n_else)
381 var after_else = v.current_flow_context
382
383 v.make_merge_flow(after_then, after_else)
384 end
385 end
386
387 redef class AWhileExpr
388 redef fun accept_flow_visitor(v)
389 do
390 var before_loop = v.make_sub_flow
391
392 var after_expr = v.visit_expr(self.n_expr)
393
394 v.current_flow_context = after_expr.when_true
395 v.enter_visit(self.n_block)
396 var after_block = v.current_flow_context
397
398 before_loop.add_loop(after_block)
399 v.merge_continues_to(before_loop, self.continue_mark)
400
401 v.current_flow_context = after_expr.when_false
402 v.merge_breaks(self.break_mark)
403 end
404 end
405
406 redef class ALoopExpr
407 redef fun accept_flow_visitor(v)
408 do
409 var before_loop = v.make_sub_flow
410
411 v.enter_visit(self.n_block)
412
413 var after_block = v.current_flow_context
414
415 before_loop.add_loop(after_block)
416 v.merge_continues_to(before_loop, self.continue_mark)
417
418 v.make_unreachable_flow
419 v.merge_breaks(self.break_mark)
420 end
421 end
422
423 redef class AForExpr
424 redef fun accept_flow_visitor(v)
425 do
426 for g in n_groups do
427 v.enter_visit(g.n_expr)
428 end
429
430 var before_loop = v.make_sub_flow
431
432 v.enter_visit(self.n_block)
433
434 var after_block = v.current_flow_context
435
436 before_loop.add_loop(after_block)
437 v.merge_continues_to(before_loop, self.continue_mark)
438
439 v.make_merge_flow(v.current_flow_context, before_loop)
440 v.merge_breaks(self.break_mark)
441 end
442 end
443
444 redef class AWithExpr
445 redef fun accept_flow_visitor(v)
446 do
447 super
448 v.merge_breaks(self.break_mark)
449 end
450 end
451
452 redef class AAssertExpr
453 redef fun accept_flow_visitor(v)
454 do
455 var after_expr = v.visit_expr(self.n_expr)
456
457 v.current_flow_context = after_expr.when_false
458 v.enter_visit(n_else)
459 # the after context of n_else is a dead end, so we do not care
460
461 v.current_flow_context = after_expr.when_true
462 end
463 end
464
465 redef class AOrExpr
466 redef fun accept_flow_visitor(v)
467 do
468 var after_expr = v.visit_expr(self.n_expr)
469
470 v.current_flow_context = after_expr.when_false
471 var after_expr2 = v.visit_expr(self.n_expr2)
472
473 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
474 merge_true.name = "OR TRUE"
475
476 v.make_true_false_flow(merge_true, after_expr2.when_false)
477 end
478 end
479
480 redef class AImpliesExpr
481 redef fun accept_flow_visitor(v)
482 do
483 var after_expr = v.visit_expr(self.n_expr)
484
485 v.current_flow_context = after_expr.when_true
486 var after_expr2 = v.visit_expr(self.n_expr2)
487
488 var merge_true = v.make_merge_flow(after_expr.when_false, after_expr2.when_true)
489 merge_true.name = "OR TRUE"
490
491 v.make_true_false_flow(merge_true, after_expr2.when_false)
492 end
493 end
494
495 redef class AAndExpr
496 redef fun accept_flow_visitor(v)
497 do
498 var after_expr = v.visit_expr(self.n_expr)
499
500 v.current_flow_context = after_expr.when_true
501 var after_expr2 = v.visit_expr(self.n_expr2)
502
503 var merge_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
504 merge_false.name = "AND FALSE"
505
506 v.make_true_false_flow(after_expr2.when_true, merge_false)
507 end
508 end
509
510 redef class ANotExpr
511 redef fun accept_flow_visitor(v)
512 do
513 var after_expr = v.visit_expr(self.n_expr)
514
515 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
516 end
517 end
518
519 redef class AOrElseExpr
520 redef fun accept_flow_visitor(v)
521 do
522 super
523 end
524 end
525
526 redef class AEqExpr
527 redef fun accept_flow_visitor(v)
528 do
529 super
530 v.make_sub_true_false_flow
531 end
532 end
533
534
535 redef class ANeExpr
536 redef fun accept_flow_visitor(v)
537 do
538 super
539 v.make_sub_true_false_flow
540 end
541 end
542
543 redef class AIsaExpr
544 redef fun accept_flow_visitor(v)
545 do
546 super
547 v.make_sub_true_false_flow
548 end
549 end
550
551 redef class AParExpr
552 redef fun accept_flow_visitor(v)
553 do
554 var after_expr = v.visit_expr(self.n_expr)
555 v.current_flow_context = after_expr
556 end
557 end
558
559 redef class AOnceExpr
560 redef fun accept_flow_visitor(v)
561 do
562 var after_expr = v.visit_expr(self.n_expr)
563 v.current_flow_context = after_expr
564 end
565 end