Callref bugfix in interpreter and compilers + autosav
[nit.git] / src / semantize / typing.nit
index 3f973fd..b9acd5e 100644 (file)
@@ -181,7 +181,6 @@ private class TypeVisitor
                return self.visit_expr_subtype(nexpr, self.type_bool(nexpr))
        end
 
-
        fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
        do
                var sub = nexpr.mtype
@@ -297,7 +296,13 @@ private class TypeVisitor
                return mclass.mclass_type
        end
 
-       fun get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+       # Construction of a specific callsite according to the current context.
+       # Three entry points exist to create a callsite based on knowledge.
+       # The `build_callsite_by_name` is a top entry point, the method find the mpropdefs to call by the name of this.
+       # see `build_callsite_by_property` and `build_callsite_by_propdef` for more detail.
+       # If you already know the mpropdef to call use directly the `get_method_by_propdef` method
+       # If you just know the mproperty use the `build_callsite_by_property` method to display error if no `mpropdef` is found in the context
+       fun build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
        do
                var unsafe_type = self.anchor_to(recvtype)
 
@@ -327,23 +332,37 @@ private class TypeVisitor
 
                assert mproperty isa MMethod
 
+               return build_callsite_by_property(node, recvtype, mproperty, recv_is_self)
+       end
+
+       # The `build_callsite_by_property` finds the mpropdefs to call by the `MMethod`.
+       # If the mpropdef is found in the context it builds a new `Callsite`.
+       fun build_callsite_by_property(node: ANode, recvtype: MType, mproperty: MMethod, recv_is_self: Bool): nullable CallSite
+       do
+               var unsafe_type = self.anchor_to(recvtype)
+
+               if recvtype isa MNullType then
+                       var objclass = get_mclass(node, "Object")
+                       if objclass == null then return null # Forward error
+                       unsafe_type = objclass.mclass_type
+               end
                # `null` only accepts some methods of object.
                if recvtype isa MNullType and not mproperty.is_null_safe then
