X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 1383302..83bebc2 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -47,7 +47,7 @@ private class TypeVisitor # The analyzed property var mpropdef: nullable MPropDef - var selfvariable: Variable = new Variable("self") + var selfvariable = new Variable("self") # Is `self` use restricted? # * no explicit `self` @@ -101,9 +101,9 @@ private class TypeVisitor end # Check that `sub` is a subtype of `sup`. - # If `sub` is not a valud suptype, then display an error on `node` an return null. + # 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 insafe subtype (ie an imlicit cast is required), then return `sup`. + # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`. # # The point of the return type is to determinate the usable type on an expression: # If the suptype is safe, then the return type is the one on the expression typed by `sub`. @@ -112,7 +112,7 @@ private class TypeVisitor do if self.is_subtype(sub, sup) then return sub if self.is_subtype(sub, self.anchor_to(sup)) then - # FIXME workarround to the current unsafe typing policy. To remove once fixed virtual types exists. + # FIXME workaround to the current unsafe typing policy. To remove once fixed virtual types exists. #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end @@ -188,13 +188,47 @@ private class TypeVisitor if sup == null then return null # Forward error if sup == sub then - self.modelbuilder.warning(node, "Warning: Expression is already a {sup}.") + self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup}.") else if self.is_subtype(sub, sup) then - self.modelbuilder.warning(node, "Warning: Expression is already a {sup} since it is a {sub}.") + self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup} since it is a {sub}.") end return sup end + # Special verification on != and == for null + # Return true + fun null_test(anode: ABinopExpr) + do + var mtype = anode.n_expr.mtype + var mtype2 = anode.n_expr2.mtype + + if mtype == null or mtype2 == null then return + + if not mtype2 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 + + # Check for type adaptation + var variable = anode.n_expr.its_variable + if variable == null then return + + 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) + 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) + else + abort + end + end + fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty do return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name) @@ -238,6 +272,11 @@ private class TypeVisitor end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if name == "new" and mproperty == null then + name = "init" + mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + end + if mproperty == null then #self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})") if recv_is_self then @@ -266,9 +305,9 @@ private class TypeVisitor if info != null and self.mpropdef.mproperty.deprecation == null then var mdoc = info.mdoc if mdoc != null then - self.modelbuilder.warning(node, "Deprecation Warning: Method '{name}' is deprecated: {mdoc.content.first}") + self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated: {mdoc.content.first}") else - self.modelbuilder.warning(node, "Deprecation Warning: Method '{name}' is deprecated.") + self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated.") end end @@ -280,7 +319,7 @@ private class TypeVisitor else if propdefs.length == 1 then mpropdef = propdefs.first else - self.modelbuilder.warning(node, "Warning: confliting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}") + self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}") mpropdef = mproperty.intro end @@ -304,7 +343,16 @@ private class TypeVisitor return callsite end - # Visit the expressions of args and cheik their conformity with the corresponding typi in signature + fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite + do + var unsafe_type = self.anchor_to(recvtype) + var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if mproperty == null then return null + return get_method(node, recvtype, name, recv_is_self) + end + + + # Visit the expressions of args and check their conformity with the corresponding type in signature # The point of this method is to handle varargs correctly # Note: The signature must be correctly adapted fun check_signature(node: ANode, args: Array[AExpr], name: String, msignature: MSignature): Bool @@ -334,11 +382,18 @@ private class TypeVisitor self.visit_expr_subtype(args[j], paramtype) end if vararg_rank >= 0 then - var varargs = new Array[AExpr] var paramtype = msignature.mparameters[vararg_rank].mtype - for j in [vararg_rank..vararg_rank+vararg_decl] do - varargs.add(args[j]) - self.visit_expr_subtype(args[j], paramtype) + var first = args[vararg_rank] + if vararg_decl == 0 and first isa AVarargExpr then + var mclass = get_mclass(node, "Array") + if mclass == null then return false # Forward error + var array_mtype = mclass.get_mtype([paramtype]) + self.visit_expr_subtype(first.n_expr, array_mtype) + first.mtype = first.n_expr.mtype + else + for j in [vararg_rank..vararg_rank+vararg_decl] do + self.visit_expr_subtype(args[j], paramtype) + end end end return true @@ -386,7 +441,6 @@ private class TypeVisitor fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType do if col.length == 1 then return col.first - var res = new Array[nullable MType] for t1 in col do if t1 == null then continue # return null var found = true @@ -409,7 +463,7 @@ end # A specific method call site with its associated informations. class CallSite - # The assiciated node for location + # The associated node for location var node: ANode # The static type of the receiver (possibly unresolved) @@ -422,7 +476,7 @@ class CallSite var anchor: nullable MClassType # Is the receiver self? - # If "for_self", virtual types of the signature are keeped + # If "for_self", virtual types of the signature are kept # If "not_for_self", virtual type are erased var recv_is_self: Bool @@ -452,12 +506,12 @@ end redef class FlowContext # Store changes of types because of type evolution - private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType] - private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]] + private var vars = new HashMap[Variable, nullable MType] + private var cache = new HashMap[Variable, nullable Array[nullable MType]] # Adapt the variable to a static type # Warning1: do not modify vars directly. - # Warning2: sub-flow may have cached a unadapted variabial + # Warning2: sub-flow may have cached a unadapted variable private fun set_var(variable: Variable, mtype: nullable MType) do self.vars[variable] = mtype @@ -501,7 +555,7 @@ redef class APropdef do end - # The variable associated to the reciever (if any) + # The variable associated to the receiver (if any) var selfvariable: nullable Variable end @@ -738,7 +792,7 @@ redef class AContinueExpr do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@ -749,7 +803,7 @@ redef class ABreakExpr do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@ -762,9 +816,9 @@ redef class AReturnExpr var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype if nexpr != null then if ret_type != null then - var mtype = v.visit_expr_subtype(nexpr, ret_type) + v.visit_expr_subtype(nexpr, ret_type) else - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) v.error(self, "Error: Return with value in a procedure.") end else if ret_type != null then @@ -846,6 +900,10 @@ redef class AForExpr var method_item: nullable CallSite var method_next: nullable CallSite var method_key: nullable CallSite + var method_finish: nullable CallSite + + var method_lt: nullable CallSite + var method_successor: nullable CallSite private fun do_type_iterator(v: TypeVisitor, mtype: MType) do @@ -937,6 +995,8 @@ redef class AForExpr end self.method_next = nextdef + self.method_finish = v.try_get_method(self, ittype, "finish", false) + if is_map then var keydef = v.get_method(self, ittype, "key", false) if keydef == null then @@ -945,6 +1005,19 @@ redef class AForExpr end self.method_key = keydef end + + if self.variables.length == 1 and n_expr isa ARangeExpr then + var variable = variables.first + var vtype = variable.declared_type.as(not null) + + if n_expr isa AOrangeExpr then + self.method_lt = v.get_method(self, vtype, "<", false) + else + self.method_lt = v.get_method(self, vtype, "<=", false) + end + + self.method_successor = v.get_method(self, vtype, "successor", false) + end end redef fun accept_typing(v) @@ -1097,19 +1170,37 @@ redef class AArrayExpr redef fun accept_typing(v) do + var mtype: nullable MType = null + var ntype = self.n_type + if ntype != null then + mtype = v.resolve_mtype(ntype) + if mtype == null then return # Skip error + end var mtypes = new Array[nullable MType] + var useless = false for e in self.n_exprs.n_exprs do var t = v.visit_expr(e) if t == null then return # Skip error end - mtypes.add(t) + if mtype != null then + if v.check_subtype(e, t, mtype) == null then return # Skip error + if t == mtype then useless = true + else + mtypes.add(t) + end + end + if mtype == null then + mtype = v.merge_types(self, mtypes) end - var mtype = v.merge_types(self, mtypes) if mtype == null then v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}") return end + if useless then + 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 var mclass = v.get_mclass(self, "Array") if mclass == null then return # Forward error var array_mtype = mclass.get_mtype([mtype]) @@ -1177,9 +1268,9 @@ redef class AIsaExpr var variable = self.n_expr.its_variable if variable != null then - 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" + #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(variable, mtype) end @@ -1212,19 +1303,26 @@ redef class AAsNotnullExpr self.mtype = mtype if mtype isa MClassType then - v.modelbuilder.warning(self, "Warning: expression is already not null, since it is a `{mtype}`.") + 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, "Warning: expression is already not null, since it is a `{mtype}: {u}`.") + v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.") return end end end -redef class AProxyExpr +redef class AParExpr + redef fun accept_typing(v) + do + self.mtype = v.visit_expr(self.n_expr) + end +end + +redef class AOnceExpr redef fun accept_typing(v) do self.mtype = v.visit_expr(self.n_expr) @@ -1306,16 +1404,7 @@ redef class AEqExpr redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_true.set_var(variable, mtype) - self.after_flow_context.when_false.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}") + v.null_test(self) end end redef class ANeExpr @@ -1323,16 +1412,7 @@ redef class ANeExpr redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_false.set_var(variable, mtype) - self.after_flow_context.when_true.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}") + v.null_test(self) end end redef class ALtExpr @@ -1489,14 +1569,16 @@ redef class ASuperExpr redef fun accept_typing(v) do - var recvtype = v.anchor + var anchor = v.anchor + assert anchor != null + var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mproperty = v.mpropdef.mproperty if not mproperty isa MMethod then v.error(self, "Error: super only usable in a method") return end - var superprops = mproperty.lookup_super_definitions(v.mmodule, recvtype) + var superprops = mproperty.lookup_super_definitions(v.mmodule, anchor) if superprops.length == 0 then if mproperty.is_init and v.mpropdef.is_intro then process_superinit(v) @@ -1522,18 +1604,20 @@ redef class ASuperExpr private fun process_superinit(v: TypeVisitor) do - var recvtype = v.anchor + var anchor = v.anchor + assert anchor != null + var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mpropdef = v.mpropdef assert mpropdef isa MMethodDef var mproperty = mpropdef.mproperty var superprop: nullable MMethodDef = null for msupertype in mpropdef.mclassdef.supertypes do - msupertype = msupertype.anchor_to(v.mmodule, recvtype) + msupertype = msupertype.anchor_to(v.mmodule, anchor) var errcount = v.modelbuilder.toolcontext.error_count var candidate = v.try_get_mproperty_by_name2(self, msupertype, mproperty.name).as(nullable MMethod) if candidate == null then - if v.modelbuilder.toolcontext.error_count > errcount then return # Forard error + if v.modelbuilder.toolcontext.error_count > errcount then return # Forward error continue # Try next super-class end if superprop != null and candidate.is_root_init then @@ -1543,13 +1627,13 @@ redef class ASuperExpr v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}") return end - var candidatedefs = candidate.lookup_definitions(v.mmodule, recvtype) + var candidatedefs = candidate.lookup_definitions(v.mmodule, anchor) if superprop != null and superprop.mproperty == candidate then if superprop == candidatedefs.first then continue candidatedefs.add(superprop) end if candidatedefs.length > 1 then - v.error(self, "Error: confliting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}") + v.error(self, "Error: conflicting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}") return end superprop = candidatedefs.first @@ -1596,11 +1680,13 @@ redef class ANewExpr # The constructor invoked by the new. var callsite: nullable CallSite + # The designated type + var recvtype: nullable MClassType + redef fun accept_typing(v) do var recvtype = v.resolve_mtype(self.n_type) if recvtype == null then return - self.mtype = recvtype if not recvtype isa MClassType then if recvtype isa MNullableType then @@ -1610,26 +1696,34 @@ redef class ANewExpr v.error(self, "Type error: cannot instantiate the formal type {recvtype}.") return end - else - if recvtype.mclass.kind == abstract_kind then - v.error(self, "Cannot instantiate abstract class {recvtype}.") - return - else if recvtype.mclass.kind == interface_kind then - v.error(self, "Cannot instantiate interface {recvtype}.") - return - end end + self.recvtype = recvtype + var name: String var nid = self.n_id if nid != null then name = nid.text else - name = "init" + name = "new" end var callsite = v.get_method(self, recvtype, name, false) if callsite == null then return + if not callsite.mproperty.is_new then + if recvtype.mclass.kind == abstract_kind then + v.error(self, "Cannot instantiate abstract class {recvtype}.") + return + else if recvtype.mclass.kind == interface_kind then + v.error(self, "Cannot instantiate interface {recvtype}.") + return + end + self.mtype = recvtype + else + self.mtype = callsite.msignature.return_mtype + assert self.mtype != null + end + self.callsite = callsite if not callsite.mproperty.is_init_for(recvtype.mclass) then @@ -1729,6 +1823,16 @@ redef class AIssetAttrExpr end end +redef class AVarargExpr + redef fun accept_typing(v) + do + # This kind of pseudo-expression can be only processed trough a signature + # See `check_signature` + # Other cases are a syntax error. + v.error(self, "Syntax error: unexpected `...`") + end +end + ### redef class ADebugTypeExpr @@ -1741,7 +1845,7 @@ redef class ADebugTypeExpr var mtype = v.resolve_mtype(ntype) if mtype != null and mtype != expr then var umtype = v.anchor_to(mtype) - v.modelbuilder.warning(self, "Found type {expr} (-> {unsafe}), expected {mtype} (-> {umtype})") + v.modelbuilder.warning(self, "debug", "Found type {expr} (-> {unsafe}), expected {mtype} (-> {umtype})") end self.is_typed = true end