syntax: 'meth' -> 'fun', 'attr' -> 'var'
[nit.git] / src / syntax / control_flow.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008-2009 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 and variable visibility in property bodies, statements and expressions
18 package control_flow
19
20 import syntax_base
21
22 # Associate symbols to variable and variables to type
23 # Can be nested
24 abstract class VariableContext
25 # Look for the variable from its name
26 # Return null if nothing found
27 fun [](s: Symbol): nullable Variable
28 do
29 if _dico.has_key(s) then
30 return _dico[s]
31 else
32 return null
33 end
34 end
35
36 # Register a new variable with its name
37 fun add(v: Variable)
38 do
39 _dico[v.name] = v
40 _all_variables.add(v)
41 end
42
43 fun mark_is_set(v: Variable)
44 do
45 _set_variables.add(v)
46 end
47
48 fun check_is_set(n: PNode, v: Variable)
49 do
50 if v.must_be_set and not is_set(v) then
51 _visitor.error(n, "Error: variable '{v}' is possibly unset.")
52 var x = self
53 while true do
54 print " {x.node.locate}: {x._set_variables.join(", ")} ; {x._dico.join(", ")}"
55 var x0 = x
56 if x0 isa SubVariableContext then
57 x = x0.prev
58 else
59 break
60 end
61 end
62 end
63 end
64
65 # The effective static type of a given variable
66 # May be different from the declaration static type
67 fun stype(v: Variable): nullable MMType
68 do
69 if _stypes.has_key(v) then
70 return _stypes[v]
71 else
72 return v.stype
73 end
74 end
75
76 # Set effective static type of a given variable
77 # May be different from the declaration static type
78 fun stype=(v: Variable, t: nullable MMType)
79 do
80 _stypes[v] = t
81 end
82
83 # Variables by name (in the current context only)
84 var _dico: Map[Symbol, Variable]
85
86 # All variables in all contextes
87 var _all_variables: Set[Variable]
88
89 # Updated static type of variables
90 var _stypes: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
91
92 # Build a new VariableContext
93 fun sub(node: PNode): SubVariableContext
94 do
95 return new SubVariableContext.with_prev(self, node)
96 end
97
98 # Build a nested VariableContext with new variable information
99 fun sub_with(node: PNode, v: Variable, t: MMType): SubVariableContext
100 do
101 var ctx = sub(node)
102 ctx.stype(v) = t
103 return ctx
104 end
105
106 # The visitor of the context (used to display error)
107 var _visitor: AbsSyntaxVisitor
108
109 # The syntax node that introduced the context
110 readable var _node: PNode
111
112 init(visitor: AbsSyntaxVisitor, node: PNode)
113 do
114 _visitor = visitor
115 _node = node
116 _dico = new HashMap[Symbol, Variable]
117 end
118
119 # Is a control flow break met? (return, break, continue)
120 readable writable var _unreash: Bool = false
121
122 # Is a control flow already broken?
123 # Used to avoid repeating the same error message
124 readable writable var _already_unreash: Bool = false
125
126 # Set of variable that are set (assigned)
127 readable var _set_variables: HashSet[Variable] = new HashSet[Variable]
128
129 # Is a variable set?
130 fun is_set(v: Variable): Bool
131 do
132 return _set_variables.has(v)
133 end
134
135 # Merge back one flow context information
136 fun merge(ctx: VariableContext)
137 do
138 if ctx.unreash then
139 unreash = true
140 if ctx.already_unreash then already_unreash = true
141 return
142 end
143 for v in _all_variables do
144 if not is_set(v) and ctx.is_set(v) then
145 mark_is_set(v)
146 end
147 var s = stype(v)
148 var s1 = ctx.stype(v)
149 if s1 != s then stype(v) = s1
150 end
151 end
152
153 # Merge back two alternative flow context informations
154 fun merge2(ctx1, ctx2, basectx: VariableContext)
155 do
156 if ctx1.unreash then
157 merge(ctx2)
158 else if ctx2.unreash then
159 merge(ctx1)
160 end
161 for v in _all_variables do
162 if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
163 mark_is_set(v)
164 end
165
166 var s = stype(v)
167 var s1 = ctx1.stype(v)
168 var s2 = ctx2.stype(v)
169 if s1 == s and s2 == s then
170 # NOP
171 else if s1 == s2 then
172 stype(v) = s1
173 else if s2 == null or s1 < s2 then
174 stype(v) = s2
175 else if s1 == null or s2 < s1 then
176 stype(v) = s1
177 else
178 stype(v) = basectx.stype(v)
179 end
180 end
181 end
182
183 redef fun to_s
184 do
185 var s = new Buffer
186 s.append(node.locate)
187 for v in _all_variables do
188 var t = stype(v)
189 if t == null then continue
190 s.append(" {v}:{t}")
191 end
192 return s.to_s
193 end
194 end
195
196 class RootVariableContext
197 special VariableContext
198 init(visitor: AbsSyntaxVisitor, node: PNode)
199 do
200 super(visitor, node)
201 _all_variables = new HashSet[Variable]
202 end
203 end
204
205 class SubVariableContext
206 special VariableContext
207 readable var _prev: VariableContext
208
209 redef fun [](s)
210 do
211 if _dico.has_key(s) then
212 return _dico[s]
213 else
214 return prev[s]
215 end
216 end
217
218 redef fun stype(v)
219 do
220 if _stypes.has_key(v) then
221 return _stypes[v]
222 else
223 return prev.stype(v)
224 end
225 end
226
227 init with_prev(p: VariableContext, node: PNode)
228 do
229 init(p._visitor, node)
230 _prev = p
231 _all_variables = p._all_variables
232 end
233
234 redef fun is_set(v)
235 do
236 return _set_variables.has(v) or _prev.is_set(v)
237 end
238 end
239
240 redef class Variable
241 # Is the variable must be set before being used ?
242 fun must_be_set: Bool do return false
243 end
244
245 redef class VarVariable
246 redef fun must_be_set do return true
247 end