typing: add `check_can_be_null` to fix and factorize as_notnull transformations
authorJean Privat <jean@pryen.org>
Sat, 4 Apr 2015 13:31:11 +0000 (20:31 +0700)
committerJean Privat <jean@pryen.org>
Wed, 8 Apr 2015 00:52:01 +0000 (07:52 +0700)
Signed-off-by: Jean Privat <jean@pryen.org>

src/semantize/typing.nit

index 6a6d943..f4d851e 100644 (file)
@@ -194,6 +194,34 @@ private class TypeVisitor
                return sup
        end
 
                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)
        # Special verification on != and == for null
        # Return true
        fun null_test(anode: ABinopExpr)
@@ -205,24 +233,24 @@ private class TypeVisitor
 
                if not mtype2 isa MNullType then return
 
 
                if not mtype2 isa MNullType then return
 
+               if mtype isa MNullType then return
+
                # Check of useless null
                # 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
 
 
                # 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)
                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)
                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
                else
                        abort
                end
@@ -450,7 +478,7 @@ private class TypeVisitor
                        var found = true
                        for t2 in col do
                                if t2 == null then continue # return null
                        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
                                        t1 = t1.as_nullable
                                end
                                if not is_subtype(t2, t1) then found = false
@@ -1121,12 +1149,16 @@ redef class AOrElseExpr
                        return # Skip error
                end
 
                        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
 
                var t = v.merge_types(self, [t1, t2])
                if t == null then
                        t = v.mmodule.object_type
-                       if t2 isa MNullableType then
+                       if v.can_be_null(t2) then
                                t = t.as_nullable
                        end
                        #v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
                                t = t.as_nullable
                        end
                        #v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
@@ -1355,22 +1387,12 @@ redef class AAsNotnullExpr
                        v.error(self, "Type error: as(not null) on null")
                        return
                end
                        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
                end
+
+               self.mtype = mtype
        end
 end
 
        end
 end