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