typing: ANew distinguish the type of the class and the returned type
[nit.git] / src / semantize / typing.nit
index 02de84b..8c648a0 100644 (file)
@@ -47,7 +47,7 @@ private class TypeVisitor
        # The analyzed property
        var mpropdef: nullable MPropDef
 
-       var selfvariable: Variable = new Variable("self")
+       var selfvariable = new Variable("self")
 
        # Is `self` use restricted?
        # * no explicit `self`
@@ -101,9 +101,9 @@ private class TypeVisitor
        end
 
        # Check that `sub` is a subtype of `sup`.
-       # If `sub` is not a valud suptype, then display an error on `node` an return null.
+       # If `sub` is not a valid suptype, then display an error on `node` an return null.
        # If `sub` is a safe subtype of `sup` then return `sub`.
-       # If `sub` is an insafe subtype (ie an imlicit cast is required), then return `sup`.
+       # 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:
        # If the suptype is safe, then the return type is the one on the expression typed by `sub`.
@@ -112,7 +112,7 @@ private class TypeVisitor
        do
                if self.is_subtype(sub, sup) then return sub
                if self.is_subtype(sub, self.anchor_to(sup)) then
-                       # FIXME workarround to the current unsafe typing policy. To remove once fixed virtual types exists.
+                       # 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
@@ -195,6 +195,40 @@ private class TypeVisitor
                return sup
        end
 
+       # Special verification on != and == for null
+       # Return true
+       fun null_test(anode: ABinopExpr)
+       do
+               var mtype = anode.n_expr.mtype
+               var mtype2 = anode.n_expr2.mtype
+
+               if mtype == null or mtype2 == null then return
+
+               if not mtype2 isa MNullType then return
+
+               # Check of useless null
+               if not mtype isa MNullableType then
+                       if not anchor_to(mtype) isa MNullableType then
+                               modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
+                       end
+                       return
+               end
+
+               # Check for type adaptation
+               var variable = anode.n_expr.its_variable
+               if variable == null then return
+
+               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.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.mtype)
+               else
+                       abort
+               end
+       end
+
        fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty
        do
                return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name)
@@ -238,6 +272,11 @@ private class TypeVisitor
                end
 
                var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+               if name == "new" and mproperty == null then
+                       name = "init"
+                       mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+               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
@@ -304,7 +343,16 @@ private class TypeVisitor
                return callsite
        end
 
-       # Visit the expressions of args and cheik their conformity with the corresponding typi in signature
+       fun try_get_method(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)
+       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
@@ -334,11 +382,18 @@ private class TypeVisitor
                        self.visit_expr_subtype(args[j], paramtype)
                end
                if vararg_rank >= 0 then
-                       var varargs = new Array[AExpr]
                        var paramtype = msignature.mparameters[vararg_rank].mtype
-                       for j in [vararg_rank..vararg_rank+vararg_decl] do
-                               varargs.add(args[j])
-                               self.visit_expr_subtype(args[j], paramtype)
+                       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
+                               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)
+                               end
                        end
                end
                return true
@@ -386,7 +441,6 @@ private class TypeVisitor
        fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
        do
                if col.length == 1 then return col.first
-               var res = new Array[nullable MType]
                for t1 in col do
                        if t1 == null then continue # return null
                        var found = true
@@ -409,7 +463,7 @@ end
 
 # A specific method call site with its associated informations.
 class CallSite
-       # The assiciated node for location
+       # The associated node for location
        var node: ANode
 
        # The static type of the receiver (possibly unresolved)
@@ -422,7 +476,7 @@ class CallSite
        var anchor: nullable MClassType
 
        # Is the receiver self?
-       # If "for_self", virtual types of the signature are keeped
+       # If "for_self", virtual types of the signature are kept
        # If "not_for_self", virtual type are erased
        var recv_is_self: Bool
 
@@ -452,12 +506,12 @@ end
 
 redef class FlowContext
        # Store changes of types because of type evolution
