Merge: MModule: new method `add_mclassdef` to avoid incoherent cache state.
authorJean Privat <jean@pryen.org>
Wed, 14 Aug 2019 15:22:30 +0000 (11:22 -0400)
committerJean Privat <jean@pryen.org>
Wed, 14 Aug 2019 15:22:30 +0000 (11:22 -0400)
Currently, the constructor of `MClassDef` would directly add itself in the `MModule`, like so:
`mmodule.mclassdefs.add(self)`. However, this could out of date `MModule:flatten_mclass_hierarchy`.
If we want better support for model manipulation after all the semantic phases, we need more logic when adding a new `MClassDef` in the hierarchy.

Moreover,  this allows better protection since `MClassDef` no longer needs to know `mmodule.mclassdefs`.

Signed-off-by: Louis-Vincent Boudreault <lv.boudreault95@gmail.com>

Pull-Request: #2777
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

src/model/model.nit

index c86bf96..e3fa3fd 100644 (file)
@@ -204,19 +204,37 @@ redef class MModule
        do
                var res = self.flatten_mclass_hierarchy_cache
                if res != null then return res
-               res = new POSet[MClass]
+                self.flatten_mclass_hierarchy_cache = new POSet[MClass]
                for m in self.in_importation.greaters do
                        for cd in m.mclassdefs do
-                               var c = cd.mclass
-                               res.add_node(c)
-                               for s in cd.supertypes do
-                                       res.add_edge(c, s.mclass)
-                               end
+                                unsafe_update_hierarchy_cache(cd)
                        end
                end
-               self.flatten_mclass_hierarchy_cache = res
-               return res
-       end
+               return self.flatten_mclass_hierarchy_cache.as(not null)
+       end
+
+        # Adds another class definition in the modue.
+        # Updates the class hierarchy cache.
+        fun add_mclassdef(mclassdef: MClassDef)
+        do
+                self.mclassdefs.add(mclassdef)
+                if self.flatten_mclass_hierarchy_cache != null then
+                        unsafe_update_hierarchy_cache(mclassdef)
+                end
+        end
+
+        # Adds a class definition inside `flatten_mclass_hierarchy_cache` without
+        # null check. The caller must have initialized the cache.
+        protected fun unsafe_update_hierarchy_cache(mclassdef: MClassDef)
+        do
+                var hierarchy = self.flatten_mclass_hierarchy_cache.as(not null)
+                # Update the cache
+                var c = mclassdef.mclass
+                hierarchy.add_node(c)
+                for s in mclassdef.supertypes do
+                        hierarchy.add_edge(c, s.mclass)
+                end
+        end
 
        # Sort a given array of classes using the linearization order of the module
        # The most general is first, the most specific is last
@@ -651,7 +669,7 @@ class MClassDef
        init
        do
                self.mclass = bound_mtype.mclass
-               mmodule.mclassdefs.add(self)
+               mmodule.add_mclassdef(self)
                mclass.mclassdefs.add(self)
                if mclass.intro_mmodule == mmodule then
                        assert not isset mclass._intro