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 #################################################################
24 # All-in-one context for flow control.
27 # * set/unset variable
28 # * adaptive type of variable
29 # FlowContextes are imutables, new contexts are created:
30 # * as an empty root context
31 # * as the adaptation of a existing context (see methods sub_*)
32 # * as the merge of existing contexts
33 abstract class FlowContext
34 # Display an error localised on `n' if the variable `v' is not set
35 fun check_is_set
(n
: ANode, v
: Variable)
37 if v
.must_be_set
and not is_set
(v
) then
38 _visitor
.error
(n
, "Error: variable '{v}' is possibly unset.")
42 # The effective static type of a given variable
43 # May be different from the declaration static type
44 fun stype
(v
: Variable): nullable MMType
49 # Return a context where the variable is marked as set
50 fun sub_setvariable
(v
: Variable): FlowContext
52 var ctx
= new SubFlowContext.with_prev
(self, node
)
53 ctx
._set_variables
.add
(v
)
57 # Return a context where unreash == true
58 fun sub_unreash
(node
: ANode): FlowContext
60 var ctx
= new SubFlowContext.with_prev
(self, node
)
65 # Return a context where v is casted as t
66 fun sub_with
(node
: ANode, v
: Variable, t
: MMType): FlowContext
68 return new CastFlowContext(self, node
, v
, t
)
71 # Merge various alternative contexts (all must be reashable)
72 # Note that self can belong to alternatives
74 fun merge
(node
: ANode, alternatives
: Array[FlowContext]): FlowContext
76 for a
in alternatives
do assert not a
.unreash
77 if alternatives
.length
== 1 then return alternatives
.first
78 return new MergeFlowContext(self, node
, alternatives
)
81 # Merge only context that are reachable
82 # Used for if/then/else merges
84 fun merge_reash
(node
: ANode, alt1
, alt2
: FlowContext): FlowContext
88 return self.sub_unreash
(node
)
96 if alt2
.unreash
or alt1
== alt2
then
101 # return merge(node, [alt1])
104 return merge
(node
, [alt1
, alt2
])
108 # The visitor of the context (used to display error)
109 var _visitor
: AbsSyntaxVisitor
111 # The syntax node that introduced the context
112 readable var _node
: ANode
114 init(visitor
: AbsSyntaxVisitor, node
: ANode)
120 # Is a control flow break met? (return, break, continue)
121 readable var _unreash
: Bool = false
123 # Is a control flow already broken?
124 # Used to avoid repeating the same error message
125 readable writable var _already_unreash
: Bool = false
127 # Set of variable that are set (assigned)
128 readable var _set_variables
: HashSet[Variable] = new HashSet[Variable]
131 fun is_set
(v
: Variable): Bool
133 return _set_variables
.has
(v
)
137 # Root of a variable context hierarchy
138 class RootFlowContext
140 init(visitor
: AbsSyntaxVisitor, node
: ANode)
146 # Contexts that are an evolution of a single previous context
149 readable var _prev
: FlowContext
153 return _set_variables
.has
(v
) or _prev
.is_set
(v
)
161 init with_prev
(p
: FlowContext, node
: ANode)
163 init(p
._visitor
, node
)
168 # A variable context where a variable got a type adptation
169 class CastFlowContext
171 # The casted variable
172 var _variable
: Variable
174 # The new static type of the variable
175 var _stype
: nullable MMType
179 if v
== _variable
then
186 init(p
: FlowContext, node
: ANode, v
: Variable, s
: nullable MMType)
194 # Context that resulting from the combinaisons of other contexts.
195 # Most of the merge computation are done lasily.
196 class MergeFlowContext
198 var _base
: FlowContext
199 var _alts
: Array[FlowContext]
201 # Updated static type of variables
202 var _stypes
: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
204 init(base
: FlowContext, node
: ANode, alts
: Array[FlowContext])
206 super(base
._visitor
, node
)
213 if _stypes
.has_key
(v
) then
216 var s
= merge_stype
(v
)
222 private fun merge_stype
(v
: Variable): nullable MMType
224 var candidate
: nullable MMType = null
225 var is_nullable
= false
226 var same_candidate
: nullable MMType = _alts
.first
.stype
(v
)
232 if t
!= same_candidate
then
233 same_candidate
= null
235 if t
isa MMTypeNone then
239 if t
isa MMNullableType then
243 if candidate
== null or candidate
< t
then
247 if same_candidate
!= null then
248 return same_candidate
251 if candidate
== null then
252 return _visitor
.type_none
254 candidate
= candidate
.as_nullable
257 if candidate
== null then
258 return _base
.stype
(v
)
262 if not t
< candidate
then
263 return _base
.stype
(v
)
272 if _set_variables
.has
(v
) then
276 if not ctx
.is_set
(v
) then
280 _set_variables
.add
(v
)
288 # Is the variable must be set before being used ?
289 fun must_be_set
: Bool do return false
292 redef class VarVariable
293 redef fun must_be_set
do return true