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>

1  2 
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/semantize/typing.nit

@@@ -59,43 -59,6 +59,43 @@@ redef class ModelBuilde
                self.toolcontext.info("*** GENERATING C ***", 1)
  
                var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
 +              compiler.do_compilation
 +              compiler.display_stats
 +
 +              var time1 = get_time
 +              self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
 +              write_and_make(compiler)
 +      end
 +end
 +
 +# Compiler that use global compilation and perform hard optimisations like:
 +#   * customization
 +#   * switch dispatch
 +#   * inlining
 +class GlobalCompiler
 +      super AbstractCompiler
 +
 +      redef type VISITOR: GlobalCompilerVisitor
 +
 +      # The result of the RTA (used to know live types and methods)
 +      var runtime_type_analysis: RapidTypeAnalysis
 +
 +      init
 +      do
 +              var file = new_file("{mainmodule.c_name}.nitgg")
 +              self.header = new CodeWriter(file)
 +              self.live_primitive_types = new Array[MClassType]
 +              for t in runtime_type_analysis.live_types do
 +                      if t.ctype != "val*" or t.mclass.name == "Pointer" then
 +                              self.live_primitive_types.add(t)
 +                      end
 +              end
 +      end
 +
 +      redef fun do_compilation
 +      do
 +              var compiler = self
 +
                compiler.compile_header
  
                if mainmodule.model.get_mclasses_by_name("Pointer") != null then
                # Compile until all runtime_functions are visited
                while not compiler.todos.is_empty do
                        var m = compiler.todos.shift
 -                      self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
 +                      modelbuilder.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
                        m.compile_to_c(compiler)
                end
 -              self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
 -
 -              compiler.display_stats
 -
 -              var time1 = get_time
 -              self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
 -              write_and_make(compiler)
 -      end
 -end
 -
 -# Compiler that use global compilation and perform hard optimisations like:
 -#   * customization
 -#   * switch dispatch
 -#   * inlining
 -class GlobalCompiler
 -      super AbstractCompiler
 -
 -      redef type VISITOR: GlobalCompilerVisitor
 -
 -      # The result of the RTA (used to know live types and methods)
 -      var runtime_type_analysis: RapidTypeAnalysis
 +              modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
  
 -      init
 -      do
 -              var file = new_file("{mainmodule.c_name}.nitgg")
 -              self.header = new CodeWriter(file)
 -              self.live_primitive_types = new Array[MClassType]
 -              for t in runtime_type_analysis.live_types do
 -                      if t.ctype != "val*" or t.mclass.name == "Pointer" then
 -                              self.live_primitive_types.add(t)
 -                      end
 -              end
        end
  
        # Compile class names (for the class_name and output_class_name methods)
@@@ -441,7 -434,7 +441,7 @@@ class GlobalCompilerVisito
                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);")
