X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 9676168..d0c34ac 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -20,6 +20,7 @@ module typing import modelize import local_var_init +import literal redef class ToolContext var typing_phase: Phase = new TypingPhase(self, [flow_phase, modelize_property_phase, local_var_init_phase]) @@ -184,12 +185,12 @@ private class TypeVisitor end - fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType + fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType do - var sub = visit_expr(nexpr) + var sub = nexpr.mtype if sub == null then return null # Forward error - var sup = self.resolve_mtype(ntype) + var sup = ntype.mtype if sup == null then return null # Forward error if sup == sub then @@ -217,6 +218,10 @@ private class TypeVisitor # Else return true. fun check_can_be_null(anode: ANode, mtype: MType): Bool do + if mtype isa MNullType then + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is always `null`.") + return true + end if can_be_null(mtype) then return true if mtype isa MFormalType then @@ -240,7 +245,7 @@ private class TypeVisitor if not mtype2 isa MNullType then return # Check of useless null - if not check_can_be_null(anode.n_expr, mtype) then return + if not can_be_null(mtype) then return if mtype isa MNullType then # Because of type adaptation, we cannot just stop here @@ -384,7 +389,7 @@ private class TypeVisitor end end - var callsite = new CallSite(node, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) + var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) return callsite end @@ -409,26 +414,30 @@ private class TypeVisitor return null end else if args.length != msignature.arity then - if msignature.arity == msignature.min_arity then - modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") - return null - end + # Too much argument if args.length > msignature.arity then - modelbuilder.error(node, "Error: expected at most {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") - return null - end - if args.length < msignature.min_arity then - modelbuilder.error(node, "Error: expected at least {msignature.min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") return null end + # Other cases are managed later end + #debug("CALL {unsafe_type}.{msignature}") # Associate each parameter to a position in the arguments var map = new SignatureMap - var setted = args.length - msignature.min_arity + # Special case for the isolated last argument + # TODO: reify this method characteristics (where? the param, the signature, the method?) + var last_is_padded = mproperty.name.chars.last == '=' + var nbargs = args.length + if last_is_padded then + nbargs -= 1 + assert not args.last isa ANamedargExpr + map.map[msignature.arity - 1] = args.length - 1 + self.visit_expr_subtype(args.last, msignature.mparameters.last.mtype) + end # First, handle named arguments for i in [0..args.length[ do @@ -440,10 +449,6 @@ private class TypeVisitor modelbuilder.error(e.n_id, "Error: no parameter `{name}` for `{mproperty}{msignature}`.") return null end - if not param.is_default then - modelbuilder.error(e, "Error: parameter `{name}` is not optional for `{mproperty}{msignature}`.") - return null - end var idx = msignature.mparameters.index_of(param) var prev = map.map.get_or_null(idx) if prev != null then @@ -451,10 +456,12 @@ private class TypeVisitor return null end map.map[idx] = i - setted -= 1 e.mtype = self.visit_expr_subtype(e.n_expr, param.mtype) end + # Number of minimum mandatory remaining parameters + var min_arity = 0 + # Second, associate remaining parameters var vararg_decl = args.length - msignature.arity var j = 0 @@ -463,16 +470,16 @@ private class TypeVisitor if map.map.has_key(i) then continue var param = msignature.mparameters[i] - if param.is_default then - if setted > 0 then - setted -= 1 - else - continue - end - end # Search the next free argument: skip named arguments since they are already associated - while args[j] isa ANamedargExpr do j += 1 + while j < nbargs and args[j] isa ANamedargExpr do j += 1 + if j >= nbargs then + if not param.mtype isa MNullableType then + min_arity = j + 1 + end + j += 1 + continue + end var arg = args[j] map.map[i] = j j += 1 @@ -486,6 +493,16 @@ private class TypeVisitor self.visit_expr_subtype(arg, paramtype) end + if min_arity > 0 then + if last_is_padded then min_arity += 1 + if min_arity < msignature.arity then + modelbuilder.error(node, "Error: expected at least {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + else + modelbuilder.error(node, "Error: expected {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.") + end + return null + end + # Third, check varargs if vararg_rank >= 0 then var paramtype = msignature.mparameters[vararg_rank].mtype @@ -604,8 +621,8 @@ end # A specific method call site with its associated informations. class CallSite - # The associated node for location - var node: ANode + # The associated location of the callsite + var location: Location # The static type of the receiver (possibly unresolved) var recv: MType @@ -638,9 +655,9 @@ class CallSite # If null then no specific association is required. var signaturemap: nullable SignatureMap = null - private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool + private fun check_signature(v: TypeVisitor, node: ANode, args: Array[AExpr]): Bool do - var map = v.check_signature(self.node, args, self.mproperty, self.msignature) + var map = v.check_signature(node, args, self.mproperty, self.msignature) signaturemap = map return map == null end @@ -648,7 +665,7 @@ end redef class Variable # The declared type of the variable - var declared_type: nullable MType + var declared_type: nullable MType is writable # Was the variable type-adapted? # This is used to speedup type retrieval while it remains `false` @@ -718,9 +735,6 @@ end redef class AMethPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nblock = self.n_block - if nblock == null then return - var mpropdef = self.mpropdef if mpropdef == null then return # skip error @@ -742,12 +756,18 @@ redef class AMethPropdef variable.declared_type = mtype end + var nblock = self.n_block + if nblock == null then return + loop v.dirty = false v.visit_stmt(nblock) if not v.has_loop or not v.dirty then break end + var post_visitor = new PostTypingVisitor(v) + post_visitor.enter_visit(self) + 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, "Error: reached end of function; expected `return` with a value.") @@ -755,6 +775,19 @@ redef class AMethPropdef end end +private class PostTypingVisitor + super Visitor + var type_visitor: TypeVisitor + redef fun visit(n) do + n.visit_all(self) + n.accept_post_typing(type_visitor) + end +end + +redef class ANode + private fun accept_post_typing(v: TypeVisitor) do end +end + redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do @@ -960,7 +993,7 @@ redef class AVarReassignExpr v.set_variable(self, variable, rettype) - self.is_typed = true + self.is_typed = rettype != null end end @@ -1006,9 +1039,11 @@ redef class AReturnExpr else v.visit_expr(nexpr) v.error(nexpr, "Error: `return` with value in a procedure.") + return end else if ret_type != null then v.error(self, "Error: `return` without value in a function.") + return end self.is_typed = true end @@ -1302,8 +1337,9 @@ redef class AOrElseExpr end 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 + self.mtype = t2 + return + else if v.can_be_null(t1) then t1 = t1.as_notnull end @@ -1319,35 +1355,51 @@ redef class AOrElseExpr end self.mtype = t end -end -redef class ATrueExpr - redef fun accept_typing(v) + redef fun accept_post_typing(v) do - self.mtype = v.type_bool(self) + var t1 = n_expr.mtype + if t1 == null then + return + else + v.check_can_be_null(n_expr, t1) + end end end -redef class AFalseExpr +redef class ATrueExpr redef fun accept_typing(v) do self.mtype = v.type_bool(self) end end -redef class AIntExpr +redef class AFalseExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "Int") - if mclass == null then return # Forward error - self.mtype = mclass.mclass_type + self.mtype = v.type_bool(self) end end -redef class AByteExpr +redef class AIntegerExpr redef fun accept_typing(v) do - var mclass = v.get_mclass(self, "Byte") + var mclass: nullable MClass = null + if value isa Byte then + mclass = v.get_mclass(self, "Byte") + else if value isa Int then + mclass = v.get_mclass(self, "Int") + else if value isa Int8 then + mclass = v.get_mclass(self, "Int8") + else if value isa Int16 then + mclass = v.get_mclass(self, "Int16") + else if value isa UInt16 then + mclass = v.get_mclass(self, "UInt16") + else if value isa Int32 then + mclass = v.get_mclass(self, "Int32") + else if value isa UInt32 then + mclass = v.get_mclass(self, "UInt32") + end if mclass == null then return # Forward error self.mtype = mclass.mclass_type end @@ -1522,7 +1574,10 @@ redef class AIsaExpr var cast_type: nullable MType redef fun accept_typing(v) do - var mtype = v.visit_expr_cast(self, self.n_expr, self.n_type) + v.visit_expr(n_expr) + + var mtype = v.resolve_mtype(n_type) + self.cast_type = mtype var variable = self.n_expr.its_variable @@ -1536,12 +1591,24 @@ redef class AIsaExpr self.mtype = v.type_bool(self) end + + redef fun accept_post_typing(v) + do + v.check_expr_cast(self, self.n_expr, self.n_type) + end end redef class AAsCastExpr redef fun accept_typing(v) do - self.mtype = v.visit_expr_cast(self, self.n_expr, self.n_type) + v.visit_expr(n_expr) + + self.mtype = v.resolve_mtype(n_type) + end + + redef fun accept_post_typing(v) + do + v.check_expr_cast(self, self.n_expr, self.n_type) end end @@ -1556,12 +1623,19 @@ redef class AAsNotnullExpr return end - if v.check_can_be_null(n_expr, mtype) then + if v.can_be_null(mtype) then mtype = mtype.as_notnull end self.mtype = mtype end + + redef fun accept_post_typing(v) + do + var mtype = n_expr.mtype + if mtype == null then return + v.check_can_be_null(n_expr, mtype) + end end redef class AParExpr @@ -1646,7 +1720,7 @@ redef class ASendExpr var args = compute_raw_arguments - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) if callsite.mproperty.is_init then var vmpropdef = v.mpropdef @@ -1686,18 +1760,24 @@ redef class ABinopExpr redef fun property_name do return operator redef fun property_node do return n_op end -redef class AEqExpr + +redef class AEqFormExpr redef fun accept_typing(v) do super v.null_test(self) end -end -redef class ANeExpr - redef fun accept_typing(v) + + redef fun accept_post_typing(v) do - super - v.null_test(self) + var mtype = n_expr.mtype + var mtype2 = n_expr2.mtype + + if mtype == null or mtype2 == null then return + + if not mtype2 isa MNullType then return + + v.check_can_be_null(n_expr, mtype) end end @@ -1708,14 +1788,14 @@ end redef class ACallExpr - redef fun property_name do return n_id.text - redef fun property_node do return n_id + redef fun property_name do return n_qid.n_id.text + redef fun property_node do return n_qid 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 property_name do return n_qid.n_id.text + "=" + redef fun property_node do return n_qid redef fun compute_raw_arguments do var res = n_args.to_a @@ -1759,7 +1839,7 @@ redef class ASendReassignFormExpr var args = compute_raw_arguments - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) var readtype = callsite.msignature.return_mtype if readtype == null then @@ -1776,15 +1856,15 @@ redef class ASendReassignFormExpr args = args.to_a # duplicate so raw_arguments keeps only the getter args args.add(self.n_value) - wcallsite.check_signature(v, args) + wcallsite.check_signature(v, node, args) self.is_typed = true end end redef class ACallReassignExpr - redef fun property_name do return n_id.text - redef fun property_node do return n_id + redef fun property_name do return n_qid.n_id.text + redef fun property_node do return n_qid.n_id redef fun compute_raw_arguments do return n_args.to_a end @@ -1897,12 +1977,12 @@ redef class ASuperExpr var msignature = superprop.new_msignature or else superprop.msignature.as(not null) msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) - var callsite = new CallSite(self, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) + var callsite = new CallSite(hot_location, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) self.callsite = callsite var args = self.n_args.to_a if args.length > 0 then - callsite.check_signature(v, args) + callsite.check_signature(v, self, args) else # Check there is at least enough parameters if mpropdef.msignature.arity < msignature.arity then @@ -1956,11 +2036,11 @@ redef class ANewExpr var kind = recvtype.mclass.kind var name: String - var nid = self.n_id + var nqid = self.n_qid var node: ANode - if nid != null then - name = nid.text - node = nid + if nqid != null then + name = nqid.n_id.text + node = nqid else name = "new" node = self.n_kwnew @@ -2001,7 +2081,7 @@ redef class ANewExpr end var args = n_args.to_a - callsite.check_signature(v, args) + callsite.check_signature(v, node, args) end end @@ -2061,7 +2141,7 @@ redef class AAttrAssignExpr var mtype = self.attr_type v.visit_expr_subtype(self.n_value, mtype) - self.is_typed = true + self.is_typed = mtype != null end end @@ -2072,9 +2152,9 @@ redef class AAttrReassignExpr var mtype = self.attr_type if mtype == null then return # Skip error - self.resolve_reassignment(v, mtype, mtype) + var rettype = self.resolve_reassignment(v, mtype, mtype) - self.is_typed = true + self.is_typed = rettype != null end end