From fdf567f99165686e94af06aef668dee127fac3e6 Mon Sep 17 00:00:00 2001 From: paulklint Date: Fri, 13 Mar 2026 21:31:08 +0100 Subject: [PATCH 1/8] Root cause of this issues lmMloc > lmTpl should be lmMloc >= lmTpl; --- .../compiler/lang/rascalcore/check/CheckerCommon.rsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc index 592e642e3e..b9df1cb0fe 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc @@ -224,8 +224,8 @@ bool tplOutdated(MODID moduleId, PathConfig pcfg){ = getTPLReadLoc(qualifiedModuleName, pcfg); lmMloc = lastModified(mloc); lmTpl = lastModified(tpl); - res = !found || lmMloc > lmTpl; - //println("tplOutdated : ; mloc: \> tpl: : lmTpl>, (, )"); + res = !found || lmMloc >= lmTpl; + // println("tplOutdated : ; mloc: \> tpl: : lmTpl>, (, )"); return res; } catch _: { return false; From 795a49d7be00d0f6bfefa5696d25bc0471e35e5d Mon Sep 17 00:00:00 2001 From: paulklint Date: Fri, 13 Mar 2026 21:32:08 +0100 Subject: [PATCH 2/8] Auxiliary safety measure. --- src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc index 8a95b397a5..d3eeda94e3 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc @@ -552,7 +552,7 @@ bool uptodateTPls(list[loc] candidates, list[str] mnames, PathConfig pcfg){ for(int i <- index(candidates)){ mloc = candidates[i]; = getTPLReadLoc(mnames[i], pcfg); - if(!found || lastModified(mloc) > lastModified(tpl)){ + if(!found || lastModified(mloc) >= lastModified(tpl)){ return false; } } From bee2ce35c3508879769ab67133a06d14d286f6c8 Mon Sep 17 00:00:00 2001 From: paulklint Date: Thu, 19 Mar 2026 14:41:14 +0100 Subject: [PATCH 3/8] Commented out println --- .../rascalmpl/compiler/lang/rascalcore/check/ADTandGrammar.rsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/ADTandGrammar.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/ADTandGrammar.rsc index e60eb823db..1f2f356428 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/ADTandGrammar.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/ADTandGrammar.rsc @@ -178,7 +178,7 @@ tuple[bool, TModel, ModuleStatus] addGrammar(MODID moduleId, set[MODID] imports, = getTModelForModule(m, ms); if(!found) { msg = error("Cannot add grammar or tmodel since `` is not found", ms.moduleLocs[moduleId] ? |unknown:///|); - println(msg); // TODO: Just to record this event; this should probably go to a log file + //println(msg); // TODO: Just to record this event; this should probably go to a log file ms.messages[moduleId] ? {} += { msg }; tm1 = tmodel(modelName=qualifiedModuleName, messages=[msg]); return ; From a99e9b48fcfcff45117cb6044f1b3cbe0835cba8 Mon Sep 17 00:00:00 2001 From: paulklint Date: Thu, 19 Mar 2026 14:42:18 +0100 Subject: [PATCH 4/8] Undo previous > to >= change --- .../rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc index b9df1cb0fe..b8f89b6272 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc @@ -224,7 +224,7 @@ bool tplOutdated(MODID moduleId, PathConfig pcfg){ = getTPLReadLoc(qualifiedModuleName, pcfg); lmMloc = lastModified(mloc); lmTpl = lastModified(tpl); - res = !found || lmMloc >= lmTpl; + res = !found || lmMloc > lmTpl; // println("tplOutdated : ; mloc: \> tpl: : lmTpl>, (, )"); return res; } catch _: { From 047afc77329d647073f631527c24b488874f6faf Mon Sep 17 00:00:00 2001 From: paulklint Date: Thu, 19 Mar 2026 14:43:25 +0100 Subject: [PATCH 5/8] Improved computation of require definitions --- .../compiler/lang/rascalcore/check/Import.rsc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc index 220ba9e2d2..103f5c28c8 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc @@ -328,10 +328,11 @@ str getModuleNameFromAnyLogical(loc l){ tuple[bool, ModuleStatus] importsAndExtendsAreBinaryCompatible(TModel tm, set[MODID] importsAndExtends, ModuleStatus ms){ moduleName = tm.modelName; physical2logical = invertUnique(tm.logical2physical); - - modRequires = { lg | l <- range(tm.useDef), - physical2logical[l]?, lg := physical2logical[l], - moduleName !:= getModuleNameFromAnyLogical(lg) }; + modRequires = {lg | l <- range(tm.useDef), + l in physical2logical, + lg := physical2logical[l], + moduleName != getModuleNameFromAnyLogical(lg) + }; provided = {}; if(!isEmpty(modRequires)){ for(m <- importsAndExtends){ @@ -342,8 +343,6 @@ tuple[bool, ModuleStatus] importsAndExtendsAreBinaryCompatible(TModel tm, set[MO } } - //println(" requires "); - if(isEmpty(modRequires - provided)){ //println("importsAndExtendsAreBinaryCompatible : satisfied"); return ; From e0b225d5334bf54c09e2549e9997516845e4dc04 Mon Sep 17 00:00:00 2001 From: paulklint Date: Thu, 19 Mar 2026 14:44:52 +0100 Subject: [PATCH 6/8] Small refactoring and added asserts --- .../check/tests/BinaryDependencyTests.rsc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc index 5bf5c69c70..80b0af45b8 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc @@ -46,6 +46,8 @@ import String; import lang::rascalcore::check::ModuleLocations; import util::FileSystem; import util::SemVer; +import lang::rascalcore::check::TestShared; +import lang::rascalcore::check::tests::StaticTestingUtils; // ---- Utilities for test setup ---------------------------------------------- @@ -99,6 +101,9 @@ str writeModule(str mname, str mtext){ throw "Parse error in "; } +loc getModuleLoc(str mname, Project pd) + = src(pd.name) + ".rsc"; + PathConfig createPathConfig(str pname){ return pathConfig( srcs=[src(pname)], @@ -111,7 +116,9 @@ PathConfig createPathConfig(str pname){ Project addModule(str mname, str mtext, Project pd){ pd.modules[mname] = writeModule(mname, mtext); - writeFile(src(pd.name) + ".rsc", pd.modules[mname]); + mloc = getModuleLoc(mname, pd); + writeFile(mloc, pd.modules[mname]); + assert exists(mloc) : " does not exist after write"; return pd; } @@ -119,14 +126,18 @@ Project changeModule(str mname, str mtext, Project pd){ if(!pd.modules[mname]?) throw "Module does not exist in "; pd.modules[mname] = writeModule(mname, mtext); - writeFile(src(pd.name) + ".rsc", pd.modules[mname]); + mloc = getModuleLoc(mname, pd); + writeFile(mloc, pd.modules[mname]); + assert exists(mloc) : " does not exist after write"; return pd; } Project removeSourceOfModule(str mname, Project pd){ if(!pd.modules[mname]?) throw "Cannot remove non-existing module "; pd.modules = delete(pd.modules, mname); - remove(src(pd.name) + ".rsc", recursive=true); + mloc = getModuleLoc(mname, pd); + remove(mloc, recursive=true); + assert !exists(mloc): " not removed"; return pd; } @@ -533,7 +544,7 @@ test bool incompatibleVersionsOfBinaryLibrary(){ // Important: we do not recompile TP (and thus it will contain the outdated version of IO) - // Update Checks' modification time to make sure it will rechecked + // Update Checks' modification time to make sure it will be rechecked touch(getRascalModuleLocation("Check", core.pcfg)); // Recompile Check and discover the error return checkExpectErrors("Check", ["Review of dependencies, reconfiguration or recompilation needed: binary module `TP` depends (indirectly) on incompatible module(s)"], core.pcfg, remove = [rascal, typepal, core]); From 003c5b931e80544611b4b90037d6029b8b9f50a7 Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Mon, 23 Mar 2026 12:28:24 +0100 Subject: [PATCH 7/8] Make sure files cannot race on their timestamp --- .../uri/libraries/MemoryResolver.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/org/rascalmpl/uri/libraries/MemoryResolver.java b/src/org/rascalmpl/uri/libraries/MemoryResolver.java index c174c9ae37..3f14739f3e 100644 --- a/src/org/rascalmpl/uri/libraries/MemoryResolver.java +++ b/src/org/rascalmpl/uri/libraries/MemoryResolver.java @@ -25,6 +25,8 @@ import java.nio.file.NoSuchFileException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + import org.checkerframework.checker.nullness.qual.Nullable; import org.rascalmpl.uri.FileAttributes; import org.rascalmpl.uri.ISourceLocationInputOutput; @@ -62,8 +64,29 @@ */ public class MemoryResolver implements ISourceLocationInputOutput { + private static class MemoryEntry extends FSEntry { + /** keep track of the timestamp across entries */ + private static final AtomicLong now = new AtomicLong(System.currentTimeMillis()); + + /** + * Every time we're called, we get a new timestamp, to make sure file written at roughly the same + * time don't get the same modification timestamp + * @return + */ + private static long timestamp() { + return now.accumulateAndGet(System.currentTimeMillis(), (now, prev) -> { + if (now > prev) { + return now; + } + if (now == prev) { + return now + 1; + } + // else : prev > now + return prev + 1; + }); + } private final @Nullable byte[] contents; public MemoryEntry() { @@ -71,7 +94,11 @@ public MemoryEntry() { } public MemoryEntry(@Nullable byte[] contents) { - this(System.currentTimeMillis(), System.currentTimeMillis(), contents); + this(timestamp(), contents); + } + + private MemoryEntry(long created, @Nullable byte[] contents) { + this(created, created, contents); } public MemoryEntry(long created, long lastModified) { @@ -83,19 +110,11 @@ public MemoryEntry(long created, long lastModified, @Nullable byte[] contents) { } public MemoryEntry newContents(byte[] newContents) { - long newTimestamp = System.currentTimeMillis(); - if (newTimestamp <= getLastModified()) { - newTimestamp = getLastModified() + 1; - } - return new MemoryEntry(getCreated(), newTimestamp, newContents); + return new MemoryEntry(getCreated(), timestamp(), newContents); } public MemoryEntry copy() { - long newTimestamp = System.currentTimeMillis(); - if (newTimestamp <= getLastModified()) { - newTimestamp = getLastModified() + 1; - } - return new MemoryEntry(newTimestamp, newTimestamp, contents); + return new MemoryEntry(timestamp(), contents); } } From e20c77f551ab43e44abb29e4a3b6fdf3bb4c2579 Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Mon, 23 Mar 2026 16:45:58 +0100 Subject: [PATCH 8/8] Porting fixes over from the branch --- .../check/tests/BinaryDependencyTests.rsc | 1 - .../check/tests/ChangeScenarioTests.rsc | 23 +++++++++++++++++++ .../rascalcore/check/tests/PackagerTests.rsc | 2 +- .../check/tests/StaticTestingUtils.rsc | 14 +++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc index 80b0af45b8..1cee586e9f 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc @@ -46,7 +46,6 @@ import String; import lang::rascalcore::check::ModuleLocations; import util::FileSystem; import util::SemVer; -import lang::rascalcore::check::TestShared; import lang::rascalcore::check::tests::StaticTestingUtils; diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeScenarioTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeScenarioTests.rsc index 00c5b855b5..cef2ace507 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeScenarioTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/ChangeScenarioTests.rsc @@ -469,6 +469,29 @@ test bool breakingChange1(){ return expectReChecks(D, ["C", "D"]); } +test bool fixedErrorsDisappearCompact() { + clearMemory(); + pcfg = getDefaultTestingPathConfig(); + + mlocs = writeModules(" + module ParserBase + "); + + assert checkModulesOK(mlocs, pathConfig=pcfg) : "Precondition failed: no errors expected!"; + + // Introduce a type error (import of module that does not exist) + l = writeModule(" + module ParserBase + + import vis::ParseTree; // module does not exist -\> error + + "); + + assert missingModuleInModule(l, pathConfig=pcfg) : "Precondition failed: expected at least one error, but got none!"; + + return true; +} + test bool fixedErrorsDisappear() { // ht @toinehartman clearMemory(); pcfg = getReleasedStandardLibraryTestingPathConfig(); diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/PackagerTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/PackagerTests.rsc index 62902a8459..ee153433ed 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/PackagerTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/PackagerTests.rsc @@ -48,7 +48,7 @@ private void runChecker(PathConfig pcfg, list[str] mnames) { test bool packagerRewritesTModelsCorrectly () { println("**** Checking List"); - tplRoot = |memory:///|; + tplRoot = |memory://packager-test/|; originalBin = tplRoot + "rascal-lib"; rewrittenBin = tplRoot + "rascal-lib-rewritten"; rascalLibPcfg = diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/StaticTestingUtils.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/StaticTestingUtils.rsc index 85c1d4e8a8..8393190b56 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/StaticTestingUtils.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/StaticTestingUtils.rsc @@ -42,6 +42,7 @@ import Relation; import Set; import util::Reflective; import ParseTree; +import util::FileSystem; import lang::rascalcore::check::RascalConfig; import lang::rascalcore::check::Checker; @@ -104,6 +105,19 @@ void removeModule(str mname){ remove(pcfg.generatedResources + ".tpl"); } +void printModules(){ + println("\<\<\<\<"); + for(f <- find(testRoot, "rsc")){ + println(" : + '"); + } + for(f <- find(testRoot, "tpl")){ + println(": "); + } + println("\>\>\>\>"); +} + + set[Message] getErrorMessages(ModuleStatus r) = { m | m <- getAllMessages(r), m is error };