From ba594c0a0a05a96eef18883f2a2bffc554574d1f Mon Sep 17 00:00:00 2001 From: Jean Privat Date: Fri, 19 Jul 2013 09:35:02 -0400 Subject: [PATCH 1/1] src: extract modelize_property.nit from modelbuiler.nit Signed-off-by: Jean Privat --- src/modelbuilder.nit | 857 +------------------------------------------ src/modelize_property.nit | 886 +++++++++++++++++++++++++++++++++++++++++++++ src/typing.nit | 2 +- 3 files changed, 889 insertions(+), 856 deletions(-) create mode 100644 src/modelize_property.nit diff --git a/src/modelbuilder.nit b/src/modelbuilder.nit index d658d0a..84b89e6 100644 --- a/src/modelbuilder.nit +++ b/src/modelbuilder.nit @@ -51,7 +51,6 @@ redef class ToolContext private var modelbuilder_real: nullable ModelBuilder = null var modelize_class_phase: Phase = new ModelizeClassPhase(self, null) - var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase]) end private class ModelizeClassPhase @@ -63,16 +62,6 @@ private class ModelizeClassPhase end end -private class ModelizePropertyPhase - super Phase - redef fun process_nmodule(nmodule) - do - for nclassdef in nmodule.n_classdefs do - toolcontext.modelbuilder.build_properties(nclassdef) - end - end -end - # A model builder knows how to load nit source files and build the associated model class ModelBuilder # The model where new modules, classes and properties are added @@ -674,122 +663,6 @@ class ModelBuilder # Register the nclassdef associated to each mclassdef # FIXME: why not refine the MClassDef class with a nullable attribute? var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef] - # Register the npropdef associated to each mpropdef - # FIXME: why not refine the MPropDef class with a nullable attribute? - var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef] - - # Build the properties of `nclassdef'. - # REQUIRE: all superclasses are built. - private fun build_properties(nclassdef: AClassdef) - do - # Force building recursively - if nclassdef.build_properties_is_done then return - nclassdef.build_properties_is_done = true - var mclassdef = nclassdef.mclassdef.as(not null) - if mclassdef.in_hierarchy == null then return # Skip error - for superclassdef in mclassdef.in_hierarchy.direct_greaters do - build_properties(mclassdef2nclassdef[superclassdef]) - end - - for npropdef in nclassdef.n_propdefs do - npropdef.build_property(self, nclassdef) - end - for npropdef in nclassdef.n_propdefs do - npropdef.build_signature(self, nclassdef) - end - for npropdef in nclassdef.n_propdefs do - npropdef.check_signature(self, nclassdef) - end - process_default_constructors(nclassdef) - end - - # Introduce or inherit default constructor - # This is the last part of `build_properties'. - private fun process_default_constructors(nclassdef: AClassdef) - do - var mclassdef = nclassdef.mclassdef.as(not null) - - # Are we a refinement - if not mclassdef.is_intro then return - - # Is the class forbid constructors? - if not mclassdef.mclass.kind.need_init then return - - # Is there already a constructor defined? - for mpropdef in mclassdef.mpropdefs do - if not mpropdef isa MMethodDef then continue - if mpropdef.mproperty.is_init then return - end - - if not nclassdef isa AStdClassdef then return - - var mmodule = nclassdef.mclassdef.mmodule - # Do we inherit for a constructor? - var combine = new Array[MMethod] - var inhc: nullable MClass = null - for st in mclassdef.supertypes do - var c = st.mclass - if not c.kind.need_init then continue - st = st.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype) - var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod) - if candidate != null and candidate.intro.msignature.arity == 0 then - combine.add(candidate) - continue - end - var inhc2 = c.inherit_init_from - if inhc2 == null then inhc2 = c - if inhc2 == inhc then continue - if inhc != null then - self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}") - else - inhc = inhc2 - end - end - if combine.is_empty and inhc != null then - # TODO: actively inherit the consturctor - self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3) - mclassdef.mclass.inherit_init_from = inhc - return - end - if not combine.is_empty and inhc != null then - self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}") - return - end - - if not combine.is_empty then - nclassdef.super_inits = combine - var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) - var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) - var mparameters = new Array[MParameter] - var msignature = new MSignature(mparameters, null) - mpropdef.msignature = msignature - mprop.is_init = true - nclassdef.mfree_init = mpropdef - self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3) - return - end - - # Collect undefined attributes - var mparameters = new Array[MParameter] - for npropdef in nclassdef.n_propdefs do - if npropdef isa AAttrPropdef and npropdef.n_expr == null then - if npropdef.mpropdef == null then return # Skip broken attribute - var paramname = npropdef.mpropdef.mproperty.name.substring_from(1) - var ret_type = npropdef.mpropdef.static_mtype - if ret_type == null then return - var mparameter = new MParameter(paramname, ret_type, false) - mparameters.add(mparameter) - end - end - - var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) - var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) - var msignature = new MSignature(mparameters, null) - mpropdef.msignature = msignature - mprop.is_init = true - nclassdef.mfree_init = mpropdef - self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3) - end # Return the static type associated to the node `ntype'. # `classdef' is the context where the call is made (used to understand formal types) @@ -941,28 +814,11 @@ redef class AModule end -redef class MClass - # The class whose self inherit all the constructors. - # FIXME: this is needed to implement the crazy constructor mixin thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder - var inherit_init_from: nullable MClass = null -end - redef class AClassdef # The associated MClass once build by a `ModelBuilder' var mclass: nullable MClass # The associated MClassDef once build by a `ModelBuilder' var mclassdef: nullable MClassDef - var build_properties_is_done: Bool = false - # The list of super-constructor to call at the start of the free constructor - # FIXME: this is needed to implement the crazy constructor thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder - var super_inits: nullable Collection[MMethod] = null - - # The free init (implicitely constructed by the class if required) - var mfree_init: nullable MMethodDef = null - - # What is the APropdef associated to a MProperty? - # Used to check multiple definition of a property. - var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef] end redef class AClasskind @@ -987,7 +843,7 @@ end redef class AVisibility # The visibility level associated with the AST node class - private fun mvisibility: MVisibility is abstract + fun mvisibility: MVisibility is abstract end redef class AIntrudeVisibility redef fun mvisibility do return intrude_visibility @@ -1008,713 +864,4 @@ redef class AType # Is the mtype a valid one? var checked_mtype: Bool = false -end - -# - -redef class Prod - # Join the text of all tokens - # Used to get the 'real name' of method definitions. - fun collect_text: String - do - var v = new TextCollectorVisitor - v.enter_visit(self) - assert v.text != "" - return v.text - end -end - -private class TextCollectorVisitor - super Visitor - var text: String = "" - redef fun visit(n) - do - if n isa Token then text += n.text - n.visit_all(self) - end -end - -redef class APropdef - private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) - do - end - private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) - do - end - private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) - do - end - private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility - do - var mvisibility = public_visibility - if nvisibility != null then mvisibility = nvisibility.mvisibility - if nclassdef.mclassdef.mclass.visibility == private_visibility then - if mvisibility == protected_visibility then - assert nvisibility != null - modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.") - else if mvisibility == private_visibility then - assert nvisibility != null - # Not yet - # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.") - end - mvisibility = private_visibility - end - return mvisibility - end - - private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty) - do - if nvisibility == null then return - var mvisibility = nvisibility.mvisibility - if mvisibility != mprop.visibility and mvisibility != public_visibility then - modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}") - end - end - - private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool - do - if nclassdef.mprop2npropdef.has_key(mprop) then - modelbuilder.error(self, "Error: A property {mprop} is already defined in class {nclassdef.mclassdef.mclass}.") - return false - end - if kwredef == null then - if need_redef then - modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") - return false - end - else - if not need_redef then - modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") - return false - end - end - return true - end - -end - -redef class ASignature - # Is the model builder has correctly visited the signature - var is_visited = false - # Names of parameters from the AST - # REQUIRE: is_visited - var param_names = new Array[String] - # Types of parameters from the AST - # REQUIRE: is_visited - var param_types = new Array[MType] - # Rank of the vararg (of -1 if none) - # REQUIRE: is_visited - var vararg_rank: Int = -1 - # Return type - var ret_type: nullable MType = null - - # Visit and fill information about a signature - private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool - do - var param_names = self.param_names - var param_types = self.param_types - for np in self.n_params do - param_names.add(np.n_id.text) - var ntype = np.n_type - if ntype != null then - var mtype = modelbuilder.resolve_mtype(nclassdef, ntype) - if mtype == null then return false # Skip error - for i in [0..param_names.length-param_types.length[ do - param_types.add(mtype) - end - if np.n_dotdotdot != null then - if self.vararg_rank != -1 then - modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg") - return false - else - self.vararg_rank = param_names.length - 1 - end - end - end - end - var ntype = self.n_type - if ntype != null then - self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype) - if self.ret_type == null then return false # Skip errir - end - - for nclosure in self.n_closure_decls do - if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false - end - - self.is_visited = true - return true - end - - # Build a visited signature - fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature - do - if param_names.length != param_types.length then - # Some parameters are typed, other parameters are not typed. - modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.") - return null - end - - var mparameters = new Array[MParameter] - for i in [0..param_names.length[ do - var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank) - mparameters.add(mparameter) - end - - var msignature = new MSignature(mparameters, ret_type) - return msignature - end -end - -redef class AMethPropdef - # The associated MMethodDef once build by a `ModelBuilder' - var mpropdef: nullable MMethodDef - - # The associated super init if any - var super_init: nullable MMethod - redef fun build_property(modelbuilder, nclassdef) - do - var is_init = self isa AInitPropdef - var mclassdef = nclassdef.mclassdef.as(not null) - var name: String - var amethodid = self.n_methid - var name_node: ANode - if amethodid == null then - if self isa AMainMethPropdef then - name = "main" - name_node = self - else if self isa AConcreteInitPropdef then - name = "init" - name_node = self.n_kwinit - else if self isa AExternInitPropdef then - name = "init" - name_node = self.n_kwnew - else - abort - end - else if amethodid isa AIdMethid then - name = amethodid.n_id.text - name_node = amethodid - else - # operator, bracket or assign - name = amethodid.collect_text - name_node = amethodid - - if name == "-" and self.n_signature.n_params.length == 0 then - name = "unary -" - end - end - - var mprop: nullable MMethod = null - if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod) - if mprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) - mprop = new MMethod(mclassdef, name, mvisibility) - mprop.is_init = is_init - mprop.is_new = self isa AExternInitPropdef - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return - else - if n_kwredef == null then - if self isa AMainMethPropdef then - # no warning - else - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop) then return - end - end - check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) - end - nclassdef.mprop2npropdef[mprop] = self - - var mpropdef = new MMethodDef(mclassdef, mprop, self.location) - - self.mpropdef = mpropdef - modelbuilder.mpropdef2npropdef[mpropdef] = self - if mpropdef.is_intro then - modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3) - else - modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3) - end - end - - redef fun build_signature(modelbuilder, nclassdef) - do - var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mmodule = mpropdef.mclassdef.mmodule - var nsig = self.n_signature - - # Retrieve info from the signature AST - var param_names = new Array[String] # Names of parameters from the AST - var param_types = new Array[MType] # Types of parameters from the AST - var vararg_rank = -1 - var ret_type: nullable MType = null # Return type from the AST - if nsig != null then - if not nsig.visit_signature(modelbuilder, nclassdef) then return - param_names = nsig.param_names - param_types = nsig.param_types - vararg_rank = nsig.vararg_rank - ret_type = nsig.ret_type - end - - # Look for some signature to inherit - # FIXME: do not inherit from the intro, but from the most specific - var msignature: nullable MSignature = null - if not mpropdef.is_intro then - msignature = mpropdef.mproperty.intro.msignature - if msignature == null then return # Skip error - - # Check inherited signature arity - if param_names.length != msignature.arity then - var node: ANode - if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}") - return - end - else if mpropdef.mproperty.is_init then - # FIXME UGLY: inherit signature from a super-constructor - for msupertype in nclassdef.mclassdef.supertypes do - msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype) - var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name) - if candidate != null then - if msignature == null then - msignature = candidate.intro.as(MMethodDef).msignature - end - end - end - end - - - # Inherit the signature - if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then - # Parameters are untyped, thus inherit them - param_types = new Array[MType] - for mparameter in msignature.mparameters do - param_types.add(mparameter.mtype) - end - vararg_rank = msignature.vararg_rank - end - if msignature != null and ret_type == null then - ret_type = msignature.return_mtype - end - - if param_names.length != param_types.length then - # Some parameters are typed, other parameters are not typed. - modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.") - return - end - - var mparameters = new Array[MParameter] - for i in [0..param_names.length[ do - var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank) - mparameters.add(mparameter) - end - - msignature = new MSignature(mparameters, ret_type) - mpropdef.msignature = msignature - - if nsig != null then - for nclosure in nsig.n_closure_decls do - var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef) - if clos_signature == null then return - var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false) - msignature.mclosures.add(mparameter) - end - end - - end - - redef fun check_signature(modelbuilder, nclassdef) - do - var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mmodule = mpropdef.mclassdef.mmodule - var nsig = self.n_signature - var mysignature = self.mpropdef.msignature - if mysignature == null then return # Error thus skiped - - # Lookup for signature in the precursor - # FIXME all precursors should be considered - if not mpropdef.is_intro then - var msignature = mpropdef.mproperty.intro.msignature - if msignature == null then return - - var precursor_ret_type = msignature.return_mtype - var ret_type = mysignature.return_mtype - if ret_type != null and precursor_ret_type == null then - modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") - return - end - - if mysignature.arity > 0 then - # Check parameters types - for i in [0..mysignature.arity[ do - var myt = mysignature.mparameters[i].mtype - var prt = msignature.mparameters[i].mtype - if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and - not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then - modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") - end - end - end - if precursor_ret_type != null then - if ret_type == null then - # Inherit the return type - ret_type = precursor_ret_type - else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then - modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") - end - end - end - end -end - -redef class AAttrPropdef - # The associated MAttributeDef once build by a `ModelBuilder' - var mpropdef: nullable MAttributeDef - # The associated getter (read accessor) if any - var mreadpropdef: nullable MMethodDef - # The associated setter (write accessor) if any - var mwritepropdef: nullable MMethodDef - redef fun build_property(modelbuilder, nclassdef) - do - var mclassdef = nclassdef.mclassdef.as(not null) - var mclass = mclassdef.mclass - - var name: String - if self.n_id != null then - name = self.n_id.text - else - name = self.n_id2.text - end - - if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.") - else if mclass.kind == enum_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.") - else if mclass.kind == extern_kind then - modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.") - end - - var nid = self.n_id - if nid != null then - # Old attribute style - var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name) - if mprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) - mprop = new MAttribute(mclassdef, name, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return - else - assert mprop isa MAttribute - check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return - end - nclassdef.mprop2npropdef[mprop] = self - - var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) - self.mpropdef = mpropdef - modelbuilder.mpropdef2npropdef[mpropdef] = self - - var nreadable = self.n_readable - if nreadable != null then - var readname = name.substring_from(1) - var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod) - if mreadprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility) - mreadprop = new MMethod(mclassdef, readname, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return - else - if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return - check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop) - end - nclassdef.mprop2npropdef[mreadprop] = self - - var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) - self.mreadpropdef = mreadpropdef - modelbuilder.mpropdef2npropdef[mreadpropdef] = self - end - - var nwritable = self.n_writable - if nwritable != null then - var writename = name.substring_from(1) + "=" - var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod) - if mwriteprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility) - mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return - else - if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return - check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop) - end - nclassdef.mprop2npropdef[mwriteprop] = self - - var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) - self.mwritepropdef = mwritepropdef - modelbuilder.mpropdef2npropdef[mwritepropdef] = self - end - else - # New attribute style - var nid2 = self.n_id2.as(not null) - var mprop = new MAttribute(mclassdef, "@" + name, none_visibility) - var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) - self.mpropdef = mpropdef - modelbuilder.mpropdef2npropdef[mpropdef] = self - - var readname = name - var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod) - if mreadprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) - mreadprop = new MMethod(mclassdef, readname, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return - else - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return - check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop) - end - nclassdef.mprop2npropdef[mreadprop] = self - - var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) - self.mreadpropdef = mreadpropdef - modelbuilder.mpropdef2npropdef[mreadpropdef] = self - - var writename = name + "=" - var nwritable = self.n_writable - var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod) - var nwkwredef: nullable Token = null - if nwritable != null then nwkwredef = nwritable.n_kwredef - if mwriteprop == null then - var mvisibility - if nwritable != null then - mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility) - else - mvisibility = private_visibility - end - mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return - else - if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return - if nwritable != null then - check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop) - end - end - nclassdef.mprop2npropdef[mwriteprop] = self - - var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) - self.mwritepropdef = mwritepropdef - modelbuilder.mpropdef2npropdef[mwritepropdef] = self - end - end - - redef fun build_signature(modelbuilder, nclassdef) - do - var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mmodule = mpropdef.mclassdef.mmodule - var mtype: nullable MType = null - - var ntype = self.n_type - if ntype != null then - mtype = modelbuilder.resolve_mtype(nclassdef, ntype) - if mtype == null then return - end - - if mtype == null then - var nexpr = self.n_expr - if nexpr != null then - if nexpr isa ANewExpr then - mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type) - else if nexpr isa AIntExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") - if cla != null then mtype = cla.mclass_type - else if nexpr isa AFloatExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ACharExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ABoolExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool") - if cla != null then mtype = cla.mclass_type - else if nexpr isa ASuperstringExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") - if cla != null then mtype = cla.mclass_type - else if nexpr isa AStringFormExpr then - var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") - if cla != null then mtype = cla.mclass_type - else - modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.") - end - - else - modelbuilder.error(self, "Error: Untyped attribute {mpropdef}") - end - end - - if mtype == null then return - - mpropdef.static_mtype = mtype - - var mreadpropdef = self.mreadpropdef - if mreadpropdef != null then - var msignature = new MSignature(new Array[MParameter], mtype) - mreadpropdef.msignature = msignature - end - - var msritepropdef = self.mwritepropdef - if mwritepropdef != null then - var name: String - if n_id != null then - name = n_id.text.substring_from(1) - else - name = n_id2.text - end - var mparameter = new MParameter(name, mtype, false) - var msignature = new MSignature([mparameter], null) - mwritepropdef.msignature = msignature - end - end - - redef fun check_signature(modelbuilder, nclassdef) - do - var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mmodule = mpropdef.mclassdef.mmodule - var ntype = self.n_type - var mtype = self.mpropdef.static_mtype - if mtype == null then return # Error thus skiped - - # Lookup for signature in the precursor - # FIXME all precursors should be considered - if not mpropdef.is_intro then - var precursor_type = mpropdef.mproperty.intro.static_mtype - if precursor_type == null then return - - if mtype != precursor_type then - modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.") - return - end - end - - # Check getter and setter - var meth = self.mreadpropdef - if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth) - meth = self.mwritepropdef - if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth) - end - - private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef) - do - var mmodule = mpropdef.mclassdef.mmodule - var nsig = self.n_type - var mysignature = mpropdef.msignature - if mysignature == null then return # Error thus skiped - - # Lookup for signature in the precursor - # FIXME all precursors should be considered - if not mpropdef.is_intro then - var msignature = mpropdef.mproperty.intro.msignature - if msignature == null then return - - if mysignature.arity != msignature.arity then - var node: ANode - if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}") - return - end - var precursor_ret_type = msignature.return_mtype - var ret_type = mysignature.return_mtype - if ret_type != null and precursor_ret_type == null then - var node: ANode - if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") - return - end - - if mysignature.arity > 0 then - # Check parameters types - for i in [0..mysignature.arity[ do - var myt = mysignature.mparameters[i].mtype - var prt = msignature.mparameters[i].mtype - if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and - not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then - var node: ANode - if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") - end - end - end - if precursor_ret_type != null then - if ret_type == null then - # Inherit the return type - ret_type = precursor_ret_type - else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then - var node: ANode - if nsig != null then node = nsig else node = self - modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") - end - end - end - end -end - -redef class ATypePropdef - # The associated MVirtualTypeDef once build by a `ModelBuilder' - var mpropdef: nullable MVirtualTypeDef - redef fun build_property(modelbuilder, nclassdef) - do - var mclassdef = nclassdef.mclassdef.as(not null) - var name = self.n_id.text - var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name) - if mprop == null then - var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) - mprop = new MVirtualTypeProp(mclassdef, name, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return - else - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return - assert mprop isa MVirtualTypeProp - check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) - end - nclassdef.mprop2npropdef[mprop] = self - - var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location) - self.mpropdef = mpropdef - end - - redef fun build_signature(modelbuilder, nclassdef) - do - var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mmodule = mpropdef.mclassdef.mmodule - var mtype: nullable MType = null - - var ntype = self.n_type - mtype = modelbuilder.resolve_mtype(nclassdef, ntype) - if mtype == null then return - - mpropdef.bound = mtype - # print "{mpropdef}: {mtype}" - end - - redef fun check_signature(modelbuilder, nclassdef) - do - var bound = self.mpropdef.bound - - # Fast case: the bound is not a formal type - if not bound isa MVirtualType then return - - var mmodule = nclassdef.mclassdef.mmodule - var anchor = nclassdef.mclassdef.bound_mtype - - # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type - var seen = [self.mpropdef.mproperty.mvirtualtype] - loop - if seen.has(bound) then - seen.add(bound) - modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}") - return - end - seen.add(bound) - var next = bound.lookup_bound(mmodule, anchor) - if not next isa MVirtualType then return - bound = next - end - end -end +end \ No newline at end of file diff --git a/src/modelize_property.nit b/src/modelize_property.nit new file mode 100644 index 0000000..115f348 --- /dev/null +++ b/src/modelize_property.nit @@ -0,0 +1,886 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Analysis and verification of property definitions to instantiate model element +module modelize_property + +import parser +import model +import poset +import opts +import toolcontext +import phase +import modelbuilder + +redef class ToolContext + var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase]) +end + +private class ModelizePropertyPhase + super Phase + redef fun process_nmodule(nmodule) + do + for nclassdef in nmodule.n_classdefs do + toolcontext.modelbuilder.build_properties(nclassdef) + end + end +end + +redef class ModelBuilder + # Register the npropdef associated to each mpropdef + # FIXME: why not refine the MPropDef class with a nullable attribute? + var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef] + + # Build the properties of `nclassdef'. + # REQUIRE: all superclasses are built. + private fun build_properties(nclassdef: AClassdef) + do + # Force building recursively + if nclassdef.build_properties_is_done then return + nclassdef.build_properties_is_done = true + var mclassdef = nclassdef.mclassdef.as(not null) + if mclassdef.in_hierarchy == null then return # Skip error + for superclassdef in mclassdef.in_hierarchy.direct_greaters do + build_properties(mclassdef2nclassdef[superclassdef]) + end + + for npropdef in nclassdef.n_propdefs do + npropdef.build_property(self, nclassdef) + end + for npropdef in nclassdef.n_propdefs do + npropdef.build_signature(self, nclassdef) + end + for npropdef in nclassdef.n_propdefs do + npropdef.check_signature(self, nclassdef) + end + process_default_constructors(nclassdef) + end + + # Introduce or inherit default constructor + # This is the last part of `build_properties'. + private fun process_default_constructors(nclassdef: AClassdef) + do + var mclassdef = nclassdef.mclassdef.as(not null) + + # Are we a refinement + if not mclassdef.is_intro then return + + # Is the class forbid constructors? + if not mclassdef.mclass.kind.need_init then return + + # Is there already a constructor defined? + for mpropdef in mclassdef.mpropdefs do + if not mpropdef isa MMethodDef then continue + if mpropdef.mproperty.is_init then return + end + + if not nclassdef isa AStdClassdef then return + + var mmodule = nclassdef.mclassdef.mmodule + # Do we inherit for a constructor? + var combine = new Array[MMethod] + var inhc: nullable MClass = null + for st in mclassdef.supertypes do + var c = st.mclass + if not c.kind.need_init then continue + st = st.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype) + var candidate = self.try_get_mproperty_by_name2(nclassdef, mmodule, st, "init").as(nullable MMethod) + if candidate != null and candidate.intro.msignature.arity == 0 then + combine.add(candidate) + continue + end + var inhc2 = c.inherit_init_from + if inhc2 == null then inhc2 = c + if inhc2 == inhc then continue + if inhc != null then + self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {inhc} and {c}") + else + inhc = inhc2 + end + end + if combine.is_empty and inhc != null then + # TODO: actively inherit the consturctor + self.toolcontext.info("{mclassdef} inherits all constructors from {inhc}", 3) + mclassdef.mclass.inherit_init_from = inhc + return + end + if not combine.is_empty and inhc != null then + self.error(nclassdef, "Error: Cannot provide a defaut constructor: conflict for {combine.join(", ")} and {inhc}") + return + end + + if not combine.is_empty then + nclassdef.super_inits = combine + var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) + var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + var mparameters = new Array[MParameter] + var msignature = new MSignature(mparameters, null) + mpropdef.msignature = msignature + mprop.is_init = true + nclassdef.mfree_init = mpropdef + self.toolcontext.info("{mclassdef} gets a free empty constructor {mpropdef}{msignature}", 3) + return + end + + # Collect undefined attributes + var mparameters = new Array[MParameter] + for npropdef in nclassdef.n_propdefs do + if npropdef isa AAttrPropdef and npropdef.n_expr == null then + if npropdef.mpropdef == null then return # Skip broken attribute + var paramname = npropdef.mpropdef.mproperty.name.substring_from(1) + var ret_type = npropdef.mpropdef.static_mtype + if ret_type == null then return + var mparameter = new MParameter(paramname, ret_type, false) + mparameters.add(mparameter) + end + end + + var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility) + var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location) + var msignature = new MSignature(mparameters, null) + mpropdef.msignature = msignature + mprop.is_init = true + nclassdef.mfree_init = mpropdef + self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3) + end +end + +redef class MClass + # The class whose self inherit all the constructors. + # FIXME: this is needed to implement the crazy constructor mixin thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder + var inherit_init_from: nullable MClass = null +end + +redef class AClassdef + var build_properties_is_done: Bool = false + # The list of super-constructor to call at the start of the free constructor + # FIXME: this is needed to implement the crazy constructor thing of the of old compiler. We need to think what to do with since this cannot stay in the modelbuilder + var super_inits: nullable Collection[MMethod] = null + + # The free init (implicitely constructed by the class if required) + var mfree_init: nullable MMethodDef = null + + # What is the APropdef associated to a MProperty? + # Used to check multiple definition of a property. + var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef] +end + +redef class Prod + # Join the text of all tokens + # Used to get the 'real name' of method definitions. + fun collect_text: String + do + var v = new TextCollectorVisitor + v.enter_visit(self) + assert v.text != "" + return v.text + end +end + +private class TextCollectorVisitor + super Visitor + var text: String = "" + redef fun visit(n) + do + if n isa Token then text += n.text + n.visit_all(self) + end +end + +redef class APropdef + private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) + do + end + private fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) + do + end + private fun check_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef) + do + end + private fun new_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility): MVisibility + do + var mvisibility = public_visibility + if nvisibility != null then mvisibility = nvisibility.mvisibility + if nclassdef.mclassdef.mclass.visibility == private_visibility then + if mvisibility == protected_visibility then + assert nvisibility != null + modelbuilder.error(nvisibility, "Error: The only legal visibility for properties in a private class is private.") + else if mvisibility == private_visibility then + assert nvisibility != null + # Not yet + # modelbuilder.warning(nvisibility, "Warning: private is unrequired since the only legal visibility for properties in a private class is private.") + end + mvisibility = private_visibility + end + return mvisibility + end + + private fun check_redef_property_visibility(modelbuilder: ModelBuilder, nclassdef: AClassdef, nvisibility: nullable AVisibility, mprop: MProperty) + do + if nvisibility == null then return + var mvisibility = nvisibility.mvisibility + if mvisibility != mprop.visibility and mvisibility != public_visibility then + modelbuilder.error(nvisibility, "Error: redefinition changed the visibility from a {mprop.visibility} to a {mvisibility}") + end + end + + private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool + do + if nclassdef.mprop2npropdef.has_key(mprop) then + modelbuilder.error(self, "Error: A property {mprop} is already defined in class {nclassdef.mclassdef.mclass}.") + return false + end + if kwredef == null then + if need_redef then + modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") + return false + end + else + if not need_redef then + modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") + return false + end + end + return true + end + +end + +redef class ASignature + # Is the model builder has correctly visited the signature + var is_visited = false + # Names of parameters from the AST + # REQUIRE: is_visited + var param_names = new Array[String] + # Types of parameters from the AST + # REQUIRE: is_visited + var param_types = new Array[MType] + # Rank of the vararg (of -1 if none) + # REQUIRE: is_visited + var vararg_rank: Int = -1 + # Return type + var ret_type: nullable MType = null + + # Visit and fill information about a signature + private fun visit_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): Bool + do + var param_names = self.param_names + var param_types = self.param_types + for np in self.n_params do + param_names.add(np.n_id.text) + var ntype = np.n_type + if ntype != null then + var mtype = modelbuilder.resolve_mtype(nclassdef, ntype) + if mtype == null then return false # Skip error + for i in [0..param_names.length-param_types.length[ do + param_types.add(mtype) + end + if np.n_dotdotdot != null then + if self.vararg_rank != -1 then + modelbuilder.error(np, "Error: {param_names[self.vararg_rank]} is already a vararg") + return false + else + self.vararg_rank = param_names.length - 1 + end + end + end + end + var ntype = self.n_type + if ntype != null then + self.ret_type = modelbuilder.resolve_mtype(nclassdef, ntype) + if self.ret_type == null then return false # Skip errir + end + + for nclosure in self.n_closure_decls do + if not nclosure.n_signature.visit_signature(modelbuilder, nclassdef) then return false + end + + self.is_visited = true + return true + end + + # Build a visited signature + fun build_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef): nullable MSignature + do + if param_names.length != param_types.length then + # Some parameters are typed, other parameters are not typed. + modelbuilder.error(self.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.") + return null + end + + var mparameters = new Array[MParameter] + for i in [0..param_names.length[ do + var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank) + mparameters.add(mparameter) + end + + var msignature = new MSignature(mparameters, ret_type) + return msignature + end +end + +redef class AMethPropdef + # The associated MMethodDef once build by a `ModelBuilder' + var mpropdef: nullable MMethodDef + + # The associated super init if any + var super_init: nullable MMethod + redef fun build_property(modelbuilder, nclassdef) + do + var is_init = self isa AInitPropdef + var mclassdef = nclassdef.mclassdef.as(not null) + var name: String + var amethodid = self.n_methid + var name_node: ANode + if amethodid == null then + if self isa AMainMethPropdef then + name = "main" + name_node = self + else if self isa AConcreteInitPropdef then + name = "init" + name_node = self.n_kwinit + else if self isa AExternInitPropdef then + name = "init" + name_node = self.n_kwnew + else + abort + end + else if amethodid isa AIdMethid then + name = amethodid.n_id.text + name_node = amethodid + else + # operator, bracket or assign + name = amethodid.collect_text + name_node = amethodid + + if name == "-" and self.n_signature.n_params.length == 0 then + name = "unary -" + end + end + + var mprop: nullable MMethod = null + if not is_init or n_kwredef != null then mprop = modelbuilder.try_get_mproperty_by_name(name_node, mclassdef, name).as(nullable MMethod) + if mprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) + mprop = new MMethod(mclassdef, name, mvisibility) + mprop.is_init = is_init + mprop.is_new = self isa AExternInitPropdef + if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return + else + if n_kwredef == null then + if self isa AMainMethPropdef then + # no warning + else + if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mprop) then return + end + end + check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) + end + nclassdef.mprop2npropdef[mprop] = self + + var mpropdef = new MMethodDef(mclassdef, mprop, self.location) + + self.mpropdef = mpropdef + modelbuilder.mpropdef2npropdef[mpropdef] = self + if mpropdef.is_intro then + modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3) + else + modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3) + end + end + + redef fun build_signature(modelbuilder, nclassdef) + do + var mpropdef = self.mpropdef + if mpropdef == null then return # Error thus skiped + var mmodule = mpropdef.mclassdef.mmodule + var nsig = self.n_signature + + # Retrieve info from the signature AST + var param_names = new Array[String] # Names of parameters from the AST + var param_types = new Array[MType] # Types of parameters from the AST + var vararg_rank = -1 + var ret_type: nullable MType = null # Return type from the AST + if nsig != null then + if not nsig.visit_signature(modelbuilder, nclassdef) then return + param_names = nsig.param_names + param_types = nsig.param_types + vararg_rank = nsig.vararg_rank + ret_type = nsig.ret_type + end + + # Look for some signature to inherit + # FIXME: do not inherit from the intro, but from the most specific + var msignature: nullable MSignature = null + if not mpropdef.is_intro then + msignature = mpropdef.mproperty.intro.msignature + if msignature == null then return # Skip error + + # Check inherited signature arity + if param_names.length != msignature.arity then + var node: ANode + if nsig != null then node = nsig else node = self + modelbuilder.error(node, "Redef error: {mpropdef} redefines {mpropdef.mproperty.intro} with {param_names.length} parameter(s), {msignature.arity} expected. Signature is {mpropdef}{msignature}") + return + end + else if mpropdef.mproperty.is_init then + # FIXME UGLY: inherit signature from a super-constructor + for msupertype in nclassdef.mclassdef.supertypes do + msupertype = msupertype.anchor_to(mmodule, nclassdef.mclassdef.bound_mtype) + var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name) + if candidate != null then + if msignature == null then + msignature = candidate.intro.as(MMethodDef).msignature + end + end + end + end + + + # Inherit the signature + if msignature != null and param_names.length != param_types.length and param_names.length == msignature.arity and param_types.length == 0 then + # Parameters are untyped, thus inherit them + param_types = new Array[MType] + for mparameter in msignature.mparameters do + param_types.add(mparameter.mtype) + end + vararg_rank = msignature.vararg_rank + end + if msignature != null and ret_type == null then + ret_type = msignature.return_mtype + end + + if param_names.length != param_types.length then + # Some parameters are typed, other parameters are not typed. + modelbuilder.error(nsig.n_params[param_types.length], "Error: Untyped parameter `{param_names[param_types.length]}'.") + return + end + + var mparameters = new Array[MParameter] + for i in [0..param_names.length[ do + var mparameter = new MParameter(param_names[i], param_types[i], i == vararg_rank) + mparameters.add(mparameter) + end + + msignature = new MSignature(mparameters, ret_type) + mpropdef.msignature = msignature + + if nsig != null then + for nclosure in nsig.n_closure_decls do + var clos_signature = nclosure.n_signature.build_signature(modelbuilder, nclassdef) + if clos_signature == null then return + var mparameter = new MParameter(nclosure.n_id.text, clos_signature, false) + msignature.mclosures.add(mparameter) + end + end + + end + + redef fun check_signature(modelbuilder, nclassdef) + do + var mpropdef = self.mpropdef + if mpropdef == null then return # Error thus skiped + var mmodule = mpropdef.mclassdef.mmodule + var nsig = self.n_signature + var mysignature = self.mpropdef.msignature + if mysignature == null then return # Error thus skiped + + # Lookup for signature in the precursor + # FIXME all precursors should be considered + if not mpropdef.is_intro then + var msignature = mpropdef.mproperty.intro.msignature + if msignature == null then return + + var precursor_ret_type = msignature.return_mtype + var ret_type = mysignature.return_mtype + if ret_type != null and precursor_ret_type == null then + modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") + return + end + + if mysignature.arity > 0 then + # Check parameters types + for i in [0..mysignature.arity[ do + var myt = mysignature.mparameters[i].mtype + var prt = msignature.mparameters[i].mtype + if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and + not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then + modelbuilder.error(nsig.n_params[i], "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") + end + end + end + if precursor_ret_type != null then + if ret_type == null then + # Inherit the return type + ret_type = precursor_ret_type + else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then + modelbuilder.error(nsig.n_type.as(not null), "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") + end + end + end + end +end + +redef class AAttrPropdef + # The associated MAttributeDef once build by a `ModelBuilder' + var mpropdef: nullable MAttributeDef + # The associated getter (read accessor) if any + var mreadpropdef: nullable MMethodDef + # The associated setter (write accessor) if any + var mwritepropdef: nullable MMethodDef + redef fun build_property(modelbuilder, nclassdef) + do + var mclassdef = nclassdef.mclassdef.as(not null) + var mclass = mclassdef.mclass + + var name: String + if self.n_id != null then + name = self.n_id.text + else + name = self.n_id2.text + end + + if mclass.kind == interface_kind or mclassdef.mclass.kind == enum_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the interface {mclass}.") + else if mclass.kind == enum_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the enum class {mclass}.") + else if mclass.kind == extern_kind then + modelbuilder.error(self, "Error: Attempt to define attribute {name} in the extern class {mclass}.") + end + + var nid = self.n_id + if nid != null then + # Old attribute style + var mprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, name) + if mprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) + mprop = new MAttribute(mclassdef, name, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return + else + assert mprop isa MAttribute + check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) + if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return + end + nclassdef.mprop2npropdef[mprop] = self + + var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) + self.mpropdef = mpropdef + modelbuilder.mpropdef2npropdef[mpropdef] = self + + var nreadable = self.n_readable + if nreadable != null then + var readname = name.substring_from(1) + var mreadprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, readname).as(nullable MMethod) + if mreadprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility) + mreadprop = new MMethod(mclassdef, readname, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return + else + if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return + check_redef_property_visibility(modelbuilder, nclassdef, nreadable.n_visibility, mreadprop) + end + nclassdef.mprop2npropdef[mreadprop] = self + + var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) + self.mreadpropdef = mreadpropdef + modelbuilder.mpropdef2npropdef[mreadpropdef] = self + end + + var nwritable = self.n_writable + if nwritable != null then + var writename = name.substring_from(1) + "=" + var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid, mclassdef, writename).as(nullable MMethod) + if mwriteprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility) + mwriteprop = new MMethod(mclassdef, writename, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return + else + if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return + check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop) + end + nclassdef.mprop2npropdef[mwriteprop] = self + + var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) + self.mwritepropdef = mwritepropdef + modelbuilder.mpropdef2npropdef[mwritepropdef] = self + end + else + # New attribute style + var nid2 = self.n_id2.as(not null) + var mprop = new MAttribute(mclassdef, "@" + name, none_visibility) + var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) + self.mpropdef = mpropdef + modelbuilder.mpropdef2npropdef[mpropdef] = self + + var readname = name + var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod) + if mreadprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) + mreadprop = new MMethod(mclassdef, readname, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return + else + if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return + check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mreadprop) + end + nclassdef.mprop2npropdef[mreadprop] = self + + var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) + self.mreadpropdef = mreadpropdef + modelbuilder.mpropdef2npropdef[mreadpropdef] = self + + var writename = name + "=" + var nwritable = self.n_writable + var mwriteprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, writename).as(nullable MMethod) + var nwkwredef: nullable Token = null + if nwritable != null then nwkwredef = nwritable.n_kwredef + if mwriteprop == null then + var mvisibility + if nwritable != null then + mvisibility = new_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility) + else + mvisibility = private_visibility + end + mwriteprop = new MMethod(mclassdef, writename, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return + else + if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return + if nwritable != null then + check_redef_property_visibility(modelbuilder, nclassdef, nwritable.n_visibility, mwriteprop) + end + end + nclassdef.mprop2npropdef[mwriteprop] = self + + var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) + self.mwritepropdef = mwritepropdef + modelbuilder.mpropdef2npropdef[mwritepropdef] = self + end + end + + redef fun build_signature(modelbuilder, nclassdef) + do + var mpropdef = self.mpropdef + if mpropdef == null then return # Error thus skiped + var mmodule = mpropdef.mclassdef.mmodule + var mtype: nullable MType = null + + var ntype = self.n_type + if ntype != null then + mtype = modelbuilder.resolve_mtype(nclassdef, ntype) + if mtype == null then return + end + + if mtype == null then + var nexpr = self.n_expr + if nexpr != null then + if nexpr isa ANewExpr then + mtype = modelbuilder.resolve_mtype(nclassdef, nexpr.n_type) + else if nexpr isa AIntExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Int") + if cla != null then mtype = cla.mclass_type + else if nexpr isa AFloatExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Float") + if cla != null then mtype = cla.mclass_type + else if nexpr isa ACharExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Char") + if cla != null then mtype = cla.mclass_type + else if nexpr isa ABoolExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "Bool") + if cla != null then mtype = cla.mclass_type + else if nexpr isa ASuperstringExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") + if cla != null then mtype = cla.mclass_type + else if nexpr isa AStringFormExpr then + var cla = modelbuilder.try_get_mclass_by_name(nexpr, mmodule, "String") + if cla != null then mtype = cla.mclass_type + else + modelbuilder.error(self, "Error: Untyped attribute {mpropdef}. Implicit typing allowed only for literals and new.") + end + + else + modelbuilder.error(self, "Error: Untyped attribute {mpropdef}") + end + end + + if mtype == null then return + + mpropdef.static_mtype = mtype + + var mreadpropdef = self.mreadpropdef + if mreadpropdef != null then + var msignature = new MSignature(new Array[MParameter], mtype) + mreadpropdef.msignature = msignature + end + + var msritepropdef = self.mwritepropdef + if mwritepropdef != null then + var name: String + if n_id != null then + name = n_id.text.substring_from(1) + else + name = n_id2.text + end + var mparameter = new MParameter(name, mtype, false) + var msignature = new MSignature([mparameter], null) + mwritepropdef.msignature = msignature + end + end + + redef fun check_signature(modelbuilder, nclassdef) + do + var mpropdef = self.mpropdef + if mpropdef == null then return # Error thus skiped + var mmodule = mpropdef.mclassdef.mmodule + var ntype = self.n_type + var mtype = self.mpropdef.static_mtype + if mtype == null then return # Error thus skiped + + # Lookup for signature in the precursor + # FIXME all precursors should be considered + if not mpropdef.is_intro then + var precursor_type = mpropdef.mproperty.intro.static_mtype + if precursor_type == null then return + + if mtype != precursor_type then + modelbuilder.error(ntype.as(not null), "Redef Error: Wrong static type. found {mtype}, expected {precursor_type}.") + return + end + end + + # Check getter and setter + var meth = self.mreadpropdef + if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth) + meth = self.mwritepropdef + if meth != null then self.check_method_signature(modelbuilder, nclassdef, meth) + end + + private fun check_method_signature(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MMethodDef) + do + var mmodule = mpropdef.mclassdef.mmodule + var nsig = self.n_type + var mysignature = mpropdef.msignature + if mysignature == null then return # Error thus skiped + + # Lookup for signature in the precursor + # FIXME all precursors should be considered + if not mpropdef.is_intro then + var msignature = mpropdef.mproperty.intro.msignature + if msignature == null then return + + if mysignature.arity != msignature.arity then + var node: ANode + if nsig != null then node = nsig else node = self + modelbuilder.error(node, "Redef Error: {mysignature.arity} parameters found, {msignature.arity} expected. Signature is {mpropdef}{msignature}") + return + end + var precursor_ret_type = msignature.return_mtype + var ret_type = mysignature.return_mtype + if ret_type != null and precursor_ret_type == null then + var node: ANode + if nsig != null then node = nsig else node = self + modelbuilder.error(node, "Redef Error: {mpropdef.mproperty} is a procedure, not a function.") + return + end + + if mysignature.arity > 0 then + # Check parameters types + for i in [0..mysignature.arity[ do + var myt = mysignature.mparameters[i].mtype + var prt = msignature.mparameters[i].mtype + if not myt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, prt) and + not prt.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, myt) then + var node: ANode + if nsig != null then node = nsig else node = self + modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt}.") + end + end + end + if precursor_ret_type != null then + if ret_type == null then + # Inherit the return type + ret_type = precursor_ret_type + else if not ret_type.is_subtype(mmodule, nclassdef.mclassdef.bound_mtype, precursor_ret_type) then + var node: ANode + if nsig != null then node = nsig else node = self + modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type}.") + end + end + end + end +end + +redef class ATypePropdef + # The associated MVirtualTypeDef once build by a `ModelBuilder' + var mpropdef: nullable MVirtualTypeDef + redef fun build_property(modelbuilder, nclassdef) + do + var mclassdef = nclassdef.mclassdef.as(not null) + var name = self.n_id.text + var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name) + if mprop == null then + var mvisibility = new_property_visibility(modelbuilder, nclassdef, self.n_visibility) + mprop = new MVirtualTypeProp(mclassdef, name, mvisibility) + if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return + else + if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return + assert mprop isa MVirtualTypeProp + check_redef_property_visibility(modelbuilder, nclassdef, self.n_visibility, mprop) + end + nclassdef.mprop2npropdef[mprop] = self + + var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location) + self.mpropdef = mpropdef + end + + redef fun build_signature(modelbuilder, nclassdef) + do + var mpropdef = self.mpropdef + if mpropdef == null then return # Error thus skiped + var mmodule = mpropdef.mclassdef.mmodule + var mtype: nullable MType = null + + var ntype = self.n_type + mtype = modelbuilder.resolve_mtype(nclassdef, ntype) + if mtype == null then return + + mpropdef.bound = mtype + # print "{mpropdef}: {mtype}" + end + + redef fun check_signature(modelbuilder, nclassdef) + do + var bound = self.mpropdef.bound + + # Fast case: the bound is not a formal type + if not bound isa MVirtualType then return + + var mmodule = nclassdef.mclassdef.mmodule + var anchor = nclassdef.mclassdef.bound_mtype + + # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type + var seen = [self.mpropdef.mproperty.mvirtualtype] + loop + if seen.has(bound) then + seen.add(bound) + modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}") + return + end + seen.add(bound) + var next = bound.lookup_bound(mmodule, anchor) + if not next isa MVirtualType then return + bound = next + end + end +end diff --git a/src/typing.nit b/src/typing.nit index db1f656..bf5cc99 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -19,7 +19,7 @@ module typing import flow -import modelbuilder +import modelize_property import phase import local_var_init -- 1.7.9.5