From 32dfbf03701519302594cffed1e534d4c1d61062 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 12:06:14 +0200 Subject: [PATCH 1/2] Update `lookupPathsWide` to avoid solve loop --- .../typepal/ConfigurableScopeGraph.rsc | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index d6df65c..960089b 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -426,21 +426,15 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ private set[loc] lookupPathsWide(loc scope, Use use, PathRole pathRole){ // dbgEnter("lookupPathsWide: in scope , role ");; res = {}; - - seenParents = {}; - solve(res, scope) { - next_path: - for( <- pathsByPathRole[pathRole] ? {}, parent notin seenParents){ - seenParents += parent; - for(loc def <- lookupScopeWide(parent, use)){ - switch(isAcceptablePathFun(parent, def, use, pathRole, the_solver)){ - case acceptBinding(): - res += def; - case ignoreContinue(): - continue; - case ignoreSkipPath(): - continue next_path; - } + for ( <- pathsByPathRole[pathRole] ? {}) { + for (loc def <- lookupScopeWide(parent, use)) { + switch (isAcceptablePathFun(parent, def, use, pathRole, the_solver)) { + case acceptBinding(): + res += def; + case ignoreContinue(): + continue; // Continue inner loop + case ignoreSkipPath(): + break; // Break inner loop (continue outer loop) } } } From cdf4f1e6bad078e2a756c7cb45fb1f9e60d06975 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 14:00:51 +0200 Subject: [PATCH 2/2] Add function, including caching of its results, to get the target of each path with a provided role and source --- .../typepal/ConfigurableScopeGraph.rsc | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index 960089b..aa8e227 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -421,12 +421,29 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ return res; } + // Cache to store results of `getPathTargets`. The assumption is that + // semantic paths might change between calls, so the cache needs to be + // invalidated when `the_solver.getPathsByPathRole()` returns an updated + // value (relative to the previous call of `getPathTargets`). + map[PathRole, map[loc, set[loc]]] getPathTargetsCache = (); + + // Gets the target of each path with the provided role and source + set[loc] getPathTargets(PathRole role, loc source) { + if (role notin getPathTargetsCache) { + getPathTargetsCache[role] = (); + } + if (source notin getPathTargetsCache[role]) { + getPathTargetsCache[role][source] = {target | <- pathsByPathRole[role]}; + } + return getPathTargetsCache[role][source]; + } + //@memo // Find all (semantics induced, one-level) bindings for use in given syntactic scope via PathRole private set[loc] lookupPathsWide(loc scope, Use use, PathRole pathRole){ // dbgEnter("lookupPathsWide: in scope , role ");; res = {}; - for ( <- pathsByPathRole[pathRole] ? {}) { + for (loc parent <- getPathTargets(pathRole, scope)) { for (loc def <- lookupScopeWide(parent, use)) { switch (isAcceptablePathFun(parent, def, use, pathRole, the_solver)) { case acceptBinding(): @@ -498,6 +515,7 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ if(current_pathsByPathRole != pathsByPathRole){ pathsByPathRole = current_pathsByPathRole; pathRoles = domain(pathsByPathRole); + getPathTargetsCache = (); } scope = u.scope;