nitc/typing: document `merge_types`
[nit.git] / src / semantize / typing.nit
index 73e1bd0..dd98336 100644 (file)
@@ -102,9 +102,9 @@ private class TypeVisitor
        end
 
        # Check that `sub` is a subtype of `sup`.
-       # If `sub` is not a valid suptype, then display an error on `node` an return null.
-       # If `sub` is a safe subtype of `sup` then return `sub`.
-       # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`.
+       # If `sub` is not a valid suptype, then display an error on `node` and return `null`.
+       # If `sub` is a safe subtype of `sup`, then return `sub`.
+       # If `sub` is an unsafe subtype (i.e., an implicit cast is required), then return `sup`.
        #
        # The point of the return type is to determinate the usable type on an expression when `autocast` is true:
        # If the suptype is safe, then the return type is the one on the expression typed by `sub`.
@@ -117,7 +117,7 @@ private class TypeVisitor
                        #node.debug("Unsafe typing: expected {sup}, got {sub}")
                        return sup
                end
-               if sup isa MBottomType then return null # Skip error
+               if sup isa MErrorType then return null # Skip error
                if sub.need_anchor then
                        var u = anchor_to(sub)
                        self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
@@ -600,6 +600,29 @@ private class TypeVisitor
                flow.set_var(self, variable, mtype)
        end
 
+       # Find the exact representable most specific common super-type in `col`.
+       #
+       # Try to find the most specific common type that is a super-type of each types
+       # in `col`.
+       # In most cases, the result is simply the most general type in `col`.
+       # If nullables types are involved, then the nullable information is correctly preserved.
+       # If incomparable super-types exists in `col`, them no solution is given and the `null`
+       # value is returned (since union types are non representable in Nit)
+       #
+       # The `null` values in `col` are ignored, nulltypes (MNullType) are considered.
+       #
+       # Returns the `null` value if:
+       #
+       # * `col` is empty
+       # * `col` only have null values
+       # * there is a conflict
+       #
+       # Example (with a diamond A,B,C,D):
+       #
+       # * merge(A,B,C) -> A, because A is the most general type in {A,B,C}
+       # * merge(C,B) -> null, there is conflict, because `B or C` cannot be represented
+       # * merge(A,nullable B) -> nullable A, because A is the most general type and
+       #   the nullable information is preserved
        fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
        do
                if col.length == 1 then return col.first
@@ -677,6 +700,11 @@ class CallSite
                if map == null then is_broken = true
                return map == null
        end
+
+       # Information about the callsite to display on a node
+       fun dump_info(v: ASTDump): String do
+               return "{recv}.{mpropdef}{msignature}"
+       end
 end
 
 redef class Variable
@@ -880,6 +908,19 @@ redef class AExpr
        #
        # This attribute is meaning less on expressions not used as attributes.
        var vararg_decl: Int = 0
+
+       redef fun dump_info(v) do
+               var res = super
+               var mtype = self.mtype
+               if mtype != null then
+                       res += v.yellow(":{mtype}")
+               end
+               var ict = self.implicit_cast_to
+               if ict != null then
+                       res += v.yellow("(.as({ict}))")
+               end
+               return res
+       end
 end
 
 redef class ABlockExpr
@@ -1480,14 +1521,14 @@ redef class AugmentedStringFormExpr
        var newline: nullable CallSite = null
        # Regex::extended, used for suffix `b` on `re`
        var extended: nullable CallSite = null
-       # NativeString::to_bytes_with_copy, used for prefix `b`
+       # CString::to_bytes_with_copy, used for prefix `b`
        var to_bytes_with_copy: nullable CallSite = null
 
        redef fun accept_typing(v) do
                var mclass = v.get_mclass(self, "String")
                if mclass == null then return # Forward error
                if is_bytestring then
-                       to_bytes_with_copy = v.get_method(self, v.mmodule.native_string_type, "to_bytes_with_copy", false)
+                       to_bytes_with_copy = v.get_method(self, v.mmodule.c_string_type, "to_bytes_with_copy", false)
                        mclass = v.get_mclass(self, "Bytes")
                else if is_re then
                        to_re = v.get_method(self, mclass.mclass_type, "to_re", false)
@@ -1684,6 +1725,16 @@ redef class AIsaExpr
        do
                v.check_expr_cast(self, self.n_expr, self.n_type)
        end
+
+       redef fun dump_info(v) do
+               var res = super
+               var mtype = self.cast_type
+               if mtype != null then
+                       res += v.yellow(".as({mtype})")
+               end
+               return res
+       end
+
 end
 
 redef class AAsCastExpr
@@ -1849,6 +1900,15 @@ redef class ASendExpr
        fun raw_arguments: Array[AExpr] do return compute_raw_arguments
 
        private fun compute_raw_arguments: Array[AExpr] is abstract
+
+       redef fun dump_info(v) do
+               var res = super
+               var callsite = self.callsite
+               if callsite != null then
+                       res += v.yellow(" call="+callsite.dump_info(v))
+               end
+               return res
+       end
 end
 
 redef class ABinopExpr
@@ -2099,6 +2159,19 @@ redef class ASuperExpr
 
                self.is_typed = true
        end
+
+       redef fun dump_info(v) do
+               var res = super
+               var callsite = self.callsite
+               if callsite != null then
+                       res += v.yellow(" super-init="+callsite.dump_info(v))
+               end
+               var mpropdef = self.mpropdef
+               if mpropdef != null then
+                       res += v.yellow(" call-next-method="+mpropdef.to_s)
+               end
+               return res
+       end
 end
 
 ####
@@ -2179,6 +2252,15 @@ redef class ANewExpr
                var args = n_args.to_a
                callsite.check_signature(v, node, args)
        end
+
+       redef fun dump_info(v) do
+               var res = super
+               var callsite = self.callsite
+               if callsite != null then
+                       res += v.yellow(" call="+callsite.dump_info(v))
+               end
+               return res
+       end
 end
 
 ####
@@ -2219,6 +2301,16 @@ redef class AAttrFormExpr
                attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr)
                self.attr_type = attr_type
        end
+
+       redef fun dump_info(v) do
+               var res = super
+               var mproperty = self.mproperty
+               var attr_type = self.attr_type
+               if mproperty != null then
+                       res += v.yellow(" attr={mproperty}:{attr_type or else "BROKEN"}")
+               end
+               return res
+       end
 end
 
 redef class AAttrExpr