model: add new type `MNotNullType` to force that null is excluded from a type.
[nit.git] / src / model / model.nit
index 3a64638..a391ea4 100644 (file)
@@ -477,6 +477,9 @@ class MClass
        end
 
        private var get_mtype_cache = new HashMap[Array[MType], MGenericType]
+
+       # Is there a `new` factory to allow the pseudo instantiation?
+       var has_new_factory = false is writable
 end
 
 
@@ -694,6 +697,8 @@ abstract class MType
                if sup isa MNullableType then
                        sup_accept_null = true
                        sup = sup.mtype
+               else if sup isa MNotNullType then
+                       sup = sup.mtype
                else if sup isa MNullType then
                        sup_accept_null = true
                end
@@ -701,16 +706,20 @@ abstract class MType
                # Can `sub` provide null or not?
                # Thus we can match with `sup_accept_null`
                # Also discard the nullable marker if it exists
+               var sub_reject_null = false
                if sub isa MNullableType then
                        if not sup_accept_null then return false
                        sub = sub.mtype
+               else if sub isa MNotNullType then
+                       sub_reject_null = true
+                       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 `sub` is a formal type, then it is accepted if its bound is accepted
-               while sub isa MParameterType or sub isa MVirtualType do
+               while sub isa MFormalType do
                        #print "3.is {sub} a {sup}?"
 
                        # A unfixed formal type can only accept itself
@@ -718,12 +727,16 @@ abstract class MType
 
                        assert anchor != null
                        sub = sub.lookup_bound(mmodule, anchor)
+                       if sub_reject_null then sub = sub.as_notnull
 
                        #print "3.is {sub} a {sup}?"
 
                        # Manage the second layer of null/nullable
                        if sub isa MNullableType then
-                               if not sup_accept_null then return false
+                               if not sup_accept_null and not sub_reject_null then return false
+                               sub = sub.mtype
+                       else if sub isa MNotNullType then
+                               sub_reject_null = true
                                sub = sub.mtype
                        else if sub isa MNullType then
                                return sup_accept_null
@@ -731,10 +744,10 @@ abstract class MType
                end
                #print "4.is {sub} a {sup}? <- no more resolution"
 
-               assert sub isa MClassType # It is the only remaining type
+               assert sub isa MClassType else print "{sub} <? {sub}" # It is the only remaining type
 
                # A unfixed formal type can only accept itself
-               if sup isa MParameterType or sup isa MVirtualType then
+               if sup isa MFormalType then
                        return false
                end
 
@@ -995,6 +1008,13 @@ abstract class MType
                return self
        end
 
+       # Returns the not null version of the type.
+       # That is `self` minus the `null` value.
+       #
+       # For most types, this return `self`.
+       # For formal types, this returns a special `MNotNullType`
+       fun as_notnull: MType do return self
+
        private var as_nullable_cache: nullable MType = null
 
 
@@ -1256,9 +1276,19 @@ class MGenericType
        end
 end
 
+# A formal type (either virtual of parametric).
+#
+# The main issue with formal types is that they offer very little information on their own
+# and need a context (anchor and mmodule) to be useful.
+abstract class MFormalType
+       super MType
+
+       redef var as_notnull = new MNotNullType(self) is lazy
+end
+
 # A virtual formal type.
 class MVirtualType
-       super MType
+       super MFormalType
 
        # The property associated with the type.
        # Its the definitions of this property that determine the bound or the virtual type.
@@ -1386,7 +1416,7 @@ end
 # Note that parameter types are shared among class refinements.
 # Therefore parameter only have an internal name (see `to_s` for details).
 class MParameterType
-       super MType
+       super MFormalType
 
        # The generic class where the parameter belong
        var mclass: MClass
@@ -1508,33 +1538,24 @@ class MParameterType
        end
 end
 
-# A type prefixed with "nullable"
-class MNullableType
+# A type that decorates another type.
+#
+# The point of this class is to provide a common implementation of sevices that just forward to the original type.
+# Specific decorator are expected to redefine (or to extend) the default implementation as this suit them.
+abstract class MProxyType
        super MType
-
-       # The base type of the nullable type
+       # The base type
        var mtype: MType
 
        redef fun model do return self.mtype.model
-
-       init
-       do
-               self.to_s = "nullable {mtype}"
-       end
-
-       redef var to_s: String is noinit
-
-       redef var full_name is lazy do return "nullable {mtype.full_name}"
-
-       redef var c_name is lazy do return "nullable__{mtype.c_name}"
-
        redef fun need_anchor do return mtype.need_anchor
-       redef fun as_nullable do return self
-       redef fun as_notnullable do return mtype
+       redef fun as_nullable do return mtype.as_nullable
+       redef fun as_notnull do return mtype.as_notnull
+       redef fun as_notnullable do return mtype.as_notnullable
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
                var res = self.mtype.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
-               return res.as_nullable
+               return res
        end
 
        redef fun can_resolve_for(mtype, anchor, mmodule)
@@ -1542,12 +1563,10 @@ class MNullableType
                return self.mtype.can_resolve_for(mtype, anchor, mmodule)
        end
 
-       # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_nullable`
        redef fun lookup_fixed(mmodule, resolved_receiver)
        do
                var t = mtype.lookup_fixed(mmodule, resolved_receiver)
-               if t == mtype then return self
-               return t.as_nullable
+               return t
        end
 
        redef fun depth do return self.mtype.depth
@@ -1573,6 +1592,64 @@ class MNullableType
        end
 end
 
+# A type prefixed with "nullable"
+class MNullableType
+       super MProxyType
+
+       init
+       do
+               self.to_s = "nullable {mtype}"
+       end
+
+       redef var to_s: String is noinit
+
+       redef var full_name is lazy do return "nullable {mtype.full_name}"
+
+       redef var c_name is lazy do return "nullable__{mtype.c_name}"
+
+       redef fun as_nullable do return self
+       redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
+       do
+               var res = super
+               return res.as_nullable
+       end
+
+       # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_nullable`
+       redef fun lookup_fixed(mmodule, resolved_receiver)
+       do
+               var t = super
+               if t == mtype then return self
+               return t.as_nullable
+       end
+end
+
+# A non-null version of a formal type.
+#
+# When a formal type in bounded to a nullable type, this is the type of the not null version of it.
+class MNotNullType
+       super MProxyType
+
+       redef fun to_s do return "not null {mtype}"
+       redef var full_name is lazy do return "not null {mtype.full_name}"
+       redef var c_name is lazy do return "notnull__{mtype.c_name}"
+
+       redef fun as_notnull do return self
+
+       redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
+       do
+               var res = super
+               return res.as_notnull
+       end
+
+       # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_notnull`
+       redef fun lookup_fixed(mmodule, resolved_receiver)
+       do
+               var t = super
+               if t == mtype then return self
+               return t.as_notnull
+       end
+end
+
 # The type of the only value null
 #
 # The is only one null type per model, see `MModel::null_type`.
@@ -1583,6 +1660,9 @@ class MNullType
        redef fun full_name do return "null"
        redef fun c_name do return "null"
        redef fun as_nullable do return self
+
+       # Aborts on `null`
+       redef fun as_notnull do abort # sorry...
        redef fun need_anchor do return false
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
        redef fun can_resolve_for(mtype, anchor, mmodule) do return true