@@@ -90,55 -90,12 +90,55 @@@ redef class ModelBuilde
                self.toolcontext.info("*** GENERATING C ***", 1)
  
                var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
 +              compiler.do_compilation
 +              compiler.display_stats
 +
 +              var time1 = get_time
 +              self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
 +              write_and_make(compiler)
 +      end
 +
 +      # Count number of invocations by VFT
 +      private var nb_invok_by_tables = 0
 +      # Count number of invocations by direct call
 +      private var nb_invok_by_direct = 0
 +      # Count number of invocations by inlining
 +      private var nb_invok_by_inline = 0
 +end
 +
 +# Singleton that store the knowledge about the separate compilation process
 +class SeparateCompiler
 +      super AbstractCompiler
 +
 +      redef type VISITOR: SeparateCompilerVisitor
 +
 +      # The result of the RTA (used to know live types and methods)
 +      var runtime_type_analysis: nullable RapidTypeAnalysis
 +
 +      private var undead_types: Set[MType] = new HashSet[MType]
 +      private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
 +
 +      private var type_ids: Map[MType, Int] is noinit
 +      private var type_colors: Map[MType, Int] is noinit
 +      private var opentype_colors: Map[MType, Int] is noinit
 +      protected var method_colors: Map[PropertyLayoutElement, Int] is noinit
 +      protected var attr_colors: Map[MAttribute, Int] is noinit
 +
 +      init do
 +              var file = new_file("nit.common")
 +              self.header = new CodeWriter(file)
 +              self.compile_box_kinds
 +      end
 +
 +      redef fun do_compilation
 +      do
 +              var compiler = self
                compiler.compile_header
  
                var c_name = mainmodule.c_name
  
                # compile class structures
 -              self.toolcontext.info("Property coloring", 2)
 +              modelbuilder.toolcontext.info("Property coloring", 2)
                compiler.new_file("{c_name}.classes")
                compiler.do_property_coloring
                for m in mainmodule.in_importation.greaters do
  
                # compile methods
                for m in mainmodule.in_importation.greaters do
 -                      self.toolcontext.info("Generate C for module {m.full_name}", 2)
 +                      modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2)
                        compiler.new_file("{m.c_name}.sep")
                        compiler.compile_module_to_c(m)
                end
  
                # compile live & cast type structures
 -              self.toolcontext.info("Type coloring", 2)
 +              modelbuilder.toolcontext.info("Type coloring", 2)
                compiler.new_file("{c_name}.types")
 +              compiler.compile_types
 +      end
 +
 +      # Color and compile type structures and cast information
 +      fun compile_types
 +      do
 +              var compiler = self
 +
                var mtypes = compiler.do_type_coloring
                for t in mtypes do
                        compiler.compile_type_to_c(t)
                        compiler.compile_type_to_c(t)
                end
  
 -              compiler.display_stats
 -
 -              var time1 = get_time
 -              self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
 -              write_and_make(compiler)
 -      end
 -
 -      # Count number of invocations by VFT
 -      private var nb_invok_by_tables = 0
 -      # Count number of invocations by direct call
 -      private var nb_invok_by_direct = 0
 -      # Count number of invocations by inlining
 -      private var nb_invok_by_inline = 0
 -end
 -
 -# Singleton that store the knowledge about the separate compilation process
 -class SeparateCompiler
 -      super AbstractCompiler
 -
 -      redef type VISITOR: SeparateCompilerVisitor
 -
 -      # The result of the RTA (used to know live types and methods)
 -      var runtime_type_analysis: nullable RapidTypeAnalysis
 -
 -      private var undead_types: Set[MType] = new HashSet[MType]
 -      private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
 -
 -      private var type_ids: Map[MType, Int] is noinit
 -      private var type_colors: Map[MType, Int] is noinit
 -      private var opentype_colors: Map[MType, Int] is noinit
 -      protected var method_colors: Map[PropertyLayoutElement, Int] is noinit
 -      protected var attr_colors: Map[MAttribute, Int] is noinit
 -
 -      init do
 -              var file = new_file("nit.common")
 -              self.header = new CodeWriter(file)
 -              self.compile_box_kinds
        end
  
        redef fun compile_header_structs do
@@@ -1077,8 -1063,8 +1077,8 @@@ class SeparateCompilerVisito
        do
                var rta = compiler.runtime_type_analysis
                var mmethod = callsite.mproperty
 -              # TODO: Inlining of new-style constructors
 -              if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then
 +              # TODO: Inlining of new-style constructors with initializers
 +              if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
                        var tgs = rta.live_targets(callsite)
                        if tgs.length == 1 then
                                # DIRECT CALL
                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
                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
diff --combined src/semantize/typing.nit
@@@ -266,8 -266,13 +266,13 @@@ private class TypeVisito
  
                #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 +761,6 @@@ redef class AReassignFormExp
  
                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
@@@ -1243,7 -1243,7 +1243,7 @@@ redef class AArrayExp
                if mtype == null then
                        mtype = v.merge_types(self, mtypes)
                end
 -              if mtype == null then
 +              if mtype == null or mtype isa MNullType then
                        v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}")
                        return
                end
@@@ -1407,10 -1407,6 +1407,6 @@@ redef class ASendExp
                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 +1550,6 @@@ redef class ASendReassignFormExp
                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)