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