Merge: null as receiver
authorJean Privat <jean@pryen.org>
Thu, 8 Jan 2015 02:49:57 +0000 (21:49 -0500)
committerJean Privat <jean@pryen.org>
Thu, 8 Jan 2015 02:49:57 +0000 (21:49 -0500)
Accepts literal `null` as a receiver of `==`, `!=` and `is_same_instance`.

This could help to finish #1041

Does people need other methods of Object available for `null`?

Pull-Request: #1082
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/interpreter/naive_interpreter.nit
src/rapid_type_analysis.nit
src/semantize/typing.nit
tests/base_null.nit [new file with mode: 0644]
tests/sav/base_null.res [new file with mode: 0644]

index 0b42b7b..e633e36 100644 (file)
@@ -441,7 +441,7 @@ class GlobalCompilerVisitor
                if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then
                        # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
                        self.add("if ({args.first} == NULL) \{ /* Special null case */")
-                       if m.name == "==" then
+                       if m.name == "==" or m.name == "is_same_instance" then
                                assert res != null
                                if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} == NULL);")
index 8152658..eb2437c 100644 (file)
@@ -1126,10 +1126,10 @@ class SeparateCompilerVisitor
                var res: nullable RuntimeVariable = null
                var recv = arguments.first
                var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
-               var maybenull = recv.mcasttype isa MNullableType and consider_null
+               var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
                if maybenull then
                        self.add("if ({recv} == NULL) \{")
-                       if mmethod.name == "==" then
+                       if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
                                var arg = arguments[1]
                                if arg.mcasttype isa MNullableType then
@@ -1156,15 +1156,15 @@ class SeparateCompilerVisitor
                else
                        self.add("\{")
                end
-               if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
-                       if res == null then res = self.new_var(bool_type)
-                       # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
+               if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
+                       # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
                        var arg = arguments[1]
                        if arg.mcasttype isa MNullType then
-                               if mmethod.name == "==" then
-                                       self.add("{res} = 0; /* arg is null but recv is not */")
-                               else
+                               if res == null then res = self.new_var(bool_type)
+                               if mmethod.name == "!=" then
                                        self.add("{res} = 1; /* arg is null and recv is not */")
+                               else # `==` and `is_same_instance`
+                                       self.add("{res} = 0; /* arg is null but recv is not */")
                                end
                                self.add("\}") # closes the null case
                                self.add("if (0) \{") # what follow is useless, CC will drop it
index 4bbe635..5cc3892 100644 (file)
@@ -458,7 +458,7 @@ class NaiveInterpreter
        fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance
        do
                if mtype isa MNullType then
-                       if mproperty.name == "==" then
+                       if mproperty.name == "==" or mproperty.name == "is_same_instance" then
                                return self.bool_instance(args[0] == args[1])
                        else if mproperty.name == "!=" then
                                return self.bool_instance(args[0] != args[1])
index 7881db0..2d9fee4 100644 (file)
@@ -87,8 +87,7 @@ class RapidTypeAnalysis
                var anchor = callsite.anchor
                if anchor != null then mtype = mtype.anchor_to(callsite.mmodule, anchor)
                mtype = mtype.as_notnullable
-               assert mtype isa MClassType
-               mtype = mtype.mclass.intro.bound_mtype
+               if mtype isa MClassType then mtype = mtype.mclass.intro.bound_mtype
                var mproperty = callsite.mproperty
                var res = live_targets_cache[mtype, mproperty]
                if res != null then return res
index 9d24a21..ae697d4 100644 (file)
@@ -266,8 +266,13 @@ private class TypeVisitor
 
                #debug("recv: {recvtype} (aka {unsafe_type})")
                if recvtype isa MNullType then
-                       self.error(node, "Error: Method '{name}' call on 'null'.")
-                       return null
+                       # `null` only accepts some methods of object.
+                       if name == "==" or name == "!=" or name == "is_same_instance" then
+                               unsafe_type = mmodule.object_type.as_nullable
+                       else
+                               self.error(node, "Error: Method '{name}' call on 'null'.")
+                               return null
+                       end
                end
 
                var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
@@ -756,11 +761,6 @@ redef class AReassignFormExpr
 
                self.read_type = readtype
 
-               if readtype isa MNullType then
-                       v.error(self, "Error: Method '{reassign_name}' call on 'null'.")
-                       return null
-               end
-
                var callsite = v.get_method(self, readtype, reassign_name, false)
                if callsite == null then return null # Skip error
                self.reassign_callsite = callsite
@@ -1407,10 +1407,6 @@ redef class ASendExpr
                var name = self.property_name
 
                if recvtype == null then return # Forward error
-               if recvtype isa MNullType then
-                       v.error(self, "Error: Method '{name}' call on 'null'.")
-                       return
-               end
 
                var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr)
                if callsite == null then return
@@ -1554,10 +1550,6 @@ redef class ASendReassignFormExpr
                var name = self.property_name
 
                if recvtype == null then return # Forward error
-               if recvtype isa MNullType then
-                       v.error(self, "Error: Method '{name}' call on 'null'.")
-                       return
-               end
 
                var for_self = self.n_expr isa ASelfExpr
                var callsite = v.get_method(self, recvtype, name, for_self)
diff --git a/tests/base_null.nit b/tests/base_null.nit
new file mode 100644 (file)
index 0000000..78f362a
--- /dev/null
@@ -0,0 +1,42 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+class A
+       redef fun output do 10.output
+end
+
+var a: Object = new A
+var na: nullable Object = new A
+var nn: nullable Object = null
+
+(null == a).output
+(null == na).output
+(null == nn).output
+(null == null).output
+
+'\n'.output
+
+null.is_same_instance(a).output
+null.is_same_instance(na).output
+null.is_same_instance(nn).output
+null.is_same_instance(null).output
+
+'\n'.output
+
+(null != a).output
+(null != na).output
+(null != nn).output
+(null != null).output
diff --git a/tests/sav/base_null.res b/tests/sav/base_null.res
new file mode 100644 (file)
index 0000000..80acf2a
--- /dev/null
@@ -0,0 +1,16 @@
+base_null.nit:28,2--13: Warning: expression is not null, since it is a `null`.
+base_null.nit:42,2--13: Warning: expression is not null, since it is a `null`.
+false
+false
+true
+true
+
+false
+false
+true
+true
+
+true
+true
+false
+false