Merge: subset: Add grammar and kind
[nit.git] / src / modelize / modelize_class.nit
index 553263b..853d8ff 100644 (file)
@@ -149,7 +149,6 @@ redef class ModelBuilder
        private fun build_a_mclassdef(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
                if mclass == null then return # Skip error
 
@@ -161,8 +160,45 @@ redef class ModelBuilder
                        return
                end
 
+               var bound_mtype = build_a_bound_mtype(nmodule, nclassdef)
+               if bound_mtype == null then return
+               var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location)
+               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
+                       else if mclassdef.is_intro and mclass.visibility >= public_visibility then
+                               advice(nclassdef, "missing-doc", "Documentation warning: Undocumented public class `{mclass}`")
+                       end
+               end
+
+               if mclassdef.is_intro then
+                       self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
+               else
+                       self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
+               end
+       end
+
+       # Determine the type parameter bounds for `nclassdef`.
+       #
+       # In case of error, return `null`.
+       #
+       # REQUIRE: `nmodule.mmodule != null`
+       # REQUIRE: `nclassdef.mclass != null`
+       private fun build_a_bound_mtype(nmodule: AModule, nclassdef: AClassdef): nullable MClassType
+       do
+               var mmodule = nmodule.mmodule.as(not null)
+               var mclass = nclassdef.mclass.as(not null)
+
                var bounds = new Array[MType]
                if nclassdef isa AStdClassdef and mclass.arity > 0 then
+                       var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+
                        # Revolve bound for formal parameters
                        for i in [0..mclass.arity[ do
                                if nclassdef.n_formaldefs.is_empty then
@@ -180,7 +216,7 @@ redef class ModelBuilder
                                var nfdt = nfd.n_type
                                if nfdt != null then
                                        var bound = resolve_mtype3_unchecked(mmodule, null, null, nfdt, false)
-                                       if bound == null then return # Forward error
+                                       if bound == null then return null # Forward error
                                        if bound.need_anchor then
                                                # No F-bounds!
                                                error(nfd, "Error: formal parameter type `{pname}` bounded with a formal parameter type.")
@@ -191,7 +227,7 @@ redef class ModelBuilder
                                else if mclass.mclassdefs.is_empty then
                                        if objectclass == null then
                                                error(nfd, "Error: formal parameter type `{pname}` unbounded but no `Object` class exists.")
-                                               return
+                                               return null
                                        end
                                        # No bound, then implicitely bound by nullable Object
                                        var bound = objectclass.mclass_type.as_nullable
@@ -206,41 +242,39 @@ redef class ModelBuilder
                        end
                end
 
-               var bound_mtype = mclass.get_mtype(bounds)
-               var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location)
-               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
-                       else if mclassdef.is_intro and mclass.visibility >= public_visibility then
-                               advice(nclassdef, "missing-doc", "Documentation warning: Undocumented public class `{mclass}`")
-                       end
-               end
-
-               if mclassdef.is_intro then
-                       self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
-               else
-                       self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
-               end
+               return mclass.get_mtype(bounds)
        end
 
        # Visit the AST and set the super-types of the `MClassDef` objects
-       private fun collect_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
+       private fun build_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
        do
                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
                if mclass == null then return
                var mclassdef = nclassdef.mclassdef
                if mclassdef == null then return
 
+               var supertypes = collect_supertypes(nmodule, nclassdef, mclassdef.is_intro)
+               mclassdef.set_supertypes(supertypes)
+               if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
+       end
+
+       # List the supertypes specified or implied by `nclassdef`.
+       #
+       # REQUIRE: `nmodule.mmodule != null`
+       # REQUIRE: `nclassdef.mclass != null`
+       private fun collect_supertypes(nmodule: AModule, nclassdef: AClassdef,
+                       is_intro: Bool): Array[MClassType]
+       do
+               var mmodule = nmodule.mmodule.as(not null)
+               var mclass = nclassdef.mclass.as(not null)
+               var name = mclass.name
+               var kind = mclass.kind
+
+               var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+               var pointerclass = try_get_mclass_by_name(nmodule, mmodule, "Pointer")
+
                # Do we need to specify Object as a super class?
                var specobject = true
 
@@ -253,42 +287,43 @@ redef class ModelBuilder
                        for nsc in nclassdef.n_superclasses do
                                specobject = false
                                var ntype = nsc.n_type
-                               var mtype = resolve_mtype_unchecked(mclassdef, ntype, false)
+                               var mtype = resolve_mtype3_unchecked(mmodule, mclass, null,
+                                               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
+                                       error(ntype, "Error: a supertype cannot be a formal type.")
+                                       continue
                                end
-                               if not mclass.kind.can_specialize(mtype.mclass.kind) then
-                                       error(ntype, "Error: {mclass.kind} `{mclass}` cannot specialize {mtype.mclass.kind} `{mtype.mclass}`.")
+                               var superclass = mtype.mclass
+                               var super_kind = superclass.kind
+                               if not kind.can_specialize(super_kind) then
+                                       error(ntype, "Error: {kind} `{mclass}` cannot specialize {super_kind} `{superclass}`.")
                                end
                                supertypes.add mtype
                                #print "new super : {mclass} < {mtype}"
-                               if mtype.mclass.kind == extern_kind then specpointer = false
+                               if super_kind == extern_kind then specpointer = false
                        end
                end
 
-               if mclassdef.is_intro and objectclass != null then
-                       if mclass.kind == extern_kind and mclass.name != "Pointer" then
+               if is_intro and objectclass != null then
+                       if kind == extern_kind and name != "Pointer" then
                                # it is an extern class, but not a Pointer
                                if pointerclass == null then
                                        error(nclassdef, "Error: `Pointer` must be defined first.")
-                                       return
+                                       return supertypes
                                end
                                if specpointer then supertypes.add pointerclass.mclass_type
                        else if specobject then
-                               if mclass.name != "Object" then
+                               if name != "Object" then
                                        # it is a standard class without super class (but is not Object)
                                        supertypes.add objectclass.mclass_type
-                               else if mclass.kind != interface_kind then
+                               else if kind != interface_kind then
                                        error(nclassdef, "Error: `Object` must be an {interface_kind}.")
-                                       return
                                end
                        end
                end
 
-               mclassdef.set_supertypes(supertypes)
-               if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
+               return supertypes
        end
 
        # Check the validity of the specialization heirarchy
@@ -344,7 +379,7 @@ redef class ModelBuilder
 
                # Create inheritance on all classdefs
                for nclassdef in nmodule.n_classdefs do
-                       self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
+                       self.build_a_mclassdef_inheritance(nmodule, nclassdef)
                end
 
                # Create the mclassdef hierarchy
@@ -435,8 +470,11 @@ redef class ModelBuilder
                        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
+
+                               # If the supertype is `null` or don’t refer to a class, we
+                               # already raised an error.
+                               if not mtype isa MClassType then continue
+
                                var sc = mtype.mclass
                                if not parents.has(sc) or sc == objectclass then
                                        # Skip the warning on generated code