src: create groups for related things
[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 var flow_phase: Phase = new FlowPhase(self, [scope_phase])
24 end
25
26 private class FlowPhase
27 super Phase
28
29 redef fun process_npropdef(npropdef) do npropdef.do_flow(toolcontext)
30 end
31
32 # The visitor that derermine flowcontext for nodes
33 private class FlowVisitor
34 super Visitor
35
36 var current_flow_context: FlowContext
37
38 var toolcontext: ToolContext
39
40 init(toolcontext: ToolContext)
41 do
42 self.toolcontext = toolcontext
43 current_flow_context = new FlowContext
44 flows.add(current_flow_context)
45 current_flow_context.is_start = true
46 end
47
48 var first: nullable ANode
49
50 redef fun visit(node)
51 do
52 if first == null then first = node
53
54 if current_flow_context.node == null then current_flow_context.node = node
55 node.accept_flow_visitor(self)
56 if node isa AExpr then
57 var flow = self.current_flow_context
58 node.after_flow_context = flow
59 # Force the creation of a specific merge after the analysis of the node.
60 if flow.when_true != flow or flow.when_false != flow then
61 self.make_sub_flow
62 self.current_flow_context.name = "AUTOSUB"
63 end
64 end
65
66 if first == node then
67 #self.printflow
68 end
69 end
70
71 fun visit_expr(node: AExpr): FlowContext
72 do
73 self.enter_visit(node)
74 return node.after_flow_context.as(not null)
75 end
76
77 var flows: Array[FlowContext] = new Array[FlowContext]
78
79 fun printflow
80 do
81 var file = new OFStream.open("flow.dot")
82 file.write("digraph \{\n")
83 for f in flows do
84 var s = ""
85 if f.node isa AExpr then
86 s = "\\nmain={f.node.as(AExpr).after_flow_context.object_id}"
87 end
88 file.write "F{f.object_id} [label=\"{f.object_id}\\n{f.node.location}\\n{f.node.class_name}\\n{f.name}{s}\"];\n"
89 for p in f.previous do
90 file.write "F{p.object_id} -> F{f.object_id};\n"
91 end
92 if f.when_true != f then
93 file.write "F{f.object_id} -> F{f.when_true.object_id}[label=TRUE, style=dotted];\n"
94 end
95 if f.when_false != f then
96 file.write "F{f.object_id} -> F{f.when_false.object_id}[label=FALSE,style=dotted];\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.continues 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.breaks 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: Array[FlowContext] = new Array[FlowContext]
191
192 # Additional reachable flow that loop up to self.
193 # Loops apears in `loop`, `while`, `for`, and with `continue`
194 var loops: Array[FlowContext] = 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 explicitely 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 repeaed 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 debuging)
220 var node: nullable ANode = null
221
222 # Additional information for the flor (for debuging)
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 AContinueExpr
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 ABreakExpr
338 # The flow just before it become unreachable
339 fun before_flow_context: nullable FlowContext
340 do
341 var after = self.after_flow_context
342 if after == null then return null
343 return after.previous.first
344 end
345 redef fun accept_flow_visitor(v)
346 do
347 super
348 v.make_unreachable_flow
349 end
350 end
351
352 redef class AAbortExpr
353 redef fun accept_flow_visitor(v)
354 do
355 super
356 v.make_unreachable_flow
357 end
358 end
359
360 redef class ADoExpr
361 redef fun accept_flow_visitor(v)
362 do
363 super
364 v.merge_breaks(self.escapemark)
365 end
366 end
367
368 redef class AIfExpr
369 redef fun accept_flow_visitor(v)
370 do
371 var after_expr = v.visit_expr(self.n_expr)
372
373 v.current_flow_context = after_expr.when_true
374 v.enter_visit(self.n_then)
375 var after_then = v.current_flow_context
376
377 v.current_flow_context = after_expr.when_false
378 v.enter_visit(self.n_else)
379 var after_else = v.current_flow_context
380
381 v.make_merge_flow(after_then, after_else)
382 end
383 end
384
385 redef class AIfexprExpr
386 redef fun accept_flow_visitor(v)
387 do
388 var after_expr = v.visit_expr(self.n_expr)
389
390 v.current_flow_context = after_expr.when_true
391 v.enter_visit(self.n_then)
392 var after_then = v.current_flow_context
393
394 v.current_flow_context = after_expr.when_false
395 v.enter_visit(self.n_else)
396 var after_else = v.current_flow_context
397
398 v.make_merge_flow(after_then, after_else)
399 end
400 end
401
402 redef class AWhileExpr
403 redef fun accept_flow_visitor(v)
404 do
405 var before_loop = v.make_sub_flow
406
407 var after_expr = v.visit_expr(self.n_expr)
408
409 v.current_flow_context = after_expr.when_true
410 v.enter_visit(self.n_block)
411 var after_block = v.current_flow_context
412
413 before_loop.add_loop(after_block)
414 v.merge_continues_to(after_block, self.escapemark)
415
416 v.current_flow_context = after_expr.when_false
417 v.merge_breaks(self.escapemark)
418 end
419 end
420
421 redef class ALoopExpr
422 redef fun accept_flow_visitor(v)
423 do
424 var before_loop = v.make_sub_flow
425
426 v.enter_visit(self.n_block)
427
428 var after_block = v.current_flow_context
429
430 before_loop.add_loop(after_block)
431 v.merge_continues_to(after_block, self.escapemark)
432
433 v.make_unreachable_flow
434 v.merge_breaks(self.escapemark)
435 end
436 end
437
438 redef class AForExpr
439 redef fun accept_flow_visitor(v)
440 do
441 v.enter_visit(self.n_expr)
442
443 var before_loop = v.make_sub_flow
444
445 v.enter_visit(self.n_block)
446
447 var after_block = v.current_flow_context
448
449 before_loop.add_loop(after_block)
450 v.merge_continues_to(after_block, self.escapemark)
451
452 v.make_merge_flow(v.current_flow_context, before_loop)
453 v.merge_breaks(self.escapemark)
454 end
455 end
456
457 redef class AAssertExpr
458 redef fun accept_flow_visitor(v)
459 do
460 var after_expr = v.visit_expr(self.n_expr)
461
462 v.current_flow_context = after_expr.when_false
463 v.enter_visit(n_else)
464 # the after context of n_else is a dead end, so we do not care
465
466 v.current_flow_context = after_expr.when_true
467 end
468 end
469
470 redef class AOrExpr
471 redef fun accept_flow_visitor(v)
472 do
473 var after_expr = v.visit_expr(self.n_expr)
474
475 v.current_flow_context = after_expr.when_false
476 var after_expr2 = v.visit_expr(self.n_expr2)
477
478 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
479 merge_true.name = "OR TRUE"
480
481 v.make_true_false_flow(merge_true, after_expr2.when_false)
482 end
483 end
484
485 redef class AImpliesExpr
486 redef fun accept_flow_visitor(v)
487 do
488 var after_expr = v.visit_expr(self.n_expr)
489
490 v.current_flow_context = after_expr.when_true
491 var after_expr2 = v.visit_expr(self.n_expr2)
492
493 var merge_true = v.make_merge_flow(after_expr.when_false, after_expr2.when_true)
494 merge_true.name = "OR TRUE"
495
496 v.make_true_false_flow(merge_true, after_expr2.when_false)
497 end
498 end
499
500 redef class AAndExpr
501 redef fun accept_flow_visitor(v)
502 do
503 var after_expr = v.visit_expr(self.n_expr)
504
505 v.current_flow_context = after_expr.when_true
506 var after_expr2 = v.visit_expr(self.n_expr2)
507
508 var merge_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
509 merge_false.name = "AND FALSE"
510
511 v.make_true_false_flow(after_expr2.when_true, merge_false)
512 end
513 end
514
515 redef class ANotExpr
516 redef fun accept_flow_visitor(v)
517 do
518 var after_expr = v.visit_expr(self.n_expr)
519
520 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
521 end
522 end
523
524 redef class AOrElseExpr
525 redef fun accept_flow_visitor(v)
526 do
527 super
528 end
529 end
530
531 redef class AEqExpr
532 redef fun accept_flow_visitor(v)
533 do
534 super
535 v.make_sub_true_false_flow
536 end
537 end
538
539
540 redef class ANeExpr
541 redef fun accept_flow_visitor(v)
542 do
543 super
544 v.make_sub_true_false_flow
545 end
546 end
547
548 redef class AIsaExpr
549 redef fun accept_flow_visitor(v)
550 do
551 super
552 v.make_sub_true_false_flow
553 end
554 end
555
556 redef class AProxyExpr
557 redef fun accept_flow_visitor(v)
558 do
559 var after_expr = v.visit_expr(self.n_expr)
560 v.current_flow_context = after_expr
561 end
562 end