import syntax_base
import escape
+import control_flow
redef class MMSrcModule
# Walk trough the module and type statments and expressions
end
end
-# Associate symbols to variable and variables to type
-# Can be nested
-abstract class VariableContext
- # Look for the variable from its name
- # Return null if nothing found
- meth [](s: Symbol): Variable
- do
- if _dico.has_key(s) then
- return _dico[s]
- else
- return null
- end
- end
-
- # Register a new variable with its name
- meth add(v: Variable)
- do
- _dico[v.name] = v
- _all_variables.add(v)
- end
-
- meth mark_is_set(v: Variable)
- do
- _set_variables.add(v)
- end
-
- meth check_is_set(n: PNode, v: Variable)
- do
- if v.must_be_set and not is_set(v) then
- _visitor.error(n, "Error: variable '{v}' is possibly unset.")
- var x = self
- while true do
- print " {x.node.locate}: {x._set_variables.join(", ")} ; {x._dico.join(", ")}"
- var x0 = x
- if x0 isa SubVariableContext then
- x = x0.prev
- else
- break
- end
- end
- end
- end
-
- # The effective static type of a given variable
- # May be different from the declaration static type
- meth stype(v: Variable): MMType
- do
- return v.stype
- end
-
- # Variables by name (in the current context only)
- attr _dico: Map[Symbol, Variable]
-
- # All variables in all contextes
- attr _all_variables: Set[Variable]
-
- # Build a new VariableContext
- meth sub(node: PNode): SubVariableContext
- do
- return new SubVariableContext.with_prev(self, node)
- end
-
- # Build a nested VariableContext with new variable information
- meth sub_with(node: PNode, v: Variable, t: MMType): SubVariableContext
- do
- return new CastVariableContext.with_prev(self, node, v, t)
- end
-
- # The visitor of the context (used to display error)
- attr _visitor: AbsSyntaxVisitor
-
- # The syntax node that introduced the context
- readable attr _node: PNode
-
- init(visitor: AbsSyntaxVisitor, node: PNode)
- do
- _visitor = visitor
- _node = node
- _dico = new HashMap[Symbol, Variable]
- end
-
- # Is a control flow break met? (return, break, continue)
- readable writable attr _unreash: Bool = false
-
- # Is a control flow already broken?
- # Used to avoid repeating the same error message
- readable writable attr _already_unreash: Bool = false
-
- # Set of variable that are set (assigned)
- readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
-
- # Is a variable set?
- meth is_set(v: Variable): Bool
- do
- return _set_variables.has(v)
- end
-
- # Merge back one flow context information
- meth merge(ctx: VariableContext)
- do
- if ctx.unreash then
- unreash = true
- if ctx.already_unreash then already_unreash = true
- return
- end
- for v in _all_variables do
- if not is_set(v) and ctx.is_set(v) then
- mark_is_set(v)
- end
- end
- end
-
- # Merge back two alternative flow context informations
- meth merge2(ctx1, ctx2: VariableContext)
- do
- if ctx1.unreash then
- merge(ctx2)
- else if ctx2.unreash then
- merge(ctx1)
- end
- for v in _all_variables do
- if not is_set(v) and ctx1.is_set(v) and ctx2.is_set(v) then
- mark_is_set(v)
- end
- end
- end
-end
-
-class RootVariableContext
-special VariableContext
- init(visitor: AbsSyntaxVisitor, node: PNode)
- do
- super(visitor, node)
- _all_variables = new HashSet[Variable]
- end
-end
-
-class SubVariableContext
-special VariableContext
- readable attr _prev: VariableContext
-
- redef meth [](s)
- do
- if _dico.has_key(s) then
- return _dico[s]
- else
- return prev[s]
- end
- end
-
- redef meth stype(v)
- do
- return prev.stype(v)
- end
-
- init with_prev(p: VariableContext, node: PNode)
- do
- init(p._visitor, node)
- _prev = p
- _all_variables = p._all_variables
- end
-
- redef meth is_set(v)
- do
- return _set_variables.has(v) or _prev.is_set(v)
- end
-end
-
-class CastVariableContext
-special SubVariableContext
- attr _variable: Variable
- attr _var_type: MMType
-
- redef meth stype(v)
- do
- if _variable == v then
- return _var_type
- end
- return prev.stype(v)
- end
-
- init with_prev(p: VariableContext, node: PNode, v: Variable, t: MMType)
- do
- super(p, node)
- _variable = v
- _var_type =t
- end
-end
-
-redef class Variable
- # Is the variable must be set before being used ?
- meth must_be_set: Bool do return false
-end
-
-redef class VarVariable
- redef meth must_be_set do return true
-end
-
###############################################################################
do
v.variable_ctx.mark_is_set(variable)
var t = v.variable_ctx.stype(variable)
- v.check_conform_expr(n_value, t)
+ if v.check_conform_expr(n_value, variable.stype) then
+ # Fall back to base type if current type does not match
+ if not n_value.stype < t then
+ v.variable_ctx.stype(variable) = variable.stype
+ end
+ end
_is_typed = true
end
end
redef class AReassignFormExpr
- # Compute and check method used through the reassigment operator
- private meth do_lvalue_typing(v: TypingVisitor, type_lvalue: MMType)
+ # Compute and check method used through the reassigment operator
+ # On success return the static type of the result of the reassigment operator
+ # Else display an error and return null
+ private meth do_rvalue_typing(v: TypingVisitor, type_lvalue: MMType): MMType
do
if type_lvalue == null then
- return
+ return null
end
var name = n_assign_op.method_name
var lc = type_lvalue.local_class
if not lc.has_global_property_by_name(name) then
v.error(self, "Error: Method '{name}' doesn't exists in {type_lvalue}.")
- return
+ return null
end
var prop = lc.select_method(name)
prop.global.check_visibility(v, self, v.module, false)
var psig = prop.signature_for(type_lvalue)
_assign_method = prop
- if not v.check_conform_expr(n_value, psig[0].not_for_self) then return
- if not v.check_conform(self, psig.return_type.not_for_self, n_value.stype) then return
+ if not v.check_conform_expr(n_value, psig[0].not_for_self) then return null
+ return psig.return_type.not_for_self
end
# Method used through the reassigment operator (once computed)
v.variable_ctx.check_is_set(self, variable)
v.variable_ctx.mark_is_set(variable)
var t = v.variable_ctx.stype(variable)
- do_lvalue_typing(v, t)
+ var t2 = do_rvalue_typing(v, t)
+ if t2 == null then return
+ if v.check_conform(self, t2, variable.stype) then
+ # Fall back to base type if current type does not match
+ if not t2 < t then
+ v.variable_ctx.stype(variable) = variable.stype
+ end
+ end
_is_typed = true
end
end
do
do_typing(v)
if prop == null then return
- do_lvalue_typing(v, attr_type)
+ var t = do_rvalue_typing(v, attr_type)
+ if t == null then return
+ v.check_conform(self, t, n_value.stype)
_is_typed = true
end
end
var t = prop.signature_for(n_expr.stype).return_type
if not n_expr.is_self then t = t.not_for_self
- do_lvalue_typing(v, t)
+ var t2 = do_rvalue_typing(v, t)
+ if t2 == null then return
+ v.check_conform(self, t2, n_value.stype)
_read_prop = prop
var old_args = arguments