typing: compute FlowContext::collect_types without recursion
[nit.git] / src / semantize / typing.nit
index ce51fa3..8ca8c6e 100644 (file)
@@ -52,6 +52,7 @@ private class TypeVisitor
        # Is `self` use restricted?
        # * no explicit `self`
        # * method called on the implicit self must be top-level
+       # Currently only used for `new` factory since there is no valid receiver inside
        var is_toplevel_context = false
 
        init
@@ -71,7 +72,7 @@ private class TypeVisitor
                        selfvariable.declared_type = mclass.mclass_type
 
                        var mprop = mpropdef.mproperty
-                       if mprop isa MMethod and (mprop.is_toplevel or mprop.is_new) then
+                       if mprop isa MMethod and mprop.is_new then
                                is_toplevel_context = true
                        end
                end
@@ -455,7 +456,7 @@ private class TypeVisitor
                        #node.debug("*** START Collected for {variable}")
                        var mtypes = flow.collect_types(variable)
                        #node.debug("**** END Collected for {variable}")
-                       if mtypes == null or mtypes.length == 0 then
+                       if mtypes.length == 0 then
                                return variable.declared_type
                        else if mtypes.length == 1 then
                                return mtypes.first
@@ -544,7 +545,6 @@ end
 redef class FlowContext
        # Store changes of types because of type evolution
        private var vars = new HashMap[Variable, nullable MType]
-       private var cache = new HashMap[Variable, nullable Array[nullable MType]]
 
        # Adapt the variable to a static type
        # Warning1: do not modify vars directly.
@@ -552,36 +552,37 @@ redef class FlowContext
        private fun set_var(variable: Variable, mtype: nullable MType)
        do
                self.vars[variable] = mtype
-               self.cache.keys.remove(variable)
        end
 
-       private fun collect_types(variable: Variable): nullable Array[nullable MType]
+       # Look in the flow and previous flow and collect all first reachable type adaptation of a local variable
+       private fun collect_types(variable: Variable): Array[nullable MType]
        do
-               if cache.has_key(variable) then
-                       return cache[variable]
-               end
-               var res: nullable Array[nullable MType] = null
-               if vars.has_key(variable) then
-                       var mtype = vars[variable]
-                       res = [mtype]
-               else if self.previous.is_empty then
-                       # Root flow
-                       res = [variable.declared_type]
-               else
-                       for flow in self.previous do
-                               if flow.is_unreachable then continue
-                               var r2 = flow.collect_types(variable)
-                               if r2 == null then continue
-                               if res == null then
-                                       res = r2.to_a
-                               else
-                                       for t in r2 do
-                                               if not res.has(t) then res.add(t)
-                                       end
+               #node.debug "flow for {variable}"
+               var res = new Array[nullable MType]
+
+               var todo = [self]
+               var seen = new HashSet[FlowContext]
+               while not todo.is_empty do
+                       var f = todo.pop
+                       if f.is_unreachable then continue
+                       if seen.has(f) then continue
+                       seen.add f
+
+                       if f.vars.has_key(variable) then
+                               # Found something. Collect it and do not process further on this path
+                               res.add f.vars[variable]
+                               #f.node.debug "process {variable}: got {f.vars[variable] or else "X"}"
+                       else
+                               todo.add_all f.previous
+                               todo.add_all f.loops
+                               if f.previous.is_empty then
+                                       # Root flowcontext mean a parameter or something related
+                                       res.add variable.declared_type
+                                       #f.node.debug "root process {variable}: got {variable.declared_type or else "X"}"
                                end
                        end
                end
-               cache[variable] = res
+               #self.node.debug "##### end flow for {variable}: {res.join(" ")}"
                return res
        end
 end
@@ -748,6 +749,7 @@ redef class AVardeclExpr
 
                #debug("var {variable}: {mtype}")
 
+               self.mtype = mtype
                self.is_typed = true
        end
 end
@@ -1106,6 +1108,24 @@ redef class AForExpr
        end
 end
 
+redef class AWithExpr
+       var method_start: nullable CallSite
+       var method_finish: nullable CallSite
+
+       redef fun accept_typing(v: TypeVisitor)
+       do
+               var mtype = v.visit_expr(n_expr)
+               if mtype == null then return
+
+               method_start = v.get_method(self, mtype, "start", n_expr isa ASelfExpr)
+               method_finish = v.get_method(self, mtype, "finish", n_expr isa ASelfExpr)
+
+               v.visit_stmt(n_block)
+               self.mtype = n_block.mtype
+               self.is_typed = true
+       end
+end
+
 redef class AAssertExpr
        redef fun accept_typing(v)
        do