Merge: new `with` statement
[nit.git] / src / semantize / typing.nit
index 15d38a2..cf8bbda 100644 (file)
@@ -115,7 +115,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 +199,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 +238,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 +279,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
 
@@ -268,7 +298,9 @@ private class TypeVisitor
                if recvtype isa MNullType then
                        # `null` only accepts some methods of object.
                        if name == "==" or name == "!=" or name == "is_same_instance" then
-                               unsafe_type = mmodule.object_type.as_nullable
+                               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
@@ -328,13 +360,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
@@ -450,7 +483,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
@@ -569,14 +602,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])
@@ -587,7 +624,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
@@ -599,7 +636,9 @@ redef class AAttrPropdef
        do
                if not has_value 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
 
@@ -698,7 +737,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
 
@@ -1003,7 +1044,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
@@ -1140,12 +1181,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}")
@@ -1210,8 +1257,11 @@ 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
@@ -1374,22 +1424,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
 
@@ -1836,7 +1876,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