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
66 # Variables by name (in the current context only)
67 var _dico
: Map[Symbol, Variable]
69 # All variables in all contextes
70 var _all_variables
: Set[Variable]
72 # Build a new VariableContext
73 fun sub
(node
: ANode): VariableContext
75 return new SubVariableContext.with_prev
(self, node
)
78 # Build a nested VariableContext with new variable information
79 fun sub_with
(node
: ANode, v
: Variable, t
: MMType): VariableContext
81 return new CastVariableContext(self, node
, v
, t
)
84 # Merge various alternative contexts
85 # Note that self can belong to alternatives
86 fun merge
(node
: ANode, alternatives
: Array[VariableContext], base
: VariableContext): VariableContext
88 return new MergeVariableContext(self, node
, alternatives
, base
)
91 # Merge only context that are reachable
92 # Used for if/then/else merges
94 fun merge_reash
(node
: ANode, alt1
, alt2
: VariableContext, base
: VariableContext): VariableContext
107 if alt2
.unreash
or alt1
== alt2
then
111 return merge
(node
, [alt1
], base
)
114 return merge
(node
, [alt1
, alt2
], base
)
118 # The visitor of the context (used to display error)
119 var _visitor
: AbsSyntaxVisitor
121 # The syntax node that introduced the context
122 readable var _node
: ANode
124 init(visitor
: AbsSyntaxVisitor, node
: ANode)
128 _dico
= new HashMap[Symbol, Variable]
131 # Is a control flow break met? (return, break, continue)
132 readable writable var _unreash
: Bool = false
134 # Is a control flow already broken?
135 # Used to avoid repeating the same error message
136 readable writable var _already_unreash
: Bool = false
138 # Set of variable that are set (assigned)
139 readable var _set_variables
: HashSet[Variable] = new HashSet[Variable]
142 fun is_set
(v
: Variable): Bool
144 return _set_variables
.has
(v
)
150 s
.append
(node
.location
.to_s
)
151 for v
in _all_variables
do
153 if t
== null then continue
159 private fun is_in
(ctx
: VariableContext): Bool = self == ctx
162 # Root of a variable context hierarchy
163 class RootVariableContext
164 special VariableContext
165 init(visitor
: AbsSyntaxVisitor, node
: ANode)
168 _all_variables
= new HashSet[Variable]
172 # Contexts that can see local variables of a prevous context
173 # Local variables added to this context are not shared with the previous context
174 class SubVariableContext
175 special VariableContext
176 readable var _prev
: VariableContext
180 return ctx
== self or _prev
.is_in
(ctx
)
185 if _dico
.has_key
(s
) then
194 return _set_variables
.has
(v
) or _prev
.is_set
(v
)
202 init with_prev
(p
: VariableContext, node
: ANode)
204 init(p
._visitor
, node
)
206 _all_variables
= p
._all_variables
210 # A variable context where a variable got a type evolution
211 class CastVariableContext
212 special SubVariableContext
213 # The casted variable
214 var _variable
: Variable
216 # The new static type of the variable
217 var _stype
: nullable MMType
221 if v
== _variable
then
228 init(p
: VariableContext, node
: ANode, v
: Variable, s
: nullable MMType)
236 # Context that follows a previous context but where
237 # Variable current static type and variable is_set depends on the combinaison of other contexts
238 class MergeVariableContext
239 special SubVariableContext
240 var _base
: VariableContext
241 var _alts
: Array[VariableContext]
243 # Updated static type of variables
244 var _stypes
: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
246 init(prev
: VariableContext, node
: ANode, alts
: Array[VariableContext], base
: VariableContext)
248 assert prev
.is_in
(base
) else print
"{node.location}: Error: prev {prev.node.location} is not in base {base.node.location}"
249 for a
in alts
do assert a
.is_in
(prev
) else print
"{node.location}: Error: alternative {a.node.location} is not in prev {prev.node.location}"
250 with_prev
(prev
, node
)
257 if _stypes
.has_key
(v
) then
260 var s
= merge_stype
(v
)
266 private fun merge_stype
(v
: Variable): nullable MMType
268 var candidate
: nullable MMType = null
269 var is_nullable
= false
270 var same_candidate
: nullable MMType = _alts
.first
.stype
(v
)
276 if t
!= same_candidate
then
277 same_candidate
= null
279 if t
isa MMTypeNone then
283 if t
isa MMNullableType then
287 if candidate
== null or candidate
< t
then
291 if same_candidate
!= null then
292 return same_candidate
295 if candidate
== null then
296 return _visitor
.type_none
298 candidate
= candidate
.as_nullable
301 if candidate
== null then
302 return _base
.stype
(v
)
306 if not t
< candidate
then
307 return _base
.stype
(v
)
316 if _set_variables
.has
(v
) then
320 if not ctx
.is_set
(v
) then
321 print
"{node.location}: is_set({v}) ? false : because not set in {ctx.node.location}"
325 _set_variables
.add
(v
)
333 # Is the variable must be set before being used ?
334 fun must_be_set
: Bool do return false
337 redef class VarVariable
338 redef fun must_be_set
do return true