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