-                       self.error(node, "Error: method `{name}` called on `null`.")
+                       self.error(node, "Error: method `{mproperty.name}` called on `null`.")
                        return null
                else if unsafe_type isa MNullableType and not mproperty.is_null_safe then
                        modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.")
                end
 
                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: `{mproperty.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 `{mproperty.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 accessed by `self`.")
+                       self.modelbuilder.error(node, "Error: method `{mproperty.name}` is protected and can only accessed by `self`.")
                        return null
                end
 
@@ -351,25 +370,31 @@ private class TypeVisitor
                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 `{mproperty.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 `{mproperty.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}`.")
-                       return null
+                       self.modelbuilder.error(node, "Type Error: no definition found for property `{mproperty.name}` in `{unsafe_type}`.")
+                       abort
+                       #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 `{mproperty.name}` in `{unsafe_type}`: {propdefs.join(" ")}")
                        mpropdef = mproperty.intro
                end
 
+               return build_callsite_by_propdef(node, recvtype, mpropdef, recv_is_self)
+       end
 
+       # The `build_callsite_by_propdef` builds the callsite directly with the `mprodef` passed in argument.
+       fun build_callsite_by_propdef(node: ANode, recvtype: MType, mpropdef: MMethodDef, recv_is_self: Bool): nullable CallSite
+       do
                var msignature = mpropdef.new_msignature or else mpropdef.msignature
                if msignature == null then return null # skip error
                msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature)
@@ -386,19 +411,18 @@ private class TypeVisitor
                        end
                end
 
-               var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast)
+               var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mpropdef.mproperty, mpropdef, msignature, erasure_cast)
                return callsite
        end
 
-       fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+       fun try_build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
        do
                var unsafe_type = self.anchor_to(recvtype)
                var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
                if mproperty == null then return null
-               return get_method(node, recvtype, name, recv_is_self)
+               return build_callsite_by_name(node, recvtype, name, recv_is_self)
        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
@@ -419,7 +443,6 @@ private class TypeVisitor
                        # Other cases are managed later
                end
 
-
                #debug("CALL {unsafe_type}.{msignature}")
 
                # Associate each parameter to a position in the arguments
@@ -1113,7 +1136,7 @@ redef class AReassignFormExpr
 
                self.read_type = readtype
 
-               var callsite = v.get_method(self.n_assign_op, readtype, reassign_name, false)
+               var callsite = v.build_callsite_by_name(self.n_assign_op, readtype, reassign_name, false)
                if callsite == null then return null # Skip error
                self.reassign_callsite = callsite
 
@@ -1151,7 +1174,6 @@ redef class AVarReassignExpr
        end
 end
 
-
 redef class AContinueExpr
        redef fun accept_typing(v)
        do
@@ -1318,7 +1340,7 @@ redef class AForGroup
                if objcla == null then return
 
                # check iterator method
-               var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr)
+               var itdef = v.build_callsite_by_name(self, mtype, "iterator", n_expr isa ASelfExpr)
                if itdef == null then
                        v.error(self, "Type Error: `for` expects a type providing an `iterator` method, got `{mtype}`.")
                        return
@@ -1375,31 +1397,31 @@ redef class AForGroup
                self.coltype = mtype.as(MClassType)
 
                # get methods is_ok, next, item
-               var ikdef = v.get_method(self, ittype, "is_ok", false)
+               var ikdef = v.build_callsite_by_name(self, ittype, "is_ok", false)
                if ikdef == null then
                        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)
+               var itemdef = v.build_callsite_by_name(self, ittype, "item", false)
                if itemdef == null then
                        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)
+               var nextdef = v.build_callsite_by_name(self, ittype, "next", false)
                if nextdef == null then
                        v.error(self, "Type Error: `for` expects a method `next` in type {ittype}.")
                        return
                end
                self.method_next = nextdef
 
-               self.method_finish = v.try_get_method(self, ittype, "finish", false)
+               self.method_finish = v.try_build_callsite_by_name(self, ittype, "finish", false)
 
                if is_map then
-                       var keydef = v.get_method(self, ittype, "key", false)
+                       var keydef = v.build_callsite_by_name(self, ittype, "key", false)
                        if keydef == null then
                                v.error(self, "Type Error: `for` expects a method `key` in type `{ittype}`.")
                                return
@@ -1412,12 +1434,12 @@ redef class AForGroup
                        var vtype = variable.declared_type.as(not null)
 
                        if n_expr isa AOrangeExpr then
-                               self.method_lt = v.get_method(self, vtype, "<", false)
+                               self.method_lt = v.build_callsite_by_name(self, vtype, "<", false)
                        else
-                               self.method_lt = v.get_method(self, vtype, "<=", false)
+                               self.method_lt = v.build_callsite_by_name(self, vtype, "<=", false)
                        end
 
-                       self.method_successor = v.get_method(self, vtype, "successor", false)
+                       self.method_successor = v.build_callsite_by_name(self, vtype, "successor", false)
                end
        end
 end
@@ -1431,8 +1453,8 @@ redef class AWithExpr
                var mtype = v.visit_expr(n_expr)
                if mtype == null then return
 
-               method_start = v.get_method(self, mtype, "start", n_expr isa ASelfExpr)
-               method_finish = v.get_method(self, mtype, "finish", n_expr isa ASelfExpr)
+               method_start = v.build_callsite_by_name(self, mtype, "start", n_expr isa ASelfExpr)
+               method_finish = v.build_callsite_by_name(self, mtype, "finish", n_expr isa ASelfExpr)
 
                v.visit_stmt(n_block)
                self.mtype = n_block.mtype
@@ -1477,7 +1499,6 @@ redef class AAndExpr
        end
 end
 
-
 redef class ANotExpr
        redef fun accept_typing(v)
        do
@@ -1605,10 +1626,10 @@ redef class AugmentedStringFormExpr
                var mclass = v.get_mclass(self, "String")
                if mclass == null then return # Forward error
                if is_bytestring then
-                       to_bytes_with_copy = v.get_method(self, v.mmodule.c_string_type, "to_bytes_with_copy", false)
+                       to_bytes_with_copy = v.build_callsite_by_name(self, v.mmodule.c_string_type, "to_bytes_with_copy", false)
                        mclass = v.get_mclass(self, "Bytes")
                else if is_re then
-                       to_re = v.get_method(self, mclass.mclass_type, "to_re", false)
+                       to_re = v.build_callsite_by_name(self, mclass.mclass_type, "to_re", false)
                        for i in suffix.chars do
                                mclass = v.get_mclass(self, "Regex")
                                if mclass == null then
@@ -1618,13 +1639,13 @@ redef class AugmentedStringFormExpr
                                var service = ""
                                if i == 'i' then
                                        service = "ignore_case="
-                                       ignore_case = v.get_method(self, mclass.mclass_type, service, false)
+                                       ignore_case = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
                                else if i == 'm' then
                                        service = "newline="
-                                       newline = v.get_method(self, mclass.mclass_type, service, false)
+                                       newline = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
                                else if i == 'b' then
                                        service = "extended="
-                                       extended = v.get_method(self, mclass.mclass_type, service, false)
+                                       extended = v.build_callsite_by_name(self, mclass.mclass_type, service, false)
                                else
                                        v.error(self, "Type Error: Unrecognized suffix {i} in prefixed Regex")
                                        abort
@@ -1718,8 +1739,8 @@ redef class AArrayExpr
                if mclass == null then return # Forward error
                var array_mtype = mclass.get_mtype([mtype])
 
-               with_capacity_callsite = v.get_method(self, array_mtype, "with_capacity", false)
-               push_callsite = v.get_method(self, array_mtype, "push", false)
+               with_capacity_callsite = v.build_callsite_by_name(self, array_mtype, "with_capacity", false)
+               push_callsite = v.build_callsite_by_name(self, array_mtype, "push", false)
 
                self.mtype = array_mtype
        end
@@ -1753,9 +1774,9 @@ redef class ARangeExpr
                # get the constructor
                var callsite
                if self isa ACrangeExpr then
-                       callsite = v.get_method(self, mtype, "init", false)
+                       callsite = v.build_callsite_by_name(self, mtype, "init", false)
                else if self isa AOrangeExpr then
-                       callsite = v.get_method(self, mtype, "without_last", false)
+                       callsite = v.build_callsite_by_name(self, mtype, "without_last", false)
                else
                        abort
                end
@@ -1942,7 +1963,7 @@ redef class ASendExpr
                                var systype = sysclass.mclass_type
                                mproperty = v.try_get_mproperty_by_name2(node, systype, name)
                                if mproperty != null then
-                                       callsite = v.get_method(node, systype, name, false)
+                                       callsite = v.build_callsite_by_name(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
@@ -1954,7 +1975,7 @@ redef class ASendExpr
                end
                if callsite == null then
                        # If still nothing, just exit
-                       callsite = v.get_method(node, recvtype, name, nrecv isa ASelfExpr)
+                       callsite = v.build_callsite_by_name(node, recvtype, name, nrecv isa ASelfExpr)
                        if callsite == null then return
                end
 
@@ -1963,7 +1984,9 @@ redef class ASendExpr
 
                var args = compute_raw_arguments
 
-               callsite.check_signature(v, node, args)
+                if not self isa ACallrefExpr then
+                       callsite.check_signature(v, node, args)
+                end
 
                if callsite.mproperty.is_init then
                        var vmpropdef = v.mpropdef
@@ -2046,7 +2069,6 @@ redef class AUnaryopExpr
        redef fun compute_raw_arguments do return new Array[AExpr]
 end
 
-
 redef class ACallExpr
        redef fun property_name do return n_qid.n_id.text
        redef fun property_node do return n_qid
@@ -2092,7 +2114,7 @@ redef class ASendReassignFormExpr
                if recvtype == null then return # Forward error
 
                var for_self = self.n_expr isa ASelfExpr
-               var callsite = v.get_method(node, recvtype, name, for_self)
+               var callsite = v.build_callsite_by_name(node, recvtype, name, for_self)
 
                if callsite == null then return
                self.callsite = callsite
@@ -2107,7 +2129,7 @@ redef class ASendReassignFormExpr
                        return
                end
 
-               var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
+               var wcallsite = v.build_callsite_by_name(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
                if wcallsite == null then return
                self.write_callsite = wcallsite
 
@@ -2139,6 +2161,77 @@ redef class AInitExpr
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
+redef class ACallrefExpr
+       redef fun property_name do return n_qid.n_id.text
+       redef fun property_node do return n_qid
+       redef fun compute_raw_arguments do return n_args.to_a
+
+       redef fun accept_typing(v)
+       do
+               super # do the job as if it was a real call
+               var res = callsite.mproperty
+
+                var msignature = callsite.mpropdef.msignature
+                var recv = callsite.recv
+                assert msignature != null
+                var arity = msignature.mparameters.length
+
+                var routine_type_name = "ProcRef"
+                if msignature.return_mtype != null then
+                        routine_type_name = "FunRef"
+                end
+
+                var target_routine_class = "{routine_type_name}{arity}"
+                var routine_mclass = v.get_mclass(self, target_routine_class)
+
+                if routine_mclass == null then
+                        v.error(self, "Error: missing functional types, try `import functional`")
+                        return
+                end
+
+                var types_list = new Array[MType]
+                for param in msignature.mparameters do
+                        if param.is_vararg then
+                                types_list.push(v.mmodule.array_type(param.mtype))
+                        else
+                                types_list.push(param.mtype)
+                        end
+                end
+                if msignature.return_mtype != null then
+                        types_list.push(msignature.return_mtype.as(not null))
+                end
+
+                # Why we need an anchor :
+                #
+                # ~~~~nitish
+                # class A[E]
+                #       def toto(x: E) do print "{x}"
+                # end
+                #
+                # var a = new A[Int]
+                # var f = &a.toto # without anchor : ProcRef1[E]
+                #                # with anchor : ProcRef[Int]
+                # ~~~~
+               # However, we can only anchor if we can resolve every formal
+               # parameter, here's an example where we can't.
+               # ~~~~nitish
+               # class A[E]
+               #       fun bar: A[E] do return self
+               #       fun foo: Fun0[A[E]] do return &bar # here we can't anchor
+               # end
+               # var f1 = a1.foo # when this expression will be evaluated,
+               #                 # `a1` will anchor `&bar` returned by `foo`.
+               # print f1.call
+               # ~~~~
+               var routine_type = routine_mclass.get_mtype(types_list)
+               if not recv.need_anchor then
+                       routine_type = routine_type.anchor_to(v.mmodule, recv.as(MClassType))
+               end
+                is_typed = true
+               self.mtype = routine_type
+       end
+end
+
 redef class AExprs
        fun to_a: Array[AExpr] do return self.n_exprs.to_a
 end
@@ -2330,7 +2423,7 @@ redef class ANewExpr
                        return
                end
 
-               var callsite = v.get_method(node, recvtype, name, false)
+               var callsite = v.build_callsite_by_name(node, recvtype, name, false)
                if callsite == null then return
 
                if not callsite.mproperty.is_new then
@@ -2423,7 +2516,6 @@ redef class AAttrExpr
        end
 end
 
-
 redef class AAttrAssignExpr
        redef fun accept_typing(v)
        do