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
24 abstract class ScopeContext
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 # Build a new ScopeContext
48 fun sub
(node
: ANode): ScopeContext
50 return new SubScopeContext.with_prev
(self, node
)
53 # Variables by name (in the current context only)
54 var _dico
: Map[Symbol, Variable] = new HashMap[Symbol, Variable]
56 # All variables in all contextes
57 var _all_variables
: Set[Variable]
59 # The visitor of the context (used to display error)
60 var _visitor
: AbsSyntaxVisitor
62 # The syntax node that introduced the context
63 readable var _node
: ANode
65 init(visitor
: AbsSyntaxVisitor, node
: ANode)
72 # Root of a variable scope context hierarchy
73 class RootScopeContext
75 init(visitor
: AbsSyntaxVisitor, node
: ANode)
78 _all_variables
= new HashSet[Variable]
82 # Contexts that can see local variables of a prevous context
83 # Local variables added to this context are not shared with the previous context
86 readable var _prev
: ScopeContext
90 if _dico
.has_key
(s
) then
97 init with_prev
(p
: ScopeContext, node
: ANode)
99 init(p
._visitor
, node
)
101 _all_variables
= p
._all_variables
105 #################################################################
107 # All-in-one context for flow control.
110 # * set/unset variable
111 # * adaptive type of variable
112 # FlowContextes are imutables, new contexts are created:
113 # * as an empty root context
114 # * as the adaptation of a existing context (see methods sub_*)
115 # * as the merge of existing contexts
116 abstract class FlowContext
117 # Display an error localised on `n' if the variable `v' is not set
118 fun check_is_set
(n
: ANode, v
: Variable)
120 if v
.must_be_set
and not is_set
(v
) then
121 _visitor
.error
(n
, "Error: variable '{v}' is possibly unset.")
125 # The effective static type of a given variable
126 # May be different from the declaration static type
127 fun stype
(v
: Variable): nullable MMType
132 # Return a context where the variable is marked as set
133 fun sub_setvariable
(v
: Variable): FlowContext
135 var ctx
= new SubFlowContext.with_prev
(self, node
)
136 ctx
._set_variables
.add
(v
)
140 # Return a context where unreash == true
141 fun sub_unreash
(node
: ANode): FlowContext
143 var ctx
= new SubFlowContext.with_prev
(self, node
)
148 # Return a context where v is casted as t
149 fun sub_with
(node
: ANode, v
: Variable, t
: MMType): FlowContext
151 return new CastFlowContext(self, node
, v
, t
)
154 # Merge various alternative contexts (all must be reashable)
155 # Note that self can belong to alternatives
157 fun merge
(node
: ANode, alternatives
: Array[FlowContext]): FlowContext
159 for a
in alternatives
do assert not a
.unreash
160 if alternatives
.length
== 1 then return alternatives
.first
161 return new MergeFlowContext(self, node
, alternatives
)
164 # Merge only context that are reachable
165 # Used for if/then/else merges
167 fun merge_reash
(node
: ANode, alt1
, alt2
: FlowContext): FlowContext
171 return self.sub_unreash
(node
)
179 if alt2
.unreash
or alt1
== alt2
then
181 #if alt1 == self then
184 # return merge(node, [alt1])
187 return merge
(node
, [alt1
, alt2
])
191 # The visitor of the context (used to display error)
192 var _visitor
: AbsSyntaxVisitor
194 # The syntax node that introduced the context
195 readable var _node
: ANode
197 init(visitor
: AbsSyntaxVisitor, node
: ANode)
203 # Is a control flow break met? (return, break, continue)
204 readable var _unreash
: Bool = false
206 # Is a control flow already broken?
207 # Used to avoid repeating the same error message
208 readable writable var _already_unreash
: Bool = false
210 # Set of variable that are set (assigned)
211 readable var _set_variables
: HashSet[Variable] = new HashSet[Variable]
214 fun is_set
(v
: Variable): Bool
216 return _set_variables
.has
(v
)
220 # Root of a variable context hierarchy
221 class RootFlowContext
223 init(visitor
: AbsSyntaxVisitor, node
: ANode)
229 # Contexts that are an evolution of a single previous context
232 readable var _prev
: FlowContext
236 return _set_variables
.has
(v
) or _prev
.is_set
(v
)
244 init with_prev
(p
: FlowContext, node
: ANode)
246 init(p
._visitor
, node
)
251 # A variable context where a variable got a type adptation
252 class CastFlowContext
253 special SubFlowContext
254 # The casted variable
255 var _variable
: Variable
257 # The new static type of the variable
258 var _stype
: nullable MMType
262 if v
== _variable
then
269 init(p
: FlowContext, node
: ANode, v
: Variable, s
: nullable MMType)
277 # Context that resulting from the combinaisons of other contexts.
278 # Most of the merge computation are done lasily.
279 class MergeFlowContext
281 var _base
: FlowContext
282 var _alts
: Array[FlowContext]
284 # Updated static type of variables
285 var _stypes
: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
287 init(base
: FlowContext, node
: ANode, alts
: Array[FlowContext])
289 super(base
._visitor
, node
)
296 if _stypes
.has_key
(v
) then
299 var s
= merge_stype
(v
)
305 private fun merge_stype
(v
: Variable): nullable MMType
307 var candidate
: nullable MMType = null
308 var is_nullable
= false
309 var same_candidate
: nullable MMType = _alts
.first
.stype
(v
)
315 if t
!= same_candidate
then
316 same_candidate
= null
318 if t
isa MMTypeNone then
322 if t
isa MMNullableType then
326 if candidate
== null or candidate
< t
then
330 if same_candidate
!= null then
331 return same_candidate
334 if candidate
== null then
335 return _visitor
.type_none
337 candidate
= candidate
.as_nullable
340 if candidate
== null then
341 return _base
.stype
(v
)
345 if not t
< candidate
then
346 return _base
.stype
(v
)
355 if _set_variables
.has
(v
) then
359 if not ctx
.is_set
(v
) then
363 _set_variables
.add
(v
)
371 # Is the variable must be set before being used ?
372 fun must_be_set
: Bool do return false
375 redef class VarVariable
376 redef fun must_be_set
do return true