X-Git-Url: http://nitlanguage.org diff --git a/src/typing.nit b/src/typing.nit index 9feb83b..462fe04 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -18,9 +18,7 @@ # By OO-services we mean message sending, attribute access, instantiation, etc. module typing -import flow -import modelize_property -import phase +import modelize import local_var_init redef class ToolContext @@ -34,61 +32,74 @@ end private class TypeVisitor var modelbuilder: ModelBuilder - var nclassdef: AClassdef - var mpropdef: MPropDef + + # The module of the analysis + # Used to correctly query the model + var mmodule: MModule + + # The static type of the receiver + # Mainly used for type tests and type resolutions + var anchor: nullable MClassType + + # The analyzed mclassdef + var mclassdef: nullable MClassDef + + # The analyzed property + var mpropdef: nullable MPropDef var selfvariable: Variable = new Variable("self") - init(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MPropDef) + # Is `self` use restricted? + # * no explicit `self` + # * method called on the implicit self must be top-level + var is_toplevel_context = false + + init(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef) do self.modelbuilder = modelbuilder - self.nclassdef = nclassdef - self.mpropdef = mpropdef + self.mmodule = mmodule - var mclass = nclassdef.mclassdef.mclass + if mpropdef != null then + self.mpropdef = mpropdef + var mclassdef = mpropdef.mclassdef + self.mclassdef = mclassdef + self.anchor = mclassdef.bound_mtype - var selfvariable = new Variable("self") - self.selfvariable = selfvariable - selfvariable.declared_type = mclass.mclass_type - end + var mclass = mclassdef.mclass - fun mmodule: MModule do return self.nclassdef.mclassdef.mmodule + var selfvariable = new Variable("self") + self.selfvariable = selfvariable + selfvariable.declared_type = mclass.mclass_type - fun anchor: MClassType do return self.nclassdef.mclassdef.bound_mtype + var mprop = mpropdef.mproperty + if mprop isa MMethod and mprop.is_toplevel then + is_toplevel_context = true + end + end + end fun anchor_to(mtype: MType): MType do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype + var anchor = anchor + if anchor == null then + assert not mtype.need_anchor + return mtype + end return mtype.anchor_to(mmodule, anchor) end fun is_subtype(sub, sup: MType): Bool do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype return sub.is_subtype(mmodule, anchor, sup) end fun resolve_for(mtype, subtype: MType, for_self: Bool): MType do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype #print "resolve_for {mtype} sub={subtype} forself={for_self} mmodule={mmodule} anchor={anchor}" var res = mtype.resolve_for(subtype, anchor, mmodule, not for_self) return res end - # Retrieve the signature of a `MMethodDef` resolved for a specific call. - # This method is an helper to symplify the query on the model. - # - # Note: `for_self` indicates if the reciever is self or not. - # If yes, virtual types are not resolved. - fun resolve_signature_for(mmethoddef: MMethodDef, recv: MType, for_self: Bool): MSignature - do - return self.resolve_for(mmethoddef.msignature.as(not null), recv, for_self).as(MSignature) - 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 a safe subtype of `sup` then return `sub`. @@ -176,11 +187,9 @@ private class TypeVisitor var sup = self.resolve_mtype(ntype) if sup == null then return null # Forward error - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype if sup == sub then self.modelbuilder.warning(node, "Warning: Expression is already a {sup}.") - else if self.is_subtype(sub, sup) and not sup.need_anchor then + else if self.is_subtype(sub, sup) then self.modelbuilder.warning(node, "Warning: Expression is already a {sup} since it is a {sub}.") end return sup @@ -188,24 +197,22 @@ private class TypeVisitor fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty do - return self.modelbuilder.try_get_mproperty_by_name2(anode, self.nclassdef.mclassdef.mmodule, mtype, name) + return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name) end fun resolve_mtype(node: AType): nullable MType do - return self.modelbuilder.resolve_mtype(self.nclassdef, node) + return self.modelbuilder.resolve_mtype(mmodule, mclassdef, node) end fun try_get_mclass(node: ANode, name: String): nullable MClass do - var mmodule = self.nclassdef.mclassdef.mmodule var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name) return mclass end fun get_mclass(node: ANode, name: String): nullable MClass do - var mmodule = self.nclassdef.mclassdef.mmodule 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}'.") @@ -242,8 +249,16 @@ private class TypeVisitor end assert mproperty isa MMethod - if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility then - self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self. {mproperty.intro_mclassdef.mmodule.visibility_for(self.mmodule)}") + + if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then + error(node, "Error: '{name}' is not a top-level method, thus need a receiver.") + end + if not recv_is_self and mproperty.is_toplevel then + error(node, "Error: cannot call '{name}', a top-level method, with a receiver.") + end + + if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then + self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.") return null end @@ -260,12 +275,13 @@ private class TypeVisitor end - var msignature = self.resolve_signature_for(mpropdef, recvtype, recv_is_self) + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + 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 - if rettype isa MNullableType then rettype = rettype.mtype + rettype = rettype.as_notnullable if rettype isa MParameterType then var erased_rettype = msignature.return_mtype assert erased_rettype != null @@ -274,7 +290,7 @@ private class TypeVisitor end end - var callsite = new CallSite(node, recvtype, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) + var callsite = new CallSite(node, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) return callsite end @@ -386,9 +402,15 @@ class CallSite # The assiciated node for location var node: ANode - # The statis type of the receiver + # The static type of the receiver (possibly unresolved) var recv: MType + # The module where the callsite is present + var mmodule: MModule + + # The anchor to use with `recv` or `msignature` + var anchor: nullable MClassType + # Is the receiver self? # If "for_self", virtual types of the signature are keeped # If "not_for_self", virtual type are erased @@ -473,17 +495,16 @@ redef class APropdef var selfvariable: nullable Variable end -redef class AConcreteMethPropdef +redef class AMethPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nclassdef = self.parent.as(AClassdef) - var mpropdef = self.mpropdef.as(not null) - var v = new TypeVisitor(modelbuilder, nclassdef, mpropdef) - self.selfvariable = v.selfvariable - var nblock = self.n_block if nblock == null then return + var mpropdef = self.mpropdef.as(not null) + 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 @@ -508,8 +529,8 @@ end redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nclassdef = self.parent.as(AClassdef) - var v = new TypeVisitor(modelbuilder, nclassdef, self.mpropdef.as(not null)) + var mpropdef = self.mpropdef.as(not null) + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var nexpr = self.n_expr @@ -524,7 +545,7 @@ end redef class AExpr # The static type of the expression. - # null if self is a statement of in case of error + # null if self is a statement or in case of error var mtype: nullable MType = null # Is the statement correctly typed? @@ -810,11 +831,11 @@ end redef class AForExpr var coltype: nullable MClassType - var method_iterator: nullable MMethod - var method_is_ok: nullable MMethod - var method_item: nullable MMethod - var method_next: nullable MMethod - var method_key: nullable MMethod + var method_iterator: nullable CallSite + var method_is_ok: nullable CallSite + var method_item: nullable CallSite + var method_next: nullable CallSite + var method_key: nullable CallSite private fun do_type_iterator(v: TypeVisitor, mtype: MType) do @@ -828,22 +849,12 @@ redef class AForExpr if objcla == null then return # check iterator method - var unsafe_type = v.anchor_to(mtype) - if v.try_get_mproperty_by_name2(self, unsafe_type, "iterator") == null then - if v.try_get_mproperty_by_name2(self, unsafe_type, "iterate") == null then - v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.") - else - v.modelbuilder.error(self, "NOT YET IMPLEMENTED: Do 'for' on {mtype}") - end - return - end - - var itdef = v.get_method(self, mtype, "iterator", true) + var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr) if itdef == null then v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.") return end - self.method_iterator = itdef.mproperty + self.method_iterator = itdef # check that iterator return something var ittype = itdef.msignature.return_mtype @@ -891,7 +902,7 @@ redef class AForExpr # anchor formal and virtual types if mtype.need_anchor then mtype = v.anchor_to(mtype) - if mtype isa MNullableType then mtype = mtype.mtype + mtype = mtype.as_notnullable self.coltype = mtype.as(MClassType) # get methods is_ok, next, item @@ -900,21 +911,21 @@ redef class AForExpr v.error(self, "Type Error: 'for' expects a method 'is_ok' in 'Iterator' type {ittype}.") return end - self.method_is_ok = ikdef.mproperty + self.method_is_ok = ikdef var itemdef = v.get_method(self, ittype, "item", false) if itemdef == null then v.error(self, "Type Error: 'for' expects a method 'item' in 'Iterator' type {ittype}.") return end - self.method_item = itemdef.mproperty + self.method_item = itemdef var nextdef = v.get_method(self, ittype, "next", false) if nextdef == null then v.error(self, "Type Error: 'for' expects a method 'next' in 'Iterator' type {ittype}.") return end - self.method_next = nextdef.mproperty + self.method_next = nextdef if is_map then var keydef = v.get_method(self, ittype, "key", false) @@ -922,7 +933,7 @@ redef class AForExpr v.error(self, "Type Error: 'for' expects a method 'key' in 'Iterator' type {ittype}.") return end - self.method_key = keydef.mproperty + self.method_key = keydef end end @@ -994,9 +1005,7 @@ redef class AOrElseExpr return # Skip error end - if t1 isa MNullableType then - t1 = t1.mtype - end + t1 = t1.as_notnullable var t = v.merge_types(self, [t1, t2]) if t == null then @@ -1067,12 +1076,15 @@ redef class ASuperstringExpr if mclass == null then return # Forward error self.mtype = mclass.mclass_type for nexpr in self.n_exprs do - var t = v.visit_expr(nexpr) + v.visit_expr_subtype(nexpr, v.mmodule.object_type) end end end redef class AArrayExpr + var with_capacity_callsite: nullable CallSite + var push_callsite: nullable CallSite + redef fun accept_typing(v) do var mtypes = new Array[nullable MType] @@ -1090,11 +1102,18 @@ redef class AArrayExpr end var mclass = v.get_mclass(self, "Array") if mclass == null then return # Forward error - self.mtype = mclass.get_mtype([mtype]) + var array_mtype = mclass.get_mtype([mtype]) + + with_capacity_callsite = v.get_method(self, array_mtype, "with_capacity", false) + push_callsite = v.get_method(self, array_mtype, "push", false) + + self.mtype = array_mtype end end redef class ARangeExpr + var init_callsite: nullable CallSite + redef fun accept_typing(v) do var discrete_class = v.get_mclass(self, "Discrete") @@ -1105,13 +1124,28 @@ redef class ARangeExpr if t1 == null or t2 == null then return var mclass = v.get_mclass(self, "Range") if mclass == null then return # Forward error + var mtype if v.is_subtype(t1, t2) then - self.mtype = mclass.get_mtype([t2]) + mtype = mclass.get_mtype([t2]) else if v.is_subtype(t2, t1) then - self.mtype = mclass.get_mtype([t1]) + mtype = mclass.get_mtype([t1]) else v.error(self, "Type Error: Cannot create range: {t1} vs {t2}") + return end + + self.mtype = mtype + + # get the constructor + var callsite + if self isa ACrangeExpr then + callsite = v.get_method(self, mtype, "init", false) + else if self isa AOrangeExpr then + callsite = v.get_method(self, mtype, "without_last", false) + else + abort + end + init_callsite = callsite end end @@ -1155,6 +1189,8 @@ redef class AAsNotnullExpr redef fun accept_typing(v) do var mtype = v.visit_expr(self.n_expr) + if mtype == null then return # Forward error + if mtype isa MNullType then v.error(self, "Type error: as(not null) on null") return @@ -1163,8 +1199,18 @@ redef class AAsNotnullExpr self.mtype = mtype.mtype return end - # TODO: warn on useless as not null self.mtype = mtype + + if mtype isa MClassType then + v.modelbuilder.warning(self, "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}`.") + return + end end end @@ -1179,6 +1225,9 @@ redef class ASelfExpr redef var its_variable: nullable Variable redef fun accept_typing(v) do + if v.is_toplevel_context and not self isa AImplicitSelfExpr then + v.error(self, "Error: self cannot be used in top-level method.") + end var variable = v.selfvariable self.its_variable = variable self.mtype = v.get_variable(self, variable) @@ -1208,7 +1257,6 @@ redef class ASendExpr var msignature = callsite.msignature var args = compute_raw_arguments - self.raw_arguments = args callsite.check_signature(v, args) @@ -1217,6 +1265,9 @@ redef class ASendExpr if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then v.error(self, "Can call a init only in another init") end + if vmpropdef isa MMethodDef and vmpropdef.mproperty.is_root_init and not callsite.mproperty.is_root_init then + v.error(self, "Error: {vmpropdef} cannot call a factory {callsite.mproperty}") + end end var ret = msignature.return_mtype @@ -1232,7 +1283,7 @@ redef class ASendExpr private fun property_name: String is abstract # An array of all arguments (excluding self) - var raw_arguments: nullable Array[AExpr] + fun raw_arguments: Array[AExpr] do return compute_raw_arguments private fun compute_raw_arguments: Array[AExpr] is abstract end @@ -1369,7 +1420,6 @@ redef class ASendReassignFormExpr self.callsite = callsite var args = compute_raw_arguments - self.raw_arguments = args callsite.check_signature(v, args) @@ -1426,7 +1476,8 @@ redef class ASuperExpr redef fun accept_typing(v) do - var recvtype = v.nclassdef.mclassdef.bound_mtype + var recvtype = v.anchor + assert recvtype != null var mproperty = v.mpropdef.mproperty if not mproperty isa MMethod then v.error(self, "Error: super only usable in a method") @@ -1444,7 +1495,8 @@ redef class ASuperExpr # FIXME: covariance of return type in linear extension? var superprop = superprops.first - var msignature = v.resolve_signature_for(superprop, recvtype, true) + var msignature = superprop.msignature.as(not null) + msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) var args = self.n_args.to_a if args.length > 0 then v.check_signature(self, args, mproperty.name, msignature) @@ -1457,12 +1509,13 @@ redef class ASuperExpr private fun process_superinit(v: TypeVisitor) do - var recvtype = v.nclassdef.mclassdef.bound_mtype + var recvtype = v.anchor + assert recvtype != null var mpropdef = v.mpropdef assert mpropdef isa MMethodDef var mproperty = mpropdef.mproperty var superprop: nullable MMethodDef = null - for msupertype in v.nclassdef.mclassdef.supertypes do + for msupertype in mpropdef.mclassdef.supertypes do msupertype = msupertype.anchor_to(v.mmodule, recvtype) var errcount = v.modelbuilder.toolcontext.error_count var candidate = v.try_get_mproperty_by_name2(self, msupertype, mproperty.name).as(nullable MMethod) @@ -1470,12 +1523,15 @@ redef class ASuperExpr if v.modelbuilder.toolcontext.error_count > errcount then return # Forard error continue # Try next super-class end - if superprop != null and superprop.mproperty != candidate then + if superprop != null and candidate.is_root_init then + continue + end + if superprop != null and superprop.mproperty != candidate and not superprop.mproperty.is_root_init then 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) - if superprop != null then + if superprop != null and superprop.mproperty == candidate then if superprop == candidatedefs.first then continue candidatedefs.add(superprop) end @@ -1490,8 +1546,10 @@ redef class ASuperExpr return end - var msignature = v.resolve_signature_for(superprop, recvtype, true) - var callsite = new CallSite(self, recvtype, true, superprop.mproperty, superprop, msignature, false) + 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) self.callsite = callsite var args = self.n_args.to_a