From 67b709e6f013b5a377c6e27e477f612e41cdbf87 Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Fri, 23 Oct 2026 12:05:15 +0200 Subject: [PATCH 1/3] Working on detecting incorrect memo configurations --- .../rascalmpl/interpreter/result/NamedFunction.java | 12 ++++++++++++ .../library/lang/rascal/tests/basic/Memoization.rsc | 2 +- .../test/functionality/MemoizationTests.java | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/org/rascalmpl/interpreter/result/NamedFunction.java b/src/org/rascalmpl/interpreter/result/NamedFunction.java index 4323f410129..be1c0fac320 100644 --- a/src/org/rascalmpl/interpreter/result/NamedFunction.java +++ b/src/org/rascalmpl/interpreter/result/NamedFunction.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.Expression; @@ -30,6 +31,8 @@ import org.rascalmpl.ast.TagString; import org.rascalmpl.ast.Tags; import org.rascalmpl.exceptions.ImplementationError; +import org.rascalmpl.exceptions.RuntimeExceptionFactory; +import org.rascalmpl.exceptions.StackTrace; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.control_exceptions.MatchFailed; import org.rascalmpl.interpreter.env.Environment; @@ -39,6 +42,7 @@ import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IInteger; import io.usethesource.vallang.ISet; +import io.usethesource.vallang.IString; import io.usethesource.vallang.IValue; import io.usethesource.vallang.type.Type; @@ -58,6 +62,8 @@ abstract public class NamedFunction extends AbstractFunction { private final int memoizationTimeout; private final int memoizationMaxEntries; + private static final Pattern EMPTY_TAG = Pattern.compile("^\\{\\w*\\}$"); + public NamedFunction(AbstractAST ast, IEvaluator> eval, Type functionType, Type dynamicType, List initializers, String name, boolean varargs, boolean isDefault, boolean isTest, Environment env) { super(ast, eval, functionType, dynamicType, initializers, varargs, env); @@ -73,6 +79,12 @@ public NamedFunction(AbstractAST ast, IEvaluator> eval, Type func IValue memoTag = tags.get("memo"); if (memoTag != null) { this.hasMemoization = true; + if (memoTag instanceof IString) { + var tagValue = ((IString)memoTag).getValue(); + if (!tagValue.isEmpty() && !EMPTY_TAG.matcher(tagValue).matches()) { + throw RuntimeExceptionFactory.illegalArgument(memoTag, "Invalid memo configuration, in most cases @memo = {..} is needed instead of @memo{}", ((FunctionDeclaration)ast).getTags(), new StackTrace()); + } + } if (!(memoTag instanceof ISet)) { memoTag = vf.set(memoTag); } diff --git a/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc b/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc index e634d4f75f9..037574ce9e5 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc +++ b/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc @@ -38,7 +38,7 @@ test bool memoCalledCorrectly2(set[value] x) { test bool memoExpire() { int callCount2 = 0; - @memo=maximumSize(10) + @memo{maximumSize(10)} int call2(int i) { callCount2 += 1; return callCount2; diff --git a/test/org/rascalmpl/test/functionality/MemoizationTests.java b/test/org/rascalmpl/test/functionality/MemoizationTests.java index 433ce99dd57..a9ca7e1ce30 100644 --- a/test/org/rascalmpl/test/functionality/MemoizationTests.java +++ b/test/org/rascalmpl/test/functionality/MemoizationTests.java @@ -79,7 +79,7 @@ public void memoryIsReleasedEntries() throws InterruptedException { public void memoryIsReleasedCombination() throws InterruptedException { prepare("int n = 0;"); prepareMore("import util::Memo;"); - prepareMore("@memo={expireAfter(seconds=2),maximumSize(200)} int calc(int w) { n +=1; return n; }"); + prepareMore("@memo{expireAfter(seconds=2),maximumSize(200)} int calc(int w) { n +=1; return n; }"); assertTrue("Memo works", runTestInSameEvaluator("( true | it && calc(i) == i + 1 | i <- [0..100])")); assertTrue("Memo works", runTestInSameEvaluator("calc(1) == 2")); prepareMore("for (i <- [0..100]) { calc(100 + i); }"); From 5a62d61140c41ddb4aeeaa19f97c0c06a631a7ef Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Fri, 23 Oct 2026 12:36:21 +0200 Subject: [PATCH 2/3] Added a warning about incorrect memo configuration --- src/org/rascalmpl/interpreter/result/NamedFunction.java | 4 +--- .../rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/org/rascalmpl/interpreter/result/NamedFunction.java b/src/org/rascalmpl/interpreter/result/NamedFunction.java index be1c0fac320..aeb8aee8ed3 100644 --- a/src/org/rascalmpl/interpreter/result/NamedFunction.java +++ b/src/org/rascalmpl/interpreter/result/NamedFunction.java @@ -31,8 +31,6 @@ import org.rascalmpl.ast.TagString; import org.rascalmpl.ast.Tags; import org.rascalmpl.exceptions.ImplementationError; -import org.rascalmpl.exceptions.RuntimeExceptionFactory; -import org.rascalmpl.exceptions.StackTrace; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.control_exceptions.MatchFailed; import org.rascalmpl.interpreter.env.Environment; @@ -82,7 +80,7 @@ public NamedFunction(AbstractAST ast, IEvaluator> eval, Type func if (memoTag instanceof IString) { var tagValue = ((IString)memoTag).getValue(); if (!tagValue.isEmpty() && !EMPTY_TAG.matcher(tagValue).matches()) { - throw RuntimeExceptionFactory.illegalArgument(memoTag, "Invalid memo configuration, in most cases @memo = {..} is needed instead of @memo{}", ((FunctionDeclaration)ast).getTags(), new StackTrace()); + eval.warning("Ignored memo configuration " + tagValue + ", in most cases `@memo = {..}` is needed instead of `@memo{}`", ((FunctionDeclaration)ast).getTags().getLocation()); } } if (!(memoTag instanceof ISet)) { diff --git a/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc b/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc index 037574ce9e5..72d175b1cab 100644 --- a/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc +++ b/src/org/rascalmpl/library/lang/rascal/tests/basic/Memoization.rsc @@ -38,7 +38,7 @@ test bool memoCalledCorrectly2(set[value] x) { test bool memoExpire() { int callCount2 = 0; - @memo{maximumSize(10)} + @memo={maximumSize(10)} int call2(int i) { callCount2 += 1; return callCount2; From 35c8792cc7e57015f0de939c4c8ece59aa2fbdf4 Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Fri, 23 Oct 2026 12:42:53 +0200 Subject: [PATCH 3/3] Fixed all memo configurations --- .../compiler/lang/rascalcore/check/ATypeBase.rsc | 3 ++- .../compiler/lang/rascalcore/check/ModuleLocations.rsc | 3 ++- .../compiler/lang/rascalcore/check/RascalConfig.rsc | 5 +++-- src/org/rascalmpl/interpreter/result/NamedFunction.java | 8 ++------ .../rascalmpl/test/functionality/MemoizationTests.java | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/ATypeBase.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/ATypeBase.rsc index 62e3fe12171..bda7c86441b 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/ATypeBase.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/ATypeBase.rsc @@ -39,6 +39,7 @@ extend lang::rascal::\syntax::Rascal; import List; import Set; //import Node; +import util::Memo; import String; data Keyword @@ -89,7 +90,7 @@ data AType (str alabel = "") | avalue() ; -@memo{expireAfter(minutes=5),maximumSize(1000)} +@memo={expireAfter(minutes=5),maximumSize(1000)} AType overloadedAType(rel[loc, IdRole, AType] overloads){ if(all( <- overloads, aadt(_, _, _) := t)){ str adtName = ""; diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc index bf72bb487bd..228da061701 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc @@ -35,6 +35,7 @@ import util::Reflective; import util::FileSystem; import lang::rascalcore::check::BasicRascalConfig; import lang::rascalcore::check::LogicalLocations; +import util::Memo; str makeFileName(str qualifiedModuleName, str extension = "rsc") { str qnameSlashes = replaceAll(qualifiedModuleName, "::", "/"); @@ -101,7 +102,7 @@ int commonPrefix(list[str] rdir, list[str] rm){ } return size(rm); } -@memo{expireAfter(minutes=5),maximumSize(500)} +@memo={expireAfter(minutes=5),maximumSize(500)} @synopsis{Find the module name corresponding to a given module location via its (src, tpl or logical) location} str getRascalModuleName(loc moduleLoc, PathConfig pcfg){ modulePath = moduleLoc.path; diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/RascalConfig.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/RascalConfig.rsc index e02a5287979..1fec2716c42 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/RascalConfig.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/RascalConfig.rsc @@ -55,6 +55,7 @@ import Map; import Set; import Relation; import String; +import util::Memo; str parserPackage = "org.rascalmpl.core.library.lang.rascalcore.grammar.tests.generated_parsers"; @@ -136,7 +137,7 @@ bool rascalMayOverload(set[loc] defs, map[loc, Define] defines){ set[IdRole] defBeforeUseRoles = {variableId(), moduleVariableId(), formalId(), keywordFormalId(), patternVariableId()}; -@memo{expireAfter(minutes=5),maximumSize(1000)} +@memo={expireAfter(minutes=5),maximumSize(1000)} Accept rascalIsAcceptableSimple(loc def, Use use, Solver s){ //println("rascalIsAcceptableSimple: *** *** def=, use="); Define d = s.getDefine(def); @@ -577,7 +578,7 @@ loc rascalCreateLogicalLoc(Define def, str modelName, PathConfig pcfg){ return def.defined; } -@memo{expireAfter(minutes=5),maximumSize(1000)} +@memo={expireAfter(minutes=5),maximumSize(1000)} rel[str shortName, str longName] getRascalModuleNames(PathConfig pcfg){ longNames = {}; for(srcdir <- pcfg.srcs){ diff --git a/src/org/rascalmpl/interpreter/result/NamedFunction.java b/src/org/rascalmpl/interpreter/result/NamedFunction.java index aeb8aee8ed3..8e7d0a4ecc1 100644 --- a/src/org/rascalmpl/interpreter/result/NamedFunction.java +++ b/src/org/rascalmpl/interpreter/result/NamedFunction.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.Expression; @@ -60,8 +59,6 @@ abstract public class NamedFunction extends AbstractFunction { private final int memoizationTimeout; private final int memoizationMaxEntries; - private static final Pattern EMPTY_TAG = Pattern.compile("^\\{\\w*\\}$"); - public NamedFunction(AbstractAST ast, IEvaluator> eval, Type functionType, Type dynamicType, List initializers, String name, boolean varargs, boolean isDefault, boolean isTest, Environment env) { super(ast, eval, functionType, dynamicType, initializers, varargs, env); @@ -78,9 +75,8 @@ public NamedFunction(AbstractAST ast, IEvaluator> eval, Type func if (memoTag != null) { this.hasMemoization = true; if (memoTag instanceof IString) { - var tagValue = ((IString)memoTag).getValue(); - if (!tagValue.isEmpty() && !EMPTY_TAG.matcher(tagValue).matches()) { - eval.warning("Ignored memo configuration " + tagValue + ", in most cases `@memo = {..}` is needed instead of `@memo{}`", ((FunctionDeclaration)ast).getTags().getLocation()); + if (!((IString)memoTag).getValue().isBlank()) { + eval.warning("Ignored memo configuration " + memoTag + ", in most cases `@memo={..}` is needed instead of `@memo{..}`", ((FunctionDeclaration)ast).getTags().getLocation()); } } if (!(memoTag instanceof ISet)) { diff --git a/test/org/rascalmpl/test/functionality/MemoizationTests.java b/test/org/rascalmpl/test/functionality/MemoizationTests.java index a9ca7e1ce30..433ce99dd57 100644 --- a/test/org/rascalmpl/test/functionality/MemoizationTests.java +++ b/test/org/rascalmpl/test/functionality/MemoizationTests.java @@ -79,7 +79,7 @@ public void memoryIsReleasedEntries() throws InterruptedException { public void memoryIsReleasedCombination() throws InterruptedException { prepare("int n = 0;"); prepareMore("import util::Memo;"); - prepareMore("@memo{expireAfter(seconds=2),maximumSize(200)} int calc(int w) { n +=1; return n; }"); + prepareMore("@memo={expireAfter(seconds=2),maximumSize(200)} int calc(int w) { n +=1; return n; }"); assertTrue("Memo works", runTestInSameEvaluator("( true | it && calc(i) == i + 1 | i <- [0..100])")); assertTrue("Memo works", runTestInSameEvaluator("calc(1) == 2")); prepareMore("for (i <- [0..100]) { calc(100 + i); }");