syntax: better internal VariableContex stype handling
[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 meth [](s: Symbol): 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 meth add(v: Variable)
38 do
39 _dico[v.name] = v
40 _all_variables.add(v)
41 end
42
43 meth mark_is_set(v: Variable)
44 do
45 _set_variables.add(v)
46 end
47
48 meth 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 meth stype(v: Variable): 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 meth stype=(v: Variable, t: MMType)
79 do
80 _stypes[v] = t
81 end
82
83 # Variables by name (in the current context only)
84 attr _dico: Map[Symbol, Variable]
85
86 # All variables in all contextes
87 attr _all_variables: Set[Variable]
88
89 # Updated static type of variables
90 attr _stypes: Map[Variable, MMType] = new HashMap[Variable, MMType]
91
92 # Build a new VariableContext
93 meth 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 meth 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 attr _visitor: AbsSyntaxVisitor
108
109 # The syntax node that introduced the context
110 readable attr _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 attr _unreash: Bool = false
121
122 # Is a control flow already broken?
123 # Used to avoid repeating the same error message
124 readable writable attr _already_unreash: Bool = false
125
126 # Set of variable that are set (assigned)
127 readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
128
129 # Is a variable set?
130 meth is_set(v: Variable): Bool
131 do
132 return _set_variables.has(v)
133 end
134
135 # Merge back one flow context information
136 meth 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 end
148 end
149
150 # Merge back two alternative flow context informations
151 meth merge2(ctx1, ctx2: VariableContext)
152 do
153 if ctx1.unreash then
154 merge(ctx2)
155 else if ctx2.unreash then
156 merge(ctx1)
157 end
158 for v in _all_variables do
159 if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
160 mark_is_set(v)
161 end
162 end
163 end
164 end
165
166 class RootVariableContext
167 special VariableContext
168 init(visitor: AbsSyntaxVisitor, node: PNode)
169 do
170 super(visitor, node)
171 _all_variables = new HashSet[Variable]
172 end
173 end
174
175 class SubVariableContext
176 special VariableContext
177 readable attr _prev: VariableContext
178
179 redef meth [](s)
180 do
181 if _dico.has_key(s) then
182 return _dico[s]
183 else
184 return prev[s]
185 end
186 end
187
188 redef meth stype(v)
189 do
190 if _stypes.has_key(v) then
191 return _stypes[v]
192 else
193 return prev.stype(v)
194 end
195 end
196
197 init with_prev(p: VariableContext, node: PNode)
198 do
199 init(p._visitor, node)
200 _prev = p
201 _all_variables = p._all_variables
202 end
203
204 redef meth is_set(v)
205 do
206 return _set_variables.has(v) or _prev.is_set(v)
207 end
208 end
209
210 redef class Variable
211 # Is the variable must be set before being used ?
212 meth must_be_set: Bool do return false
213 end
214
215 redef class VarVariable
216 redef meth must_be_set do return true
217 end