-       private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType]
-       private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]]
+       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 variabial
+       # Warning2: sub-flow may have cached a unadapted variable
        private fun set_var(variable: Variable, mtype: nullable MType)
        do
                self.vars[variable] = mtype
@@ -501,7 +555,7 @@ redef class APropdef
        do
        end
 
-       # The variable associated to the reciever (if any)
+       # The variable associated to the receiver (if any)
        var selfvariable: nullable Variable
 end
 
@@ -738,7 +792,7 @@ redef class AContinueExpr
        do
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = v.visit_expr(nexpr)
+                       v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@ -749,7 +803,7 @@ redef class ABreakExpr
        do
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = v.visit_expr(nexpr)
+                       v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@ -762,9 +816,9 @@ redef class AReturnExpr
                var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype
                if nexpr != null then
                        if ret_type != null then
-                               var mtype = v.visit_expr_subtype(nexpr, ret_type)
+                               v.visit_expr_subtype(nexpr, ret_type)
                        else
-                               var mtype = v.visit_expr(nexpr)
+                               v.visit_expr(nexpr)
                                v.error(self, "Error: Return with value in a procedure.")
                        end
                else if ret_type != null then
@@ -846,6 +900,10 @@ redef class AForExpr
        var method_item: nullable CallSite
        var method_next: nullable CallSite
        var method_key: nullable CallSite
+       var method_finish: nullable CallSite
+
+       var method_lt: nullable CallSite
+       var method_successor: nullable CallSite
 
        private fun do_type_iterator(v: TypeVisitor, mtype: MType)
        do
@@ -937,6 +995,8 @@ redef class AForExpr
                end
                self.method_next = nextdef
 
+               self.method_finish = v.try_get_method(self, ittype, "finish", false)
+
                if is_map then
                        var keydef = v.get_method(self, ittype, "key", false)
                        if keydef == null then
@@ -945,6 +1005,19 @@ redef class AForExpr
                        end
                        self.method_key = keydef
                end
+
+               if self.variables.length == 1 and n_expr isa ARangeExpr then
+                       var variable = variables.first
+                       var vtype = variable.declared_type.as(not null)
+
+                       if n_expr isa AOrangeExpr then
+                               self.method_lt = v.get_method(self, vtype, "<", false)
+                       else
+                               self.method_lt = v.get_method(self, vtype, "<=", false)
+                       end
+
+                       self.method_successor = v.get_method(self, vtype, "successor", false)
+               end
        end
 
        redef fun accept_typing(v)
@@ -1097,19 +1170,37 @@ redef class AArrayExpr
 
        redef fun accept_typing(v)
        do
+               var mtype: nullable MType = null
+               var ntype = self.n_type
+               if ntype != null then
+                       mtype = v.resolve_mtype(ntype)
+                       if mtype == null then return # Skip error
+               end
                var mtypes = new Array[nullable MType]
+               var useless = false
                for e in self.n_exprs.n_exprs do
                        var t = v.visit_expr(e)
                        if t == null then
                                return # Skip error
                        end
-                       mtypes.add(t)
+                       if mtype != null then
+                               if v.check_subtype(e, t, mtype) == null then return # Skip error
+                               if t == mtype then useless = true
+                       else
+                               mtypes.add(t)
+                       end
+               end
+               if mtype == null then
+                       mtype = v.merge_types(self, mtypes)
                end
-               var mtype = v.merge_types(self, mtypes)
                if mtype == null then
                        v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}")
                        return
                end
+               if useless then
+                       assert ntype != null
+                       v.modelbuilder.warning(ntype, "useless-type", "Warning: useless type declaration `{mtype}` in literal Array since it can be inferred from the elements type.")
+               end
                var mclass = v.get_mclass(self, "Array")
                if mclass == null then return # Forward error
                var array_mtype = mclass.get_mtype([mtype])
@@ -1177,9 +1268,9 @@ redef class AIsaExpr
 
                var variable = self.n_expr.its_variable
                if variable != null then
