X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 3140de5..38a689a 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -115,7 +115,12 @@ private class TypeVisitor #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end - self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + if sub.need_anchor then + var u = anchor_to(sub) + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}") + else + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + end return null end @@ -178,7 +183,7 @@ private class TypeVisitor end - private fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType + fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType do var sub = visit_expr(nexpr) if sub == null then return null # Forward error @@ -194,6 +199,34 @@ private class TypeVisitor return sup end + # Can `mtype` be null (up to the current knowledge)? + fun can_be_null(mtype: MType): Bool + do + if mtype isa MNullableType or mtype isa MNullType then return true + if mtype isa MFormalType then + var x = anchor_to(mtype) + if x isa MNullableType or x isa MNullType then return true + end + return false + end + + # Check that `mtype` can be null (up to the current knowledge). + # + # If not then display a `useless-null-test` warning on node and return false. + # Else return true. + fun check_can_be_null(anode: ANode, mtype: MType): Bool + do + if can_be_null(mtype) then return true + + if mtype isa MFormalType then + var res = anchor_to(mtype) + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.") + else + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") + end + return false + end + # Special verification on != and == for null # Return true fun null_test(anode: ABinopExpr) @@ -205,24 +238,24 @@ private class TypeVisitor if not mtype2 isa MNullType then return + if mtype isa MNullType then return + # Check of useless null - if not mtype isa MNullableType then - if not anchor_to(mtype) isa MNullableType then - modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") - end - return - end + if not check_can_be_null(anode.n_expr, mtype) then return + + mtype = mtype.as_notnull # Check for type adaptation var variable = anode.n_expr.its_variable if variable == null then return + # One is null (mtype2 see above) the other is not null if anode isa AEqExpr then anode.after_flow_context.when_true.set_var(variable, mtype2) - anode.after_flow_context.when_false.set_var(variable, mtype.mtype) + anode.after_flow_context.when_false.set_var(variable, mtype) else if anode isa ANeExpr then anode.after_flow_context.when_false.set_var(variable, mtype2) - anode.after_flow_context.when_true.set_var(variable, mtype.mtype) + anode.after_flow_context.when_true.set_var(variable, mtype) else abort end @@ -246,10 +279,7 @@ private class TypeVisitor fun get_mclass(node: ANode, name: String): nullable MClass do - var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name) - if mclass == null then - self.modelbuilder.error(node, "Type Error: missing primitive class `{name}'.") - end + var mclass = modelbuilder.get_mclass_by_name(node, mmodule, name) return mclass end @@ -266,8 +296,15 @@ private class TypeVisitor #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 + var objclass = get_mclass(node, "Object") + if objclass == null then return null # Forward error + unsafe_type = objclass.mclass_type + 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) @@ -323,13 +360,14 @@ private class TypeVisitor end - var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var msignature = mpropdef.new_msignature or else mpropdef.msignature + if msignature == null then return null # skip error msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature) var erasure_cast = false var rettype = mpropdef.msignature.return_mtype if not recv_is_self and rettype != null then - rettype = rettype.as_notnullable + rettype = rettype.undecorate if rettype isa MParameterType then var erased_rettype = msignature.return_mtype assert erased_rettype != null @@ -445,7 +483,7 @@ private class TypeVisitor var found = true for t2 in col do if t2 == null then continue # return null - if t2 isa MNullableType or t2 isa MNullType then + if can_be_null(t2) and not can_be_null(t1) then t1 = t1.as_nullable end if not is_subtype(t2, t1) then found = false @@ -564,14 +602,18 @@ redef class AMethPropdef var nblock = self.n_block if nblock == null then return - var mpropdef = self.mpropdef.as(not null) + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var mmethoddef = self.mpropdef.as(not null) - for i in [0..mmethoddef.msignature.arity[ do - var mtype = mmethoddef.msignature.mparameters[i].mtype - if mmethoddef.msignature.vararg_rank == i then + var msignature = mmethoddef.msignature + if msignature == null then return # skip error + for i in [0..msignature.arity[ do + var mtype = msignature.mparameters[i].mtype + if msignature.vararg_rank == i then var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array") if arrayclass == null then return # Skip error mtype = arrayclass.get_mtype([mtype]) @@ -582,7 +624,7 @@ redef class AMethPropdef end v.visit_stmt(nblock) - if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then + if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then # We reach the end of the function without having a return, it is bad v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).") end @@ -592,7 +634,11 @@ end redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var mpropdef = self.mpropdef.as(not null) + if not has_value then return + + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable @@ -604,6 +650,10 @@ redef class AAttrPropdef var nblock = self.n_block if nblock != null then v.visit_stmt(nblock) + if not nblock.after_flow_context.is_unreachable then + # We reach the end of the init without having a return, it is bad + v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).") + end end end end @@ -633,6 +683,12 @@ redef class AExpr do v.error(self, "no implemented accept_typing for {self.class_name}") end + + # Is non-null if `self` is a leaf of a comprehension array construction. + # In this case, the enclosing literal array node is designated. + # The result of the evaluation of `self` must be + # stored inside the designated array (there is an implicit `push`) + var comprehension: nullable AArrayExpr = null end redef class ABlockExpr @@ -668,7 +724,11 @@ redef class AVardeclExpr var nexpr = self.n_expr if nexpr != null then if mtype != null then - v.visit_expr_subtype(nexpr, mtype) + var etype = v.visit_expr_subtype(nexpr, mtype) + if etype == mtype then + assert ntype != null + v.modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition for variable `{variable.name}`") + end else mtype = v.visit_expr(nexpr) if mtype == null then return # Skip error @@ -677,7 +737,9 @@ redef class AVardeclExpr var decltype = mtype if mtype == null or mtype isa MNullType then - decltype = v.get_mclass(self, "Object").mclass_type.as_nullable + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # skip error + decltype = objclass.mclass_type.as_nullable if mtype == null then mtype = decltype end @@ -746,11 +808,6 @@ redef class AReassignFormExpr 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 @@ -853,7 +910,12 @@ redef class AIfExpr v.visit_stmt(n_then) v.visit_stmt(n_else) + self.is_typed = true + + if n_then != null and n_else == null then + self.mtype = n_then.mtype + end end end @@ -960,7 +1022,7 @@ redef class AForExpr is_col = true end - if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then + if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then # Map Iterator var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla) var variables = self.variables @@ -981,7 +1043,7 @@ redef class AForExpr # anchor formal and virtual types if mtype.need_anchor then mtype = v.anchor_to(mtype) - mtype = mtype.as_notnullable + mtype = mtype.undecorate self.coltype = mtype.as(MClassType) # get methods is_ok, next, item @@ -1039,6 +1101,7 @@ redef class AForExpr self.do_type_iterator(v, mtype) v.visit_stmt(n_block) + self.mtype = n_block.mtype self.is_typed = true end end @@ -1099,12 +1162,18 @@ redef class AOrElseExpr return # Skip error end - t1 = t1.as_notnullable + if t1 isa MNullType then + v.error(n_expr, "Type error: or else on null") + else if v.check_can_be_null(n_expr, t1) then + t1 = t1.as_notnull + end var t = v.merge_types(self, [t1, t2]) if t == null then - t = v.mmodule.object_type - if t2 isa MNullableType then + var c = v.get_mclass(self, "Object") + if c == null then return # forward error + t = c.mclass_type + if v.can_be_null(t2) then t = t.as_nullable end #v.error(self, "Type Error: ambiguous type {t1} vs {t2}") @@ -1169,16 +1238,41 @@ redef class ASuperstringExpr var mclass = v.get_mclass(self, "String") if mclass == null then return # Forward error self.mtype = mclass.mclass_type + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # Forward error + var objtype = objclass.mclass_type for nexpr in self.n_exprs do - v.visit_expr_subtype(nexpr, v.mmodule.object_type) + v.visit_expr_subtype(nexpr, objtype) end end end redef class AArrayExpr + # The `with_capacity` method on Array var with_capacity_callsite: nullable CallSite + + # The `push` method on arrays var push_callsite: nullable CallSite + # The element of each type + var element_mtype: nullable MType + + # Set that `self` is a part of comprehension array `na` + # If `self` is a `for`, or a `if`, then `set_comprehension` is recursively applied. + private fun set_comprehension(n: nullable AExpr) + do + if n == null then + return + else if n isa AForExpr then + set_comprehension(n.n_block) + else if n isa AIfExpr then + set_comprehension(n.n_then) + set_comprehension(n.n_else) + else + # is a leave + n.comprehension = self + end + end redef fun accept_typing(v) do var mtype: nullable MType = null @@ -1189,11 +1283,12 @@ redef class AArrayExpr end var mtypes = new Array[nullable MType] var useless = false - for e in self.n_exprs.n_exprs do + for e in self.n_exprs do var t = v.visit_expr(e) if t == null then return # Skip error end + set_comprehension(e) if mtype != null then if v.check_subtype(e, t, mtype) == null then return # Skip error if t == mtype then useless = true @@ -1204,7 +1299,7 @@ redef class AArrayExpr 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 @@ -1212,6 +1307,9 @@ redef class AArrayExpr assert ntype != null v.modelbuilder.warning(ntype, "useless-type", "Warning: useless type declaration `{mtype}` in literal Array since it can be inferred from the elements type.") end + + self.element_mtype = mtype + var mclass = v.get_mclass(self, "Array") if mclass == null then return # Forward error var array_mtype = mclass.get_mtype([mtype]) @@ -1307,22 +1405,12 @@ redef class AAsNotnullExpr v.error(self, "Type error: as(not null) on null") return end - if mtype isa MNullableType then - self.mtype = mtype.mtype - return - end - self.mtype = mtype - if mtype isa MClassType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}`.") - return - end - assert mtype.need_anchor - var u = v.anchor_to(mtype) - if not u isa MNullableType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.") - return + if v.check_can_be_null(n_expr, mtype) then + mtype = mtype.as_notnull end + + self.mtype = mtype end end @@ -1365,10 +1453,6 @@ redef class ASendExpr 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 @@ -1512,10 +1596,6 @@ redef class ASendReassignFormExpr 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) @@ -1777,7 +1857,8 @@ redef class AAttrFormExpr var mpropdefs = mproperty.lookup_definitions(v.mmodule, unsafe_type) assert mpropdefs.length == 1 var mpropdef = mpropdefs.first - var attr_type = mpropdef.static_mtype.as(not null) + var attr_type = mpropdef.static_mtype + if attr_type == null then return # skip error attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr) self.attr_type = attr_type end