Merge: Sys is top
[nit.git] / src / semantize / typing.nit
index 4955eda..e51269f 100644 (file)
@@ -52,6 +52,7 @@ private class TypeVisitor
        # Is `self` use restricted?
        # * no explicit `self`
        # * method called on the implicit self must be top-level
+       # Currently only used for `new` factory since there is no valid receiver inside
        var is_toplevel_context = false
 
        init
@@ -71,7 +72,7 @@ private class TypeVisitor
                        selfvariable.declared_type = mclass.mclass_type
 
                        var mprop = mpropdef.mproperty
-                       if mprop isa MMethod and (mprop.is_toplevel or mprop.is_new) then
+                       if mprop isa MMethod and mprop.is_new then
                                is_toplevel_context = true
                        end
                end
@@ -115,7 +116,12 @@ private class TypeVisitor
                        #node.debug("Unsafe typing: expected {sup}, got {sub}")
                        return sup
                end
-               self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+               if sub.need_anchor then
+                       var u = anchor_to(sub)
+                       self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}")
+               else
+                       self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+               end
                return null
        end
 
@@ -194,6 +200,34 @@ private class TypeVisitor
                return sup
        end
 
+       # Can `mtype` be null (up to the current knowledge)?
+       fun can_be_null(mtype: MType): Bool
+       do
+               if mtype isa MNullableType or mtype isa MNullType then return true
+               if mtype isa MFormalType then
+                       var x = anchor_to(mtype)
+                       if x isa MNullableType or x isa MNullType then return true
+               end
+               return false
+       end
+
+       # Check that `mtype` can be null (up to the current knowledge).
+       #
+       # If not then display a `useless-null-test` warning on node and return false.
+       # Else return true.
+       fun check_can_be_null(anode: ANode, mtype: MType): Bool
+       do
+               if can_be_null(mtype) then return true
+
+               if mtype isa MFormalType then
+                       var res = anchor_to(mtype)
+                       modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.")
+               else
+                       modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
+               end
+               return false
+       end
+
        # Special verification on != and == for null
        # Return true
        fun null_test(anode: ABinopExpr)
@@ -205,24 +239,24 @@ private class TypeVisitor
 
                if not mtype2 isa MNullType then return
 
+               if mtype 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
+               if not check_can_be_null(anode.n_expr, mtype) then return
+
+               mtype = mtype.as_notnull
 
                # Check for type adaptation
                var variable = anode.n_expr.its_variable
                if variable == null then return
 
+               # One is null (mtype2 see above) the other is not null
                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)
+                       anode.after_flow_context.when_false.set_var(variable, 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)
+                       anode.after_flow_context.when_true.set_var(variable, mtype)
                else
                        abort
                end
@@ -246,10 +280,7 @@ private class TypeVisitor
 
        fun get_mclass(node: ANode, name: String): nullable MClass
        do
-               var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name)
-               if mclass == null then
-                       self.modelbuilder.error(node, "Type Error: missing primitive class `{name}'.")
-               end
+               var mclass = modelbuilder.get_mclass_by_name(node, mmodule, name)
                return mclass
        end
 
@@ -266,8 +297,15 @@ 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
+                               var objclass = get_mclass(node, "Object")
+                               if objclass == null then return null # Forward error
+                               unsafe_type = objclass.mclass_type
+                       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)
@@ -323,13 +361,14 @@ private class TypeVisitor
                end
 
 
-               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               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)
 
                var erasure_cast = false
                var rettype = mpropdef.msignature.return_mtype
                if not recv_is_self and rettype != null then
-                       rettype = rettype.as_notnullable
+                       rettype = rettype.undecorate
                        if rettype isa MParameterType then
                                var erased_rettype = msignature.return_mtype
                                assert erased_rettype != null
@@ -445,7 +484,7 @@ private class TypeVisitor
                        var found = true
                        for t2 in col do
                                if t2 == null then continue # return null
