Callref bugfix in interpreter and compilers + autosav
[nit.git] / src / semantize / typing.nit
index b173445..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
@@ -424,7 +423,6 @@ private class TypeVisitor
                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
@@ -445,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
@@ -1177,7 +1174,6 @@ redef class AVarReassignExpr
        end
 end
 
-
 redef class AContinueExpr
        redef fun accept_typing(v)
        do
@@ -1503,7 +1499,6 @@ redef class AAndExpr
        end
 end
 
-
 redef class ANotExpr
        redef fun accept_typing(v)
        do
@@ -1929,6 +1924,11 @@ redef class ASendExpr
        # The property invoked by the send.
        var callsite: nullable CallSite
 
+       # Is self a safe call (with `x?.foo`)?
+       # If so and the receiver is null, then the arguments won't be evaluated
+       # and the call skipped (replaced with null).
+       var is_safe: Bool = false
+
        redef fun bad_expr_message(child)
        do
                if child == self.n_expr then
@@ -1941,6 +1941,13 @@ redef class ASendExpr
        do
                var nrecv = self.n_expr
                var recvtype = v.visit_expr(nrecv)
+
+               if nrecv isa ASafeExpr then
+                       # Has the receiver the form `x?.foo`?
+                       # For parsing "reasons" the `?` is in the receiver node, not the call node.
+                       is_safe = true
+               end
+
                var name = self.property_name
                var node = self.property_node
 
@@ -1977,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
@@ -1991,6 +2000,10 @@ redef class ASendExpr
 
                var ret = msignature.return_mtype
                if ret != null then
+                       if is_safe then
+                               # A safe receiver makes that the call is not executed and returns null
+                               ret = ret.as_nullable
+                       end
                        self.mtype = ret
                else
                        self.is_typed = true
@@ -2056,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
@@ -2149,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
@@ -2433,7 +2516,6 @@ redef class AAttrExpr
        end
 end
 
-
 redef class AAttrAssignExpr
        redef fun accept_typing(v)
        do
@@ -2474,6 +2556,28 @@ redef class AIssetAttrExpr
        end
 end
 
+redef class ASafeExpr
+       redef fun accept_typing(v)
+       do
+               var mtype = v.visit_expr(n_expr)
+               if mtype == null then return # Skip error
+
+               if mtype isa MNullType then
+                       # While `null?.foo` is semantically well defined and should not execute `foo` and just return `null`,
+                       # currently `null.foo` is forbidden so it seems coherent to also forbid `null?.foo`
+                       v.modelbuilder.error(self, "Error: safe operator `?` on `null`.")
+                       return
+               end
+
+               self.mtype = mtype.as_notnull
+
+               if not v.can_be_null(mtype) then
+                       v.modelbuilder.warning(self, "useless-safe", "Warning: useless safe operator `?` on non-nullable value.")
+                       return
+               end
+       end
+end
+
 redef class AVarargExpr
        redef fun accept_typing(v)
        do