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