X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 50ba70e..3463b07 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -36,17 +36,17 @@ private class TypeVisitor # The module of the analysis # Used to correctly query the model - var mmodule: MModule + var mmodule: MModule is noinit # The static type of the receiver # Mainly used for type tests and type resolutions - var anchor: nullable MClassType = null + var anchor: MClassType is noinit # The analyzed mclassdef - var mclassdef: nullable MClassDef = null + var mclassdef: MClassDef is noinit # The analyzed property - var mpropdef: nullable MPropDef + var mpropdef: MPropDef var selfvariable = new Variable("self") @@ -59,33 +59,25 @@ private class TypeVisitor init do var mpropdef = self.mpropdef + var mclassdef = mpropdef.mclassdef + mmodule = mclassdef.mmodule + self.mclassdef = mclassdef + self.anchor = mclassdef.bound_mtype - if mpropdef != null then - self.mpropdef = mpropdef - var mclassdef = mpropdef.mclassdef - self.mclassdef = mclassdef - self.anchor = mclassdef.bound_mtype - - var mclass = mclassdef.mclass + var mclass = mclassdef.mclass - var selfvariable = new Variable("self") - self.selfvariable = selfvariable - selfvariable.declared_type = mclass.mclass_type + var selfvariable = new Variable("self") + self.selfvariable = selfvariable + selfvariable.declared_type = mclass.mclass_type - var mprop = mpropdef.mproperty - if mprop isa MMethod and mprop.is_new then - is_toplevel_context = true - end + var mprop = mpropdef.mproperty + if mprop isa MMethod and mprop.is_new then + is_toplevel_context = true end end fun anchor_to(mtype: MType): MType do - var anchor = anchor - if anchor == null then - assert not mtype.need_anchor - return mtype - end return mtype.anchor_to(mmodule, anchor) end @@ -283,7 +275,7 @@ private class TypeVisitor fun resolve_mtype(node: AType): nullable MType do - return self.modelbuilder.resolve_mtype(mmodule, mclassdef, node) + return self.modelbuilder.resolve_mtype(mclassdef, node) end fun try_get_mclass(node: ANode, name: String): nullable MClass @@ -580,7 +572,16 @@ private class TypeVisitor return mtypes.first else var res = merge_types(node,mtypes) - if res == null then res = variable.declared_type + if res == null then + res = variable.declared_type + # Try to fallback to a non-null version + if res != null and can_be_null(res) then do + for t in mtypes do + if t != null and can_be_null(t) then break label + end + res = res.as_notnull + end label + end return res end end @@ -600,6 +601,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 +645,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,7 +737,7 @@ end class CallSite super MEntity - redef var location: Location + redef var location # The static type of the receiver (possibly unresolved) var recv: MType @@ -682,11 +782,13 @@ class CallSite fun dump_info(v: ASTDump): String do return "{recv}.{mpropdef}{msignature}" end + + redef fun mdoc_or_fallback do return mproperty.intro.mdoc end redef class Variable # The declared type of the variable - var declared_type: nullable MType is writable + var declared_type: nullable MType = null is writable # Was the variable type-adapted? # This is used to speedup type retrieval while it remains `false` @@ -759,7 +861,7 @@ redef class AMethPropdef var mpropdef = self.mpropdef if mpropdef == null then return # skip error - var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) + var v = new TypeVisitor(modelbuilder, mpropdef) self.selfvariable = v.selfvariable var mmethoddef = self.mpropdef.as(not null) @@ -826,7 +928,7 @@ redef class AAttrPropdef var mpropdef = self.mreadpropdef if mpropdef == null or mpropdef.msignature == null then return # skip error - var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) + var v = new TypeVisitor(modelbuilder, mpropdef) self.selfvariable = v.selfvariable var nexpr = self.n_expr @@ -1475,9 +1577,7 @@ end redef class ACharExpr redef fun accept_typing(v) do var mclass: nullable MClass = null - if is_ascii then - mclass = v.get_mclass(self, "Byte") - else if is_code_point then + if is_code_point then mclass = v.get_mclass(self, "Int") else mclass = v.get_mclass(self, "Char") @@ -1689,9 +1789,16 @@ redef class AIsaExpr #var to = if mtype != null then mtype.to_s else "invalid" #debug("adapt {variable}: {from} -> {to}") - # 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) + var thentype = v.intersect_types(self, orig, mtype) + if thentype != orig then + self.after_flow_context.when_true.set_var(v, variable, thentype) + #debug "{variable}:{orig or else "?"} isa {mtype or else "?"} -> then {thentype or else "?"}" + end + + var elsetype = v.diff_types(self, orig, mtype) + if elsetype != orig then + self.after_flow_context.when_false.set_var(v, variable, elsetype) + #debug "{variable}:{orig or else "?"} isa {mtype or else "?"} -> else {elsetype or else "?"}" end end @@ -1908,6 +2015,10 @@ redef class AEqFormExpr if mtype == null or mtype2 == null then return + if mtype == v.type_bool(self) and (n_expr2 isa AFalseExpr or n_expr2 isa ATrueExpr) then + v.modelbuilder.warning(self, "useless-truism", "Warning: useless comparison to a Bool literal.") + end + if not mtype2 isa MNullType then return v.check_can_be_null(n_expr, mtype) @@ -2030,7 +2141,6 @@ redef class ASuperExpr redef fun accept_typing(v) do var anchor = v.anchor - assert anchor != null var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mproperty = v.mpropdef.mproperty @@ -2069,7 +2179,6 @@ redef class ASuperExpr private fun process_superinit(v: TypeVisitor) do var anchor = v.anchor - assert anchor != null var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mpropdef = v.mpropdef