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