From b221486b6c41c604be44d5da0005a5a982b6add1 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 10:27:20 +0200 Subject: [PATCH 01/14] Prepare separate main branch for performance improvements --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 291fb64..089e89b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -9,6 +9,7 @@ on: pull_request: branches: - main + - performance-improvements-main env: MAVEN_OPTS: "-Xmx6G -Dhttps.protocols=TLSv1.2 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true" From 6aafa1d741650f593b7716e4f64fab0de3e8d7d1 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 10:29:00 +0200 Subject: [PATCH 02/14] Add conversion function for field `definesMap` of `TModel` to make its usage in `bindWide` more efficient --- .../typepal/ConfigurableScopeGraph.rsc | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index d6df65c..7ad6d8b 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -404,11 +404,30 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ /* parents) and definitions that can be reached in a single step via semantic links */ /************************************************************************************/ + // Convert `tm.definesMap` to a more efficient representation for the kind + // of lookups that are performed in `bindWide`. The idea is to convert only + // once, and enjoy a return on investment each time when a lookup is + // performed in it (instead of also needing a `domainR` call each time). + map[loc, map[str, map[IdRole, set[loc]]]] convertDefinesMap() { + // Conversion function for the outer map + map[loc, map[str, map[IdRole, set[loc]]]] convert(map[loc, map[str, rel[IdRole, loc]]] scope2id2pairs) { + return (scope: convert(scope2id2pairs[scope]) | loc scope <- scope2id2pairs); + } + // Conversion function for the inner maps + map[str, map[IdRole, set[loc]]] convert(map[str, rel[IdRole, loc]] id2pairs) { + return (id: Relation::index(id2pairs[id]) | str id <- id2pairs); + } + return convert(tm.definesMap); + } + + // Convert only once + map[loc, map[str, map[IdRole, set[loc]]]] scope2id2role2defs = convertDefinesMap(); + //@memo // Retrieve all bindings for use in given syntactic scope private set[loc] bindWide(loc scope, str id, set[IdRole] idRoles){ - idsInScope = (scope in tm.definesMap) ? tm.definesMap[scope] : (); - foundDefs = id in idsInScope ? domainR(idsInScope[id], idRoles)<1> : {}; + map[IdRole, set[loc]] role2defs = (scope2id2role2defs[scope] ? ())[id] ? (); + foundDefs = {*(role2defs[role] ? {}) | IdRole role <- idRoles}; // dbg("bindWide: , =\> "); return foundDefs; } From 1ce56e796052c450ee8b89a3dcfcb61567e6df5a Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 11:32:53 +0200 Subject: [PATCH 03/14] Add cache for `lookupWide` --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index d6df65c..56fa767 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -497,13 +497,24 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ return res; } - public set[loc] lookupWide(Use u){ + // Cache to store results of `lookupWide`. The assumption is that syntactic + // scopes will not change between calls, but semantic paths might, so the + // cache needs to be invalidated when `the_solver.getPathsByPathRole()` + // returns an updated value (relative to the previous call of `lookupWide`). + map[Use, set[loc]] lookupWideCache = (); + public set[loc] lookupWide(Use u){ // Update current paths and pathRoles current_pathsByPathRole = the_solver.getPathsByPathRole(); if(current_pathsByPathRole != pathsByPathRole){ pathsByPathRole = current_pathsByPathRole; pathRoles = domain(pathsByPathRole); + lookupWideCache = (); + } + + if (u in lookupWideCache) { + set[loc] defs = lookupWideCache[u]; + if (isEmpty(defs)) throw NoBinding(); else return defs; } scope = u.scope; @@ -512,6 +523,7 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ // dbgPaths(); if(!(u has qualifierRoles)){ defs = {def | loc def <- lookupNestWide(scope, u), isAcceptableSimpleFun(def, u, the_solver) == acceptBinding()}; + lookupWideCache[u] = defs; // dbg("lookupWide: =\> "); if(isEmpty(defs)) throw NoBinding(); else return defs; } else { @@ -528,6 +540,7 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ scopeLookups = lookupNestWide(qscope, use(u.ids[-1], "", u.occ, qscope, u.idRoles)); defs += { def | def <- scopeLookups, isAcceptableQualifiedFun(def, u, the_solver) == acceptBinding()}; } + lookupWideCache[u] = defs; if(!isEmpty(defs)){ // dbg("lookupWide: returns:\n\t==\> <}>"); return defs; From 32dfbf03701519302594cffed1e534d4c1d61062 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 12:06:14 +0200 Subject: [PATCH 04/14] 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 05/14] 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; From 325e17e16cafd6756c823c7aa33103f401c30b92 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 29 Jul 2026 15:40:02 +0200 Subject: [PATCH 06/14] Add flag to configure if messages in `TModel`s should be sorted --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 2 ++ src/analysis/typepal/Solver.rsc | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index d6df65c..0cd8e31 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -163,6 +163,8 @@ data TypePalConfig( bool enableErrorFixes = true, + bool enableSortedMessages = true, + int cutoffForNameSimilarity = 3 ); diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index 2a118e9..d3d3404 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -1830,7 +1830,11 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ } } messages = visit(messages) { case loc l => solver_toPhysicalLoc(l) }; - tm.messages = sortMostPrecise(toList(toSet(messages))); + messages = toList(toSet(messages)); // Remove duplicates + if (tm.config.enableSortedMessages) { + messages = sortMostPrecise(messages); + } + tm.messages = messages; assertValidDefines(tm); assertValidUseDef(tm, thisSolver); From 01a69232d99c82c5746700787ad17dc014762e93 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Tue, 4 Aug 2026 09:54:59 +0200 Subject: [PATCH 07/14] Add `filterUnused` as a faster alternative to `reportUnused` --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 7 +++++++ src/analysis/typepal/Solver.rsc | 13 +++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index d6df65c..c6d1b5d 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -75,6 +75,11 @@ bool defaultReportUnused (loc _, TModel _) { return false; } +list[loc] defaultFilterUnused(list[loc] defs, TModel tm) { + bool(loc, TModel) reportUnused = tm.config.reportUnused; + return [d | loc d <- defs, reportUnused(d, tm)]; +} + // https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#:~:text=A%20URI%20is%20composed%20from,)%2C%20and%20the%20character%20%25%20. // gen-delims: : / ? # [ ] @ // sub-delims: ! $ & ' ( ) * + , ; @@ -157,6 +162,8 @@ data TypePalConfig( bool(loc def, TModel tm) reportUnused = defaultReportUnused, + list[loc](list[loc] defs, TModel tm) filterUnused = defaultFilterUnused, + loc (Define def, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc, list[str] (Use u, TModel tm) similarNames = defaultSimilarNames, diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index 2a118e9..36e030b 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -141,7 +141,7 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ AType(AType containerType, Tree selector, loc scope, Solver s) getTypeInNamelessTypeFun = defaultGetTypeInNamelessType; - bool(loc def, TModel tm) reportUnused = defaultReportUnused; + list[loc](list[loc] defs, TModel tm) filterUnused = defaultFilterUnused; map[loc,loc] logical2physical = tm.logical2physical; @@ -177,7 +177,7 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ getTypeNamesAndRole = tc.getTypeNamesAndRole; getTypeInTypeFromDefineFun = tc.getTypeInTypeFromDefine; getTypeInNamelessTypeFun = tc.getTypeInNamelessType; - reportUnused = tc.reportUnused; + filterUnused = tc.filterUnused; } TypePalConfig solver_getConfig() = tm.config; @@ -1823,12 +1823,9 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ }; tm.defines = toSet(ldefines); - for(Define def <- tm.defines){ - defdefined = solver_toPhysicalLoc(def.defined); - if(defdefined notin def2uses && defdefined notin doubleDefs && reportUnused(defdefined, tm)){ - messages += warning("Unused ``", defdefined); - } - } + list[loc] unused = filterUnused([l | Define def <- tm.defines, loc l := solver_toPhysicalLoc(def.defined), l notin def2uses, l notin doubleDefs], tm); + messages += [warning("Unused ``", l) | loc l <- unused, Define def := definitions[l]]; + messages = visit(messages) { case loc l => solver_toPhysicalLoc(l) }; tm.messages = sortMostPrecise(toList(toSet(messages))); From 26bf10f31bc7620e21c0cb19f53dffd07b1d5890 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 5 Aug 2026 13:02:41 +0200 Subject: [PATCH 08/14] Update `resolvePaths` by separating initialization and updating of `pathsByPathRole`, and by making the initialization part faster --- src/analysis/typepal/Solver.rsc | 49 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index 2a118e9..8f91615 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -1024,18 +1024,47 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ } newPaths = { tup | tup: <- newPaths, u != d }; tm.referPaths = referPaths; - newPathFound = !isEmpty(newPaths); - if( newPathFound // we found new paths - || (!isEmpty(tm.paths) && isEmpty(pathsByPathRole)) // pathsByPathRole not yet initialized - ){ - tm.paths += newPaths; - pathsByPathRole = (); - for( <- tm.paths){ - pathsByPathRole[r] ? {} += {}; - } + tm.paths += newPaths; + + initPathsByPathRole(); + updatePathsByPathRole(newPaths); + return !isEmpty(newPaths); + } + + void initPathsByPathRole() { + if (!isEmpty(pathsByPathRole)) { // Already initialized + return; } - return newPathFound; + paths = tm.paths; + if (isEmpty(paths)) { // Nothing to initialize + return; + } + + pathsByPathRole = (r: {} | <_, PathRole r, _> <- paths); + for (PathRole r <- pathsByPathRole) { + pathsByPathRole[r] = { | <- paths}; + } + // That is, first compute the keys, and second compute the total + // values. It seems to be significantly faster than computing keys + // and total values together: + // ``` + // pathsByPathRole = (r: { | <- paths} | <_, PathRole r, _> <- paths); + // ``` + // It also seems to be significantly faster than computing keys + // and partial values iteratively: + // ``` + // pathsByPathRole = (); + // for ( <- tm.paths) { + // pathsByPathRole[r] ? {} += {}; + // } + // ``` + } + + void updatePathsByPathRole(Paths newPaths) { + for( <- newPaths){ + pathsByPathRole[r] ? {} += {}; + } } // ---- "equal" and "requireEqual" ---------------------------------------- From f54b384ab6e104c22402638e3d785c3189984f33 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 7 Aug 2026 13:56:03 +0200 Subject: [PATCH 09/14] Update check for illegal overloading of unused definitions to avoid scope graph lookups for unused definitions with a unique identifier --- src/analysis/typepal/Solver.rsc | 71 +++++++++++++++++---------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index 2a118e9..6c5da5f 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -1038,6 +1038,41 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ return newPathFound; } + // ---- Illegal overloading ----------------------------------------------- + + void checkIllegalOverloadingOfUnusedDefinitions() { + map[str, set[Define]] definesById = Relation::index({ | Define d <- defines}); + for (str id <- definesById) { + set[Define] definesOfId = definesById[id]; + + // `id` can be illegally overloaded only if it isn't unique (i.e., + // it has at least two definitions). + for (size(definesOfId) >= 2, Define d <- definesOfId, !isUsed(d)) { + + // Turn unused definition into use and check for double + // declarations using scope graph lookup (i.e., not each + // definition in `definesOfId` might be in scope of `d`). + Use u = use(d.id, d.orgId, d.defined, d.scope, {d.idRole}); + try { + set[loc] foundDefs = scopeGraph.lookup(u); + if (size(foundDefs) > 1 && !mayOverloadFun(foundDefs, definitions)) { + doubleDefs += foundDefs; + messages += [error("Double declaration of ``", d1, + causes=[info("Other declaration of ``", d2) | d2 <- foundDefs, d2 != d1 ]) + | d1 <- foundDefs, isContainedIn(u.scope, definitions[d1].scope, logical2physical) + ]; + } + } + catch NoBinding(): {;} + catch TypeUnavailable(): {;} + } + } + } + + bool isUsed(Define d) { + return d.defined in def2uses; + } + // ---- "equal" and "requireEqual" ---------------------------------------- bool solver_equal(value given, value expected){ @@ -1467,41 +1502,7 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ } } - // Check for illegal overloading of unused definitions - set[loc] unusedDefs = domain(definitions) - actuallyUsedDefs; - - for(ud <- unusedDefs){ - udef = definitions[ud]; - - scope = udef.scope; - id = udef.id; - orgId = udef.orgId; - idRole = udef.idRole; - defined = udef.defined; - //if(defined in logical2physical) defined = logical2physical[defined]; - - u = use(id, orgId, defined, scope, {idRole}); // turn each unused definition into a use and check for double declarations; - try { - foundDefs = scopeGraph.lookup(u); - if(isEmpty(foundDefs)){ - ;//throw TypePalInternalError("No binding found while checking for double definitions"); - } else - if(size(foundDefs) == 1 || mayOverloadFun(foundDefs, definitions)){ - ; - } else { - doubleDefs += foundDefs; - messages += [error("Double declaration of ``", d1, - causes=[info("Other declaration of ``", d2) | d2 <- foundDefs, d2 != d1 ]) - | d1 <- foundDefs, isContainedIn(u.scope, definitions[d1].scope, logical2physical) - ]; - } - } - catch NoBinding(): { - ;//throw TypePalInternalError("No binding found while checking for double definitions"); - } - } - - unusedDefs = actuallyUsedDefs = {}; + checkIllegalOverloadingOfUnusedDefinitions(); // Process all defines (which may create new calculators/facts) From 97568cb1793e9efe3bad0b50c254b24e9db30d03 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Mon, 10 Aug 2026 17:22:49 +0200 Subject: [PATCH 10/14] Add `newDefInfo` function to make creation of `defInfo` values faster (using an auxiliary map of "prototypes") --- src/analysis/typepal/Solver.rsc | 38 ++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index 2a118e9..a4f7e7e 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -1645,14 +1645,29 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ /****************** end of main solve loop *****************************/ + // Creating new `defType` values using the `defType` constructor takes + // significant interpreter time. To make it faster, the following map + // stores "prototypes" from which new `defType` values can be created. + map[AType, DefInfo] defInfoPrototypes = (); + + DefInfo newDefInfo(AType t, map[str, value] keywordParameters) { + DefInfo proto; + if (t in defInfoPrototypes) { + proto = defInfoPrototypes[t]; + } else { + proto = defType(t); + defInfoPrototypes[t] = proto; + } + return setKeywordParameters(proto, keywordParameters); // Create new value (i.e., leave `proto` unchanged) + } + // Eliminate all defTypeCalls before handing control to the postSolver for(loc l <- definitions){ Define def = definitions[l]; if(defTypeCall(_, AType(Solver s) getAType) := def.defInfo){ kwparams = getKeywordParameters(def.defInfo); try { - di = defType(getAType(thisSolver)); - def.defInfo = setKeywordParameters(di, kwparams); + def.defInfo = newDefInfo(getAType(thisSolver), kwparams); definitions[l] = def; } catch _: { // Guard against type incorrect defines, but record for now ; //println("Skipping (type-incorrect) def: \n"); @@ -1663,11 +1678,16 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ newDefines = for(def <- defines){ + // All `defTypeCall` values have already been eliminated from + // `definitions`, so this is a fast(er) way out. + if (def.defined in definitions) { + append definitions[def.defined]; + continue; + } if(defTypeCall(_, AType(Solver s) getAType) := def.defInfo){ kwparams = getKeywordParameters(def.defInfo); try { - di = defType(getAType(thisSolver)); - def.defInfo = setKeywordParameters(di, kwparams); + def.defInfo = newDefInfo(getAType(thisSolver), kwparams); } catch _: { // Guard against type incorrect defines, but record for now ; //println("Skipping (type-incorrect) def: \n"); } @@ -1805,16 +1825,14 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ ldefines = for(tup: <- tm.defines){ if(defInfo has tree){ l = getLogicalLoc(defInfo.tree); - if(l in tm.facts){ - dt = defType(tm.facts[l]); - tup.defInfo = setKeywordParameters(dt, getKeywordParameters(defInfo)); + if(l in facts){ + tup.defInfo = newDefInfo(facts[l], getKeywordParameters(defInfo)); } else { continue; } } else { - if(defined in tm.facts){ - dt = defType(tm.facts[defined]); - tup.defInfo = setKeywordParameters(dt, getKeywordParameters(defInfo)); + if(defined in facts){ + tup.defInfo = newDefInfo(facts[defined], getKeywordParameters(defInfo)); } else { continue; } From 06a5cd95381e8da9072c4d866d6cf6e73692f71a Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Wed, 12 Aug 2026 09:52:27 +0200 Subject: [PATCH 11/14] Update default for sorting messages to false --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index 0cd8e31..bf47a4e 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -163,7 +163,7 @@ data TypePalConfig( bool enableErrorFixes = true, - bool enableSortedMessages = true, + bool enableSortedMessages = false, int cutoffForNameSimilarity = 3 ); From 0f42f397e7eae136a7378d40f56237599ed45462 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 14 Aug 2026 10:40:53 +0200 Subject: [PATCH 12/14] Improve comment --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index 7ad6d8b..ff0f8be 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -420,7 +420,8 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ return convert(tm.definesMap); } - // Convert only once + // Convert only once. (Note: this variable is local to `newScopeGraph`, so + // always associated with the same TModel.) map[loc, map[str, map[IdRole, set[loc]]]] scope2id2role2defs = convertDefinesMap(); //@memo From ea58ab5928510092083647f839833634047a6342 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 14 Aug 2026 10:41:23 +0200 Subject: [PATCH 13/14] Rename auxiliary functions --- src/analysis/typepal/ConfigurableScopeGraph.rsc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index ff0f8be..2bf9e5e 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -410,14 +410,14 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){ // performed in it (instead of also needing a `domainR` call each time). map[loc, map[str, map[IdRole, set[loc]]]] convertDefinesMap() { // Conversion function for the outer map - map[loc, map[str, map[IdRole, set[loc]]]] convert(map[loc, map[str, rel[IdRole, loc]]] scope2id2pairs) { - return (scope: convert(scope2id2pairs[scope]) | loc scope <- scope2id2pairs); + map[loc, map[str, map[IdRole, set[loc]]]] convertOuter(map[loc, map[str, rel[IdRole, loc]]] scope2id2pairs) { + return (scope: convertInner(scope2id2pairs[scope]) | loc scope <- scope2id2pairs); } // Conversion function for the inner maps - map[str, map[IdRole, set[loc]]] convert(map[str, rel[IdRole, loc]] id2pairs) { + map[str, map[IdRole, set[loc]]] convertInner(map[str, rel[IdRole, loc]] id2pairs) { return (id: Relation::index(id2pairs[id]) | str id <- id2pairs); } - return convert(tm.definesMap); + return convertOuter(tm.definesMap); } // Convert only once. (Note: this variable is local to `newScopeGraph`, so From 6cd4fa05162371768c97dd32a9dc8507bd0f068b Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 14 Aug 2026 16:16:43 +0200 Subject: [PATCH 14/14] Remove performance improvements scaffolding --- .github/workflows/build.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 089e89b..291fb64 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -9,7 +9,6 @@ on: pull_request: branches: - main - - performance-improvements-main env: MAVEN_OPTS: "-Xmx6G -Dhttps.protocols=TLSv1.2 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"