-                               if t2 isa MNullableType or t2 isa MNullType then
+                               if can_be_null(t2) and not can_be_null(t1) then
                                        t1 = t1.as_nullable
                                end
                                if not is_subtype(t2, t1) then found = false
@@ -564,14 +603,18 @@ redef class AMethPropdef
                var nblock = self.n_block
                if nblock == null then return
 
-               var mpropdef = self.mpropdef.as(not null)
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return # skip error
+
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
 
                var mmethoddef = self.mpropdef.as(not null)
-               for i in [0..mmethoddef.msignature.arity[ do
-                       var mtype = mmethoddef.msignature.mparameters[i].mtype
-                       if mmethoddef.msignature.vararg_rank == i then
+               var msignature = mmethoddef.msignature
+               if msignature == null then return # skip error
+               for i in [0..msignature.arity[ do
+                       var mtype = msignature.mparameters[i].mtype
+                       if msignature.vararg_rank == i then
                                var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array")
                                if arrayclass == null then return # Skip error
                                mtype = arrayclass.get_mtype([mtype])
@@ -582,7 +625,7 @@ redef class AMethPropdef
                end
                v.visit_stmt(nblock)
 
-               if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then
+               if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then
                        # We reach the end of the function without having a return, it is bad
                        v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
                end
@@ -592,7 +635,11 @@ end
 redef class AAttrPropdef
        redef fun do_typing(modelbuilder: ModelBuilder)
        do
-               var mpropdef = self.mpropdef.as(not null)
+               if not has_value then return
+
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return # skip error
+
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
 
@@ -604,6 +651,10 @@ redef class AAttrPropdef
                var nblock = self.n_block
                if nblock != null then
                        v.visit_stmt(nblock)
+                       if not nblock.after_flow_context.is_unreachable then
+                               # We reach the end of the init without having a return, it is bad
+                               v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).")
+                       end
                end
        end
 end
@@ -633,6 +684,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
@@ -681,7 +738,9 @@ redef class AVardeclExpr
 
                var decltype = mtype
                if mtype == null or mtype isa MNullType then
-                       decltype = v.get_mclass(self, "Object").mclass_type.as_nullable
+                       var objclass = v.get_mclass(self, "Object")
+                       if objclass == null then return # skip error
+                       decltype = objclass.mclass_type.as_nullable
                        if mtype == null then mtype = decltype
                end
 
@@ -690,6 +749,7 @@ redef class AVardeclExpr
 
                #debug("var {variable}: {mtype}")
 
+               self.mtype = mtype
                self.is_typed = true
        end
 end
@@ -750,11 +810,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 +912,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 +1024,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
@@ -985,7 +1045,7 @@ redef class AForExpr
                # anchor formal and virtual types
                if mtype.need_anchor then mtype = v.anchor_to(mtype)
 
-               mtype = mtype.as_notnullable
+               mtype = mtype.undecorate
                self.coltype = mtype.as(MClassType)
 
                # get methods is_ok, next, item
@@ -1043,6 +1103,25 @@ 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
+
+redef class AWithExpr
+       var method_start: nullable CallSite
+       var method_finish: nullable CallSite
+
+       redef fun accept_typing(v: TypeVisitor)
+       do
+               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)
+
+               v.visit_stmt(n_block)
+               self.mtype = n_block.mtype
                self.is_typed = true
        end
 end
@@ -1103,12 +1182,18 @@ redef class AOrElseExpr
                        return # Skip error
                end
 
-               t1 = t1.as_notnullable
+               if t1 isa MNullType then
+                       v.error(n_expr, "Type error: or else on null")
+               else if v.check_can_be_null(n_expr, t1) then
+                       t1 = t1.as_notnull
+               end
 
                var t = v.merge_types(self, [t1, t2])
                if t == null then
-                       t = v.mmodule.object_type
-                       if t2 isa MNullableType then
+                       var c = v.get_mclass(self, "Object")
+                       if c == null then return # forward error
+                       t = c.mclass_type
+                       if v.can_be_null(t2) then
                                t = t.as_nullable
                        end
                        #v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
@@ -1173,16 +1258,41 @@ redef class ASuperstringExpr
                var mclass = v.get_mclass(self, "String")
                if mclass == null then return # Forward error
                self.mtype = mclass.mclass_type
+               var objclass = v.get_mclass(self, "Object")
+               if objclass == null then return # Forward error
+               var objtype = objclass.mclass_type
                for nexpr in self.n_exprs do
-                       v.visit_expr_subtype(nexpr, v.mmodule.object_type)
+                       v.visit_expr_subtype(nexpr, objtype)
                end
        end
 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
@@ -1198,6 +1308,7 @@ redef class AArrayExpr
                        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 +1319,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 +1327,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])
