nitc&lib: MapIterator keys can be nullable
[nit.git] / src / semantize / typing.nit
index bd63c0f..8fb3852 100644 (file)
@@ -39,25 +39,24 @@ private class TypeVisitor
 
        # The static type of the receiver
        # Mainly used for type tests and type resolutions
-       var anchor: nullable MClassType
+       var anchor: nullable MClassType = null
 
        # The analyzed mclassdef
-       var mclassdef: nullable MClassDef
+       var mclassdef: nullable MClassDef = null
 
        # 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`
        # * method called on the implicit self must be top-level
        var is_toplevel_context = false
 
-       init(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef)
+       init
        do
-               self.modelbuilder = modelbuilder
-               self.mmodule = mmodule
+               var mpropdef = self.mpropdef
 
                if mpropdef != null then
                        self.mpropdef = mpropdef
@@ -72,7 +71,7 @@ private class TypeVisitor
                        selfvariable.declared_type = mclass.mclass_type
 
                        var mprop = mpropdef.mproperty
-                       if mprop isa MMethod and mprop.is_toplevel then
+                       if mprop isa MMethod and (mprop.is_toplevel or mprop.is_new) then
                                is_toplevel_context = true
                        end
                end
@@ -179,7 +178,7 @@ private class TypeVisitor
        end
 
 
-       private fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
+       fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
        do
                var sub = visit_expr(nexpr)
                if sub == null then return null # Forward error
@@ -195,6 +194,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)
@@ -233,11 +266,21 @@ private class TypeVisitor
 
                #debug("recv: {recvtype} (aka {unsafe_type})")
                if recvtype isa MNullType then
-                       self.error(node, "Error: Method '{name}' call on 'null'.")
-                       return null
+                       # `null` only accepts some methods of object.
+                       if name == "==" or name == "!=" or name == "is_same_instance" then
+                               unsafe_type = mmodule.object_type.as_nullable
+                       else
+                               self.error(node, "Error: Method '{name}' call on 'null'.")
+                               return null
+                       end
                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
@@ -343,11 +386,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
@@ -395,7 +445,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
@@ -461,8 +510,8 @@ 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.
@@ -557,6 +606,10 @@ redef class AAttrPropdef
                        var mtype = self.mpropdef.static_mtype
                        v.visit_expr_subtype(nexpr, mtype)
                end
+               var nblock = self.n_block
+               if nblock != null then
+                       v.visit_stmt(nblock)
+               end
        end
 end
 
@@ -585,6 +638,12 @@ redef class AExpr
        do
                v.error(self, "no implemented accept_typing for {self.class_name}")
        end
+
+       # Is non-null if `self` is a leaf of a comprehension array construction.
+       # In this case, the enclosing literal array node is designated.
+       # The result of the evaluation of `self` must be
+       # stored inside the designated array (there is an implicit `push`)
+       var comprehension: nullable AArrayExpr = null
 end
 
 redef class ABlockExpr
@@ -620,7 +679,11 @@ redef class AVardeclExpr
                var nexpr = self.n_expr
                if nexpr != null then
                        if mtype != null then
-                               v.visit_expr_subtype(nexpr, mtype)
+                               var etype = v.visit_expr_subtype(nexpr, mtype)
+                               if etype == mtype then
+                                       assert ntype != null
+                                       v.modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition for variable `{variable.name}`")
+                               end
                        else
                                mtype = v.visit_expr(nexpr)
                                if mtype == null then return # Skip error
@@ -698,11 +761,6 @@ redef class AReassignFormExpr
 
                self.read_type = readtype
 
-               if readtype isa MNullType then
-                       v.error(self, "Error: Method '{reassign_name}' call on 'null'.")
-                       return null
-               end
-
                var callsite = v.get_method(self, readtype, reassign_name, false)
                if callsite == null then return null # Skip error
                self.reassign_callsite = callsite
@@ -747,7 +805,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
@@ -758,7 +816,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
@@ -768,12 +826,20 @@ redef class AReturnExpr
        redef fun accept_typing(v)
        do
                var nexpr = self.n_expr
-               var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype
+               var ret_type
+               var mpropdef = v.mpropdef
+               if mpropdef isa MMethodDef then
+                       ret_type = mpropdef.msignature.return_mtype
+               else if mpropdef isa MAttributeDef then
+                       ret_type = mpropdef.static_mtype
+               else
+                       abort
+               end
                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
