model: rewrite of MType::is_subtype
authorJean Privat <jean@pryen.org>
Fri, 7 Dec 2012 06:53:26 +0000 (01:53 -0500)
committerJean Privat <jean@pryen.org>
Fri, 7 Dec 2012 06:59:36 +0000 (01:59 -0500)
The previous writing was just wrong.
Mainly because the two first `resolve_for` always lost the parameter types.
But the rest of the method was also overly complex and buggy.

Note that the fixed behavior make a test pass because of the
"FIXME workaround" in typing.nit

Signed-off-by: Jean Privat <jean@pryen.org>

src/model/model.nit
tests/sav/base_var_type_evolution_null3_alt1.res

index dbaf282..fb35980 100644 (file)
@@ -506,43 +506,72 @@ abstract class MType
                        assert not sub.need_anchor
                        assert not sup.need_anchor
                end
-               # First, resolve the types
+
+               # First, resolve the formal types to a common version in the receiver
+               # The trick here is that fixed formal type will be associed to the bound
+               # And unfixed formal types will be associed to a canonical formal type.
                if sub isa MParameterType or sub isa MVirtualType then
                        assert anchor != null
-                       sub = sub.resolve_for(anchor, anchor, mmodule, false)
+                       sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false)
                end
                if sup isa MParameterType or sup isa MVirtualType then
                        assert anchor != null
-                       sup = sup.resolve_for(anchor, anchor, mmodule, false)
+                       sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false)
+               end
+
+               # Does `sup` accept null or not?
+               # Discard the nullable marker if it exists
+               var sup_accept_null = false
+               if sup isa MNullableType then
+                       sup_accept_null = true
+                       sup = sup.mtype
+               else if sup isa MNullType then
+                       sup_accept_null = true
+               end
+
+               # Can `sub` provide null or not?
+               # Thus we can match with `sup_accept_null`
+               # Also discard the nullable marker if it exists
+               if sub isa MNullableType then
+                       if not sup_accept_null then return false
+                       sub = sub.mtype
+               else if sub isa MNullType then
+                       return sup_accept_null
                end
+               # Now the case of direct null and nullable is over.
 
-               if sup isa MParameterType or sup isa MVirtualType or sup isa MNullType then
+               # A unfixed formal type can only accept itself
+               if sup isa MParameterType or sup isa MVirtualType then
                        return sub == sup
                end
+
+               # If `sub` is a formal type, then it is accepted if its bound is accepted
                if sub isa MParameterType or sub isa MVirtualType then
                        assert anchor != null
                        sub = sub.anchor_to(mmodule, anchor)
-               end
-               if sup isa MNullableType then
-                       if sub isa MNullType then
-                               return true
-                       else if sub isa MNullableType then
-                               return sub.mtype.is_subtype(mmodule, anchor, sup.mtype)
-                       else if sub isa MClassType then
-                               return sub.is_subtype(mmodule, anchor, sup.mtype)
-                       else
-                               abort
+
+                       # Manage the second layer of null/nullable
+                       if sub isa MNullableType then
+                               if not sup_accept_null then return false
+                               sub = sub.mtype
+                       else if sub isa MNullType then
+                               return sup_accept_null
                        end
                end
 
-               assert sup isa MClassType # It is the only remaining type
-               if sub isa MNullableType or sub isa MNullType then
+               assert sub isa MClassType # It is the only remaining type
+
+               if sup isa MNullType then
+                       # `sup` accepts only null
                        return false
                end
 
+               assert sup isa MClassType # It is the only remaining type
+
+               # Now both are MClassType, we need to dig
+
                if sub == sup then return true
 
-               assert sub isa MClassType # It is the only remaining type
                if anchor == null then anchor = sub # UGLY: any anchor will work
                var resolved_sub = sub.anchor_to(mmodule, anchor)
                var res = resolved_sub.collect_mclasses(mmodule).has(sup.mclass)
index 8843ed4..a556a0a 100644 (file)
@@ -1 +1,3 @@
-alt/base_var_type_evolution_null3_alt1.nit:44,8--9: Type error: expected G#0, got nullable G#0
+1
+1
+5