package typing
import syntax_base
+import escape
redef class MMSrcModule
# Walk trough the module and type statments and expressions
# Current knowledge about variables names and types
readable writable attr _variable_ctx: VariableContext
+ # Current knowledge about escapable blocks
+ readable writable attr _escapable_ctx: EscapableContext = new EscapableContext(self)
+
# The current reciever
readable writable attr _self_var: ParamVariable
# Block of the current method
readable writable attr _top_block: PExpr
- # Current closure (if any)
- readable writable attr _closure: MMClosure
-
- # Current closure method return type (for break) (if any)
- readable writable attr _closure_break_stype: MMType = null
-
- # Current closure break expressions (if any)
- readable writable attr _break_list: Array[PExpr]
-
# List of explicit invocation of constructors of super-classes
readable writable attr _explicit_super_init_calls: Array[MMMethod]
# Is a other constructor of the same class invoked
readable writable attr _explicit_other_init_call: Bool
+ # Make the if_true_variable_ctx of the expression effective
+ private meth use_if_true_variable_ctx(e: PExpr)
+ do
+ var ctx = e.if_true_variable_ctx
+ if ctx != null then
+ variable_ctx = ctx
+ end
+ end
+
init(tc, module) do super
private meth get_default_constructor_for(n: PNode, c: MMLocalClass, prop: MMSrcMethod): MMMethod
# Build a new VariableContext
meth sub: SubVariableContext
do
- return new SubVariableContext.with_prev(self, null, null)
+ return new SubVariableContext.with_prev(self)
end
# Build a nested VariableContext with new variable information
meth sub_with(v: Variable, t: MMType): SubVariableContext
do
- return new SubVariableContext.with_prev(self, v, t)
+ return new CastVariableContext.with_prev(self, v, t)
end
init
private class SubVariableContext
special VariableContext
readable attr _prev: VariableContext
- attr _variable: Variable
- attr _var_type: MMType
redef meth [](s)
do
redef meth stype(v)
do
+ return prev.stype(v)
+ end
+
+ init with_prev(p: VariableContext)
+ do
+ init
+ _prev = p
+ end
+end
+
+private class CastVariableContext
+special SubVariableContext
+ attr _variable: Variable
+ attr _var_type: MMType
+
+ redef meth stype(v)
+ do
if _variable == v then
return _var_type
end
init with_prev(p: VariableContext, v: Variable, t: MMType)
do
- init
- _prev = p
+ super(p)
_variable = v
_var_type =t
end
end
-
###############################################################################
redef class PNode
end
redef class AClosureDecl
- redef meth after_typing(v)
+ # The corresponding escapable object
+ readable attr _escapable: EscapableBlock
+
+ redef meth accept_typing(v)
do
+ # Register the closure for ClosureCallExpr
v.variable_ctx.add(variable)
+
+ var old_var_ctx = v.variable_ctx
+ v.variable_ctx = v.variable_ctx.sub
+
+ _escapable = new EscapableClosure(self, variable.closure, null)
+ v.escapable_ctx.push(_escapable)
+
+ super
+
+ v.variable_ctx = old_var_ctx
+ v.escapable_ctx.pop
end
end
redef class AContinueExpr
redef meth after_typing(v)
do
- var c = v.closure
- var t: MMType = null
- if c != null then
- if c.is_break then
- v.error(self, "Error: 'continue' forbiden in break blocks.")
- return
- end
- t = c.signature.return_type
+ var esc = compute_escapable_block(v.escapable_ctx)
+ if esc == null then return
+
+ if esc.is_break_block then
+ v.error(self, "Error: 'continue' forbiden in break blocks.")
+ return
end
+ var t = esc.continue_stype
if n_expr == null and t != null then
- v.error(self, "Error: continue with a value required in this bloc.")
+ v.error(self, "Error: continue with a value required in this block.")
else if n_expr != null and t == null then
- v.error(self, "Error: continue without value required in this bloc.")
+ v.error(self, "Error: continue without value required in this block.")
else if n_expr != null and t != null then
v.check_conform_expr(n_expr, t)
end
redef class ABreakExpr
redef meth after_typing(v)
do
- var t = v.closure_break_stype
- if n_expr == null and t != null then
- v.error(self, "Error: break with a value required in this bloc.")
- else if n_expr != null and t == null then
- v.error(self, "Error: break without value required in this bloc.")
- else if n_expr != null and t != null then
+ var esc = compute_escapable_block(v.escapable_ctx)
+ if esc == null then return
+
+ var bl = esc.break_list
+ if n_expr == null and bl != null then
+ v.error(self, "Error: break with a value required in this block.")
+ else if n_expr != null and bl == null then
+ v.error(self, "Error: break without value required in this block.")
+ else if n_expr != null and bl != null then
# Typing check can only be done later
- v.break_list.add(n_expr)
+ bl.add(n_expr)
end
end
end
v.visit(n_expr)
v.check_conform_expr(n_expr, v.type_bool)
- if n_expr.if_true_variable_ctx != null then
- v.variable_ctx = n_expr.if_true_variable_ctx
- end
+ v.use_if_true_variable_ctx(n_expr)
v.visit(n_then)
# Restore variable ctx
end
redef class AWhileExpr
- redef meth after_typing(v)
+ # The corresponding escapable block
+ readable attr _escapable: EscapableBlock
+
+ redef meth accept_typing(v)
do
+ _escapable = new EscapableBlock(self)
+ v.escapable_ctx.push(_escapable)
+
+ super
+
v.check_conform_expr(n_expr, v.type_bool)
+ v.escapable_ctx.pop
end
end
redef class AForExpr
- redef meth after_typing(v)
- do
- # pop context created in AForVardeclExpr
- var varctx = v.variable_ctx
- assert varctx isa SubVariableContext
- v.variable_ctx = varctx.prev
- end
-end
+ # The corresponding escapable block
+ readable attr _escapable: EscapableBlock
-redef class AForVardeclExpr
- redef meth after_typing(v)
+ readable attr _meth_iterator: MMMethod
+ readable attr _meth_is_ok: MMMethod
+ readable attr _meth_item: MMMethod
+ readable attr _meth_next: MMMethod
+ redef meth accept_typing(v)
do
+ _escapable = new EscapableBlock(self)
+ v.escapable_ctx.push(_escapable)
+
v.variable_ctx = v.variable_ctx.sub
var va = new AutoVariable(n_id.to_symbol, self)
variable = va
v.variable_ctx.add(va)
+ v.visit(n_expr)
+
var expr_type = n_expr.stype
if not v.check_conform_expr(n_expr, v.type_collection) then
return
end
- var prop = expr_type.local_class.select_method(once ("iterator".to_symbol))
- if prop == null then
+ _meth_iterator = expr_type.local_class.select_method(once ("iterator".to_symbol))
+ if _meth_iterator == null then
v.error(self, "Error: Collection MUST have an iterate method")
return
end
- var iter_type = prop.signature_for(expr_type).return_type
- var prop2 = iter_type.local_class.select_method(once ("item".to_symbol))
- if prop2 == null then
+ var iter_type = _meth_iterator.signature_for(expr_type).return_type
+ _meth_is_ok = iter_type.local_class.select_method(once ("is_ok".to_symbol))
+ if _meth_is_ok == null then
+ v.error(self, "Error: {iter_type} MUST have an is_ok method")
+ return
+ end
+ _meth_item = iter_type.local_class.select_method(once ("item".to_symbol))
+ if _meth_item == null then
v.error(self, "Error: {iter_type} MUST have an item method")
return
end
- var t = prop2.signature_for(iter_type).return_type
+ _meth_next = iter_type.local_class.select_method(once ("next".to_symbol))
+ if _meth_next == null then
+ v.error(self, "Error: {iter_type} MUST have a next method")
+ return
+ end
+ var t = _meth_item.signature_for(iter_type).return_type
if not n_expr.is_self then t = t.not_for_self
va.stype = t
+
+ if n_block != null then v.visit(n_block)
+
+ # pop context
+ var varctx = v.variable_ctx
+ assert varctx isa SubVariableContext
+ v.variable_ctx = varctx.prev
+
+ v.escapable_ctx.pop
end
end
redef meth after_typing(v)
do
v.check_conform_expr(n_expr, v.type_bool)
- if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx
+ v.use_if_true_variable_ctx(n_expr)
end
end
return
end
var name = n_assign_op.method_name
- var prop = type_lvalue.local_class.select_method(name)
- if prop == null then
+ 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
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
var old_var_ctx = v.variable_ctx
v.visit(n_expr)
- if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx
+ v.use_if_true_variable_ctx(n_expr)
v.visit(n_then)
v.variable_ctx = old_var_ctx
v.visit(n_else)
v.check_conform_expr(n_expr, v.type_bool)
- if not v.check_expr(n_then) or not v.check_expr(n_else) then return
-
- var t = n_then.stype
- var te = n_else.stype
- if t < te then
- t = te
- else if not te < t then
- v.error(self, "Type error: {te} is not a subtype of {t}.")
- return
- end
-
- _stype = t
+ _stype = v.check_conform_multiexpr(null, [n_then, n_else])
end
end
var old_var_ctx = v.variable_ctx
v.visit(n_expr)
- if n_expr.if_true_variable_ctx != null then v.variable_ctx = n_expr.if_true_variable_ctx
+ v.use_if_true_variable_ctx(n_expr)
v.visit(n_expr2)
if n_expr2.if_true_variable_ctx != null then
end
redef class AStringFormExpr
+ readable attr _meth_with_native: MMMethod
redef meth after_typing(v)
do
_stype = v.type_string
+ _meth_with_native = _stype.local_class.select_method(once "with_native".to_symbol)
+ if _meth_with_native == null then v.error(self, "{_stype} MUST have a with_native method.")
end
end
redef class ASuperstringExpr
+ readable attr _meth_with_capacity: MMMethod
+ readable attr _meth_add: MMMethod
+ readable attr _meth_to_s: MMMethod
+ readable attr _atype: MMType
redef meth after_typing(v)
do
_stype = v.type_string
+ _atype = v.type_array(_stype)
+ _meth_with_capacity = _atype.local_class.select_method(once "with_capacity".to_symbol)
+ if _meth_with_capacity == null then v.error(self, "{_atype} MUST have a with_capacity method.")
+ _meth_add = _atype.local_class.select_method(once "add".to_symbol)
+ if _meth_add == null then v.error(self, "{_atype} MUST have an add method.")
+ _meth_to_s = v.type_object.local_class.select_method(once "to_s".to_symbol)
+ if _meth_to_s == null then v.error(self, "Object MUST have a to_s method.")
end
end
end
redef class AArrayExpr
- private meth stype=(t: MMType) do _stype = t
+ readable attr _meth_with_capacity: MMMethod
+ readable attr _meth_add: MMMethod
redef meth after_typing(v)
do
- var stype: MMType = null
- for n in n_exprs do
- var ntype = n.stype
- if stype == null or (ntype != null and stype < ntype) then
- stype = ntype
- end
- end
- for n in n_exprs do
- v.check_conform_expr(n, stype)
- end
- _stype = v.type_array(stype)
+ var stype = v.check_conform_multiexpr(null, n_exprs)
+ if stype == null then return
+ do_typing(v, stype)
+ end
+
+ private meth do_typing(v: TypingVisitor, element_type: MMType)
+ do
+ _stype = v.type_array(element_type)
+
+ _meth_with_capacity = _stype.local_class.select_method(once "with_capacity".to_symbol)
+ if _meth_with_capacity == null then v.error(self, "{_stype} MUST have a with_capacity method.")
+ _meth_add = _stype.local_class.select_method(once "add".to_symbol)
+ if _meth_add == null then v.error(self, "{_stype} MUST have an add method.")
end
end
redef class ARangeExpr
+ readable attr _meth_init: MMMethod
redef meth after_typing(v)
do
var ntype = n_expr.stype
end
end
+redef class ACrangeExpr
+ redef meth after_typing(v)
+ do
+ super
+ _meth_init = stype.local_class.select_method(once "init".to_symbol)
+ end
+end
+redef class AOrangeExpr
+ redef meth after_typing(v)
+ do
+ super
+ _meth_init = stype.local_class.select_method(once "without_last".to_symbol)
+ end
+end
+
+
redef class ASuperExpr
special ASuperInitCall
# readable attr _prop: MMSrcMethod
if not v.check_expr(n_expr) then return
var type_recv = n_expr.stype
var name = n_id.to_symbol
- var prop = type_recv.local_class.select_attribute(name)
- if prop == null then
+ var lc = type_recv.local_class
+ if not lc.has_global_property_by_name(name) then
v.error(self, "Error: Attribute {name} doesn't exists in {type_recv}.")
return
- else if v.module.visibility_for(prop.global.local_class.module) < 3 then
+ end
+ var prop = lc.select_attribute(name)
+ if v.module.visibility_for(prop.global.local_class.module) < 3 then
v.error(self, "Error: Attribute {name} from {prop.global.local_class.module} is invisible in {v.module}")
end
_prop = prop
end
end
-class AAbsSendExpr
+class AAbsAbsSendExpr
special PExpr
# The signature of the called property
readable attr _prop_signature: MMSignature
- # Compute the called global property
- private meth do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: Array[PExpr], closure_defs: Array[PClosureDef])
- do
- var prop = get_property(v, type_recv, is_implicit_self, name)
- if prop == null then return
- var sig = get_signature(v, type_recv, prop, recv_is_self)
- if sig == null then return
- var args = process_signature(v, sig, prop.name, raw_args)
- if args == null then return
- var rtype = process_closures(v, sig, prop.name, closure_defs)
- _prop = prop
- _prop_signature = sig
- _arguments = args
- _return_type = rtype
- end
-
- private meth get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): MMMethod
- do
- if type_recv == null then return null
- var prop = type_recv.local_class.select_method(name)
- if prop == null and v.local_property.global.is_init then
- var props = type_recv.local_class.super_methods_named(name)
- if props.length > 1 then
- v.error(self, "Error: Ambigous method name '{name}' for {props.join(", ")}. Use explicit designation.")
- return null
- else if props.length == 1 then
- var p = type_recv.local_class[props.first.global]
- assert p isa MMMethod
- prop = p
- end
-
- end
- if prop == null then
- if is_implicit_self then
- v.error(self, "Error: Method or variable '{name}' unknown in {type_recv}.")
- else
- v.error(self, "Error: Method '{name}' doesn't exists in {type_recv}.")
- end
- return null
- end
- return prop
- end
-
- # Get the signature for a local property and a receiver
- private meth get_signature(v: TypingVisitor, type_recv: MMType, prop: MMMethod, recv_is_self: Bool): MMSignature
- do
- prop.global.check_visibility(v, self, v.module, recv_is_self)
- var psig = prop.signature_for(type_recv)
- if not recv_is_self then psig = psig.not_for_self
- return psig
- end
+ # The real arguments used (after star transformation) (once computed)
+ readable attr _arguments: Array[PExpr]
# Check the conformity of a set of arguments `raw_args' to a signature.
private meth process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: Array[PExpr]): Array[PExpr]
arg_idx = arg_idx + 1
end
var aa = new AArrayExpr.init_aarrayexpr(star)
- aa.stype = v.type_array(par_type)
+ aa.do_typing(v, par_type)
a = aa
else
a = raw_args[arg_idx]
do
var t = psig.return_type
var cs = psig.closures # Declared closures
+ var min_arity = 0
+ for c in cs do
+ if not c.is_optional then min_arity += 1
+ end
if cd != null then
if cs.length == 0 then
- v.error(self, "Error: {name} does not require blocs.")
- else if cs.length != cd.length then
- v.error(self, "Error: {name} requires {cs.length} blocs, {cd.length} found.")
+ v.error(self, "Error: {name} does not require blocks.")
+ else if cd.length > cs.length or cd.length < min_arity then
+ v.error(self, "Error: {name} requires {cs.length} blocks, {cd.length} found.")
else
- var old_bbst = v.closure_break_stype
- var old_bl = v.break_list
- v.closure_break_stype = t
- v.break_list = new Array[ABreakExpr]
- for i in [0..cs.length[ do
- cd[i].accept_typing2(v, cs[i])
- end
- for n in v.break_list do
- var ntype = n.stype
- if t == null or (t != null and t < ntype) then
- t = ntype
- end
- end
- for n in v.break_list do
- v.check_conform_expr(n, t)
+ # Initialize the break list if a value is required for breaks (ie. if the method is a function)
+ var break_list: Array[ABreakExpr] = null
+ if t != null then break_list = new Array[ABreakExpr]
+
+ # Process each closure definition
+ for i in [0..cd.length[ do
+ var csi = cs[i]
+ var cdi = cd[i]
+ var esc = new EscapableClosure(cdi, csi, break_list)
+ v.escapable_ctx.push(esc)
+ cdi.accept_typing2(v, esc)
+ v.escapable_ctx.pop
end
- v.closure_break_stype = old_bbst
- v.break_list = old_bl
+ # Check break type conformity
+ if break_list != null then
+ t = v.check_conform_multiexpr(t, break_list)
+ end
end
- else if cs.length != 0 then
- v.error(self, "Error: {name} requires {cs.length} blocs.")
+ else if min_arity != 0 then
+ v.error(self, "Error: {name} requires {cs.length} blocks.")
end
return t
end
+end
+
+class AAbsSendExpr
+special AAbsAbsSendExpr
+ # Compute the called global property
+ private meth do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: Array[PExpr], closure_defs: Array[PClosureDef])
+ do
+ var prop = get_property(v, type_recv, is_implicit_self, name)
+ if prop == null then return
+ var sig = get_signature(v, type_recv, prop, recv_is_self)
+ if sig == null then return
+ var args = process_signature(v, sig, prop.name, raw_args)
+ if args == null then return
+ var rtype = process_closures(v, sig, prop.name, closure_defs)
+ _prop = prop
+ _prop_signature = sig
+ _arguments = args
+ _return_type = rtype
+ end
+
+ private meth get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): MMMethod
+ do
+ if type_recv == null then return null
+ var lc = type_recv.local_class
+ var prop: MMMethod = null
+ if lc.has_global_property_by_name(name) then prop = lc.select_method(name)
+ if prop == null and v.local_property.global.is_init then
+ var props = type_recv.local_class.super_methods_named(name)
+ if props.length > 1 then
+ v.error(self, "Error: Ambigous method name '{name}' for {props.join(", ")}. Use explicit designation.")
+ return null
+ else if props.length == 1 then
+ var p = type_recv.local_class[props.first.global]
+ assert p isa MMMethod
+ prop = p
+ end
+
+ end
+ if prop == null then
+ if is_implicit_self then
+ v.error(self, "Error: Method or variable '{name}' unknown in {type_recv}.")
+ else
+ v.error(self, "Error: Method '{name}' doesn't exists in {type_recv}.")
+ end
+ return null
+ end
+ return prop
+ end
+
+ # Get the signature for a local property and a receiver
+ private meth get_signature(v: TypingVisitor, type_recv: MMType, prop: MMMethod, recv_is_self: Bool): MMSignature
+ do
+ prop.global.check_visibility(v, self, v.module, recv_is_self)
+ var psig = prop.signature_for(type_recv)
+ if not recv_is_self then psig = psig.not_for_self
+ return psig
+ end
# The invoked method (once computed)
readable attr _prop: MMMethod
- # The real arguments used (after star transformation) (once computed)
- readable attr _arguments: Array[PExpr]
-
# The return type (if any) (once computed)
readable attr _return_type: MMType
end
end
end
end
+
super
end
-
+
redef meth closure_defs
do
if n_closure_defs == null or n_closure_defs.is_empty then
var va = variable
var sig = va.closure.signature
var args = process_signature(v, sig, n_id.to_symbol, n_args.to_a)
+ if closure_defs != null then
+ process_closures(v, sig, n_id.to_symbol, closure_defs)
+ end
if args == null then return
_prop = null
_prop_signature = sig
end
redef class PClosureDef
+ # The corresponding escapable object
+ readable attr _escapable: EscapableBlock
+
attr _accept_typing2: Bool
redef meth accept_typing(v)
do
if _accept_typing2 then super
end
- private meth accept_typing2(v: TypingVisitor, clos: MMClosure) is abstract
+ private meth accept_typing2(v: TypingVisitor, esc: EscapableClosure) is abstract
end
redef class AClosureDef
- redef meth accept_typing2(v, clos)
- do
- var sig = clos.signature
+ redef meth accept_typing2(v, esc)
+ do
+ _escapable = esc
+
+ var sig = esc.closure.signature
if sig.arity != n_id.length then
v.error(self, "Error: {sig.arity} automatic variable names expected, {n_id.length} found.")
return
end
- closure = clos
-
- var old_clos = v.closure
- v.closure = clos
+ closure = esc.closure
v.variable_ctx = v.variable_ctx.sub
variables = new Array[AutoVariable]
_accept_typing2 = true
accept_typing(v)
-
- v.closure = old_clos
end
end