Add break closure.
[nit.git] / src / syntax / control_flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 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 # Analysis control flow in property bodies, statements and expressions
18 package control_flow
19
20 import syntax_base
21
22 redef class MMSrcModule
23 # Walk trough the module and type statments and expressions
24 # Require than supermodules are processed
25 meth do_control_flow(tc: ToolContext)
26 do
27 var tv = new ControlFlowVisitor(tc, self)
28 tv.visit(node)
29 end
30 end
31
32 redef class Variable
33 # Is the variable must be set before being used ?
34 meth must_be_set: Bool do return false
35 end
36
37 redef class VarVariable
38 redef meth must_be_set do return true
39 end
40
41
42
43 # Control flow visitor
44 # * Check reachability in methods
45 # * Associate breaks and continues
46 # * Check some other warning
47 private class ControlFlowVisitor
48 special AbsSyntaxVisitor
49 redef meth visit(n)
50 do
51 if n != null then n.accept_control_flow(self)
52 end
53
54 # Number of nested once
55 readable writable attr _once_count: Int = 0
56
57 # Current knowledge about variables types
58 readable writable attr _control_flow_ctx: ControlFlowContext
59
60 meth check_is_set(n: PNode, v: Variable)
61 do
62 if v.must_be_set and not control_flow_ctx.is_set(v) then
63 error(n, "Error: variable '{v}' is possibly unset.")
64 var cfc = control_flow_ctx
65 while cfc != null do
66 print("cfc: " + cfc.set_variables.join(" "))
67 cfc = cfc.prev
68 end
69 end
70 end
71
72 meth mark_is_set(v: Variable)
73 do
74 control_flow_ctx.set_variables.add(v)
75 end
76
77 init(tc, m) do super
78 end
79
80 private class ControlFlowContext
81 # Previous control flow context if any
82 readable attr _prev: ControlFlowContext
83
84 # Is a control flow break met? (return, break, continue)
85 readable writable attr _unreash: Bool = false
86
87 # Is a control flow already broken?
88 # Used to avoid repeating the same error message
89 readable writable attr _already_unreash: Bool = false
90
91 # Current controlable block (for or while)
92 readable writable attr _base_block: AControlableBlock
93
94 # Set of variable that are set (assigned)
95 readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
96
97 # Is a variable set?
98 meth is_set(v: Variable): Bool
99 do
100 return _set_variables.has(v) or (_prev != null and _prev.is_set(v))
101 end
102
103 meth sub: ControlFlowContext
104 do
105 return new ControlFlowContext.with_prev(self)
106 end
107
108 init
109 do
110 end
111
112 init with_prev(p: ControlFlowContext)
113 do
114 _prev = p
115 _unreash = p.unreash
116 _already_unreash = p.already_unreash
117 _base_block = p.base_block
118 end
119 end
120
121 ###############################################################################
122
123 redef class PNode
124 private meth accept_control_flow(v: ControlFlowVisitor)
125 do
126 accept_abs_syntax_visitor(v)
127 end
128 end
129
130 redef class AMethPropdef
131 redef meth accept_control_flow(v)
132 do
133 v.control_flow_ctx = new ControlFlowContext
134 super
135 end
136 end
137
138 redef class AConcreteMethPropdef
139 redef meth accept_control_flow(v)
140 do
141 super
142 if v.control_flow_ctx.unreash == false and method.signature.return_type != null then
143 v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
144 end
145 end
146 end
147
148 redef class AVardeclExpr
149 redef meth accept_control_flow(v)
150 do
151 super
152 if n_expr != null then v.mark_is_set(variable)
153 end
154 end
155
156 redef class ABlockExpr
157 redef meth accept_control_flow(v)
158 do
159 for e in n_expr do
160 if v.control_flow_ctx.unreash and not v.control_flow_ctx.already_unreash then
161 v.control_flow_ctx.already_unreash = true
162 v.warning(e, "Warning: unreachable statement.")
163 end
164 v.visit(e)
165 end
166 end
167 end
168
169 redef class AReturnExpr
170 redef meth accept_control_flow(v)
171 do
172 super
173 v.control_flow_ctx.unreash = true
174 end
175 end
176
177 class ABlockControler
178 special PExpr
179 readable attr _block: AControlableBlock
180 end
181
182 redef class ABreakExpr
183 special ABlockControler
184 redef meth accept_control_flow(v)
185 do
186 super
187 var block = v.control_flow_ctx.base_block
188 if block == null then
189 v.error(self, "Syntax Error: 'break' statment outside block.")
190 return
191 end
192 _block = block
193 v.control_flow_ctx.unreash = true
194 end
195 end
196 redef class AContinueExpr
197 special ABlockControler
198 redef meth accept_control_flow(v)
199 do
200 super
201 var block = v.control_flow_ctx.base_block
202 if block == null then
203 v.error(self, "Syntax Error: 'continue' outside block.")
204 return
205 end
206 _block = block
207 v.control_flow_ctx.unreash = true
208 end
209 end
210
211 redef class AAbortExpr
212 redef meth accept_control_flow(v)
213 do
214 super
215 v.control_flow_ctx.unreash = true
216 end
217 end
218
219 redef class AClosureCallExpr
220 redef meth accept_control_flow(v)
221 do
222 super
223 if variable.closure.is_break then v.control_flow_ctx.unreash = true
224 end
225 end
226
227 redef class AIfExpr
228 redef meth accept_control_flow(v)
229 do
230 v.visit(n_expr)
231
232 var old_control_flow_ctx = v.control_flow_ctx
233 v.control_flow_ctx = v.control_flow_ctx.sub
234
235 v.visit(n_then)
236
237 if n_else == null then
238 # Restore control flow ctx since the 'then" block is optional
239 v.control_flow_ctx = old_control_flow_ctx
240 else
241 # Remember what appens in the 'then'
242 var then_control_flow_ctx = v.control_flow_ctx
243 # Reset to execute the 'else'
244 v.control_flow_ctx = old_control_flow_ctx.sub
245
246 v.visit(n_else)
247
248 # Merge then and else in the old control_flow
249 old_control_flow_ctx.unreash = v.control_flow_ctx.unreash and then_control_flow_ctx.unreash
250
251 if v.control_flow_ctx.unreash then v.control_flow_ctx = then_control_flow_ctx
252 if then_control_flow_ctx.unreash then then_control_flow_ctx = v.control_flow_ctx
253 for variable in v.control_flow_ctx.set_variables do
254 if then_control_flow_ctx.is_set(variable) then
255 old_control_flow_ctx.set_variables.add(variable)
256 end
257 end
258 v.control_flow_ctx = old_control_flow_ctx
259 end
260 end
261 end
262
263 class AControlableBlock
264 special PExpr
265 redef meth accept_control_flow(v)
266 do
267 # Store old control flow values
268 var old_control_flow_ctx = v.control_flow_ctx
269 v.control_flow_ctx = v.control_flow_ctx.sub
270
271 # Register the block
272 v.control_flow_ctx.base_block = self
273
274 super
275
276 # Check control flow if any
277 check_control_flow(v)
278
279 # Restore control flow value since all controlable blocks are optionnal
280 v.control_flow_ctx = old_control_flow_ctx
281 end
282
283 private meth check_control_flow(v: ControlFlowVisitor) do end
284 end
285
286 redef class AWhileExpr
287 special AControlableBlock
288 end
289
290 redef class AForExpr
291 special AControlableBlock
292 end
293
294 redef class AVarExpr
295 redef meth accept_control_flow(v)
296 do
297 super
298 v.check_is_set(self, variable)
299 end
300 end
301
302 redef class AVarAssignExpr
303 redef meth accept_control_flow(v)
304 do
305 super
306 v.mark_is_set(variable)
307 end
308 end
309
310 redef class AVarReassignExpr
311 redef meth accept_control_flow(v)
312 do
313 super
314 v.check_is_set(self, variable)
315 v.mark_is_set(variable)
316 end
317 end
318
319 redef class AClosureDef
320 special AControlableBlock
321 redef meth accept_control_flow(v)
322 do
323 for va in variables do v.mark_is_set(va)
324 super
325 end
326
327 redef meth check_control_flow(v)
328 do
329 if v.control_flow_ctx.unreash == false then
330 if closure.signature.return_type != null then
331 v.error(self, "Control error: Reached end of bloc (a 'continue' with a value was expected).")
332 else if closure.is_break then
333 v.error(self, "Control error: Reached end of break bloc (a 'break' was expected).")
334 end
335 end
336 end
337 end
338
339 redef class AOnceExpr
340 redef meth accept_control_flow(v)
341 do
342 if v.once_count > 0 then
343 v.warning(self, "Useless once in a once expression.")
344 end
345 v.once_count = v.once_count + 1
346
347 super
348
349 v.once_count = v.once_count - 1
350 end
351 end
352