522a347a1bc2d02a34427aa4b1b54cc42a5fa0c5
[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: 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 = 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.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(after_block, 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(after_block, 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(after_block, 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 AAssertExpr
443 redef fun accept_flow_visitor(v)
444 do
445 var after_expr = v.visit_expr(self.n_expr)
446
447 v.current_flow_context = after_expr.when_false
448 v.enter_visit(n_else)
449 # the after context of n_else is a dead end, so we do not care
450
451 v.current_flow_context = after_expr.when_true
452 end
453 end
454
455 redef class AOrExpr
456 redef fun accept_flow_visitor(v)
457 do
458 var after_expr = v.visit_expr(self.n_expr)
459
460 v.current_flow_context = after_expr.when_false
461 var after_expr2 = v.visit_expr(self.n_expr2)
462
463 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
464 merge_true.name = "OR TRUE"
465
466 v.make_true_false_flow(merge_true, after_expr2.when_false)
467 end
468 end
469
470 redef class AImpliesExpr
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_true
476 var after_expr2 = v.visit_expr(self.n_expr2)
477
478 var merge_true = v.make_merge_flow(after_expr.when_false, 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 AAndExpr
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_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
494 merge_false.name = "AND FALSE"
495
496 v.make_true_false_flow(after_expr2.when_true, merge_false)
497 end
498 end
499
500 redef class ANotExpr
501 redef fun accept_flow_visitor(v)
502 do
503 var after_expr = v.visit_expr(self.n_expr)
504
505 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
506 end
507 end
508
509 redef class AOrElseExpr
510 redef fun accept_flow_visitor(v)
511 do
512 super
513 end
514 end
515
516 redef class AEqExpr
517 redef fun accept_flow_visitor(v)
518 do
519 super
520 v.make_sub_true_false_flow
521 end
522 end
523
524
525 redef class ANeExpr
526 redef fun accept_flow_visitor(v)
527 do
528 super
529 v.make_sub_true_false_flow
530 end
531 end
532
533 redef class AIsaExpr
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 AParExpr
542 redef fun accept_flow_visitor(v)
543 do
544 var after_expr = v.visit_expr(self.n_expr)
545 v.current_flow_context = after_expr
546 end
547 end
548
549 redef class AOnceExpr
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