@@ -797,7 +863,12 @@ redef class AIfExpr
 
                v.visit_stmt(n_then)
                v.visit_stmt(n_else)
+
                self.is_typed = true
+
+               if n_then != null and n_else == null then
+                       self.mtype = n_then.mtype
+               end
        end
 end
 
@@ -904,7 +975,7 @@ redef class AForExpr
                        is_col = true
                end
 
-               if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then
+               if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then
                        # Map Iterator
                        var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla)
                        var variables = self.variables
@@ -983,6 +1054,7 @@ redef class AForExpr
                self.do_type_iterator(v, mtype)
 
                v.visit_stmt(n_block)
+               self.mtype = n_block.mtype
                self.is_typed = true
        end
 end
@@ -1120,9 +1192,31 @@ redef class ASuperstringExpr
 end
 
 redef class AArrayExpr
+       # The `with_capacity` method on Array
        var with_capacity_callsite: nullable CallSite
+
+       # The `push` method on arrays
        var push_callsite: nullable CallSite
 
+       # The element of each type
+       var element_mtype: nullable MType
+
+       # Set that `self` is a part of comprehension array `na`
+       # If `self` is a `for`, or a `if`, then `set_comprehension` is recursively applied.
+       private fun set_comprehension(n: nullable AExpr)
+       do
+               if n == null then
+                       return
+               else if n isa AForExpr then
+                       set_comprehension(n.n_block)
+               else if n isa AIfExpr then
+                       set_comprehension(n.n_then)
+                       set_comprehension(n.n_else)
+               else
+                       # is a leave
+                       n.comprehension = self
+               end
+       end
        redef fun accept_typing(v)
        do
                var mtype: nullable MType = null
@@ -1133,11 +1227,12 @@ redef class AArrayExpr
                end
                var mtypes = new Array[nullable MType]
                var useless = false
-               for e in self.n_exprs.n_exprs do
+               for e in self.n_exprs do
                        var t = v.visit_expr(e)
                        if t == null then
                                return # Skip error
                        end
+                       set_comprehension(e)
                        if mtype != null then
                                if v.check_subtype(e, t, mtype) == null then return # Skip error
                                if t == mtype then useless = true
@@ -1148,7 +1243,7 @@ redef class AArrayExpr
                if mtype == null then
                        mtype = v.merge_types(self, mtypes)
                end
-               if mtype == null then
+               if mtype == null or mtype isa MNullType then
                        v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}")
                        return
                end
@@ -1156,6 +1251,9 @@ redef class AArrayExpr
                        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
+
+               self.element_mtype = mtype
+
                var mclass = v.get_mclass(self, "Array")
                if mclass == null then return # Forward error
                var array_mtype = mclass.get_mtype([mtype])
@@ -1223,9 +1321,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
@@ -1270,7 +1368,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)
@@ -1302,10 +1407,6 @@ redef class ASendExpr
                var name = self.property_name
 
                if recvtype == null then return # Forward error
-               if recvtype isa MNullType then
-                       v.error(self, "Error: Method '{name}' call on 'null'.")
-                       return
-               end
 
                var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr)
                if callsite == null then return
@@ -1352,16 +1453,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
@@ -1369,16 +1461,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
@@ -1467,10 +1550,6 @@ redef class ASendReassignFormExpr
                var name = self.property_name
 
                if recvtype == null then return # Forward error
-               if recvtype isa MNullType then
-                       v.error(self, "Error: Method '{name}' call on 'null'.")
-                       return
-               end
 
                var for_self = self.n_expr isa ASelfExpr
                var callsite = v.get_method(self, recvtype, name, for_self)
@@ -1646,11 +1725,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
@@ -1660,26 +1741,32 @@ redef class ANewExpr
                                v.error(self, "Type error: cannot instantiate the formal type {recvtype}.")
                                return
                        end
-               else
-                       if recvtype.mclass.kind == abstract_kind then
-                               v.error(self, "Cannot instantiate abstract class {recvtype}.")
-                               return
-                       else if recvtype.mclass.kind == interface_kind then
-                               v.error(self, "Cannot instantiate interface {recvtype}.")
-                               return
-                       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
+                       var kind = recvtype.mclass.kind
+                       if kind != concrete_kind then
+                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+                               return
+                       end
+                       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
@@ -1779,6 +1866,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