@@ -1311,22 +1425,12 @@ redef class AAsNotnullExpr
                        v.error(self, "Type error: as(not null) on null")
                        return
                end
-               if mtype isa MNullableType then
-                       self.mtype = mtype.mtype
-                       return
-               end
-               self.mtype = mtype
 
-               if mtype isa MClassType then
-                       v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}`.")
-                       return
-               end
-               assert mtype.need_anchor
-               var u = v.anchor_to(mtype)
-               if not u isa MNullableType then
-                       v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.")
-                       return
+               if v.check_can_be_null(n_expr, mtype) then
+                       mtype = mtype.as_notnull
                end
+
+               self.mtype = mtype
        end
 end
 
@@ -1357,6 +1461,15 @@ redef class ASelfExpr
        end
 end
 
+redef class AImplicitSelfExpr
+       # Is the implicit receiver `sys`?
+       #
+       # By default, the implicit receiver is `self`.
+       # But when there is not method for `self`, `sys` is used as a fall-back.
+       # Is this case this flag is set to `true`.
+       var is_sys = false
+end
+
 ## MESSAGE SENDING AND PROPERTY
 
 redef class ASendExpr
@@ -1365,17 +1478,38 @@ redef class ASendExpr
 
        redef fun accept_typing(v)
        do
-               var recvtype = v.visit_expr(self.n_expr)
+               var nrecv = self.n_expr
+               var recvtype = v.visit_expr(nrecv)
                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
+
+               var callsite = null
+               var unsafe_type = v.anchor_to(recvtype)
+               var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name)
+               if mproperty == null and nrecv isa AImplicitSelfExpr then
+                       # Special fall-back search in `sys` when noting found in the implicit receiver.
+                       var sysclass = v.try_get_mclass(self, "Sys")
+                       if sysclass != null then
+                               var systype = sysclass.mclass_type
+                               mproperty = v.try_get_mproperty_by_name2(self, systype, name)
+                               if mproperty != null then
+                                       callsite = v.get_method(self, systype, name, false)
+                                       if callsite == null then return # Forward error
+                                       # Update information, we are looking at `sys` now, not `self`
+                                       nrecv.is_sys = true
+                                       nrecv.its_variable = null
+                                       nrecv.mtype = systype
+                                       recvtype = systype
+                               end
+                       end
+               end
+               if callsite == null then
+                       # If still nothing, just exit
+                       callsite = v.get_method(self, recvtype, name, nrecv isa ASelfExpr)
+                       if callsite == null then return
                end
 
-               var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr)
-               if callsite == null then return
                self.callsite = callsite
                var msignature = callsite.msignature
 
@@ -1516,10 +1650,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)
@@ -1781,7 +1911,8 @@ redef class AAttrFormExpr
                var mpropdefs = mproperty.lookup_definitions(v.mmodule, unsafe_type)
                assert mpropdefs.length == 1
                var mpropdef = mpropdefs.first
-               var attr_type = mpropdef.static_mtype.as(not null)
+               var attr_type = mpropdef.static_mtype
+               if attr_type == null then return # skip error
                attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr)
                self.attr_type = attr_type
        end