@@ -199,19 +199,42 @@ def reach(start: Set[int]) -> Set[int]:
199199def compute_summaries (
200200 infos : Dict [str , FunctionInfo ],
201201 call_edges : List [Tuple [str , str ]],
202+ * ,
203+ solutions : Optional [Dict [str , Tuple [Dict [int , object ], List [DDGEdge ]]]] = None ,
202204) -> Dict [str , FunctionSummary ]:
203205 """Bottom-up composition over the SCC condensation DAG, monotone fixpoint
204- within each SCC."""
206+ within each SCC.
207+
208+ A **singleton SCC with no self-edge** is solved exactly once: the
209+ condensation is processed bottom-up, so every callee summary it reads is
210+ already final and a second pass could only recompute the same answer to
211+ observe that nothing changed. Genuinely recursive SCCs (several members,
212+ or one member calling itself) still iterate to fixpoint.
213+
214+ When *solutions* is supplied it receives each signature's converged
215+ ``(facts, ddg)`` — the by-products of the final solve, which
216+ :func:`~codeanalyzer.dataflow.sdg.assemble_sdg` would otherwise recompute
217+ from scratch. They are the same values that a fresh solve against the
218+ final summaries produces, because a converged pass is by definition one
219+ in which no member's summary changed (#155).
220+ """
205221 order = strongly_connected_components (sorted (infos ), call_edges )
222+ self_calls = {src for src , dst in call_edges if src == dst }
206223 summaries : Dict [str , FunctionSummary ] = {}
207224 for scc in order :
208225 members = [s for s in scc if s in infos ]
209- changed = True
210- while changed :
226+ if not members :
227+ continue
228+ recursive = len (members ) > 1 or members [0 ] in self_calls
229+ while True :
211230 changed = False
212231 for sig in members :
213- new , _ , _ = solve_function (infos [sig ], summaries )
232+ new , facts , ddg = solve_function (infos [sig ], summaries )
233+ if solutions is not None :
234+ solutions [sig ] = (facts , ddg )
214235 if summaries .get (sig ) != new :
215236 summaries [sig ] = new
216237 changed = True
238+ if not (recursive and changed ):
239+ break
217240 return summaries
0 commit comments