modelbuilder: mclass2nclassdef registers the first occurrence of a class
[nit.git] / src / modelize_class.nit
index 6bdf514..5bc395b 100644 (file)
@@ -33,7 +33,7 @@ private class ModelizeClassPhase
 end
 
 redef class ModelBuilder
-       # Visit the AST and create the MClass objects
+       # Visit the AST and create the `MClass` objects
        private fun build_a_mclass(nmodule: AModule, nclassdef: AClassdef)
        do
                var mmodule = nmodule.mmodule.as(not null)
@@ -54,6 +54,9 @@ redef class ModelBuilder
                        if mvisibility == protected_visibility then
                                error(nvisibility, "Error: only properties can be protected.")
                                return
+                       else if mvisibility == intrude_visibility then
+                               error(nvisibility, "Error: intrude is not a legal visibility for classes.")
+                               return
                        end
                else if nclassdef isa ATopClassdef then
                        name = "Object"
@@ -73,6 +76,10 @@ redef class ModelBuilder
 
                var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
                if mclass == null then
+                       if nclassdef isa AStdClassdef and nclassdef.n_kwredef != null then
+                               error(nclassdef, "Redef error: No imported class {name} to refine.")
+                               return
+                       end
                        mclass = new MClass(mmodule, name, arity, mkind, mvisibility)
                        #print "new class {mclass}"
                else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then
@@ -90,10 +97,12 @@ redef class ModelBuilder
                        error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
                end
                nclassdef.mclass = mclass
-               nmodule.mclass2nclassdef[mclass] = nclassdef
+               if not nmodule.mclass2nclassdef.has_key(mclass) then
+                       nmodule.mclass2nclassdef[mclass] = nclassdef
+               end
        end
 
-       # Visit the AST and create the MClassDef objects
+       # Visit the AST and create the `MClassDef` objects
        private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
        do
                var mmodule = nmodule.mmodule.as(not null)
@@ -113,7 +122,12 @@ redef class ModelBuilder
                                        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, "Warning: lowercase in the formal parameter type {ptname}")
+                                       break
+                               end
                                names.add(ptname)
+                               nfd.mtype = mclass.mclass_type.arguments[i].as(MParameterType)
                        end
 
                        # Revolve bound for formal parameter names
@@ -121,20 +135,25 @@ redef class ModelBuilder
                                var nfd = nclassdef.n_formaldefs[i]
                                var nfdt = nfd.n_type
                                if nfdt != null then
-                                       var bound = resolve_mtype_unchecked(nclassdef, nfdt, false)
+                                       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")
                                        else
                                                bounds.add(bound)
+                                               nfd.bound = bound
                                        end
                                else if mclass.mclassdefs.is_empty then
                                        # No bound, then implicitely bound by nullable Object
-                                       bounds.add(objectclass.mclass_type.as_nullable)
+                                       var bound = objectclass.mclass_type.as_nullable
+                                       bounds.add(bound)
+                                       nfd.bound = bound
                                else
                                        # Inherit the bound
-                                       bounds.add(mclass.intro.bound_mtype.arguments[i])
+                                       var bound = mclass.intro.bound_mtype.arguments[i]
+                                       bounds.add(bound)
+                                       nfd.bound = bound
                                end
                        end
                end
@@ -144,6 +163,15 @@ redef class ModelBuilder
                nclassdef.mclassdef = mclassdef
                self.mclassdef2nclassdef[mclassdef] = nclassdef
 
+               if nclassdef isa AStdClassdef then
+                       var ndoc = nclassdef.n_doc
+                       if ndoc != null then
+                               var mdoc = ndoc.to_mdoc
+                               mclassdef.mdoc = mdoc
+                               mdoc.original_mentity = mclassdef
+                       end
+               end
+
                if mclassdef.is_intro then
                        self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
                else
@@ -151,32 +179,50 @@ redef class ModelBuilder
                end
        end
 
-       # Visit the AST and set the super-types of the MClassdef objects
+       # 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 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)
 
+               # Do we need to specify Object as a super class?
                var specobject = true
+
+               # Do we need to specify Pointer as a super class? (is only valid
+               # if `nclassdef` is an extern class)
+               var specpointer = true
+
                var supertypes = new Array[MClassType]
                if nclassdef isa AStdClassdef then
                        for nsc in nclassdef.n_superclasses do
                                specobject = false
                                var ntype = nsc.n_type
