X-Git-Url: http://nitlanguage.org diff --git a/src/modelize/modelize_class.nit b/src/modelize/modelize_class.nit index 8bc6ef2..632db85 100644 --- a/src/modelize/modelize_class.nit +++ b/src/modelize/modelize_class.nit @@ -20,6 +20,7 @@ module modelize_class import modelbuilder redef class ToolContext + # Run `AModule::build_classes` on each module var modelize_class_phase: Phase = new ModelizeClassPhase(self, null) end @@ -44,6 +45,7 @@ redef class ModelBuilder var nvisibility: nullable AVisibility var mvisibility: nullable MVisibility var arity = 0 + var names = new Array[String] if nclassdef isa AStdClassdef then name = nclassdef.n_id.text nkind = nclassdef.n_classkind @@ -58,6 +60,21 @@ redef class ModelBuilder error(nvisibility, "Error: intrude is not a legal visibility for classes.") return end + # Collect formal parameter names + for i in [0..arity[ do + var nfd = nclassdef.n_formaldefs[i] + var ptname = nfd.n_id.text + if names.has(ptname) then + error(nfd, "Error: A formal parameter type `{ptname}' already exists") + return + end + for c in ptname.chars do if c >= 'a' and c<= 'z' then + warning(nfd, "formal-type-name", "Warning: lowercase in the formal parameter type {ptname}") + break + end + names.add(ptname) + end + else if nclassdef isa ATopClassdef then name = "Object" nkind = null @@ -80,7 +97,21 @@ redef class ModelBuilder error(nclassdef, "Redef error: No imported class {name} to refine.") return end - mclass = new MClass(mmodule, name, arity, mkind, mvisibility) + + # Check for conflicting class full-names in the project + if mmodule.mgroup != null and mvisibility >= protected_visibility then + var mclasses = model.get_mclasses_by_name(name) + if mclasses != null then for other in mclasses do + if other.intro_mmodule.mgroup != null and other.intro_mmodule.mgroup.mproject == mmodule.mgroup.mproject then + # Skip classes that are buggy + if other.try_intro == null then continue + error(nclassdef, "Error: A class named `{other.full_name}` is already defined in module `{other.intro_mmodule}` at {other.intro.location}.") + break + end + end + end + + mclass = new MClass(mmodule, name, names, mkind, mvisibility) #print "new class {mclass}" else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then error(nclassdef, "Error: A class {name} is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.") @@ -88,7 +119,7 @@ redef class ModelBuilder else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then error(nclassdef, "Redef error: {name} is an imported class. Add the redef keyword to refine it.") return - else if mclass.arity != arity then + else if arity != 0 and mclass.arity != arity then error(nclassdef, "Redef error: Formal parameter arity missmatch; got {arity}, expected {mclass.arity}.") return else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then @@ -121,35 +152,29 @@ redef class ModelBuilder return end - var names = new Array[String] var bounds = new Array[MType] if nclassdef isa AStdClassdef and mclass.arity > 0 then - # Collect formal parameter names + # Revolve bound for formal parameters for i in [0..mclass.arity[ do - var nfd = nclassdef.n_formaldefs[i] - var ptname = nfd.n_id.text - if names.has(ptname) then - error(nfd, "Error: A formal parameter type `{ptname}' already exists") - return - end - for c in ptname.chars do if c >= 'a' and c<= 'z' then - warning(nfd, "formal-type-name", "Warning: lowercase in the formal parameter type {ptname}") - break + if nclassdef.n_formaldefs.is_empty then + # Inherit the bound + var bound = mclass.intro.bound_mtype.arguments[i] + bounds.add(bound) + continue end - names.add(ptname) - nfd.mtype = mclass.mclass_type.arguments[i].as(MParameterType) - end - # Revolve bound for formal parameter names - for i in [0..mclass.arity[ do var nfd = nclassdef.n_formaldefs[i] + var pname = mclass.mparameters[i].name + if nfd.n_id.text != pname then + error(nfd.n_id, "Error: Formal parameter type #{i} `{nfd.n_id.text}` must be named `{pname}' as in the original definition in module `{mclass.intro.mmodule}`.") + end var nfdt = nfd.n_type if nfdt != null then var bound = resolve_mtype_unchecked(mmodule, null, nfdt, false) if bound == null then return # Forward error if bound.need_anchor then # No F-bounds! - error(nfd, "Error: Formal parameter type `{names[i]}' bounded with a formal parameter type") + error(nfd, "Error: Formal parameter type `{pname}' bounded with a formal parameter type") else bounds.add(bound) nfd.bound = bound @@ -158,6 +183,10 @@ redef class ModelBuilder warning(nfdt, "useless-bound", "Warning: Useless formal parameter type since `{bound}` cannnot have subclasses.") end else if mclass.mclassdefs.is_empty then + if objectclass == null then + error(nfd, "Error: Formal parameter type `{pname}' unbounded but no Object class exist.") + return + end # No bound, then implicitely bound by nullable Object var bound = objectclass.mclass_type.as_nullable bounds.add(bound) @@ -172,7 +201,7 @@ redef class ModelBuilder end var bound_mtype = mclass.get_mtype(bounds) - var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names) + var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location) nclassdef.mclassdef = mclassdef self.mclassdef2nclassdef[mclassdef] = nclassdef @@ -197,11 +226,14 @@ redef class ModelBuilder # Visit the AST and set the super-types of the `MClassDef` objects private fun collect_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef) do - var mmodule = nmodule.mmodule.as(not null) + var mmodule = nmodule.mmodule + if mmodule == null then return var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object") var pointerclass = try_get_mclass_by_name(nmodule, mmodule, "Pointer") - var mclass = nclassdef.mclass.as(not null) - var mclassdef = nclassdef.mclassdef.as(not null) + var mclass = nclassdef.mclass + if mclass == null then return + var mclassdef = nclassdef.mclassdef + if mclassdef == null then return # Do we need to specify Object as a super class? var specobject = true @@ -247,10 +279,12 @@ redef class ModelBuilder # Check the validity of the specialization heirarchy private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef) do - var mmodule = nmodule.mmodule.as(not null) - var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object") - var mclass = nclassdef.mclass.as(not null) - var mclassdef = nclassdef.mclassdef.as(not null) + var mmodule = nmodule.mmodule + if mmodule == null then return + var mclass = nclassdef.mclass + if mclass == null then return + var mclassdef = nclassdef.mclassdef + if mclassdef == null then return for s in mclassdef.supertypes do if s.is_subtype(mmodule, mclassdef.bound_mtype, mclassdef.bound_mtype) then @@ -267,11 +301,11 @@ redef class ModelBuilder # Force building recursively if nmodule.build_classes_is_done then return nmodule.build_classes_is_done = true - var mmodule = nmodule.mmodule.as(not null) + var mmodule = nmodule.mmodule + if mmodule == null then return for imp in mmodule.in_importation.direct_greaters do - - if not mmodule2nmodule.has_key(imp) then continue - build_classes(mmodule2nmodule[imp]) + var nimp = mmodule2node(imp) + if nimp != null then build_classes(nimp) end if errcount != toolcontext.error_count then return @@ -338,7 +372,8 @@ redef class ModelBuilder # Check clash of ancestors for nclassdef in nmodule.n_classdefs do - var mclassdef = nclassdef.mclassdef.as(not null) + var mclassdef = nclassdef.mclassdef + if mclassdef == null then continue var superclasses = new HashMap[MClass, MClassType] for scd in mclassdef.in_hierarchy.greaters do for st in scd.supertypes do @@ -362,7 +397,8 @@ redef class ModelBuilder # Check that the superclasses are not already known (by transitivity) for nclassdef in nmodule.n_classdefs do if not nclassdef isa AStdClassdef then continue - var mclassdef = nclassdef.mclassdef.as(not null) + var mclassdef = nclassdef.mclassdef + if mclassdef == null then continue # Get the direct superclasses # Since we are a mclassdef, just look at the mclassdef hierarchy @@ -398,117 +434,8 @@ redef class ModelBuilder end end - # 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] - - # Return the static type associated to the node `ntype`. - # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) - # In case of problem, an error is displayed on `ntype` and null is returned. - # FIXME: the name "resolve_mtype" is awful - fun resolve_mtype_unchecked(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType, with_virtual: Bool): nullable MType - do - var name = ntype.n_id.text - var res: MType - - # Check virtual type - if mclassdef != null and with_virtual then - var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp) - if prop != null then - if not ntype.n_types.is_empty then - error(ntype, "Type error: formal type {name} cannot have formal parameters.") - end - res = prop.mvirtualtype - if ntype.n_kwnullable != null then res = res.as_nullable - ntype.mtype = res - return res - end - end - - # Check parameter type - if mclassdef != null and mclassdef.parameter_names.has(name) then - if not ntype.n_types.is_empty then - error(ntype, "Type error: formal type {name} cannot have formal parameters.") - end - for i in [0..mclassdef.parameter_names.length[ do - if mclassdef.parameter_names[i] == name then - res = mclassdef.mclass.mclass_type.arguments[i] - if ntype.n_kwnullable != null then res = res.as_nullable - ntype.mtype = res - return res - end - end - abort - end - - # Check class - var mclass = try_get_mclass_by_name(ntype, mmodule, name) - if mclass != null then - var arity = ntype.n_types.length - if arity != mclass.arity then - if arity == 0 then - error(ntype, "Type error: '{name}' is a generic class.") - else if mclass.arity == 0 then - error(ntype, "Type error: '{name}' is not a generic class.") - else - error(ntype, "Type error: '{name}' has {mclass.arity} parameters ({arity} are provided).") - end - return null - end - if arity == 0 then - res = mclass.mclass_type - if ntype.n_kwnullable != null then res = res.as_nullable - ntype.mtype = res - return res - else - var mtypes = new Array[MType] - for nt in ntype.n_types do - var mt = resolve_mtype_unchecked(mmodule, mclassdef, nt, with_virtual) - if mt == null then return null # Forward error - mtypes.add(mt) - end - res = mclass.get_mtype(mtypes) - if ntype.n_kwnullable != null then res = res.as_nullable - ntype.mtype = res - return res - end - end - - # If everything fail, then give up :( - error(ntype, "Type error: class {name} not found in module {mmodule}.") - return null - end - - # Return the static type associated to the node `ntype`. - # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) - # In case of problem, an error is displayed on `ntype` and null is returned. - # FIXME: the name "resolve_mtype" is awful - fun resolve_mtype(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType): nullable MType - do - var mtype = ntype.mtype - if mtype == null then mtype = resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) - if mtype == null then return null # Forward error - - if ntype.checked_mtype then return mtype - if mtype isa MGenericType then - var mclass = mtype.mclass - for i in [0..mclass.arity[ do - var bound = mclass.intro.bound_mtype.arguments[i] - var nt = ntype.n_types[i] - var mt = resolve_mtype(mmodule, mclassdef, nt) - if mt == null then return null # forward error - var anchor - if mclassdef != null then anchor = mclassdef.bound_mtype else anchor = null - if not mt.is_subtype(mmodule, anchor, bound) then - error(nt, "Type error: expected {bound}, got {mt}") - return null - end - end - end - ntype.checked_mtype = true - return mtype - end - + # Registration of the nclassdef associated to each mclassdef + private var mclassdef2nclassdef = new HashMap[MClassDef, AClassdef] end redef class AModule @@ -555,11 +482,3 @@ redef class AFormaldef # The associated bound var bound: nullable MType = null end - -redef class AType - # The mtype associated to the node - var mtype: nullable MType = null - - # Is the mtype a valid one? - var checked_mtype: Bool = false -end