nitc&lib: MapIterator keys can be nullable
[nit.git] / src / semantize / typing.nit
index cdb8ae8..8fb3852 100644 (file)
@@ -178,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
@@ -266,8 +266,13 @@ 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)
@@ -633,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
@@ -750,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
@@ -857,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
 
@@ -964,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
@@ -1043,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
@@ -1180,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
@@ -1193,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
@@ -1208,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
@@ -1216,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])
@@ -1369,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
@@ -1516,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)