-                               var mtype = resolve_mtype_unchecked(nclassdef, ntype, false)
+                               var mtype = resolve_mtype_unchecked(mmodule, mclassdef, ntype, false)
                                if mtype == null then continue # Skip because of error
                                if not mtype isa MClassType then
                                        error(ntype, "Error: supertypes cannot be a formal type")
                                        return
                                end
+                               if not mclass.kind.can_specialize(mtype.mclass.kind) then
+                                       error(ntype, "Error: {mclass.kind} {mclass} cannot specialize {mtype.mclass.kind} {mtype.mclass}")
+                               end
                                supertypes.add mtype
                                #print "new super : {mclass} < {mtype}"
+                               if mtype.mclass.kind == extern_kind then specpointer = false
                        end
                end
-               if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
-                       supertypes.add objectclass.mclass_type
+
+               if mclassdef.is_intro and objectclass != null then
+                       if mclass.kind == extern_kind and mclass.name != "Pointer" then
+                               # it is an extern class, but not a Pointer
+                               if specpointer then supertypes.add pointerclass.mclass_type
+                       else if specobject and mclass.name != "Object" then
+                               # it is a standard class without super class (but is not Object)
+                               supertypes.add objectclass.mclass_type
+                       end
                end
 
                mclassdef.set_supertypes(supertypes)
@@ -198,8 +244,8 @@ redef class ModelBuilder
                end
        end
 
-       # Build the classes of the module `nmodule'.
-       # REQUIRE: classes of imported modules are already build. (let `phase' do the job)
+       # Build the classes of the module `nmodule`.
+       # REQUIRE: classes of imported modules are already build. (let `phase` do the job)
        private fun build_classes(nmodule: AModule)
        do
                var errcount = toolcontext.error_count
@@ -209,6 +255,7 @@ redef class ModelBuilder
                var mmodule = nmodule.mmodule.as(not null)
                for imp in mmodule.in_importation.direct_greaters do
 
+                       if not mmodule2nmodule.has_key(imp) then continue
                        build_classes(mmodule2nmodule[imp])
                end
 
@@ -236,8 +283,7 @@ redef class ModelBuilder
                if errcount != toolcontext.error_count then return
 
                # Create the mclassdef hierarchy
-               for nclassdef in nmodule.n_classdefs do
-                       var mclassdef = nclassdef.mclassdef.as(not null)
+               for mclassdef in mmodule.mclassdefs do
                        mclassdef.add_in_hierarchy
                end
 
@@ -253,11 +299,12 @@ redef class ModelBuilder
                # Check unchecked ntypes
                for nclassdef in nmodule.n_classdefs do
                        if nclassdef isa AStdClassdef then
+                               var mclassdef = nclassdef.mclassdef
                                # check bound of formal parameter
                                for nfd in  nclassdef.n_formaldefs do
                                        var nfdt = nfd.n_type
                                        if nfdt != null and nfdt.mtype != null then
-                                               var bound = resolve_mtype(nclassdef, nfdt)
+                                               var bound = resolve_mtype(mmodule, mclassdef, nfdt)
                                                if bound == null then return # Forward error
                                        end
                                end
@@ -265,33 +312,85 @@ redef class ModelBuilder
                                for nsc in nclassdef.n_superclasses do
                                        var ntype = nsc.n_type
                                        if ntype.mtype != null then
-                                               var mtype = resolve_mtype(nclassdef, ntype)
+                                               var mtype = resolve_mtype(mmodule, mclassdef, ntype)
                                                if mtype == null then return # Forward error
                                        end
                                end
                        end
+               end
+
+               if errcount != toolcontext.error_count then return
 
+               # Check clash of ancestors
+               for nclassdef in nmodule.n_classdefs do
+                       var mclassdef = nclassdef.mclassdef.as(not null)
+                       var superclasses = new HashMap[MClass, MClassType]
+                       for scd in mclassdef.in_hierarchy.greaters do
+                               for st in scd.supertypes do
+                                       if not superclasses.has_key(st.mclass) then
+                                               superclasses[st.mclass] = st
+                                       else if superclasses[st.mclass] != st then
+                                               var st1 = superclasses[st.mclass].resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+                                               var st2 = st.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+                                               if st1 != st2 then
+                                                       error(nclassdef, "Error: Incompatibles ancestors for {mclassdef.mclass}: {st1}, {st2}")
+                                               end
+                                       end
+                               end
+                       end
                end
 
