1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2008-2009 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Analysis control flow and variable visibility in property bodies, statements and expressions
22 # Associate symbols to variable and variables to type
24 abstract class VariableContext
25 # Look for the variable from its name
26 # Return null if nothing found
27 fun [](s
: Symbol): nullable Variable
29 if _dico
.has_key
(s
) then
36 # Register a new variable with its name
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)}.")
47 fun mark_is_set
(v
: Variable)
52 fun check_is_set
(n
: ANode, v
: Variable)
54 if v
.must_be_set
and not is_set
(v
) then
55 _visitor
.error
(n
, "Error: variable '{v}' is possibly unset.")
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
63 if _stypes
.has_key
(v
) then
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)
77 # Variables by name (in the current context only)
78 var _dico
: Map[Symbol, Variable]
80 # All variables in all contextes
81 var _all_variables
: Set[Variable]
83 # Updated static type of variables
84 var _stypes
: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
86 # Build a new VariableContext
87 fun sub
(node
: ANode): SubVariableContext
89 return new SubVariableContext.with_prev
(self, node
)
92 # Build a nested VariableContext with new variable information
93 fun sub_with
(node
: ANode, v
: Variable, t
: MMType): SubVariableContext
100 # The visitor of the context (used to display error)
101 var _visitor
: AbsSyntaxVisitor
103 # The syntax node that introduced the context
104 readable var _node
: ANode
106 init(visitor
: AbsSyntaxVisitor, node
: ANode)
110 _dico
= new HashMap[Symbol, Variable]
113 # Is a control flow break met? (return, break, continue)
114 readable writable var _unreash
: Bool = false
116 # Is a control flow already broken?
117 # Used to avoid repeating the same error message
118 readable writable var _already_unreash
: Bool = false
120 # Set of variable that are set (assigned)
121 readable var _set_variables
: HashSet[Variable] = new HashSet[Variable]
124 fun is_set
(v
: Variable): Bool
126 return _set_variables
.has
(v
)
129 # Merge back one flow context information
130 fun merge
(ctx
: VariableContext)
134 if ctx
.already_unreash
then already_unreash
= true
137 for v
in _all_variables
do
138 if not is_set
(v
) and ctx
.is_set
(v
) then
142 var s1
= ctx
.stype
(v
)
143 if s1
!= s
then stype
(v
) = s1
147 # Merge back two alternative flow context informations
148 fun merge2
(ctx1
, ctx2
, basectx
: VariableContext)
153 else if ctx2
.unreash
then
157 for v
in _all_variables
do
158 if not is_set
(v
) and ctx1
.is_set
(v
) and ctx2
.is_set
(v
) then
163 var s1
= ctx1
.stype
(v
)
164 var s2
= ctx2
.stype
(v
)
165 if s1
== s
and s2
== s
then
167 else if s1
== null or s2
== null then
170 var sm
= merge_types
(s1
, s2
)
172 stype
(v
) = basectx
.stype
(v
)
180 # Combine and get the most specific comon supertype
181 # return null if no comon supertype is found
182 private fun merge_types
(t1
, t2
: MMType): nullable MMType
184 if t1
== t2
then return t1
185 if t1
isa MMTypeNone then return t2
.as_nullable
186 if t2
isa MMTypeNone then return t1
.as_nullable
187 var is_nullable
= false
188 if t1
.is_nullable
then
192 if t2
.is_nullable
then
204 if is_nullable
then t
= t
.as_nullable
211 s
.append
(node
.location
.to_s
)
212 for v
in _all_variables
do
214 if t
== null then continue
221 class RootVariableContext
222 special VariableContext
223 init(visitor
: AbsSyntaxVisitor, node
: ANode)
226 _all_variables
= new HashSet[Variable]
230 class SubVariableContext
231 special VariableContext
232 readable var _prev
: VariableContext
236 if _dico
.has_key
(s
) then
245 if _stypes
.has_key
(v
) then
252 init with_prev
(p
: VariableContext, node
: ANode)
254 init(p
._visitor
, node
)
256 _all_variables
= p
._all_variables
261 return _set_variables
.has
(v
) or _prev
.is_set
(v
)
266 # Is the variable must be set before being used ?
267 fun must_be_set
: Bool do return false
270 redef class VarVariable
271 redef fun must_be_set
do return true