private import metrics_base
import modelbuilder
+import modelize_class
# The job of this visitor is to resolve all types found
private class ATypeCounterVisitor
fun modelbuilder: ModelBuilder do return modelbuilder_real.as(not null)
private var modelbuilder_real: nullable ModelBuilder = null
- var modelize_class_phase: Phase = new ModelizeClassPhase(self, null)
-end
-
-private class ModelizeClassPhase
- super Phase
-
- redef fun process_nmodule(nmodule)
- do
- toolcontext.modelbuilder.build_classes(nmodule)
- end
end
# A model builder knows how to load nit source files and build the associated model
# All the loaded modules
var nmodules: Array[AModule] = new Array[AModule]
- # 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)
-
- var name: String
- var nkind: nullable AClasskind
- var mkind: MClassKind
- var nvisibility: nullable AVisibility
- var mvisibility: nullable MVisibility
- var arity = 0
- if nclassdef isa AStdClassdef then
- name = nclassdef.n_id.text
- nkind = nclassdef.n_classkind
- mkind = nkind.mkind
- nvisibility = nclassdef.n_visibility
- mvisibility = nvisibility.mvisibility
- arity = nclassdef.n_formaldefs.length
- else if nclassdef isa ATopClassdef then
- name = "Object"
- nkind = null
- mkind = interface_kind
- nvisibility = null
- mvisibility = public_visibility
- else if nclassdef isa AMainClassdef then
- name = "Sys"
- nkind = null
- mkind = concrete_kind
- nvisibility = null
- mvisibility = public_visibility
- else
- abort
- end
-
- var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
- if mclass == null then
- mclass = new MClass(mmodule, name, arity, 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}.")
- return
- 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
- 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
- error(nkind, "Error: refinement changed the kind from a {mclass.kind} to a {mkind}")
- else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
- error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
- end
- nclassdef.mclass = mclass
- nmodule.mclass2nclassdef[mclass] = nclassdef
- end
-
- # 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)
- var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
- var mclass = nclassdef.mclass
- if mclass == null then return # Skip error
- #var mclassdef = nclassdef.mclassdef.as(not null)
-
- var names = new Array[String]
- var bounds = new Array[MType]
- if nclassdef isa AStdClassdef and mclass.arity > 0 then
- # Collect formal parameter names
- 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
- names.add(ptname)
- end
-
- # Revolve bound for formal parameter names
- for i in [0..mclass.arity[ do
- var nfd = nclassdef.n_formaldefs[i]
- var nfdt = nfd.n_type
- if nfdt != null then
- var bound = resolve_mtype_unchecked(nclassdef, 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)
- end
- else if mclass.mclassdefs.is_empty then
- # No bound, then implicitely bound by nullable Object
- bounds.add(objectclass.mclass_type.as_nullable)
- else
- # Inherit the bound
- bounds.add(mclass.intro.bound_mtype.arguments[i])
- end
- end
- end
-
- var bound_mtype = mclass.get_mtype(bounds)
- var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names)
- nclassdef.mclassdef = mclassdef
- self.mclassdef2nclassdef[mclassdef] = nclassdef
-
- if mclassdef.is_intro then
- self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
- else
- self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
- end
- end
-
- # 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 mclass = nclassdef.mclass.as(not null)
- var mclassdef = nclassdef.mclassdef.as(not null)
-
- var specobject = 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)
- 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
- supertypes.add mtype
- #print "new super : {mclass} < {mtype}"
- end
- end
- if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
- supertypes.add objectclass.mclass_type
- end
-
- mclassdef.set_supertypes(supertypes)
- if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
- end
-
- # 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)
-
- for s in mclassdef.supertypes do
- if s.is_subtype(mmodule, mclassdef.bound_mtype, mclassdef.bound_mtype) then
- error(nclassdef, "Error: Inheritance loop for class {mclass} with type {s}")
- end
- end
- end
-
- # 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
- # Force building recursively
- if nmodule.build_classes_is_done then return
- nmodule.build_classes_is_done = true
- var mmodule = nmodule.mmodule.as(not null)
- for imp in mmodule.in_importation.direct_greaters do
-
- build_classes(mmodule2nmodule[imp])
- end
-
- # Create all classes
- for nclassdef in nmodule.n_classdefs do
- self.build_a_mclass(nmodule, nclassdef)
- end
-
- # Create all classdefs
- for nclassdef in nmodule.n_classdefs do
- self.build_a_mclassdef(nmodule, nclassdef)
- end
-
- for nclassdef in nmodule.n_classdefs do
- if nclassdef.mclassdef == null then return # forward error
- end
-
- # Create inheritance on all classdefs
- for nclassdef in nmodule.n_classdefs do
- self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
- end
-
- # Create the mclassdef hierarchy
- for nclassdef in nmodule.n_classdefs do
- var mclassdef = nclassdef.mclassdef.as(not null)
- mclassdef.add_in_hierarchy
- end
-
- # Check inheritance
- for nclassdef in nmodule.n_classdefs do
- self.check_supertypes(nmodule, nclassdef)
- end
-
- # Check unchecked ntypes
- for nclassdef in nmodule.n_classdefs do
- if nclassdef isa AStdClassdef then
- # 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)
- if bound == null then return # Forward error
- end
- end
- # check declared super types
- for nsc in nclassdef.n_superclasses do
- var ntype = nsc.n_type
- if ntype.mtype != null then
- var mtype = resolve_mtype(nclassdef, ntype)
- if mtype == null then return # Forward error
- end
- end
- end
-
- end
-
- # TODO: Check that the super-class is not intrusive
-
- # TODO: Check that the super-class is not already known (by transitivity)
- end
-
# Register the nmodule associated to each mmodule
# FIXME: why not refine the MModule class with a nullable attribute?
var mmodule2nmodule: HashMap[MModule, AModule] = new HashMap[MModule, AModule]
- # 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'.
- # `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.
- # FIXME: the name "resolve_mtype" is awful
- fun resolve_mtype_unchecked(nclassdef: AClassdef, 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
- 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(nclassdef, 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'.
- # `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.
- # FIXME: the name "resolve_mtype" is awful
- fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
- do
- var mtype = ntype.mtype
- if mtype == null then mtype = resolve_mtype_unchecked(nclassdef, 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)
- if mt == null then return null # forward error
- if not mt.is_subtype(mmodule, mclassdef.bound_mtype, bound) then
- error(nt, "Type error: expected {bound}, got {mt}")
- return null
- end
- end
- end
- ntype.checked_mtype = true
- return mtype
- end
# Helper function to display an error on a node.
# Alias for `self.toolcontext.error(n.hot_location, text)'
var mmodule: nullable MModule
# Flag that indicate if the importation is already completed
var is_importation_done: Bool = false
- # Flag that indicate if the class and prop building is already completed
- var build_classes_is_done: Bool = false
- # 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'
- var mclass: nullable MClass
- # The associated MClassDef once build by a `ModelBuilder'
- var mclassdef: nullable MClassDef
-end
-
-redef class AClasskind
- # The class kind associated with the AST node class
- private fun mkind: MClassKind is abstract
-end
-redef class AConcreteClasskind
- redef fun mkind do return concrete_kind
-end
-redef class AAbstractClasskind
- redef fun mkind do return abstract_kind
-end
-redef class AInterfaceClasskind
- redef fun mkind do return interface_kind
-end
-redef class AEnumClasskind
- redef fun mkind do return enum_kind
-end
-redef class AExternClasskind
- redef fun mkind do return extern_kind
end
redef class AVisibility
redef class APrivateVisibility
redef fun mvisibility do return private_visibility
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
\ No newline at end of file
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# 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 class definitions to instantiate model element
+
+module modelize_class
+
+import modelbuilder
+
+redef class ToolContext
+ var modelize_class_phase: Phase = new ModelizeClassPhase(self, null)
+end
+
+private class ModelizeClassPhase
+ super Phase
+
+ redef fun process_nmodule(nmodule)
+ do
+ toolcontext.modelbuilder.build_classes(nmodule)
+ end
+end
+
+redef class ModelBuilder
+ # 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)
+
+ var name: String
+ var nkind: nullable AClasskind
+ var mkind: MClassKind
+ var nvisibility: nullable AVisibility
+ var mvisibility: nullable MVisibility
+ var arity = 0
+ if nclassdef isa AStdClassdef then
+ name = nclassdef.n_id.text
+ nkind = nclassdef.n_classkind
+ mkind = nkind.mkind
+ nvisibility = nclassdef.n_visibility
+ mvisibility = nvisibility.mvisibility
+ arity = nclassdef.n_formaldefs.length
+ else if nclassdef isa ATopClassdef then
+ name = "Object"
+ nkind = null
+ mkind = interface_kind
+ nvisibility = null
+ mvisibility = public_visibility
+ else if nclassdef isa AMainClassdef then
+ name = "Sys"
+ nkind = null
+ mkind = concrete_kind
+ nvisibility = null
+ mvisibility = public_visibility
+ else
+ abort
+ end
+
+ var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
+ if mclass == null then
+ mclass = new MClass(mmodule, name, arity, 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}.")
+ return
+ 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
+ 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
+ error(nkind, "Error: refinement changed the kind from a {mclass.kind} to a {mkind}")
+ else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
+ error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
+ end
+ nclassdef.mclass = mclass
+ nmodule.mclass2nclassdef[mclass] = nclassdef
+ end
+
+ # 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)
+ var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
+ var mclass = nclassdef.mclass
+ if mclass == null then return # Skip error
+ #var mclassdef = nclassdef.mclassdef.as(not null)
+
+ var names = new Array[String]
+ var bounds = new Array[MType]
+ if nclassdef isa AStdClassdef and mclass.arity > 0 then
+ # Collect formal parameter names
+ 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
+ names.add(ptname)
+ end
+
+ # Revolve bound for formal parameter names
+ for i in [0..mclass.arity[ do
+ var nfd = nclassdef.n_formaldefs[i]
+ var nfdt = nfd.n_type
+ if nfdt != null then
+ var bound = resolve_mtype_unchecked(nclassdef, 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)
+ end
+ else if mclass.mclassdefs.is_empty then
+ # No bound, then implicitely bound by nullable Object
+ bounds.add(objectclass.mclass_type.as_nullable)
+ else
+ # Inherit the bound
+ bounds.add(mclass.intro.bound_mtype.arguments[i])
+ end
+ end
+ end
+
+ var bound_mtype = mclass.get_mtype(bounds)
+ var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names)
+ nclassdef.mclassdef = mclassdef
+ self.mclassdef2nclassdef[mclassdef] = nclassdef
+
+ if mclassdef.is_intro then
+ self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
+ else
+ self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
+ end
+ end
+
+ # 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 mclass = nclassdef.mclass.as(not null)
+ var mclassdef = nclassdef.mclassdef.as(not null)
+
+ var specobject = 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)
+ 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
+ supertypes.add mtype
+ #print "new super : {mclass} < {mtype}"
+ end
+ end
+ if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
+ supertypes.add objectclass.mclass_type
+ end
+
+ mclassdef.set_supertypes(supertypes)
+ if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
+ end
+
+ # 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)
+
+ for s in mclassdef.supertypes do
+ if s.is_subtype(mmodule, mclassdef.bound_mtype, mclassdef.bound_mtype) then
+ error(nclassdef, "Error: Inheritance loop for class {mclass} with type {s}")
+ end
+ end
+ end
+
+ # 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
+ # Force building recursively
+ if nmodule.build_classes_is_done then return
+ nmodule.build_classes_is_done = true
+ var mmodule = nmodule.mmodule.as(not null)
+ for imp in mmodule.in_importation.direct_greaters do
+
+ build_classes(mmodule2nmodule[imp])
+ end
+
+ # Create all classes
+ for nclassdef in nmodule.n_classdefs do
+ self.build_a_mclass(nmodule, nclassdef)
+ end
+
+ # Create all classdefs
+ for nclassdef in nmodule.n_classdefs do
+ self.build_a_mclassdef(nmodule, nclassdef)
+ end
+
+ for nclassdef in nmodule.n_classdefs do
+ if nclassdef.mclassdef == null then return # forward error
+ end
+
+ # Create inheritance on all classdefs
+ for nclassdef in nmodule.n_classdefs do
+ self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
+ end
+
+ # Create the mclassdef hierarchy
+ for nclassdef in nmodule.n_classdefs do
+ var mclassdef = nclassdef.mclassdef.as(not null)
+ mclassdef.add_in_hierarchy
+ end
+
+ # Check inheritance
+ for nclassdef in nmodule.n_classdefs do
+ self.check_supertypes(nmodule, nclassdef)
+ end
+
+ # Check unchecked ntypes
+ for nclassdef in nmodule.n_classdefs do
+ if nclassdef isa AStdClassdef then
+ # 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)
+ if bound == null then return # Forward error
+ end
+ end
+ # check declared super types
+ for nsc in nclassdef.n_superclasses do
+ var ntype = nsc.n_type
+ if ntype.mtype != null then
+ var mtype = resolve_mtype(nclassdef, ntype)
+ if mtype == null then return # Forward error
+ end
+ end
+ end
+
+ end
+
+ # TODO: Check that the super-class is not intrusive
+
+ # TODO: Check that the super-class is not already known (by transitivity)
+ 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'.
+ # `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.
+ # FIXME: the name "resolve_mtype" is awful
+ fun resolve_mtype_unchecked(nclassdef: AClassdef, 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
+ 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(nclassdef, 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'.
+ # `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.
+ # FIXME: the name "resolve_mtype" is awful
+ fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
+ do
+ var mtype = ntype.mtype
+ if mtype == null then mtype = resolve_mtype_unchecked(nclassdef, 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)
+ if mt == null then return null # forward error
+ if not mt.is_subtype(mmodule, mclassdef.bound_mtype, bound) then
+ error(nt, "Type error: expected {bound}, got {mt}")
+ return null
+ end
+ end
+ end
+ ntype.checked_mtype = true
+ return mtype
+ end
+
+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?
+ # 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'
+ var mclass: nullable MClass
+ # The associated MClassDef once build by a `ModelBuilder'
+ var mclassdef: nullable MClassDef
+end
+
+redef class AClasskind
+ # The class kind associated with the AST node class
+ private fun mkind: MClassKind is abstract
+end
+redef class AConcreteClasskind
+ redef fun mkind do return concrete_kind
+end
+redef class AAbstractClasskind
+ redef fun mkind do return abstract_kind
+end
+redef class AInterfaceClasskind
+ redef fun mkind do return interface_kind
+end
+redef class AEnumClasskind
+ redef fun mkind do return enum_kind
+end
+redef class AExternClasskind
+ redef fun mkind do return extern_kind
+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
\ No newline at end of file
# 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
+import modelize_class
redef class ToolContext
var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase])