Previously, no checking was done on the signature of operators.
While this is not an issue for the model nor the tools this could yield to not POLA error messages when the operator is used. eg.
~~~nit
class A
fun +(other: A) do print "hello"
end
var a = new A
var b = a + a # Error expected expression
a + a # Error unexpected operator +
# no way do invoke `+` in fact.
~~~
With the PR, we have
~~~
Error: mandatory return type for `+`.
fun +(other: A) do print "hello"
^
~~~
The following errors are added by the PR:
* mandatory return
* not enough parameters
* too much parameters
* illegal variadic parameter
Pull-Request: #1269
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
private var free_iterator: nullable ArrayIterator[E] = null
redef fun reverse_iterator do return new ArrayReverseIterator[E](self)
+
+ # Returns a sub-array containing `count` elements starting from `from`.
+ #
+ # For most cases (see other case bellow),
+ # the first element is `from` and
+ # the last element is `from+count-1`.
+ #
+ # ~~~
+ # var a = [10, 20, 30, 40, 50]
+ # assert a.sub(0, 3) == [10, 20, 30]
+ # assert a.sub(3, 2) == [40, 50]
+ # assert a.sub(3, 1) == [40]
+ # ~~~
+ #
+ # If `count` is 0 or negative then an empty array is returned
+ #
+ # ~~~
+ # assert a.sub(3,0).is_empty
+ # assert a.sub(3,-1).is_empty
+ # ~~~
+ #
+ # If `from < 0` or `from+count>length` then inexistent elements are ignored.
+ # In this case the length of the result is lower than count.
+ #
+ # ~~~
+ # assert a.sub(-2, 4) == [10, 20]
+ # assert a.sub(4, 99) == [50]
+ # assert a.sub(-9, 99) == [10,20,30,40,50]
+ # assert a.sub(-99, 9).is_empty
+ # ~~~
+ fun sub(from: Int, count: Int): Array[E] do
+ if from < 0 then
+ count += from
+ from = 0
+ end
+ if count < 0 then
+ count = 0
+ end
+ var to = from + count
+ if to > length then
+ to = length
+ end
+ var res = new Array[E].with_capacity(to - from)
+ while from < to do
+ res.add(self[from])
+ from += 1
+ end
+ return res
+ end
end
# Resizable one dimension array of objects.
# Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
# This method is used to manage varargs in signatures and returns the real array
# of runtime variables to use in the call.
- fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
+ fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
do
var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
var res = new Array[RuntimeVariable]
res.add(recv)
- if args.is_empty then return res
+ if msignature.arity == 0 then return res
- var vararg_rank = msignature.vararg_rank
- var vararg_len = args.length - msignature.arity
- if vararg_len < 0 then vararg_len = 0
+ if map == null then
+ assert args.length == msignature.arity
+ for ne in args do
+ res.add self.expr(ne, null)
+ end
+ return res
+ end
+
+ # Eval in order of arguments, not parameters
+ var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+ for ne in args do
+ exprs.add self.expr(ne, null)
+ end
+ # Fill `res` with the result of the evaluation according to the mapping
for i in [0..msignature.arity[ do
- if i == vararg_rank then
- var ne = args[i]
- if ne isa AVarargExpr then
- var e = self.expr(ne.n_expr, null)
- res.add(e)
- continue
- end
- var vararg = new Array[RuntimeVariable]
- for j in [vararg_rank..vararg_rank+vararg_len] do
- var e = self.expr(args[j], null)
- vararg.add(e)
- end
- var elttype = msignature.mparameters[vararg_rank].mtype
+ var param = msignature.mparameters[i]
+ var j = map.map.get_or_null(i)
+ if j == null then
+ continue
+ end
+ if param.is_vararg and map.vararg_decl > 0 then
+ var vararg = exprs.sub(j, map.vararg_decl)
+ var elttype = param.mtype
var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
res.add(arg)
- else
- var j = i
- if i > vararg_rank then j += vararg_len
- var e = self.expr(args[j], null)
- res.add(e)
+ continue
end
+ res.add exprs[j]
end
return res
end
do
var recv = v.expr(self.n_expr, null)
var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
return v.compile_callsite(callsite, args)
end
end
do
var recv = v.expr(self.n_expr, null)
var callsite = self.callsite.as(not null)
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
var value = v.expr(self.n_value, null)
var callsite = self.callsite
if callsite != null then
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var args
- # Add additional arguments for the super init call
- if args.length == 1 then
+ if self.n_args.n_exprs.is_empty then
+ # Add automatic arguments for the super init call
+ args = [recv]
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
end
+ else
+ args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
end
+
# Super init call
var res = v.compile_callsite(callsite, args)
return res
end
var mpropdef = self.mpropdef.as(not null)
- var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
- if args.length == 1 then
+
+ var args
+ if self.n_args.n_exprs.is_empty then
args = v.frame.arguments
+ else
+ args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
end
- # stantard call-next-method
+ # Standard call-next-method
return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
end
end
var callsite = self.callsite
if callsite == null then return recv
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
var res2 = v.compile_callsite(callsite, args)
if res2 != null then
#self.debug("got {res2} from {mproperty}. drop {recv}")
end
end
+redef class AVarargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr, null)
+ end
+end
+
redef class ADebugTypeExpr
redef fun stmt(v)
do
# This method is used to manage varargs in signatures and returns the real array
# of instances to use in the call.
# Return `null` if one of the evaluation of the arguments return null.
- fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
+ fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
do
var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
var res = new Array[Instance]
res.add(recv)
- if args.is_empty then return res
+ if msignature.arity == 0 then return res
+
+ if map == null then
+ assert args.length == msignature.arity else debug("Expected {msignature.arity} args, got {args.length}")
+ for ne in args do
+ var e = self.expr(ne)
+ if e == null then return null
+ res.add e
+ end
+ return res
+ end
+
+ # Eval in order of arguments, not parameters
+ var exprs = new Array[Instance].with_capacity(args.length)
+ for ne in args do
+ var e = self.expr(ne)
+ if e == null then return null
+ exprs.add e
+ end
- var vararg_rank = msignature.vararg_rank
- var vararg_len = args.length - msignature.arity
- if vararg_len < 0 then vararg_len = 0
+ # Fill `res` with the result of the evaluation according to the mapping
for i in [0..msignature.arity[ do
- if i == vararg_rank then
- var ne = args[i]
- if ne isa AVarargExpr then
- var e = self.expr(ne.n_expr)
- if e == null then return null
- res.add(e)
- continue
- end
- var vararg = new Array[Instance]
- for j in [vararg_rank..vararg_rank+vararg_len] do
- var e = self.expr(args[j])
- if e == null then return null
- vararg.add(e)
- end
- var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
- res.add(self.array_instance(vararg, elttype))
- else
- var j = i
- if i > vararg_rank then j += vararg_len
- var e = self.expr(args[j])
- if e == null then return null
- res.add(e)
+ var param = msignature.mparameters[i]
+ var j = map.map.get_or_null(i)
+ if j == null then
+ continue
+ end
+ if param.is_vararg and map.vararg_decl > 0 then
+ var vararg = exprs.sub(j, map.vararg_decl)
+ var elttype = param.mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
+ var arg = self.array_instance(vararg, elttype)
+ res.add(arg)
+ continue
end
+ res.add exprs[j]
end
return res
end
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
if args == null then return null
var res = v.callsite(callsite, args)
do
var recv = v.expr(self.n_expr)
if recv == null then return
- var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
if args == null then return
var value = v.expr(self.n_value)
if value == null then return
var callsite = self.callsite
if callsite != null then
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
- if args == null then return null
- # Add additional arguments for the super init call
- if args.length == 1 then
+ var args
+ if self.n_args.n_exprs.is_empty then
+ # Add automatic arguments for the super init call
+ args = [recv]
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
end
+ else
+ args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
+ if args == null then return null
end
+
# Super init call
var res = v.callsite(callsite, args)
return res
end
- # standard call-next-method
+ # Standard call-next-method
var mpropdef = self.mpropdef
mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
- var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
- if args == null then return null
-
- if args.length == 1 then
+ var args
+ if self.n_args.n_exprs.is_empty then
args = v.frame.arguments
+ else
+ args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
+ if args == null then return null
end
+
var res = v.call(mpropdef, args)
return res
end
var callsite = self.callsite
if callsite == null then return recv
- var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
if args == null then return null
var res2 = v.callsite(callsite, args)
if res2 != null then
end
end
+redef class AVarargExpr
+ redef fun expr(v)
+ do
+ return v.expr(self.n_expr)
+ end
+end
+
redef class ADebugTypeExpr
redef fun stmt(v)
do
# 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], mproperty: MProperty, 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
modelbuilder.error(node, "Error: expected at least {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
- return false
+ return null
end
else if args.length != msignature.arity then
modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
- return false
+ return null
end
#debug("CALL {unsafe_type}.{msignature}")
+ # Associate each parameter to a position in the arguments
+ var map = new SignatureMap
+
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]
+ 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
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
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)
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, self.msignature)
+ var map = v.check_signature(self.node, args, self.mproperty, self.msignature)
+ signaturemap = map
+ return map == null
end
end
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, 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
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:911)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:960)
NativeString
N
Nit