+               if errcount != toolcontext.error_count then return
+
                # TODO: Check that the super-class is not intrusive
 
-               # TODO: Check that the super-class is not already known (by transitivity)
+               # 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)
+
+                       # Get the direct superclasses
+                       # Since we are a mclassdef, just look at the mclassdef hierarchy
+                       var parents = new Array[MClass]
+                       for sup in mclassdef.in_hierarchy.direct_greaters do
+                               parents.add(sup.mclass)
+                       end
+
+                       # Used to track duplicates of superclasses
+                       var seen_parents = new ArrayMap[MClass, AType]
+
+                       # The Object class
+                       var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+
+                       # Check each declared superclass to see if it belong to the direct superclass
+                       for nsc in nclassdef.n_superclasses do
+                               var ntype = nsc.n_type
+                               var mtype = ntype.mtype
+                               if mtype == null then continue
+                               assert mtype isa MClassType
+                               var sc = mtype.mclass
+                               if not parents.has(sc) or sc == objectclass then
+                                       warning(ntype, "Warning: superfluous super-class {mtype} in class {mclassdef.mclass}.")
+                               else if not seen_parents.has_key(sc) then
+                                       seen_parents[sc] = ntype
+                               else
+                                       warning(ntype, "Warning: duplicated super-class {mtype} in class {mclassdef.mclass}.")
+                               end
+                       end
+               end
        end
 
        # Register the nclassdef associated to each mclassdef
-       # FIXME: why not refine the MClassDef class with a nullable attribute?
+       # 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'.
-       # `classdef' is the context where the call is made (used to understand formal types)
-       # The mmodule used as context is `nclassdef.mmodule'
-       # In case of problem, an error is displayed on `ntype' and null is returned.
+       # 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(nclassdef: AClassdef, ntype: AType, with_virtual: Bool): nullable MType
+       fun resolve_mtype_unchecked(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType, with_virtual: Bool): nullable MType
        do
                var name = ntype.n_id.text
-               var mclassdef = nclassdef.mclassdef
-               var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
                var res: MType
 
                # Check virtual type
@@ -346,7 +445,7 @@ redef class ModelBuilder
                        else
                                var mtypes = new Array[MType]
                                for nt in ntype.n_types do
-                                       var mt = resolve_mtype_unchecked(nclassdef, nt, with_virtual)
+                                       var mt = resolve_mtype_unchecked(mmodule, mclassdef, nt, with_virtual)
                                        if mt == null then return null # Forward error
                                        mtypes.add(mt)
                                end
@@ -362,28 +461,27 @@ redef class ModelBuilder
                return null
        end
 
-       # Return the static type associated to the node `ntype'.
-       # `classdef' is the context where the call is made (used to understand formal types)
-       # The mmodule used as context is `nclassdef.mmodule'
-       # In case of problem, an error is displayed on `ntype' and null is returned.
+       # 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(nclassdef: AClassdef, ntype: AType): nullable MType
+       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(nclassdef, ntype, true)
+               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 mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
-                       var mclassdef = nclassdef.mclassdef
                        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(nclassdef, nt)
+                               var mt = resolve_mtype(mmodule, mclassdef, nt)
                                if mt == null then return null # forward error
-                               if not mt.is_subtype(mmodule, mclassdef.bound_mtype, bound) then
+                               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
@@ -398,15 +496,15 @@ end
 redef class AModule
        # Flag that indicate if the class building is already completed
        var build_classes_is_done: Bool = false
-       # What is the AClassdef associated to a MClass?
+       # What is the AClassdef associated to a `MClass`?
        # Used to check multiple definition of a class.
        var mclass2nclassdef: Map[MClass, AClassdef] = new HashMap[MClass, AClassdef]
 end
 
 redef class AClassdef
-       # The associated MClass once build by a `ModelBuilder'
+       # The associated MClass once build by a `ModelBuilder`
        var mclass: nullable MClass
-       # The associated MClassDef once build by a `ModelBuilder'
+       # The associated MClassDef once build by a `ModelBuilder`
        var mclassdef: nullable MClassDef
 end
 
@@ -430,6 +528,14 @@ redef class AExternClasskind
        redef fun mkind do return extern_kind
 end
 
+redef class AFormaldef
+       # The associated parameter type
+       var mtype: nullable MParameterType = null
+
+       # The associated bound
+       var bound: nullable MType = null
+end
+
 redef class AType
        # The mtype associated to the node
        var mtype: nullable MType = null