Merge: Not null types
authorJean Privat <jean@pryen.org>
Wed, 8 Apr 2015 01:01:49 +0000 (08:01 +0700)
committerJean Privat <jean@pryen.org>
Wed, 8 Apr 2015 01:01:49 +0000 (08:01 +0700)
In order to fix #86 and #1238 some preliminary work to solve remaining issues with the type system is needed. This PR is a step toward this goal.

This introduce not-null types, that is a modifier to indicate that a type cannot contain null.
Basically, this new modifier is almost useless because it is the semantic of all the types (except obviously null and nullable things). Except in one case: when one adapts a formal type whose bound is nullable.

Before the PR, the semantic was the following

~~~nit
class A[E: nullable Object]
   fun foo(e: E, o: nullable Object) do
      var o2 = o.as(not null) # the static type of o2 is `Object`
      print o2 # OK
      var e2 = e.as(not null) # the static type of e2 is still `E` because there is no `nullable` to remove
      print e2 # Error: expected Object, got E
   end
end
~~~

Obviously, the issue was not that important because people managed to program complex things in Nit and I do not remember getting some complain about that particular issue. For the rare cases of this unexpected behavior, a workaround was possible: to cast on the non-nullable bound

~~~nit
   var e2 = e.as(Object)
   print e2 # OK
~~~

Nevertheless, the behavior was still buggy since type information was lost and not POLA. Moreover, `!= null` and `or else` did not have a workaround.

So, this PR introduces a special new type-modifier for this case so that everything become sensible.

~~~nit
      var e2 = e.as(not null) # the static type of e2 is now `not null E`
      print e2 # OK
~~~

Moreover, a lot of local refactorisation was done in model.nit and typing.nit to clean and harmonize the code. So that independently of the new notnull modifier, the code is cleaner, some bugs where removed and some small features added, especially the detection of useless `or else`.

Last, but not least, the `not null` thing is only an internal modifier and is not usable as a syntactic type construction (the grammar and the AST is unchanged); `not null` can however be shown to the programmer in messages.

~~~nit
      var e2 = e.as(not null) # the static type of e2 is now `not null E`
      var e3 = e2.as(not null) # << Warning: expression is not null, since it is a `not null E` >>
~~~

I could easily add `not null` as a specific syntactic construction since everything internally is ready. but 1. does this worth it?. 2. I do not want to conflict with #1243 that also change the grammar.
As an example, is it useful to write the following? (currently refused but very easy to add after this PR)

~~~nit
class A[E: nullable Object]
   fun foo(e: not null E): not null E do
      var x = e.to_s # no null pointer exception
      # ...
      return e
   end
end

var a = new A[nullable Int]
var i = a.foo(5)
~~~

Pull-Request: #1244
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

1  2 
src/model/model.nit
src/modelize/modelize_property.nit
src/semantize/typing.nit

Simple merge
Simple merge
@@@ -1133,10 -1162,8 +1170,10 @@@ redef class AOrElseExp
  
                var t = v.merge_types(self, [t1, t2])
                if t == null then
 -                      t = v.mmodule.object_type
 +                      var c = v.get_mclass(self, "Object")
 +                      if c == null then return # forward error
 +                      t = c.mclass_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}")