# If `sub` is a safe subtype of `sup` then return `sub`.
# If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`.
#
- # The point of the return type is to determinate the usable type on an expression:
+ # The point of the return type is to determinate the usable type on an expression when `autocast` is true:
# If the suptype is safe, then the return type is the one on the expression typed by `sub`.
# Is the subtype is unsafe, then the return type is the one of an implicit cast on `sup`.
- fun check_subtype(node: ANode, sub, sup: MType): nullable MType
+ fun check_subtype(node: ANode, sub, sup: MType, autocast: Bool): nullable MType
do
if self.is_subtype(sub, sup) then return sub
- if self.is_subtype(sub, self.anchor_to(sup)) then
+ if autocast and self.is_subtype(sub, self.anchor_to(sup)) then
# FIXME workaround to the current unsafe typing policy. To remove once fixed virtual types exists.
#node.debug("Unsafe typing: expected {sup}, got {sub}")
return sup
end
if sub.need_anchor then
var u = anchor_to(sub)
- self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}")
+ self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
else
- self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+ self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}`.")
end
return null
end
end
return null # forward error
end
- self.error(nexpr, "Type error: expected expression.")
+ self.error(nexpr, "Error: expected an expression.")
return null
end
if sup == null then return null # Forward error
- var res = check_subtype(nexpr, sub, sup)
+ var res = check_subtype(nexpr, sub, sup, true)
if res != sub then
nexpr.implicit_cast_to = res
end
if sup == null then return null # Forward error
if sup == sub then
- self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup}.")
+ self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}`.")
else if self.is_subtype(sub, sup) then
- self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup} since it is a {sub}.")
+ self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}` since it is a `{sub}`.")
end
return sup
end
if not mtype2 isa MNullType then return
- if mtype isa MNullType then return
-
# Check of useless null
if not check_can_be_null(anode.n_expr, mtype) then return
- mtype = mtype.as_notnull
+ if mtype isa MNullType then
+ # Because of type adaptation, we cannot just stop here
+ # so return use `null` as a bottom type that will be merged easily (cf) `merge_types`
+ mtype = null
+ else
+ mtype = mtype.as_notnull
+ end
# Check for type adaptation
var variable = anode.n_expr.its_variable
# One is null (mtype2 see above) the other is not null
if anode isa AEqExpr then
- anode.after_flow_context.when_true.set_var(variable, mtype2)
- anode.after_flow_context.when_false.set_var(variable, mtype)
+ anode.after_flow_context.when_true.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_false.set_var(self, variable, mtype)
else if anode isa ANeExpr then
- anode.after_flow_context.when_false.set_var(variable, mtype2)
- anode.after_flow_context.when_true.set_var(variable, mtype)
+ anode.after_flow_context.when_false.set_var(self, variable, mtype2)
+ anode.after_flow_context.when_true.set_var(self, variable, mtype)
else
abort
end
if objclass == null then return null # Forward error
unsafe_type = objclass.mclass_type
else
- self.error(node, "Error: Method '{name}' call on 'null'.")
+ self.error(node, "Error: method `{name}` called on `null`.")
return null
end
end
end
if mproperty == null then
- #self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})")
if recv_is_self then
- self.modelbuilder.error(node, "Error: Method or variable '{name}' unknown in {recvtype}.")
+ self.modelbuilder.error(node, "Error: method or variable `{name}` unknown in `{recvtype}`.")
+ else if recvtype.need_anchor then
+ self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}: {unsafe_type}`.")
else
- self.modelbuilder.error(node, "Error: Method '{name}' doesn't exists in {recvtype}.")
+ self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}`.")
end
return null
end
assert mproperty isa MMethod
if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then
- error(node, "Error: '{name}' is not a top-level method, thus need a receiver.")
+ error(node, "Error: `{name}` is not a top-level method, thus need a receiver.")
end
if not recv_is_self and mproperty.is_toplevel then
- error(node, "Error: cannot call '{name}', a top-level method, with a receiver.")
+ error(node, "Error: cannot call `{name}`, a top-level method, with a receiver.")
end
if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then
- self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.")
+ self.modelbuilder.error(node, "Error: method `{name}` is protected and can only accessed by `self`.")
return null
end
if info != null and self.mpropdef.mproperty.deprecation == null then
var mdoc = info.mdoc
if mdoc != null then
- self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated: {mdoc.content.first}")
+ self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated: {mdoc.content.first}")
else
- self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated.")
+ self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated.")
end
end
var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type)
var mpropdef
if propdefs.length == 0 then
- self.modelbuilder.error(node, "Type error: no definition found for property {name} in {unsafe_type}")
+ self.modelbuilder.error(node, "Type Error: no definition found for property `{name}` in `{unsafe_type}`.")
return null
else if propdefs.length == 1 then
mpropdef = propdefs.first
else
- self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}")
+ self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{name}` in `{unsafe_type}`: {propdefs.join(" ")}")
mpropdef = mproperty.intro
end
# Visit the expressions of args and check their conformity with the corresponding type in signature
# The point of this method is to handle varargs correctly
# Note: The signature must be correctly adapted
- fun check_signature(node: ANode, args: Array[AExpr], name: String, msignature: MSignature): Bool
+ fun check_signature(node: ANode, args: Array[AExpr], mproperty: MProperty, msignature: MSignature): nullable SignatureMap
do
var vararg_rank = msignature.vararg_rank
if vararg_rank >= 0 then
if args.length < msignature.arity then
- #self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected at least {msignature.arity}. Signature is {msignature}")
- self.modelbuilder.error(node, "Error: arity mismatch; prototype is '{name}{msignature}'")
- return false
+ modelbuilder.error(node, "Error: expected at least {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+ return null
end
else if args.length != msignature.arity then
- self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected {msignature.arity}. Signature is {msignature}")
- return false
+ if msignature.arity == msignature.min_arity then
+ modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+ return null
+ end
+ if args.length > msignature.arity then
+ modelbuilder.error(node, "Error: expected at most {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+ return null
+ end
+ if args.length < msignature.min_arity then
+ modelbuilder.error(node, "Error: expected at least {msignature.min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+ return null
+ end
end
#debug("CALL {unsafe_type}.{msignature}")
+ # Associate each parameter to a position in the arguments
+ var map = new SignatureMap
+
+ var setted = args.length - msignature.min_arity
var vararg_decl = args.length - msignature.arity
+ var j = 0
for i in [0..msignature.arity[ do
- var j = i
- if i == vararg_rank then continue # skip the vararg
- if i > vararg_rank then
- j = i + vararg_decl
+ var param = msignature.mparameters[i]
+ if param.is_default then
+ if setted > 0 then
+ setted -= 1
+ else
+ continue
+ end
+ end
+ var arg = args[j]
+ map.map[i] = j
+ j += 1
+
+ if i == vararg_rank then
+ j += vararg_decl
+ continue # skip the vararg
end
- var paramtype = msignature.mparameters[i].mtype
- self.visit_expr_subtype(args[j], paramtype)
+
+ var paramtype = param.mtype
+ self.visit_expr_subtype(arg, paramtype)
end
if vararg_rank >= 0 then
var paramtype = msignature.mparameters[vararg_rank].mtype
var first = args[vararg_rank]
- if vararg_decl == 0 and first isa AVarargExpr then
+ if vararg_decl == 0 then
var mclass = get_mclass(node, "Array")
- if mclass == null then return false # Forward error
+ if mclass == null then return null # Forward error
var array_mtype = mclass.get_mtype([paramtype])
- self.visit_expr_subtype(first.n_expr, array_mtype)
- first.mtype = first.n_expr.mtype
+ if first isa AVarargExpr then
+ self.visit_expr_subtype(first.n_expr, array_mtype)
+ first.mtype = first.n_expr.mtype
+ else
+ # only one vararg, maybe `...` was forgot, so be gentle!
+ var t = visit_expr(first)
+ if t == null then return null # Forward error
+ if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
+ # Not acceptable but could be a `...`
+ error(first, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
+ return null
+ end
+ # Standard valid vararg, finish the job
+ map.vararg_decl = 1
+ self.visit_expr_subtype(first, paramtype)
+ end
else
- for j in [vararg_rank..vararg_rank+vararg_decl] do
- self.visit_expr_subtype(args[j], paramtype)
+ map.vararg_decl = vararg_decl + 1
+ for i in [vararg_rank..vararg_rank+vararg_decl] do
+ self.visit_expr_subtype(args[i], paramtype)
end
end
end
- return true
+
+ return map
end
fun error(node: ANode, message: String)
fun get_variable(node: AExpr, variable: Variable): nullable MType
do
+ if not variable.is_adapted then return variable.declared_type
+
var flow = node.after_flow_context
- if flow == null then
- self.error(node, "No context!")
- return null
- end
+ if flow == null then return null # skip error
if flow.vars.has_key(variable) then
return flow.vars[variable]
#node.debug("*** START Collected for {variable}")
var mtypes = flow.collect_types(variable)
#node.debug("**** END Collected for {variable}")
- if mtypes == null or mtypes.length == 0 then
+ if mtypes.length == 0 then
return variable.declared_type
else if mtypes.length == 1 then
return mtypes.first
end
end
+ # Some variables where type-adapted during the visit
+ var dirty = false
+
+ # Some loops had been visited during the visit
+ var has_loop = false
+
fun set_variable(node: AExpr, variable: Variable, mtype: nullable MType)
do
var flow = node.after_flow_context
assert flow != null
- flow.set_var(variable, mtype)
+ flow.set_var(self, variable, mtype)
end
fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
end
end
+# Mapping between parameters and arguments in a call.
+#
+# Parameters and arguments are not stored in the class but referenced by their position (starting from 0)
+#
+# The point of this class is to help engine and other things to map arguments in the AST to parameters of the model.
+class SignatureMap
+ # Associate a parameter to an argument
+ var map = new ArrayMap[Int, Int]
+
+ # The length of the vararg sequence
+ # 0 if no vararg or if reverse vararg (cf `AVarargExpr`)
+ var vararg_decl: Int = 0
+end
+
# A specific method call site with its associated informations.
class CallSite
# The associated node for location
# Is a implicit cast required on erasure typing policy?
var erasure_cast: Bool
+ # The mapping used on the call to associate arguments to parameters
+ # If null then no specific association is required.
+ var signaturemap: nullable SignatureMap = null
+
private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool
do
- return v.check_signature(self.node, args, self.mproperty.name, self.msignature)
+ var map = v.check_signature(self.node, args, self.mproperty, self.msignature)
+ signaturemap = map
+ return map == null
end
end
redef class Variable
# The declared type of the variable
var declared_type: nullable MType
+
+ # Was the variable type-adapted?
+ # This is used to speedup type retrieval while it remains `false`
+ private var is_adapted = false
end
redef class FlowContext
# Store changes of types because of type evolution
private var vars = new HashMap[Variable, nullable MType]
- private var cache = new HashMap[Variable, nullable Array[nullable MType]]
# Adapt the variable to a static type
# Warning1: do not modify vars directly.
# Warning2: sub-flow may have cached a unadapted variable
- private fun set_var(variable: Variable, mtype: nullable MType)
+ private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType)
do
+ if variable.declared_type == mtype and not variable.is_adapted then return
+ if vars.has_key(variable) and vars[variable] == mtype then return
self.vars[variable] = mtype
- self.cache.keys.remove(variable)
+ v.dirty = true
+ variable.is_adapted = true
+ #node.debug "set {variable} to {mtype or else "X"}"
end
- private fun collect_types(variable: Variable): nullable Array[nullable MType]
+ # Look in the flow and previous flow and collect all first reachable type adaptation of a local variable
+ private fun collect_types(variable: Variable): Array[nullable MType]
do
- if cache.has_key(variable) then
- return cache[variable]
- end
- var res: nullable Array[nullable MType] = null
- if vars.has_key(variable) then
- var mtype = vars[variable]
- res = [mtype]
- else if self.previous.is_empty then
- # Root flow
- res = [variable.declared_type]
- else
- for flow in self.previous do
- if flow.is_unreachable then continue
- var r2 = flow.collect_types(variable)
- if r2 == null then continue
- if res == null then
- res = r2.to_a
- else
- for t in r2 do
- if not res.has(t) then res.add(t)
- end
+ #node.debug "flow for {variable}"
+ var res = new Array[nullable MType]
+
+ var todo = [self]
+ var seen = new HashSet[FlowContext]
+ while not todo.is_empty do
+ var f = todo.pop
+ if f.is_unreachable then continue
+ if seen.has(f) then continue
+ seen.add f
+
+ if f.vars.has_key(variable) then
+ # Found something. Collect it and do not process further on this path
+ res.add f.vars[variable]
+ #f.node.debug "process {variable}: got {f.vars[variable] or else "X"}"
+ else
+ todo.add_all f.previous
+ todo.add_all f.loops
+ if f.previous.is_empty then
+ # Root flowcontext mean a parameter or something related
+ res.add variable.declared_type
+ #f.node.debug "root process {variable}: got {variable.declared_type or else "X"}"
end
end
end
- cache[variable] = res
+ #self.node.debug "##### end flow for {variable}: {res.join(" ")}"
return res
end
end
assert variable != null
variable.declared_type = mtype
end
- v.visit_stmt(nblock)
+
+ loop
+ v.dirty = false
+ v.visit_stmt(nblock)
+ if not v.has_loop or not v.dirty then break
+ end
if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then
# We reach the end of the function without having a return, it is bad
- v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
+ v.error(self, "Error: reached end of function; expected `return` with a value.")
end
end
end
v.visit_stmt(nblock)
if not nblock.after_flow_context.is_unreachable then
# We reach the end of the init without having a return, it is bad
- v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).")
+ v.error(self, "Error: reached end of block; expected `return`.")
end
end
end
# Return the static type of the value to store.
private fun resolve_reassignment(v: TypeVisitor, readtype, writetype: MType): nullable MType
do
- var reassign_name: String
- if self.n_assign_op isa APlusAssignOp then
- reassign_name = "+"
- else if self.n_assign_op isa AMinusAssignOp then
- reassign_name = "-"
- else
- abort
- end
+ var reassign_name = self.n_assign_op.operator
self.read_type = readtype
- var callsite = v.get_method(self, readtype, reassign_name, false)
+ var callsite = v.get_method(self.n_assign_op, readtype, reassign_name, false)
if callsite == null then return null # Skip error
self.reassign_callsite = callsite
var value_type = v.visit_expr_subtype(self.n_value, msignature.mparameters.first.mtype)
if value_type == null then return null # Skip error
- v.check_subtype(self, rettype, writetype)
+ v.check_subtype(self, rettype, writetype, false)
return rettype
end
end
v.visit_expr_subtype(nexpr, ret_type)
else
v.visit_expr(nexpr)
- v.error(self, "Error: Return with value in a procedure.")
+ v.error(nexpr, "Error: `return` with value in a procedure.")
end
else if ret_type != null then
- v.error(self, "Error: Return without value in a function.")
+ v.error(self, "Error: `return` without value in a function.")
end
self.is_typed = true
end
var t = v.merge_types(self, [t1, t2])
if t == null then
- v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
+ v.error(self, "Type Error: ambiguous type `{t1}` vs `{t2}`.")
end
self.mtype = t
end
redef class AWhileExpr
redef fun accept_typing(v)
do
+ v.has_loop = true
v.visit_expr_bool(n_expr)
-
v.visit_stmt(n_block)
self.is_typed = true
end
redef class ALoopExpr
redef fun accept_typing(v)
do
+ v.has_loop = true
v.visit_stmt(n_block)
self.is_typed = true
end
private fun do_type_iterator(v: TypeVisitor, mtype: MType)
do
if mtype isa MNullType then
- v.error(self, "Type error: 'for' cannot iterate over 'null'")
+ v.error(self, "Type Error: `for` cannot iterate over `null`.")
return
end
# check iterator method
var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr)
if itdef == null then
- v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.")
+ v.error(self, "Type Error: `for` expects a type providing an `iterator` method, got `{mtype}`.")
return
end
self.method_iterator = itdef
# check that iterator return something
var ittype = itdef.msignature.return_mtype
if ittype == null then
- v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.")
+ v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.")
return
end
var coltype = ittype.supertype_to(v.mmodule, v.anchor, colit_cla)
var variables = self.variables
if variables.length != 1 then
- v.error(self, "Type Error: 'for' expects only one variable when using 'Iterator'.")
+ v.error(self, "Type Error: `for` expects only one variable when using `Iterator`.")
else
variables.first.declared_type = coltype.arguments.first
end
var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla)
var variables = self.variables
if variables.length != 2 then
- v.error(self, "Type Error: 'for' expects two variables when using 'MapIterator'.")
+ v.error(self, "Type Error: `for` expects two variables when using `MapIterator`.")
else
variables[0].declared_type = coltype.arguments[0]
variables[1].declared_type = coltype.arguments[1]
end
if not is_col and not is_map then
- v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.")
+ v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.")
return
end
# get methods is_ok, next, item
var ikdef = v.get_method(self, ittype, "is_ok", false)
if ikdef == null then
- v.error(self, "Type Error: 'for' expects a method 'is_ok' in 'Iterator' type {ittype}.")
+ v.error(self, "Type Error: `for` expects a method `is_ok` in type `{ittype}`.")
return
end
self.method_is_ok = ikdef
var itemdef = v.get_method(self, ittype, "item", false)
if itemdef == null then
- v.error(self, "Type Error: 'for' expects a method 'item' in 'Iterator' type {ittype}.")
+ v.error(self, "Type Error: `for` expects a method `item` in type `{ittype}`.")
return
end
self.method_item = itemdef
var nextdef = v.get_method(self, ittype, "next", false)
if nextdef == null then
- v.error(self, "Type Error: 'for' expects a method 'next' in 'Iterator' type {ittype}.")
+ v.error(self, "Type Error: `for` expects a method `next` in type {ittype}.")
return
end
self.method_next = nextdef
if is_map then
var keydef = v.get_method(self, ittype, "key", false)
if keydef == null then
- v.error(self, "Type Error: 'for' expects a method 'key' in 'Iterator' type {ittype}.")
+ v.error(self, "Type Error: `for` expects a method `key` in type `{ittype}`.")
return
end
self.method_key = keydef
redef fun accept_typing(v)
do
+ v.has_loop = true
var mtype = v.visit_expr(n_expr)
if mtype == null then return
self.do_type_iterator(v, mtype)
v.visit_stmt(n_block)
+
self.mtype = n_block.mtype
self.is_typed = true
end
end
if t1 isa MNullType then
- v.error(n_expr, "Type error: or else on null")
+ v.error(n_expr, "Type Error: `or else` on `null`.")
else if v.check_can_be_null(n_expr, t1) then
t1 = t1.as_notnull
end
end
set_comprehension(e)
if mtype != null then
- if v.check_subtype(e, t, mtype) == null then return # Skip error
+ if v.check_subtype(e, t, mtype, false) == null then return # Forward error
if t == mtype then useless = true
else
mtypes.add(t)
end
end
if mtype == null then
+ # Ensure monotony for type adaptation on loops
+ if self.element_mtype != null then mtypes.add self.element_mtype
mtype = v.merge_types(self, mtypes)
end
if mtype == null or mtype isa MNullType then
else if v.is_subtype(t2, t1) then
mtype = mclass.get_mtype([t1])
else
- v.error(self, "Type Error: Cannot create range: {t1} vs {t2}")
+ v.error(self, "Type Error: cannot create range: `{t1}` vs `{t2}`.")
return
end
#var from = if orig != null then orig.to_s else "invalid"
#var to = if mtype != null then mtype.to_s else "invalid"
#debug("adapt {variable}: {from} -> {to}")
- self.after_flow_context.when_true.set_var(variable, mtype)
+ self.after_flow_context.when_true.set_var(v, variable, mtype)
end
self.mtype = v.type_bool(self)
if mtype == null then return # Forward error
if mtype isa MNullType then
- v.error(self, "Type error: as(not null) on null")
+ v.error(self, "Type Error: `as(not null)` on `null`.")
return
end
redef fun accept_typing(v)
do
if v.is_toplevel_context and not self isa AImplicitSelfExpr then
- v.error(self, "Error: self cannot be used in top-level method.")
+ v.error(self, "Error: `self` cannot be used in top-level method.")
end
var variable = v.selfvariable
self.its_variable = variable
var nrecv = self.n_expr
var recvtype = v.visit_expr(nrecv)
var name = self.property_name
+ var node = self.property_node
if recvtype == null then return # Forward error
var callsite = null
var unsafe_type = v.anchor_to(recvtype)
- var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name)
+ var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name)
if mproperty == null and nrecv isa AImplicitSelfExpr then
# Special fall-back search in `sys` when noting found in the implicit receiver.
- var sysclass = v.try_get_mclass(self, "Sys")
+ var sysclass = v.try_get_mclass(node, "Sys")
if sysclass != null then
var systype = sysclass.mclass_type
- mproperty = v.try_get_mproperty_by_name2(self, systype, name)
+ mproperty = v.try_get_mproperty_by_name2(node, systype, name)
if mproperty != null then
- callsite = v.get_method(self, systype, name, false)
+ callsite = v.get_method(node, systype, name, false)
if callsite == null then return # Forward error
# Update information, we are looking at `sys` now, not `self`
nrecv.is_sys = true
end
if callsite == null then
# If still nothing, just exit
- callsite = v.get_method(self, recvtype, name, nrecv isa ASelfExpr)
+ callsite = v.get_method(node, recvtype, name, nrecv isa ASelfExpr)
if callsite == null then return
end
if callsite.mproperty.is_init then
var vmpropdef = v.mpropdef
if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then
- v.error(self, "Can call a init only in another init")
+ v.error(node, "Error: an `init` can only be called from another `init`.")
end
if vmpropdef isa MMethodDef and vmpropdef.mproperty.is_root_init and not callsite.mproperty.is_root_init then
- v.error(self, "Error: {vmpropdef} cannot call a factory {callsite.mproperty}")
+ v.error(node, "Error: `{vmpropdef}` cannot call a factory `{callsite.mproperty}`.")
end
end
# Each subclass simply provide the correct name.
private fun property_name: String is abstract
+ # The node identifying the name (id, operator, etc) for messages.
+ #
+ # Is `self` by default
+ private fun property_node: ANode do return self
+
# An array of all arguments (excluding self)
fun raw_arguments: Array[AExpr] do return compute_raw_arguments
redef class ABinopExpr
redef fun compute_raw_arguments do return [n_expr2]
+ redef fun property_name do return operator
+ redef fun property_node do return n_op
end
redef class AEqExpr
- redef fun property_name do return "=="
redef fun accept_typing(v)
do
super
end
end
redef class ANeExpr
- redef fun property_name do return "!="
redef fun accept_typing(v)
do
super
v.null_test(self)
end
end
-redef class ALtExpr
- redef fun property_name do return "<"
-end
-redef class ALeExpr
- redef fun property_name do return "<="
-end
-redef class ALlExpr
- redef fun property_name do return "<<"
-end
-redef class AGtExpr
- redef fun property_name do return ">"
-end
-redef class AGeExpr
- redef fun property_name do return ">="
-end
-redef class AGgExpr
- redef fun property_name do return ">>"
-end
-redef class APlusExpr
- redef fun property_name do return "+"
-end
-redef class AMinusExpr
- redef fun property_name do return "-"
-end
-redef class AStarshipExpr
- redef fun property_name do return "<=>"
-end
-redef class AStarExpr
- redef fun property_name do return "*"
-end
-redef class AStarstarExpr
- redef fun property_name do return "**"
-end
-redef class ASlashExpr
- redef fun property_name do return "/"
-end
-redef class APercentExpr
- redef fun property_name do return "%"
+
+redef class AUplusExpr
+ redef fun property_name do return "unary +"
+ redef fun compute_raw_arguments do return new Array[AExpr]
end
redef class AUminusExpr
redef class ACallExpr
redef fun property_name do return n_id.text
+ redef fun property_node do return n_id
redef fun compute_raw_arguments do return n_args.to_a
end
redef class ACallAssignExpr
redef fun property_name do return n_id.text + "="
+ redef fun property_node do return n_id
redef fun compute_raw_arguments
do
var res = n_args.to_a
do
var recvtype = v.visit_expr(self.n_expr)
var name = self.property_name
+ var node = self.property_node
if recvtype == null then return # Forward error
var for_self = self.n_expr isa ASelfExpr
- var callsite = v.get_method(self, recvtype, name, for_self)
+ var callsite = v.get_method(node, recvtype, name, for_self)
if callsite == null then return
self.callsite = callsite
var readtype = callsite.msignature.return_mtype
if readtype == null then
- v.error(self, "Error: {name} is not a function")
+ v.error(node, "Error: `{name}` is not a function.")
return
end
- var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr)
+ var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
if wcallsite == null then return
self.write_callsite = wcallsite
redef class ACallReassignExpr
redef fun property_name do return n_id.text
+ redef fun property_node do return n_id
redef fun compute_raw_arguments do return n_args.to_a
end
redef class AInitExpr
redef fun property_name do return "init"
+ redef fun property_node do return n_kwinit
redef fun compute_raw_arguments do return n_args.to_a
end
assert recvtype != null
var mproperty = v.mpropdef.mproperty
if not mproperty isa MMethod then
- v.error(self, "Error: super only usable in a method")
+ v.error(self, "Error: `super` only usable in a `method`.")
return
end
var superprops = mproperty.lookup_super_definitions(v.mmodule, anchor)
process_superinit(v)
return
end
- v.error(self, "Error: No super method to call for {mproperty}.")
+ v.error(self, "Error: no super method to call for `{mproperty}`.")
return
end
# FIXME: covariance of return type in linear extension?
msignature = v.resolve_for(msignature, recvtype, true).as(MSignature)
var args = self.n_args.to_a
if args.length > 0 then
- v.check_signature(self, args, mproperty.name, msignature)
+ signaturemap = v.check_signature(self, args, mproperty, msignature)
end
self.mtype = msignature.return_mtype
self.is_typed = true
mpropdef = v.mpropdef.as(MMethodDef)
end
+ # The mapping used on the call to associate arguments to parameters.
+ # If null then no specific association is required.
+ var signaturemap: nullable SignatureMap
+
private fun process_superinit(v: TypeVisitor)
do
var anchor = v.anchor
continue
end
if superprop != null and superprop.mproperty != candidate and not superprop.mproperty.is_root_init then
- v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}")
+ v.error(self, "Error: conflicting super constructor to call for `{mproperty}`: `{candidate.full_name}`, `{superprop.mproperty.full_name}`")
return
end
var candidatedefs = candidate.lookup_definitions(v.mmodule, anchor)
candidatedefs.add(superprop)
end
if candidatedefs.length > 1 then
- v.error(self, "Error: conflicting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}")
+ v.error(self, "Error: conflicting property definitions for property `{mproperty}` in `{recvtype}`: {candidatedefs.join(", ")}")
return
end
superprop = candidatedefs.first
end
if superprop == null then
- v.error(self, "Error: No super method to call for {mproperty}.")
+ v.error(self, "Error: no super method to call for `{mproperty}`.")
return
end
else
# Check there is at least enough parameters
if mpropdef.msignature.arity < msignature.arity then
- v.error(self, "Error: Not enough implicit arguments to pass. Got {mpropdef.msignature.arity}, expected at least {msignature.arity}. Signature is {msignature}")
+ v.error(self, "Error: not enough implicit arguments to pass. Got `{mpropdef.msignature.arity}`, expected at least `{msignature.arity}`. Signature is `{msignature}`.")
return
end
# Check that each needed parameter is conform
for sp in msignature.mparameters do
var p = mpropdef.msignature.mparameters[i]
if not v.is_subtype(p.mtype, sp.mtype) then
- v.error(self, "Type error: expected argument #{i} of type {sp.mtype}, got implicit argument {p.name} of type {p.mtype}. Signature is {msignature}")
+ v.error(self, "Type Error: expected argument #{i} of type `{sp.mtype}`, got implicit argument `{p.name}` of type `{p.mtype}`. Signature is {msignature}")
return
end
i += 1
if not recvtype isa MClassType then
if recvtype isa MNullableType then
- v.error(self, "Type error: cannot instantiate the nullable type {recvtype}.")
+ v.error(self, "Type Error: cannot instantiate the nullable type `{recvtype}`.")
+ return
+ else if recvtype isa MFormalType then
+ v.error(self, "Type Error: cannot instantiate the formal type `{recvtype}`.")
return
else
- v.error(self, "Type error: cannot instantiate the formal type {recvtype}.")
+ v.error(self, "Type Error: cannot instantiate the type `{recvtype}`.")
return
end
end
var name: String
var nid = self.n_id
+ var node: ANode
if nid != null then
name = nid.text
+ node = nid
else
name = "new"
+ node = self.n_kwnew
end
if name == "intern" then
if kind != concrete_kind then
- v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+ v.error(self, "Type Error: cannot instantiate {kind} {recvtype}.")
return
end
if n_args.n_exprs.not_empty then
return
end
- var callsite = v.get_method(self, recvtype, name, false)
+ var callsite = v.get_method(node, recvtype, name, false)
if callsite == null then return
if not callsite.mproperty.is_new then
if kind != concrete_kind then
- v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+ v.error(self, "Type Error: cannot instantiate {kind} `{recvtype}`.")
return
end
self.mtype = recvtype
self.callsite = callsite
if not callsite.mproperty.is_init_for(recvtype.mclass) then
- v.error(self, "Error: {name} is not a constructor.")
+ v.error(self, "Error: `{name}` is not a constructor.")
return
end
####
redef class AAttrFormExpr
- # The attribute acceded.
+ # The attribute accessed.
var mproperty: nullable MAttribute
# The static type of the attribute.
var attr_type: nullable MType
- # Resolve the attribute acceded.
+ # Resolve the attribute accessed.
private fun resolve_property(v: TypeVisitor)
do
var recvtype = v.visit_expr(self.n_expr)
if recvtype == null then return # Skip error
- var name = self.n_id.text
+ var node = self.n_id
+ var name = node.text
if recvtype isa MNullType then
- v.error(self, "Error: Attribute '{name}' access on 'null'.")
+ v.error(node, "Error: attribute `{name}` access on `null`.")
return
end
var unsafe_type = v.anchor_to(recvtype)
- var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name)
+ var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name)
if mproperty == null then
- v.modelbuilder.error(self, "Error: Attribute {name} doesn't exists in {recvtype}.")
+ v.modelbuilder.error(node, "Error: attribute `{name}` does not exist in `{recvtype}`.")
return
end
assert mproperty isa MAttribute
var recvtype = self.n_expr.mtype.as(not null)
var bound = v.resolve_for(mtype, recvtype, false)
if bound isa MNullableType then
- v.error(self, "Error: isset on a nullable attribute.")
+ v.error(n_id, "Type Error: `isset` on a nullable attribute.")
end
self.mtype = v.type_bool(self)
end
# This kind of pseudo-expression can be only processed trough a signature
# See `check_signature`
# Other cases are a syntax error.
- v.error(self, "Syntax error: unexpected `...`")
+ v.error(self, "Syntax Error: unexpected `...`.")
end
end