X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 1e15a05..3f973fd 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,22 +59,20 @@ 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 - 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 @@ -277,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 @@ -863,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) @@ -930,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 @@ -1579,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") @@ -1907,6 +1903,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 @@ -1919,6 +1920,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 @@ -1969,6 +1977,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 @@ -2019,6 +2031,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) @@ -2141,7 +2157,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 @@ -2180,7 +2195,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 @@ -2450,6 +2464,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.modelbuilder.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