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