rta: do not try to remove things from a set in iteration
[nit.git] / src / rapid_type_analysis.nit
index ff72fac..a40ca22 100644 (file)
@@ -31,6 +31,8 @@ import auto_super_init
 import csv # for live_types_to_csv
 import ordered_tree # for live_methods_to_tree
 
+private import more_collections
+
 redef class ModelBuilder
        fun do_rapid_type_analysis(mainmodule: MModule): RapidTypeAnalysis
        do
@@ -75,6 +77,36 @@ class RapidTypeAnalysis
        # Live methods.
        var live_methods = new HashSet[MMethod]
 
+       # Live callsites.
+       var live_callsites = new HashSet[CallSite]
+
+       private var live_targets_cache = new HashMap2[MType, MProperty, Set[MMethodDef]]
+
+       # The live targets of a specific callsite.
+       fun live_targets(callsite: CallSite): Set[MMethodDef]
+       do
+               var mtype = callsite.recv
+               var anchor = callsite.anchor
+               if anchor != null then mtype = mtype.anchor_to(callsite.mmodule, anchor)
+               if mtype isa MNullableType then mtype = mtype.mtype
+               assert mtype isa MClassType
+               mtype = mtype.mclass.intro.bound_mtype
+               var mproperty = callsite.mproperty
+               var res = live_targets_cache[mtype, mproperty]
+               if res != null then return res
+               res = new ArraySet[MMethodDef]
+               live_targets_cache[mtype, mproperty] = res
+
+               for c in live_classes do
+                       var tc = c.intro.bound_mtype
+                       if not tc.is_subtype(mainmodule, null, mtype) then continue
+                       var d = mproperty.lookup_first_definition(mainmodule, tc)
+                       res.add d
+               end
+
+               return res
+       end
+
        # Live call-to-super.
        var live_super_sends = new HashSet[MMethodDef]
 
@@ -129,9 +161,16 @@ class RapidTypeAnalysis
                return tree
        end
 
-       # Methods that are are still candidate to the try_send
+       # Methods that are still candidate to the try_send
        private var totry_methods = new HashSet[MMethod]
 
+       # Methods that are are no more candidate to the try_send
+       private var totry_methods_to_remove = new Array[MMethod]
+
+       # Methods that are or were candidate to the try_send
+       # Used to ensure that try_send is only used once
+       private var try_methods = new HashSet[MMethod]
+
        # The method definitions that remain to visit
        private var todo = new List[MMethodDef]
 
@@ -294,6 +333,10 @@ class RapidTypeAnalysis
                for p in totry_methods do try_send(mtype, p)
                for p in live_super_sends do try_super_send(mtype, p)
 
+               # Remove cleared ones
+               for p in totry_methods_to_remove do totry_methods.remove(p)
+               totry_methods_to_remove.clear
+
                var bound_mtype = mtype.anchor_to(mainmodule, recv)
                for cd in bound_mtype.collect_mclassdefs(mainmodule)
                do
@@ -342,14 +385,15 @@ class RapidTypeAnalysis
                        if not live_methoddefs.has(d) then return
                end
                #print "full property: {mpropdef.mproperty} for {mpropdef.mproperty.mpropdefs.length} definitions"
-               totry_methods.remove(mpropdef.mproperty)
+               totry_methods_to_remove.add(mpropdef.mproperty)
        end
 
        fun add_send(recv: MType, mproperty: MMethod)
        do
-               if live_methods.has(mproperty) then return
+               if try_methods.has(mproperty) then return
                #print "new prop: {mproperty}"
                live_methods.add(mproperty)
+               try_methods.add(mproperty)
                if mproperty.mpropdefs.length == 1 then
                        # If there is only one definition, just add the definition and do not try again the property
                        var d = mproperty.mpropdefs.first
@@ -436,13 +480,20 @@ class RapidTypeVisitor
 
        fun add_type(mtype: MClassType) do analysis.add_new(receiver, mtype)
 
-       fun add_monomorphic_send(mtype: MType, mproperty: MMethod) do analysis.try_send(mtype.as(MClassType), mproperty)
+       fun add_monomorphic_send(mtype: MType, mproperty: MMethod)
+       do
+               analysis.live_methods.add(mproperty)
+               analysis.try_send(mtype.as(MClassType), mproperty)
+       end
 
        fun add_send(mtype: MType, mproperty: MMethod) do analysis.add_send(mtype, mproperty)
 
        fun add_cast_type(mtype: MType) do analysis.add_cast(mtype)
 
-       fun add_callsite(callsite: nullable CallSite) do if callsite != null then analysis.add_send(callsite.recv, callsite.mproperty)
+       fun add_callsite(callsite: nullable CallSite) do if callsite != null then
+               analysis.add_send(callsite.recv, callsite.mproperty)
+               analysis.live_callsites.add(callsite)
+       end
 end
 
 ###
@@ -515,8 +566,7 @@ redef class ACrangeExpr
        do
                var mtype = self.mtype.as(MClassType)
                v.add_type(mtype)
-               var prop = v.get_method(mtype, "init")
-               v.add_monomorphic_send(mtype, prop)
+               v.add_callsite(init_callsite)
        end
 end
 
@@ -525,8 +575,7 @@ redef class AOrangeExpr
        do
                var mtype = self.mtype.as(MClassType)
                v.add_type(mtype)
-               var prop = v.get_method(mtype, "without_last")
-               v.add_monomorphic_send(mtype, prop)
+               v.add_callsite(init_callsite)
        end
 end
 
@@ -605,22 +654,17 @@ end
 redef class AForExpr
        redef fun accept_rapid_type_visitor(v)
        do
-               var recvtype = self.n_expr.mtype.as(not null)
-               var colltype = self.coltype.as(not null)
-               var itmeth = v.get_method(colltype, "iterator")
-               v.add_send(recvtype, itmeth)
-               var iteratortype = itmeth.intro.msignature.return_mtype.as(MClassType).mclass.intro.bound_mtype
-               var objtype = v.get_class("Object").mclass_type
-               v.add_send(objtype, v.get_method(iteratortype, "is_ok"))
+               v.add_callsite(self.method_iterator)
+               v.add_callsite(self.method_is_ok)
                if self.variables.length == 1 then
-                       v.add_send(objtype, v.get_method(iteratortype, "item"))
+                       v.add_callsite(self.method_item)
                else if self.variables.length == 2 then
-                       v.add_send(objtype, v.get_method(iteratortype, "key"))
-                       v.add_send(objtype, v.get_method(iteratortype, "item"))
+                       v.add_callsite(self.method_key)
+                       v.add_callsite(self.method_item)
                else
                        abort
                end
-               v.add_send(objtype, v.get_method(iteratortype, "next"))
+               v.add_callsite(self.method_next)
        end
 end