model: add `MClass::signature_to_s` to use in messages.
[nit.git] / src / semantize / typing.nit
index ba08e4d..4bef8a2 100644 (file)
@@ -239,12 +239,16 @@ private class TypeVisitor
 
                if not mtype2 isa MNullType then return
 
-               if mtype isa MNullType then return
-
                # Check of useless null
                if not check_can_be_null(anode.n_expr, mtype) then return
 
-               mtype = mtype.as_notnull
+               if mtype isa MNullType then
+                       # Because of type adaptation, we cannot just stop here
+                       # so return use `null` as a bottom type that will be merged easily (cf) `merge_types`
+                       mtype = null
+               else
+                       mtype = mtype.as_notnull
+               end
 
                # Check for type adaptation
                var variable = anode.n_expr.its_variable
@@ -444,6 +448,8 @@ private class TypeVisitor
 
        fun get_variable(node: AExpr, variable: Variable): nullable MType
        do
+               if not variable.is_adapted then return variable.declared_type
+
                var flow = node.after_flow_context
                if flow == null then
                        self.error(node, "No context!")
@@ -546,6 +552,10 @@ end
 redef class Variable
        # The declared type of the variable
        var declared_type: nullable MType
+
+       # Was the variable type-adapted?
+       # This is used to speedup type retrieval while it remains `false`
+       private var is_adapted = false
 end
 
 redef class FlowContext
@@ -557,9 +567,11 @@ redef class FlowContext
        # Warning2: sub-flow may have cached a unadapted variable
        private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType)
        do
+               if variable.declared_type == mtype and not variable.is_adapted then return
                if vars.has_key(variable) and vars[variable] == mtype then return
                self.vars[variable] = mtype
                v.dirty = true
+               variable.is_adapted = true
                #node.debug "set {variable} to {mtype or else "X"}"
        end
 
@@ -1558,6 +1570,11 @@ redef class ASendExpr
        # Each subclass simply provide the correct name.
        private fun property_name: String is abstract
 
+       # The node identifying the name (id, operator, etc) for messages.
+       #
+       # Is `self` by default
+       private fun property_node: ANode do return self
+
        # An array of all arguments (excluding self)
        fun raw_arguments: Array[AExpr] do return compute_raw_arguments
 
@@ -1631,11 +1648,13 @@ end
 
 redef class ACallExpr
        redef fun property_name do return n_id.text
+       redef fun property_node do return n_id
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
 redef class ACallAssignExpr
        redef fun property_name do return n_id.text + "="
+       redef fun property_node do return n_id
        redef fun compute_raw_arguments
        do
                var res = n_args.to_a
@@ -1667,11 +1686,12 @@ redef class ASendReassignFormExpr
        do
                var recvtype = v.visit_expr(self.n_expr)
                var name = self.property_name
+               var node = self.property_node
 
                if recvtype == null then return # Forward error
 
                var for_self = self.n_expr isa ASelfExpr
-               var callsite = v.get_method(self, recvtype, name, for_self)
+               var callsite = v.get_method(node, recvtype, name, for_self)
 
                if callsite == null then return
                self.callsite = callsite
@@ -1686,7 +1706,7 @@ redef class ASendReassignFormExpr
                        return
                end
 
-               var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr)
+               var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
                if wcallsite == null then return
                self.write_callsite = wcallsite
 
@@ -1703,6 +1723,7 @@ end
 
 redef class ACallReassignExpr
        redef fun property_name do return n_id.text
+       redef fun property_node do return n_id
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
@@ -1713,6 +1734,7 @@ end
 
 redef class AInitExpr
        redef fun property_name do return "init"
+       redef fun property_node do return n_kwinit
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
@@ -1863,19 +1885,36 @@ redef class ANewExpr
                end
 
                self.recvtype = recvtype
+               var kind = recvtype.mclass.kind
 
                var name: String
                var nid = self.n_id
+               var node: ANode
                if nid != null then
                        name = nid.text
+                       node = nid
                else
                        name = "new"
+                       node = self.n_kwnew
                end
-               var callsite = v.get_method(self, recvtype, name, false)
+               if name == "intern" then
+                       if kind != concrete_kind then
+                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+                               return
+                       end
+                       if n_args.n_exprs.not_empty then
+                               v.error(n_args, "Type Error: the intern constructor expects no arguments.")
+                               return
+                       end
+                       # Our job is done
+                       self.mtype = recvtype
+                       return
+               end
+
+               var callsite = v.get_method(node, recvtype, name, false)
                if callsite == null then return
 
                if not callsite.mproperty.is_new then
-                       var kind = recvtype.mclass.kind
                        if kind != concrete_kind then
                                v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
                                return