nullable: convert lib, tools and tests
[nit.git] / src / metamodel / static_type.nit
index 7fd2776..7d57090 100644 (file)
@@ -22,13 +22,13 @@ intrude import abstractmetamodel
 
 redef class MMLocalClass
        # Cached result of get_type
-       attr _base_type_cache: MMType
+       attr _base_type_cache: nullable MMType
 
        # Return the type of self for this class
        meth get_type: MMType
        do
                if _base_type_cache == null then _base_type_cache = new MMTypeSimpleClass(self)
-               return _base_type_cache
+               return _base_type_cache.as(not null)
        end
 
        # Register a new ancestor
@@ -40,28 +40,24 @@ redef class MMLocalClass
        end
 
        # Array of ancestor that associate each superclass with the corresponding ancestor
-       readable attr _ancestors: Map[MMLocalClass, MMAncestor]
+       readable attr _ancestors: nullable Map[MMLocalClass, MMAncestor]
 
        # The ancestor type for a given superclass
        meth ancestor(c: MMLocalClass): MMType
        do
-               assert _ancestors != null
-               if _ancestors.has_key(c) then
-                       return _ancestors[c].stype
-               end
-               return null
+               return _ancestors[c].stype
        end
 end
 
 redef class MMLocalProperty
        # The signature of the property (where it is declared)
-       readable writable attr _signature: MMSignature
+       readable writable attr _signature: nullable MMSignature
 
        attr _signatures_cache: HashMap[MMType, MMSignature] = new HashMap[MMType, MMSignature]
 
        # Return the adapted signature of self for a receiver of type t
        meth signature_for(t: MMType): MMSignature do
-               if t == local_class.get_type then return signature
+               if t == local_class.get_type then return signature.as(not null)
 
                if _signatures_cache.has_key(t) then return _signatures_cache[t]
 
@@ -74,31 +70,34 @@ end
 # Signature for local properties
 class MMSignature
        # The type of the reveiver
-       readable attr _recv: MMType 
+       readable attr _recv: MMType
 
        # The parameter types
-       attr _params: Array[MMType]
+       attr _params: Array[MMType]
 
        # The return type
-       readable attr _return_type: MMType 
+       readable attr _return_type: nullable MMType
+
+       # The closure parameters
+       readable attr _closures: Array[MMClosure] = new Array[MMClosure]
 
        # Number of parameters
        meth arity: Int
        do
-               assert _params != null
                return _params.length
        end
 
        # Is self a valid subtype of an other signature
        meth <(s: MMSignature): Bool
        do
-               assert s != null
                if self == s then
                        return true
                end
                assert _recv.module == s.recv.module
-               if arity != s.arity or (_return_type == null) != (s.return_type == null) then return false
-               if _return_type != null and not _return_type < s.return_type then
+               var rt = _return_type
+               var srt = s.return_type
+               if arity != s.arity or (rt == null) != (srt == null) then return false
+               if rt != null and not rt < srt.as(not null) then
                        return false
                end
 
@@ -107,6 +106,11 @@ class MMSignature
                                return false
                        end
                end
