Callref bugfix in interpreter and compilers + autosav
[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 is noautoinit
267
268 # The ending flow
269 var after_flow_context: nullable FlowContext is noautoinit
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 # FlowContext before the block
349 var before_block = v.make_sub_flow
350
351 # Visit the bloc, then merge the breaks
352 v.enter_visit(self.n_block)
353 v.merge_breaks(self.break_mark)
354 var after_block = v.current_flow_context
355
356 # Visit the catch if there is one
357 if self.n_catch != null then
358 var before_catch = v.make_sub_flow
359 v.make_merge_flow(before_block, after_block)
360 v.enter_visit(self.n_catch)
361 var after_catch = v.current_flow_context
362 v.make_merge_flow(before_catch, after_catch)
363 end
364 end
365 end
366
367 redef class AIfExpr
368 redef fun accept_flow_visitor(v)
369 do
370 var after_expr = v.visit_expr(self.n_expr)
371
372 v.current_flow_context = after_expr.when_true
373 v.enter_visit(self.n_then)
374 var after_then = v.current_flow_context
375
376 v.current_flow_context = after_expr.when_false
377 v.enter_visit(self.n_else)
378 var after_else = v.current_flow_context
379
380 v.make_merge_flow(after_then, after_else)
381 end
382 end
383
384 redef class AIfexprExpr
385 redef fun accept_flow_visitor(v)
386 do
387 var after_expr = v.visit_expr(self.n_expr)
388
389 v.current_flow_context = after_expr.when_true
390 v.enter_visit(self.n_then)
391 var after_then = v.current_flow_context
392
393 v.current_flow_context = after_expr.when_false
394 v.enter_visit(self.n_else)
395 var after_else = v.current_flow_context
396
397 v.make_merge_flow(after_then, after_else)
398 end
399 end
400
401 redef class AWhileExpr
402 redef fun accept_flow_visitor(v)
403 do
404 var before_loop = v.make_sub_flow
405
406 var after_expr = v.visit_expr(self.n_expr)
407
408 v.current_flow_context = after_expr.when_true
409 v.enter_visit(self.n_block)
410 var after_block = v.current_flow_context
411
412 before_loop.add_loop(after_block)
413 v.merge_continues_to(before_loop, self.continue_mark)
414
415 v.current_flow_context = after_expr.when_false
416 v.merge_breaks(self.break_mark)
417 end
418 end
419
420 redef class ALoopExpr
421 redef fun accept_flow_visitor(v)
422 do
423 var before_loop = v.make_sub_flow
424
425 v.enter_visit(self.n_block)
426
427 var after_block = v.current_flow_context
428
429 before_loop.add_loop(after_block)
430 v.merge_continues_to(before_loop, self.continue_mark)
431
432 v.make_unreachable_flow
433 v.merge_breaks(self.break_mark)
434 end
435 end
436
437 redef class AForExpr
438 redef fun accept_flow_visitor(v)
439 do
440 for g in n_groups do
441 v.enter_visit(g.n_expr)
442 end
443
444 var before_loop = v.make_sub_flow
445
446 v.enter_visit(self.n_block)
447
448 var after_block = v.current_flow_context
449
450 before_loop.add_loop(after_block)
451 v.merge_continues_to(before_loop, self.continue_mark)
452
453 v.make_merge_flow(v.current_flow_context, before_loop)
454 v.merge_breaks(self.break_mark)
455 end
456 end
457
458 redef class AWithExpr
459 redef fun accept_flow_visitor(v)
460 do
461 super
462 v.merge_breaks(self.break_mark)
463 end
464 end
465
466 redef class AAssertExpr
467 redef fun accept_flow_visitor(v)
468 do
469 var after_expr = v.visit_expr(self.n_expr)
470
471 v.current_flow_context = after_expr.when_false
472 v.enter_visit(n_else)
473 # the after context of n_else is a dead end, so we do not care
474
475 v.current_flow_context = after_expr.when_true
476 end
477 end
478
479 redef class AOrExpr
480 redef fun accept_flow_visitor(v)
481 do
482 var after_expr = v.visit_expr(self.n_expr)
483
484 v.current_flow_context = after_expr.when_false
485 var after_expr2 = v.visit_expr(self.n_expr2)
486
487 var merge_true = v.make_merge_flow(after_expr.when_true, after_expr2.when_true)
488 merge_true.name = "OR TRUE"
489
490 v.make_true_false_flow(merge_true, after_expr2.when_false)
491 end
492 end
493
494 redef class AImpliesExpr
495 redef fun accept_flow_visitor(v)
496 do
497 var after_expr = v.visit_expr(self.n_expr)
498
499 v.current_flow_context = after_expr.when_true
500 var after_expr2 = v.visit_expr(self.n_expr2)
501
502 var merge_true = v.make_merge_flow(after_expr.when_false, after_expr2.when_true)
503 merge_true.name = "OR TRUE"
504
505 v.make_true_false_flow(merge_true, after_expr2.when_false)
506 end
507 end
508
509 redef class AAndExpr
510 redef fun accept_flow_visitor(v)
511 do
512 var after_expr = v.visit_expr(self.n_expr)
513
514 v.current_flow_context = after_expr.when_true
515 var after_expr2 = v.visit_expr(self.n_expr2)
516
517 var merge_false = v.make_merge_flow(after_expr.when_false, after_expr2.when_false)
518 merge_false.name = "AND FALSE"
519
520 v.make_true_false_flow(after_expr2.when_true, merge_false)
521 end
522 end
523
524 redef class ANotExpr
525 redef fun accept_flow_visitor(v)
526 do
527 var after_expr = v.visit_expr(self.n_expr)
528
529 v.make_true_false_flow(after_expr.when_false, after_expr.when_true)
530 end
531 end
532
533 redef class AOrElseExpr
534 redef fun accept_flow_visitor(v)
535 do
536 super
537 end
538 end
539
540 redef class AEqExpr
541 redef fun accept_flow_visitor(v)
542 do
543 super
544 v.make_sub_true_false_flow
545 end
546 end
547
548
549 redef class ANeExpr
550 redef fun accept_flow_visitor(v)
551 do
552 super
553 v.make_sub_true_false_flow
554 end
555 end
556
557 redef class AIsaExpr
558 redef fun accept_flow_visitor(v)
559 do
560 super
561 v.make_sub_true_false_flow
562 end
563 end
564
565 redef class AParExpr
566 redef fun accept_flow_visitor(v)
567 do
568 var after_expr = v.visit_expr(self.n_expr)
569 v.current_flow_context = after_expr
570 end
571 end
572
573 redef class AOnceExpr
574 redef fun accept_flow_visitor(v)
575 do
576 var after_expr = v.visit_expr(self.n_expr)
577 v.current_flow_context = after_expr
578 end
579 end