-                       var orig = self.n_expr.mtype
-                       var from = if orig != null then orig.to_s else "invalid"
-                       var to = if mtype != null then mtype.to_s else "invalid"
+                       #var orig = self.n_expr.mtype
+                       #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)
                end
@@ -1224,7 +1315,14 @@ redef class AAsNotnullExpr
        end
 end
 
-redef class AProxyExpr
+redef class AParExpr
+       redef fun accept_typing(v)
+       do
+               self.mtype = v.visit_expr(self.n_expr)
+       end
+end
+
+redef class AOnceExpr
        redef fun accept_typing(v)
        do
                self.mtype = v.visit_expr(self.n_expr)
@@ -1306,16 +1404,7 @@ redef class AEqExpr
        redef fun accept_typing(v)
        do
                super
-
-               var variable = self.n_expr.its_variable
-               if variable == null then return
-               var mtype = self.n_expr2.mtype
-               if not mtype isa MNullType then return
-               var vartype = v.get_variable(self, variable)
-               if not vartype isa MNullableType then return
-               self.after_flow_context.when_true.set_var(variable, mtype)
-               self.after_flow_context.when_false.set_var(variable, vartype.mtype)
-               #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}")
+               v.null_test(self)
        end
 end
 redef class ANeExpr
@@ -1323,16 +1412,7 @@ redef class ANeExpr
        redef fun accept_typing(v)
        do
                super
-
-               var variable = self.n_expr.its_variable
-               if variable == null then return
-               var mtype = self.n_expr2.mtype
-               if not mtype isa MNullType then return
-               var vartype = v.get_variable(self, variable)
-               if not vartype isa MNullableType then return
-               self.after_flow_context.when_false.set_var(variable, mtype)
-               self.after_flow_context.when_true.set_var(variable, vartype.mtype)
-               #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}")
+               v.null_test(self)
        end
 end
 redef class ALtExpr
@@ -1537,7 +1617,7 @@ redef class ASuperExpr
                        var errcount = v.modelbuilder.toolcontext.error_count
                        var candidate = v.try_get_mproperty_by_name2(self, msupertype, mproperty.name).as(nullable MMethod)
                        if candidate == null then
-                               if v.modelbuilder.toolcontext.error_count > errcount then return # Forard error
+                               if v.modelbuilder.toolcontext.error_count > errcount then return # Forward error
                                continue # Try next super-class
                        end
                        if superprop != null and candidate.is_root_init then
@@ -1553,7 +1633,7 @@ redef class ASuperExpr
                                candidatedefs.add(superprop)
                        end
                        if candidatedefs.length > 1 then
-                               v.error(self, "Error: confliting 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
@@ -1600,11 +1680,13 @@ redef class ANewExpr
        # The constructor invoked by the new.
        var callsite: nullable CallSite
 
+       # The designated type
+       var recvtype: nullable MClassType
+
        redef fun accept_typing(v)
        do
                var recvtype = v.resolve_mtype(self.n_type)
                if recvtype == null then return
-               self.mtype = recvtype
 
                if not recvtype isa MClassType then
                        if recvtype isa MNullableType then
@@ -1624,16 +1706,25 @@ redef class ANewExpr
                        end
                end
 
+               self.recvtype = recvtype
+
                var name: String
                var nid = self.n_id
                if nid != null then
                        name = nid.text
                else
-                       name = "init"
+                       name = "new"
                end
                var callsite = v.get_method(self, recvtype, name, false)
                if callsite == null then return
 
+               if not callsite.mproperty.is_new then
+                       self.mtype = recvtype
+               else
+                       self.mtype = callsite.msignature.return_mtype
+                       assert self.mtype != null
+               end
+
                self.callsite = callsite
 
                if not callsite.mproperty.is_init_for(recvtype.mclass) then
@@ -1733,6 +1824,16 @@ redef class AIssetAttrExpr
        end
 end
 
+redef class AVarargExpr
+       redef fun accept_typing(v)
+       do
+               # 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 `...`")
+       end
+end
+
 ###
 
 redef class ADebugTypeExpr