X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index b173445..c1dcb1d 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -76,6 +76,12 @@ private class TypeVisitor end end + # Display a warning on `node` if `not mpropdef.is_fictive` + fun display_warning(node: ANode, tag: String, message: String) + do + if not mpropdef.is_fictive then self.modelbuilder.warning(node, tag, message) + end + fun anchor_to(mtype: MType): MType do return mtype.anchor_to(mmodule, anchor) @@ -181,7 +187,6 @@ private class TypeVisitor return self.visit_expr_subtype(nexpr, self.type_bool(nexpr)) end - fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType do var sub = nexpr.mtype @@ -191,9 +196,9 @@ private class TypeVisitor if sup == null then return null # Forward error if sup == sub then - self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}`.") + display_warning(node, "useless-type-test", "Warning: expression is already a `{sup}`.") else if self.is_subtype(sub, sup) then - self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}` since it is a `{sub}`.") + display_warning(node, "useless-type-test", "Warning: expression is already a `{sup}` since it is a `{sub}`.") end return sup end @@ -216,16 +221,16 @@ private class TypeVisitor 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`.") + display_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 var res = anchor_to(mtype) - modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.") + display_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}`.") + display_warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") end return false end @@ -316,13 +321,22 @@ private class TypeVisitor var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) if name == "new" and mproperty == null then - name = "init" + name = "defaultinit" mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if mproperty == null then + name = "init" + mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + end end if mproperty == null then if recv_is_self then - self.modelbuilder.error(node, "Error: method or variable `{name}` unknown in `{recvtype}`.") + # FIXME This test was added to display a more explicit error when a potential duplication of root object class. + if name == "init" then + self.modelbuilder.error(node, "Possible duplication of the root class `Object`") + else + self.modelbuilder.error(node, "Error: method or variable `{name}` unknown in `{recvtype}`.") + end else if recvtype.need_anchor then self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}: {unsafe_type}`.") else @@ -371,9 +385,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, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated: {mdoc.content.first}") + display_warning(node, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated: {mdoc.content.first}") else - self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated.") + display_warning(node, "deprecated-method", "Deprecation Warning: method `{mproperty.name}` is deprecated.") end end @@ -386,7 +400,7 @@ private class TypeVisitor else if propdefs.length == 1 then mpropdef = propdefs.first else - self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{mproperty.name}` in `{unsafe_type}`: {propdefs.join(" ")}") + display_warning(node, "property-conflict", "Warning: conflicting property definitions for property `{mproperty.name}` in `{unsafe_type}`: {propdefs.join(" ")}") mpropdef = mproperty.intro end @@ -396,7 +410,7 @@ private class TypeVisitor # The `build_callsite_by_propdef` builds the callsite directly with the `mprodef` passed in argument. fun build_callsite_by_propdef(node: ANode, recvtype: MType, mpropdef: MMethodDef, recv_is_self: Bool): nullable CallSite do - var msignature = mpropdef.new_msignature or else mpropdef.msignature + var msignature = mpropdef.msignature if msignature == null then return null # skip error msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature) @@ -424,7 +438,6 @@ private class TypeVisitor return build_callsite_by_name(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 @@ -445,7 +458,6 @@ private class TypeVisitor # Other cases are managed later end - #debug("CALL {unsafe_type}.{msignature}") # Associate each parameter to a position in the arguments @@ -1177,7 +1189,6 @@ redef class AVarReassignExpr end end - redef class AContinueExpr redef fun accept_typing(v) do @@ -1503,7 +1514,6 @@ redef class AAndExpr end end - redef class ANotExpr redef fun accept_typing(v) do @@ -1735,7 +1745,7 @@ redef class AArrayExpr 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.") + v.display_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 @@ -1779,7 +1789,7 @@ redef class ARangeExpr # get the constructor var callsite if self isa ACrangeExpr then - callsite = v.build_callsite_by_name(self, mtype, "init", false) + callsite = v.build_callsite_by_name(self, mtype, "defaultinit", false) else if self isa AOrangeExpr then callsite = v.build_callsite_by_name(self, mtype, "without_last", false) else @@ -1929,6 +1939,11 @@ redef class ASendExpr # The property invoked by the send. var callsite: nullable CallSite + # Is self a safe call (with `x?.foo`)? + # If so and the receiver is null, then the arguments won't be evaluated + # and the call skipped (replaced with null). + var is_safe: Bool = false + redef fun bad_expr_message(child) do if child == self.n_expr then @@ -1941,6 +1956,13 @@ redef class ASendExpr do var nrecv = self.n_expr var recvtype = v.visit_expr(nrecv) + + if nrecv isa ASafeExpr then + # Has the receiver the form `x?.foo`? + # For parsing "reasons" the `?` is in the receiver node, not the call node. + is_safe = true + end + var name = self.property_name var node = self.property_node @@ -1977,7 +1999,9 @@ redef class ASendExpr var args = compute_raw_arguments - callsite.check_signature(v, node, args) + if not self isa ACallrefExpr then + callsite.check_signature(v, node, args) + end if callsite.mproperty.is_init then var vmpropdef = v.mpropdef @@ -1991,6 +2015,10 @@ redef class ASendExpr var ret = msignature.return_mtype if ret != null then + if is_safe then + # A safe receiver makes that the call is not executed and returns null + ret = ret.as_nullable + end self.mtype = ret else self.is_typed = true @@ -2042,7 +2070,7 @@ 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.") + v.display_warning(self, "useless-truism", "Warning: useless comparison to a Bool literal.") end if not mtype2 isa MNullType then return @@ -2056,7 +2084,6 @@ redef class AUnaryopExpr redef fun compute_raw_arguments do return new Array[AExpr] end - redef class ACallExpr redef fun property_name do return n_qid.n_id.text redef fun property_node do return n_qid @@ -2144,11 +2171,82 @@ redef class ABraReassignExpr end redef class AInitExpr - redef fun property_name do return "init" + redef fun property_name do if n_args.n_exprs.is_empty then return "init" else return "defaultinit" redef fun property_node do return n_kwinit redef fun compute_raw_arguments do return n_args.to_a end +redef class ACallrefExpr + 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 + + redef fun accept_typing(v) + do + super # do the job as if it was a real call + var res = callsite.mproperty + + var msignature = callsite.mpropdef.msignature + var recv = callsite.recv + assert msignature != null + var arity = msignature.mparameters.length + + var routine_type_name = "ProcRef" + if msignature.return_mtype != null then + routine_type_name = "FunRef" + end + + var target_routine_class = "{routine_type_name}{arity}" + var routine_mclass = v.get_mclass(self, target_routine_class) + + if routine_mclass == null then + v.error(self, "Error: missing functional types, try `import functional`") + return + end + + var types_list = new Array[MType] + for param in msignature.mparameters do + if param.is_vararg then + types_list.push(v.mmodule.array_type(param.mtype)) + else + types_list.push(param.mtype) + end + end + if msignature.return_mtype != null then + types_list.push(msignature.return_mtype.as(not null)) + end + + # Why we need an anchor : + # + # ~~~~nitish + # class A[E] + # def toto(x: E) do print "{x}" + # end + # + # var a = new A[Int] + # var f = &a.toto # without anchor : ProcRef1[E] + # # with anchor : ProcRef[Int] + # ~~~~ + # However, we can only anchor if we can resolve every formal + # parameter, here's an example where we can't. + # ~~~~nitish + # class A[E] + # fun bar: A[E] do return self + # fun foo: Fun0[A[E]] do return &bar # here we can't anchor + # end + # var f1 = a1.foo # when this expression will be evaluated, + # # `a1` will anchor `&bar` returned by `foo`. + # print f1.call + # ~~~~ + var routine_type = routine_mclass.get_mtype(types_list) + if not recv.need_anchor then + routine_type = routine_type.anchor_to(v.mmodule, recv.as(MClassType)) + end + is_typed = true + self.mtype = routine_type + end +end + redef class AExprs fun to_a: Array[AExpr] do return self.n_exprs.to_a end @@ -2242,7 +2340,7 @@ redef class ASuperExpr return end - var msignature = superprop.new_msignature or else superprop.msignature.as(not null) + var msignature = superprop.msignature.as(not null) msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) var callsite = new CallSite(hot_location, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) @@ -2433,7 +2531,6 @@ redef class AAttrExpr end end - redef class AAttrAssignExpr redef fun accept_typing(v) do @@ -2474,6 +2571,28 @@ redef class AIssetAttrExpr end end +redef class ASafeExpr + redef fun accept_typing(v) + do + var mtype = v.visit_expr(n_expr) + if mtype == null then return # Skip error + + if mtype isa MNullType then + # While `null?.foo` is semantically well defined and should not execute `foo` and just return `null`, + # currently `null.foo` is forbidden so it seems coherent to also forbid `null?.foo` + v.modelbuilder.error(self, "Error: safe operator `?` on `null`.") + return + end + + self.mtype = mtype.as_notnull + + if not v.can_be_null(mtype) then + v.display_warning(self, "useless-safe", "Warning: useless safe operator `?` on non-nullable value.") + return + end + end +end + redef class AVarargExpr redef fun accept_typing(v) do @@ -2496,7 +2615,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, "debug", "Found type {expr} (-> {unsafe}), expected {mtype} (-> {umtype})") + v.display_warning(self, "debug", "Found type {expr} (-> {unsafe}), expected {mtype} (-> {umtype})") end self.is_typed = true end