X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 08f3d65..7c1adf4 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -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 @@ -621,6 +644,82 @@ private class TypeVisitor #self.modelbuilder.warning(node, "Type Error: {col.length} conflicting types: <{col.join(", ")}>") return null end + + # Find a most general common subtype between `type1` and `type2`. + # + # Find the most general type that is a subtype of `type2` and, if possible, a subtype of `type1`. + # Basically, this return the most specific type between `type1` and `type2`. + # If nullable types are involved, the information is correctly preserved. + # If `type1` and `type2` are incomparable then `type2` is preferred (since intersection types + # are not representable in Nit). + # + # The `null` value is returned if both `type1` and `type2` are null. + # + # Examples (with diamond A,B,C,D): + # + # * intersect_types(A,B) -> B, because B is a subtype of A + # * intersect_types(B,A) -> B, because B is a subtype of A + # * intersect_types(B,C) -> C, B and C are incomparable, + # `type2` is then preferred (`B and C` cannot be represented) + # * intersect_types(nullable B,A) -> B, because B<:A and the non-null information is preserved + # * intersect_types(B,nullable C) -> C, `type2` is preferred and the non-null information is preserved + fun intersect_types(node: ANode, type1, type2: nullable MType): nullable MType + do + if type1 == null then return type2 + if type2 == null then return type1 + + if not can_be_null(type2) or not can_be_null(type1) then + type1 = type1.as_notnull + type2 = type2.as_notnull + end + + var res + if is_subtype(type1, type2) then + res = type1 + else + res = type2 + end + return res + end + + # Find a most general type that is a subtype of `type1` but not one of `type2`. + # + # Basically, this returns `type1`-`type2` but since there is no substraction type + # in Nit this just returns `type1` most of the case. + # + # The few other cases are if `type2` is a super-type and if some nullable information + # is present. + # + # The `null` value is returned if `type1` is null. + # + # Examples (with diamond A,B,C,D): + # + # * diff_types(A,B) -> A, because the notB cannot be represented + # * diff_types(B,A) -> None (absurd type), because B<:A + # * diff_types(nullable A, nullable B) -> A, because null is removed + # * diff_types(nullable B, A) -> Null, because anything but null is removed + fun diff_types(node: ANode, type1, type2: nullable MType): nullable MType + do + if type1 == null then return null + if type2 == null then return type1 + + # if t1 <: t2 then t1-t2 = bottom + if is_subtype(type1, type2) then + return modelbuilder.model.null_type.as_notnull + end + + # else if t1 <: nullable t2 then t1-t2 = nulltype + if is_subtype(type1, type2.as_nullable) then + return modelbuilder.model.null_type + end + + # else t2 can be null and type2 must accept null then null is excluded in t1 + if can_be_null(type1) and (type2 isa MNullableType or type2 isa MNullType) then + return type1.as_notnull + end + + return type1 + end end # Mapping between parameters and arguments in a call. @@ -637,8 +736,7 @@ end class CallSite super MEntity - # The associated location of the callsite - var location: Location + redef var location: Location # The static type of the receiver (possibly unresolved) var recv: MType @@ -678,6 +776,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 @@ -881,6 +984,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 @@ -1131,6 +1247,7 @@ redef class ADoExpr redef fun accept_typing(v) do v.visit_stmt(n_block) + v.visit_stmt(n_catch) self.is_typed = true end end @@ -1469,21 +1586,59 @@ redef class ACharExpr end end -redef class AStringFormExpr - redef fun accept_typing(v) - do +redef class AugmentedStringFormExpr + super AExpr + + # Text::to_re, used for prefix `re` + var to_re: nullable CallSite = null + # Regex::ignore_case, used for suffix `i` on `re` + var ignore_case: nullable CallSite = null + # Regex::newline, used for suffix `m` on `re` + var newline: nullable CallSite = null + # Regex::extended, used for suffix `b` on `re` + var extended: nullable CallSite = null + # 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 - self.mtype = mclass.mclass_type + if is_bytestring then + 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) + for i in suffix.chars do + mclass = v.get_mclass(self, "Regex") + if mclass == null then + v.error(self, "Error: `Regex` class unknown") + return + end + var service = "" + if i == 'i' then + service = "ignore_case=" + ignore_case = v.get_method(self, mclass.mclass_type, service, false) + else if i == 'm' then + service = "newline=" + newline = v.get_method(self, mclass.mclass_type, service, false) + else if i == 'b' then + service = "extended=" + extended = v.get_method(self, mclass.mclass_type, service, false) + else + v.error(self, "Type Error: Unrecognized suffix {i} in prefixed Regex") + abort + end + end + end + if mclass == null then return # Forward error + mtype = mclass.mclass_type end end redef class ASuperstringExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "String") - if mclass == null then return # Forward error - self.mtype = mclass.mclass_type + super var objclass = v.get_mclass(self, "Object") if objclass == null then return # Forward error var objtype = objclass.mclass_type @@ -1628,11 +1783,15 @@ redef class AIsaExpr var variable = self.n_expr.its_variable if variable != null then - #var orig = self.n_expr.mtype + var orig = self.n_expr.mtype #var from = if orig != null then orig.to_s else "invalid" #var to = if mtype != null then mtype.to_s else "invalid" #debug("adapt {variable}: {from} -> {to}") - self.after_flow_context.when_true.set_var(v, variable, mtype) + + # Do not adapt if there is no information gain (i.e. adapt to a supertype) + if mtype == null or orig == null or not v.is_subtype(orig, mtype) then + self.after_flow_context.when_true.set_var(v, variable, mtype) + end end self.mtype = v.type_bool(self) @@ -1642,6 +1801,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 @@ -1807,6 +1976,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 @@ -2057,6 +2235,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 #### @@ -2137,6 +2328,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 #### @@ -2177,6 +2377,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