b0e833aebad9044e068ba47e333df281423acacc
[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 v.enter_visit(self.n_expr)
427
428 var before_loop = v.make_sub_flow
429
430 v.enter_visit(self.n_block)
431
432 var after_block = v.current_flow_context
433
434 before_loop.add_loop(after_block)
435 v.merge_continues_to(before_loop, self.continue_mark)
436
437 v.make_merge_flow(v.current_flow_context, before_loop)
438 v.merge_breaks(self.break_mark)
439 end
440 end
441
442 redef class AWithExpr
443 redef fun accept_flow_visitor(v)
444 do
445 super
446 v.merge_breaks(self.break_mark)
447 end
448 end
449
450 redef class AAssertExpr
451 redef fun accept_flow_visitor(v)
452 do
453 var after_expr = v.visit_expr(self.n_expr)
454
455 v.current_flow_context = after_expr.when_false
456 v.enter_visit(n_else)
457 # the after context of n_else is a dead end, so we do not care
458
459 v.current_flow_context = after_expr.when_true
460 end
461 end
462
463 redef class AOrExpr
464 redef fun accept_flow_visitor(v)
465 do
466 var after_expr = v.visit_expr(self.n_expr)
467
468 v.current_flow_context = after_expr.when_false
469 var after_expr2 = v.visit_expr(self.n_expr2)
470
471 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
472 merge_true.name = "OR TRUE"
473
474 v.make_true_false_flow(merge_true, after_expr2.when_false)
475 end
476 end
477
478 redef class AImpliesExpr
479 redef fun accept_flow_visitor(v)
480 do
481 var after_expr = v.visit_expr(self.n_expr)
482
483 v.current_flow_context = after_expr.when_true
484 var after_expr2 = v.visit_expr(self.n_expr2)
485
486 var merge_true = v.make_merge_flow(after_expr.when_false, after_expr2.when_true)
487 merge_true.name = "OR TRUE"
488
489 v.make_true_false_flow(merge_true, after_expr2.when_false)
490 end
491 end
492
493 redef class AAndExpr
494 redef fun accept_flow_visitor(v)
495 do
496 var after_expr = v.visit_expr(self.n_expr)
497
498 v.current_flow_context = after_expr.when_true
499 var after_expr2 = v.visit_expr(self.n_expr2)
500
501 var merge_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
502 merge_false.name = "AND FALSE"
503
504 v.make_true_false_flow(after_expr2.when_true, merge_false)
505 end
506 end
507
508 redef class ANotExpr
509 redef fun accept_flow_visitor(v)
510 do
511 var after_expr = v.visit_expr(self.n_expr)
512
513 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
514 end
515 end
516
517 redef class AOrElseExpr
518 redef fun accept_flow_visitor(v)
519 do
520 super
521 end
522 end
523
524 redef class AEqExpr
525 redef fun accept_flow_visitor(v)
526 do
527 super
528 v.make_sub_true_false_flow
529 end
530 end
531
532
533 redef class ANeExpr
534 redef fun accept_flow_visitor(v)
535 do
536 super
537 v.make_sub_true_false_flow
538 end
539 end
540
541 redef class AIsaExpr
542 redef fun accept_flow_visitor(v)
543 do
544 super
545 v.make_sub_true_false_flow
546 end
547 end
548
549 redef class AParExpr
550 redef fun accept_flow_visitor(v)
551 do
552 var after_expr = v.visit_expr(self.n_expr)
553 v.current_flow_context = after_expr
554 end
555 end
556
557 redef class AOnceExpr
558 redef fun accept_flow_visitor(v)
559 do
560 var after_expr = v.visit_expr(self.n_expr)
561 v.current_flow_context = after_expr
562 end
563 end