+
+               if closures.length != s.closures.length then return false
+               for i in [0..closures.length[ do
+                       if not s.closures[i] < closures[i] then return false
+               end
                return true
        end
 
@@ -119,8 +123,8 @@ class MMSignature
 
        redef meth to_s
        do
-               var s: String
-               if _params != null and _params.length > 0 then
+               var s = new Buffer
+               if _params.length > 0 then
                        var tmp: String
                        var a = new Array[String].with_capacity(_params.length)
                        for i in [0.._params.length[ do
@@ -129,14 +133,12 @@ class MMSignature
                                #a.add("{pn}: {p}")
                                a.add(p.to_s)
                        end
-                       s = "({a.join(",")})"
-               else
-                       s = ""
+                       s.append("({a.join(",")})")
                end
                if _return_type != null then
                        s.append(": {_return_type}")
                end
-               return s
+               return s.to_s
        end
 
        # Adapt the signature to a different receiver
@@ -146,48 +148,54 @@ class MMSignature
                        return self
                end
                var mod = r.module
-               var p = _params
-               if p != null then
-                       p = new Array[MMType]
-                       for i in _params do
-                               p.add(i.for_module(mod).adapt_to(r))
-                       end
+               var p = new Array[MMType]
+               for i in _params do
+                       p.add(i.for_module(mod).adapt_to(r))
                end
                var rv = _return_type
                if rv != null then
                        rv = rv.for_module(mod).adapt_to(r)
                end
-               return new MMSignature(p,rv,r)
+               var res = new MMSignature(p,rv,r)
+               for clos in _closures do
+                       res.closures.add(clos.adaptation_to(r))
+               end
+               return res
        end
 
-       attr _not_for_self_cache: MMSignature
+       attr _not_for_self_cache: nullable MMSignature = null
 
        # Return a type approximation if the reveiver is not self
        # Useful for virtual types
        meth not_for_self: MMSignature
        do
-               var res = _not_for_self_cache
-               if res != null then return res
+               if _not_for_self_cache != null then return _not_for_self_cache.as(not null)
 
                var need_for_self = false
-               var p = _params
-               if p != null then
-                       p = new Array[MMType]
-                       for i in _params do
-                               var i2 = i.not_for_self
-                               if i != i2 then need_for_self = true
-                               p.add(i.not_for_self)
-                       end
+               var p = new Array[MMType]
+               for i in _params do
+                       var i2 = i.not_for_self
+                       if i != i2 then need_for_self = true
+                       p.add(i2)
                end
-               
+
                var rv = _return_type
                if rv != null then
-                       var rv = rv.not_for_self
+                       rv = rv.not_for_self
                        if rv != _return_type then need_for_self = true
                end
 
+               var clos = new Array[MMClosure]
+               for c in _closures do
+                       var c2 = c.not_for_self
+                       if c2 != c then need_for_self = true
+                       clos.add(c2)
+               end
+
+               var res: MMSignature
                if need_for_self then
                        res = new MMSignature(p, rv, _recv)
+                       res.closures.add_all(clos)
                else
                        res = self
                end
@@ -196,22 +204,71 @@ class MMSignature
                return res
        end
 
-       init(params: Array[MMType], return_type: MMType, r: MMType)
+       init(params: Array[MMType], return_type: nullable MMType, r: MMType)
        do
-               assert params != null
                _params = params
                _return_type = return_type
                _recv = r
        end
 end
 
+# A closure in a signature
+class MMClosure
+       # The signature of the closure
+       readable attr _signature: MMSignature
+
+       # Is the closure a brek one
+       # aka is defined with the break keyword thus does not return
+       readable attr _is_break: Bool
+
+       # Is the closure optional?
+       # ie is there a default definition
+       readable attr _is_optional: Bool
+
+       # Adapt the signature to a different receiver
+       meth adaptation_to(r: MMType): MMClosure
+       do
+               return new MMClosure(_signature.adaptation_to(r), _is_break, _is_optional)
+       end
+
+       init(s: MMSignature, is_break: Bool, is_optional: Bool)
+       do
+               _signature = s
+               _is_break = is_break
+               _is_optional = is_optional
+       end
+
+       meth not_for_self: MMClosure
+       do
+               var sig = _signature.not_for_self
+               if sig != _signature then
+                       return new MMClosure(sig, _is_break, _is_optional)
+               else
+                       return self
+               end
+       end
+
+       meth <(c: MMClosure): Bool
+       do
+               if c.is_optional and not is_optional then return false
+               if not c.is_break and is_break then return false
+               return c.signature < signature
+       end
+end
+
 # Inheritance relation between two types
 abstract class MMAncestor
        # The inherited type
-       readable writable attr _stype: MMType 
+       writable attr _stype: nullable MMType = null
+
+       # The inherited type
+       meth stype: MMType do return _stype.as(not null)
+
+       # The inheriter (heir) type
+       writable attr _inheriter: nullable MMType = null
 
        # The inheriter (heir) type
-       readable writable attr _inheriter: MMType 
+       meth inheriter: MMType do return _inheriter.as(not null)
 
        meth is_reffinement: Bool do
                return stype.module != stype.module
@@ -226,7 +283,7 @@ abstract class MMAncestor
 
        redef meth to_s
        do
-               if stype == null then
+               if _stype == null then
                        return local_class.to_s
                else
                        return stype.to_s
@@ -243,6 +300,10 @@ abstract class MMType
        # The local class that self direclty or indirectly refers to
        meth local_class: MMLocalClass is abstract
 
+       # Is the type a valid one
+       # For instance, circular dependency on formal types is invalid
+       meth is_valid: Bool do return true
+
        # Is self a valid subtype of t
        meth <(t : MMType): Bool is abstract
 
@@ -260,18 +321,105 @@ abstract class MMType
 
        # Adapt self to another local class context
        # Useful for genericity
+       # 'c' Must be a super-class of self
+       # Example:
+       #   class A[E]
+       #   class B[F] special A[F]
+       #   class C[G] special B[String]
+       #   class D special C[Float]
+       # 'A[Int]'.upcast_for('A') -> 'A[Int]'
+       # 'A[Int]'.upcast_for('B') -> abort
+       # 'B[Int]'.upcast_for('B') -> 'B[Int]'
+       # 'B[Int]'.upcast_for('A') -> 'A[Int]'
+       # 'B[Int]'.upcast_for('C') -> abort
+       # 'C[Int]'.upcast_for('C') -> 'C[Int]'
+       # 'C[Int]'.upcast_for('B') -> 'B[String]'
+       # 'C[Int]'.upcast_for('A') -> 'A[String]'
+       # 'D'.upcast_for('D') -> 'D'
+       # 'D'.upcast_for('C') -> 'C[Float]'
+       # 'D'.upcast_for('B') -> 'C[String]'
+       # 'D'.upcast_for('A') -> 'A[String]'
        meth upcast_for(c: MMLocalClass): MMType is abstract
 
        # Return a type approximation if the reveiver is not self
        # Useful for virtual types
        meth not_for_self: MMType do return self
+
+       # The nullable version of self (if needed)
+       attr _as_nullable_cache: nullable MMType = null
+
+       # IS the type can accept null?
+       meth is_nullable: Bool do return false
+
+       # Return the nullable version of the type
+       # Noop if already nullable
+       meth as_nullable: MMType do
+               var cache = _as_nullable_cache
+               if cache != null then return cache
+               var res = new MMNullableType(self)
+               _as_nullable_cache = res
+               return res
+       end
+
+       # Return the not null version of the type
+       # Noop if already not null
+       meth as_notnull: MMType do return self
+end
+
+class MMNullableType
+special MMType
+       attr _base_type: MMType
+       redef meth is_valid do return _base_type.is_valid
+       redef meth is_nullable: Bool do return true
+       redef meth as_notnull do return _base_type
+       redef meth as_nullable do return self
+       init(t: MMType) do _base_type = t
+
+       redef meth module do return _base_type.module
+
+       redef meth local_class do return _base_type.local_class
+
+       redef meth <(t)
+       do
+               return t isa MMNullableType and _base_type < t.as_notnull
+       end
+
+       redef meth to_s
+       do
+               return "nullable {_base_type}"
+       end
+
+       redef meth is_supertype(t)
+       do
+               return _base_type.is_supertype(t)
+       end
+
+       redef meth for_module(mod)
+       do
+               return _base_type.for_module(mod).as_nullable
+       end
+
+       redef meth adapt_to(recv)
+       do
+               return _base_type.adapt_to(recv).as_nullable
+       end
+
+       redef meth upcast_for(c)
+       do
+               return _base_type.upcast_for(c)
+       end
+
+       redef meth not_for_self
+       do
+               return _base_type.not_for_self.as_nullable
+       end
 end
 
 class MMTypeClass 
 special MMType
        redef readable attr _local_class: MMLocalClass
        redef meth module do return _local_class.module end
-       redef meth <(t) do return t != null and t.is_supertype(self)
+       redef meth <(t) do return t.is_supertype(self)
 
        redef meth to_s
        do
@@ -280,14 +428,10 @@ special MMType
 
        redef meth upcast_for(c)
        do
-               assert _local_class != null
-               assert c != null
-
                var t: MMType = self
                if _local_class != c then
                        t = _local_class.ancestor(c)
                end
-               assert t != null
                return t
        end
 
@@ -310,7 +454,6 @@ special MMTypeClass
                if module != mod then
                        t = _local_class.for_module(mod).get_type
                end
-               assert t != null
                return t
        end
 
@@ -325,11 +468,15 @@ end
 # The type of null
 class MMTypeNone
 special MMType
-       redef readable attr _module: MMModule
-       redef meth <(t) do return true
-       redef meth is_supertype(t) do return false
-       redef meth local_class do abort
-       redef meth upcast_for(c) do return self
+       redef readable attr _module: MMModule
+       redef meth is_nullable: Bool do return true
+       redef meth <(t) do return t isa MMTypeNone or t isa MMNullableType
+       redef meth to_s do return "null"
+       redef meth is_supertype(t) do return false
+       redef meth local_class do abort
+       redef meth upcast_for(c) do abort
+       redef meth as_nullable do return self
+       redef meth as_notnull do abort
 
        private init(m: MMModule) do _module = m
 end