diff --git a/src/astbuilder.nit b/src/astbuilder.nit index 296a950245..d0602eea2e 100644 --- a/src/astbuilder.nit +++ b/src/astbuilder.nit @@ -24,12 +24,11 @@ intrude import modelize_property # General factory to build semantic nodes in the AST of expressions class ASTBuilder - # The module used as reference for the building - # It is used to gather types and other stuff - var mmodule: MModule + super TypeContext - # The anchor used for some mechanism relying on types - var anchor: nullable MClassType + redef var mmodule: MModule + + redef var anchor: nullable MClassType # Check mmodule to avoid a new instantiation of ASTBuilder fun check_mmodule(mmodule: MModule) @@ -203,11 +202,11 @@ class ASTBuilder # Build a callsite to call the `mproperty` in the current method `caller_method`. # `is_self_call` indicate if the method caller is a property of `self` - fun create_callsite(modelbuilder: ModelBuilder, caller_property: APropdef, mproperty: MMethod, is_self_call: Bool): CallSite + fun create_callsite(mproperty: MMethod, is_self_call: Bool): CallSite do - # FIXME It's not the better solution to call `TypeVisitor` here to build a model entity, but some make need to have a callsite - var type_visitor = new TypeVisitor(modelbuilder, caller_property.mpropdef.as(not null)) - var callsite = type_visitor.build_callsite_by_property(caller_property, mproperty.intro_mclassdef.bound_mtype, mproperty, is_self_call) + var recv = mproperty.intro_mclassdef.bound_mtype + var builder = new CallSiteBuilder(mmodule.model.no_location, recv, self) + var callsite = builder.recv_is_self(is_self_call).by_property(mproperty) assert callsite != null return callsite end @@ -970,7 +969,7 @@ end redef class MClassDef redef fun create_ast_representation(astbuilder: nullable ASTBuilder): AStdClassdef do - if astbuilder == null then astbuilder = new ASTBuilder(mmodule) + if astbuilder == null then astbuilder = new ASTBuilder(mmodule, bound_mtype) var n_propdefs = new Array[APropdef] for mpropdef in self.mpropdefs do n_propdefs.add(mpropdef.create_ast_representation(astbuilder)) @@ -984,7 +983,7 @@ end redef class MAttributeDef redef fun create_ast_representation(astbuilder: nullable ASTBuilder): AAttrPropdef do - if astbuilder == null then astbuilder = new ASTBuilder(mclassdef.mmodule) + if astbuilder == null then astbuilder = new ASTBuilder(mclassdef.mmodule, mclassdef.bound_mtype) var ntype = null if self.static_mtype != null then ntype = static_mtype.create_ast_representation(astbuilder) return astbuilder.make_attribute("_" + self.name, ntype, self.visibility.create_ast_representation(astbuilder), null, null, self, null, null) @@ -993,7 +992,7 @@ end redef class MMethodDef redef fun create_ast_representation(astbuilder: nullable ASTBuilder): AMethPropdef do - if astbuilder == null then astbuilder = new ASTBuilder(mclassdef.mmodule) + if astbuilder == null then astbuilder = new ASTBuilder(mclassdef.mmodule, mclassdef.bound_mtype) var tk_redef = null if self.mproperty.intro != self then tk_redef = new TKwredef var n_signature = if self.msignature == null then new ASignature else self.msignature.create_ast_representation(astbuilder) diff --git a/src/contracts.nit b/src/contracts.nit index 92af83d29f..132be36277 100644 --- a/src/contracts.nit +++ b/src/contracts.nit @@ -244,12 +244,12 @@ private class ContractsVisitor private fun encapsulated_contract_call(visited_method: AMethPropdef, call_to_contracts: Array[ACallExpr]): AIfExpr do var sys_property = toolcontext.modelbuilder.model.get_mproperties_by_name("sys").first.as(MMethod) - var callsite_sys = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, sys_property, true) + var callsite_sys = ast_builder.create_callsite(sys_property, true) var incontract_attribute = get_incontract - var callsite_get_incontract = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, incontract_attribute.getter.as(MMethod), false) - var callsite_set_incontract = ast_builder.create_callsite(toolcontext.modelbuilder, visited_method, incontract_attribute.setter.as(MMethod), false) + var callsite_get_incontract = ast_builder.create_callsite(incontract_attribute.getter.as(MMethod), false) + var callsite_set_incontract = ast_builder.create_callsite(incontract_attribute.setter.as(MMethod), false) var n_condition = ast_builder.make_not(ast_builder.make_call(ast_builder.make_call(new ASelfExpr, callsite_sys, null), callsite_get_incontract, null)) @@ -302,7 +302,7 @@ private class CallSiteVisitor facet = contract_facet end - return ast_builder.create_callsite(toolcontext.modelbuilder, visited_propdef, facet, callsite.recv_is_self) + return ast_builder.create_callsite(facet, callsite.recv_is_self) end end @@ -486,7 +486,7 @@ redef class MExpect redef fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef) do - var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_mpropdef, self, true) + var callsite = v.ast_builder.create_callsite(self, true) var args = n_mpropdef.n_signature.make_parameter_read(v.ast_builder) var n_callexpect = v.ast_builder.make_call(new ASelfExpr, callsite,args) # Creation of the new instruction block with the call to expect condition @@ -578,7 +578,7 @@ redef class MEnsure redef fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef) do - var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_mpropdef, self, true) + var callsite = v.ast_builder.create_callsite(self, true) var n_self = new ASelfExpr # argument to call the contract method var args = n_mpropdef.n_signature.make_parameter_read(v.ast_builder) @@ -613,7 +613,7 @@ redef class MInvariant redef fun adapt_method_to_contract(v: ContractsVisitor, mfacet: MFacet, n_mpropdef: AMethPropdef) do - var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_mpropdef, self, true) + var callsite = v.ast_builder.create_callsite(self, true) var n_self = new ASelfExpr # build the call to the contract method var n_call = v.ast_builder.make_call(n_self, callsite, null) @@ -880,7 +880,7 @@ redef class MMethod var args: Array[AExpr] args = n_contractdef.n_signature.make_parameter_read(v.ast_builder) - var callsite = v.ast_builder.create_callsite(v.toolcontext.modelbuilder, n_contractdef, called, true) + var callsite = v.ast_builder.create_callsite(called, true) var n_call = v.ast_builder.make_call(new ASelfExpr, callsite, args) if self.intro.msignature.return_mtype == null then diff --git a/src/frontend/explain_assert.nit b/src/frontend/explain_assert.nit index f8b47102d3..40b0424598 100644 --- a/src/frontend/explain_assert.nit +++ b/src/frontend/explain_assert.nit @@ -75,9 +75,6 @@ private class ExplainAssertVisitor # Type of `String` (the generated code does not work without a `String`) var string_mtype: MType - # Tool to modify the AST - var builder = new ASTBuilder(mmodule) is lazy - redef fun visit(node) do # Recursively visit all sub-nodes diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 98aba7771c..1f9925333d 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -31,16 +31,45 @@ private class TypingPhase redef fun process_npropdef(npropdef) do npropdef.do_typing(toolcontext.modelbuilder) end +# Helper class to solve localized type operation +interface TypeContext + # The module of the analysis + # Used to correctly query the model + fun mmodule: MModule is abstract + + # A fully resolved static type of the current receiver + # Mainly used for type tests and type resolutions as it's the best approximation of self + fun anchor: nullable MClassType is abstract + + fun anchor_to(mtype: MType): MType + do + return mtype.anchor_to(mmodule, anchor) + end + + fun is_subtype(sub, sup: MType): Bool + do + return sub.is_subtype(mmodule, anchor, sup) + end + + fun resolve_for(mtype, subtype: MType, for_self: Bool): MType + do + #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 +end + private class TypeVisitor + super TypeContext var modelbuilder: ModelBuilder # The module of the analysis # Used to correctly query the model - var mmodule: MModule is noinit + redef var mmodule: MModule is noinit # The static type of the receiver # Mainly used for type tests and type resolutions - var anchor: MClassType is noinit + redef var anchor: MClassType is noinit # The analyzed mclassdef var mclassdef: MClassDef is noinit @@ -82,23 +111,6 @@ private class TypeVisitor 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) - end - - fun is_subtype(sub, sup: MType): Bool - do - return sub.is_subtype(mmodule, anchor, sup) - end - - fun resolve_for(mtype, subtype: MType, for_self: Bool): MType - do - #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 - # Check that `sub` is a subtype of `sup`. # If `sub` is not a valid suptype, then display an error on `node` and return `null`. # If `sub` is a safe subtype of `sup`, then return `sub`. @@ -310,14 +322,8 @@ private class TypeVisitor # If you just know the mproperty use the `build_callsite_by_property` method to display error if no `mpropdef` is found in the context fun build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite do - var unsafe_type = self.anchor_to(recvtype) - - #debug("recv: {recvtype} (aka {unsafe_type})") - if recvtype isa MNullType then - var objclass = get_mclass(node, "Object") - if objclass == null then return null # Forward error - unsafe_type = objclass.mclass_type - end + var builder = (new CallSiteBuilder(node.hot_location, recvtype, self)).recv_is_self(recv_is_self) + var unsafe_type = builder.base_type var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) if name == "new" and mproperty == null then @@ -347,25 +353,33 @@ private class TypeVisitor assert mproperty isa MMethod - return build_callsite_by_property(node, recvtype, mproperty, recv_is_self) + var callsite = builder.by_property(mproperty) + if callsite == null then return null + return check_callsite(node, builder) end # The `build_callsite_by_property` finds the mpropdefs to call by the `MMethod`. # If the mpropdef is found in the context it builds a new `Callsite`. fun build_callsite_by_property(node: ANode, recvtype: MType, mproperty: MMethod, recv_is_self: Bool): nullable CallSite do - var unsafe_type = self.anchor_to(recvtype) + var builder = (new CallSiteBuilder(node.hot_location, recvtype, self)).recv_is_self(recv_is_self) + var callsite = builder.by_property(mproperty) + if callsite == null then return null + return check_callsite(node, builder) + end + + fun check_callsite(node: ANode, builder: CallSiteBuilder): nullable CallSite + do + var recvtype = builder.recv + var callsite = builder.callsite + var mproperty = callsite.mproperty + var recv_is_self = callsite.recv_is_self - if recvtype isa MNullType then - var objclass = get_mclass(node, "Object") - if objclass == null then return null # Forward error - unsafe_type = objclass.mclass_type - end # `null` only accepts some methods of object. if recvtype isa MNullType and not mproperty.is_null_safe then self.error(node, "Error: method `{mproperty.name}` called on `null`.") - return null - else if unsafe_type isa MNullableType and not mproperty.is_null_safe then + return callsite + else if builder.base_type isa MNullableType and not mproperty.is_null_safe then modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.") end @@ -378,7 +392,7 @@ private class TypeVisitor 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 `{mproperty.name}` is protected and can only accessed by `self`.") - return null + return callsite end var info = mproperty.deprecation @@ -391,43 +405,21 @@ private class TypeVisitor end end - var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type) - var mpropdef - if propdefs.length == 0 then - self.modelbuilder.error(node, "Type Error: no definition found for property `{mproperty.name}` in `{unsafe_type}`.") - abort - #return null - else if propdefs.length == 1 then - mpropdef = propdefs.first - else - display_warning(node, "property-conflict", "Warning: conflicting property definitions for property `{mproperty.name}` in `{unsafe_type}`: {propdefs.join(" ")}") - mpropdef = mproperty.intro + if builder.mpropdefs.length == 0 then + self.modelbuilder.error(node, "Type Error: no definition found for property `{mproperty.name}` in `{builder.base_type}`.") + #abort + else if builder.mpropdefs.length > 1 then + display_warning(node, "property-conflict", "Warning: conflicting property definitions for property `{mproperty.name}` in `{builder.base_type}`: {builder.mpropdefs.join(" ")}") end - return build_callsite_by_propdef(node, recvtype, mpropdef, recv_is_self) + return callsite end # 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.msignature - if msignature == null then return null # skip error - 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 - rettype = rettype.undecorate - if rettype isa MParameterType then - var erased_rettype = msignature.return_mtype - assert erased_rettype != null - #node.debug("Erasure cast: Really a {rettype} but unsafely a {erased_rettype}") - erasure_cast = true - end - end - - var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mpropdef.mproperty, mpropdef, msignature, erasure_cast) - return callsite + var builder = new CallSiteBuilder(node.hot_location, recvtype, self) + return builder.recv_is_self(recv_is_self).by_propdef(mpropdef) end fun try_build_callsite_by_name(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite @@ -824,6 +816,99 @@ class CallSite redef fun mdoc_or_fallback do return mproperty.intro.mdoc end +# Helper class to build a callsite object with a fluent API +class CallSiteBuilder + var location: Location + var recv: MType + var context: TypeContext + + var callsite = new CallSite.intern + + var mpropdefs: Collection[MMethodDef] is noinit + + # Base type to do the lookup. is fully resolved + var base_type: MType is noinit + + init + do + if recv isa MNullType then + # We will lookup from Object if `null` is given + base_type = context.mmodule.object_type + else + base_type = context.anchor_to(recv) + end + + callsite.location = location + callsite.recv = recv + callsite.mmodule = context.mmodule + callsite.anchor = context.anchor + callsite.recv_is_self = false + callsite.erasure_cast = false + end + + fun recv_is_self(b: Bool): CallSiteBuilder + do + callsite.recv_is_self = b + return self + end + + fun by_name(name: String): nullable CallSite + do + return null + + end + + fun lookup_mproperty(mproperty: MMethod): Collection[MMethodDef] + do + callsite.mproperty = mproperty + mpropdefs = mproperty.lookup_definitions(context.mmodule, base_type) + return mpropdefs + end + + fun by_property(mproperty: MMethod): nullable CallSite + do + lookup_mproperty(mproperty) + + var mpropdef + if mpropdefs.length == 0 then + return null + else if mpropdefs.length == 1 then + mpropdef = mpropdefs.first + else + mpropdef = mproperty.intro + end + + return by_propdef(mpropdef) + end + + # The `build_callsite_by_propdef` builds the callsite directly with the `mprodef` passed in argument. + fun by_propdef(mpropdef: MMethodDef): nullable CallSite + do + var msignature = mpropdef.msignature + if msignature == null then return null # skip error + msignature = context.resolve_for(msignature, callsite.recv, callsite.recv_is_self).as(MSignature) + + var erasure_cast = false + var rettype = mpropdef.msignature.return_mtype + if not callsite.recv_is_self and rettype != null then + rettype = rettype.undecorate + if rettype isa MParameterType then + var erased_rettype = msignature.return_mtype + assert erased_rettype != null + #node.debug("Erasure cast: Really a {rettype} but unsafely a {erased_rettype}") + erasure_cast = true + end + end + + callsite.mproperty = mpropdef.mproperty + callsite.mpropdef = mpropdef + callsite.msignature = msignature + callsite.erasure_cast = erasure_cast + return callsite + end +end + + redef class Variable # The declared type of the variable var declared_type: nullable MType = null is writable