From baa36af4daa6423cd25fe048f6502a87ad646eac Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Wed, 17 Sep 2025 14:34:54 +0200 Subject: [PATCH 1/4] moved lookup of generic keyword parameters to declaration site of a constructor, such that it can be kept static and reused between field accesses --- .../rascalmpl/interpreter/env/ModuleEnvironment.java | 3 ++- .../rascalmpl/interpreter/matching/NodePattern.java | 4 ++-- .../result/ConcreteConstructorFunction.java | 3 ++- .../interpreter/result/ConstructorFunction.java | 10 ++++++---- .../interpreter/result/ConstructorResult.java | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java index f5244081013..8f27b4fbcc1 100644 --- a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java @@ -611,7 +611,8 @@ private Type makeTupleType(Type adt, String name, Type tupleType) { @Override public ConstructorFunction constructorFromTuple(AbstractAST ast, Evaluator eval, Type adt, String name, Type tupleType, List initializers) { Type cons = makeTupleType(adt, name, tupleType); - ConstructorFunction function = new ConstructorFunction(ast, eval, this, cons, initializers); + var genericKwps = lookupGenericKeywordParameters(cons.getAbstractDataType()); + ConstructorFunction function = new ConstructorFunction(ast, eval, this, cons, initializers, genericKwps); storeFunction(name, function); markNameFinal(name); markNameOverloadable(name); diff --git a/src/org/rascalmpl/interpreter/matching/NodePattern.java b/src/org/rascalmpl/interpreter/matching/NodePattern.java index e3299284844..685d90e0ba3 100644 --- a/src/org/rascalmpl/interpreter/matching/NodePattern.java +++ b/src/org/rascalmpl/interpreter/matching/NodePattern.java @@ -153,7 +153,7 @@ public void initMatch(Result subject){ for (String kwLabel : keywordParameters.keySet()) { IValue subjectParam = kwArgs.get(kwLabel); if (subjectParam == null) { - subjectParam = func.computeDefaultKeywordParameter(kwLabel, (IConstructor) subject.getValue(), ctx.getCurrentEnvt()).getValue(); + subjectParam = func.computeDefaultKeywordParameter(kwLabel, (IConstructor) subject.getValue()).getValue(); } IMatchingResult matcher = keywordParameters.get(kwLabel); matcher.initMatch(ResultFactory.makeResult(kwFormals.get(kwLabel), subjectParam, ctx)); @@ -179,7 +179,7 @@ else if (this.subject.mayHaveKeywordParameters()) { IValue subjectParam = kwArgs.get(entry.getKey()); if (subjectParam == null && func != null) { // we may have a default available - subjectParam = func.computeDefaultKeywordParameter(entry.getKey(), (IConstructor) subject.getValue(), ctx.getCurrentEnvt()).getValue(); + subjectParam = func.computeDefaultKeywordParameter(entry.getKey(), (IConstructor) subject.getValue()).getValue(); } if (func != null) { diff --git a/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java b/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java index 7b9566716d4..e433bee8456 100644 --- a/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java +++ b/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java @@ -22,6 +22,7 @@ import org.rascalmpl.ast.KeywordFormal; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.env.Environment; +import org.rascalmpl.interpreter.env.ModuleEnvironment.GenericKeywordParameters; import org.rascalmpl.types.NonTerminalType; import org.rascalmpl.types.RascalTypeFactory; @@ -40,7 +41,7 @@ public class ConcreteConstructorFunction extends ConstructorFunction { public ConcreteConstructorFunction(AbstractAST ast, Type constructorType, IEvaluator> eval, Environment env) { - super(ast, eval, env, constructorType, Collections.emptyList()); + super(ast, eval, env, constructorType, Collections.emptyList(), Collections.emptySet()); } @Override diff --git a/src/org/rascalmpl/interpreter/result/ConstructorFunction.java b/src/org/rascalmpl/interpreter/result/ConstructorFunction.java index 0b7fa2d8631..b47d4470866 100644 --- a/src/org/rascalmpl/interpreter/result/ConstructorFunction.java +++ b/src/org/rascalmpl/interpreter/result/ConstructorFunction.java @@ -48,8 +48,9 @@ public class ConstructorFunction extends NamedFunction { protected final Type constructorType; private Type kwTypes = null; // cache private final List initializers; + private final Set genericKwps; - public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Environment env, Type constructorType, List initializers) { + public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Environment env, Type constructorType, List initializers, Set genericKwps) { super(ast, eval, TF.functionType(constructorType.getAbstractDataType(), constructorType.getFieldTypes(), TypeDeclarationEvaluator.computeKeywordParametersType(initializers, eval)), @@ -63,6 +64,7 @@ public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Env ); this.constructorType = constructorType; this.initializers = initializers; + this.genericKwps = genericKwps; } public Type getConstructorType() { @@ -71,12 +73,12 @@ public Type getConstructorType() { @Override public ConstructorFunction cloneInto(Environment env) { - return new ConstructorFunction(getAst(), getEval(), env, constructorType, initializers); + return new ConstructorFunction(getAst(), getEval(), env, constructorType, initializers, genericKwps); } // TODO: refactor and make small. For now this does the job. - public Result computeDefaultKeywordParameter(String label, IConstructor value, Environment callerEnvironment) { - Set kws = callerEnvironment.lookupGenericKeywordParameters(constructorType.getAbstractDataType()); + public Result computeDefaultKeywordParameter(String label, IConstructor value) { + Set kws = genericKwps; IWithKeywordParameters wkw = value.asWithKeywordParameters(); Environment old = ctx.getCurrentEnvt(); Environment resultEnv = new Environment(declarationEnvironment, URIUtil.rootLocation("initializer"), "keyword parameter initializer"); diff --git a/src/org/rascalmpl/interpreter/result/ConstructorResult.java b/src/org/rascalmpl/interpreter/result/ConstructorResult.java index f8577dee587..0e589083013 100644 --- a/src/org/rascalmpl/interpreter/result/ConstructorResult.java +++ b/src/org/rascalmpl/interpreter/result/ConstructorResult.java @@ -157,7 +157,7 @@ public Result keywordFieldAccess(Type consType, String nam ConstructorFunction cons = ctx.getCurrentEnvt().getConstructorFunction(consType); if (cons != null) { - return (Result) cons.computeDefaultKeywordParameter(name, getValue(), ctx.getCurrentEnvt()); + return (Result) cons.computeDefaultKeywordParameter(name, getValue()); } else { // The constructor is not in scope, but there might be a generic keyword parameter in scope nevertheless From ce70afe27ca107911ca0e89085a46b42700e9b00 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 18 Sep 2025 10:56:26 +0200 Subject: [PATCH 2/4] Revert "moved lookup of generic keyword parameters to declaration site of a constructor, such that it can be kept static and reused between field accesses" This reverts commit baa36af4daa6423cd25fe048f6502a87ad646eac. --- .../rascalmpl/interpreter/env/ModuleEnvironment.java | 3 +-- .../rascalmpl/interpreter/matching/NodePattern.java | 4 ++-- .../result/ConcreteConstructorFunction.java | 3 +-- .../interpreter/result/ConstructorFunction.java | 10 ++++------ .../interpreter/result/ConstructorResult.java | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java index 8f27b4fbcc1..f5244081013 100644 --- a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java @@ -611,8 +611,7 @@ private Type makeTupleType(Type adt, String name, Type tupleType) { @Override public ConstructorFunction constructorFromTuple(AbstractAST ast, Evaluator eval, Type adt, String name, Type tupleType, List initializers) { Type cons = makeTupleType(adt, name, tupleType); - var genericKwps = lookupGenericKeywordParameters(cons.getAbstractDataType()); - ConstructorFunction function = new ConstructorFunction(ast, eval, this, cons, initializers, genericKwps); + ConstructorFunction function = new ConstructorFunction(ast, eval, this, cons, initializers); storeFunction(name, function); markNameFinal(name); markNameOverloadable(name); diff --git a/src/org/rascalmpl/interpreter/matching/NodePattern.java b/src/org/rascalmpl/interpreter/matching/NodePattern.java index 685d90e0ba3..e3299284844 100644 --- a/src/org/rascalmpl/interpreter/matching/NodePattern.java +++ b/src/org/rascalmpl/interpreter/matching/NodePattern.java @@ -153,7 +153,7 @@ public void initMatch(Result subject){ for (String kwLabel : keywordParameters.keySet()) { IValue subjectParam = kwArgs.get(kwLabel); if (subjectParam == null) { - subjectParam = func.computeDefaultKeywordParameter(kwLabel, (IConstructor) subject.getValue()).getValue(); + subjectParam = func.computeDefaultKeywordParameter(kwLabel, (IConstructor) subject.getValue(), ctx.getCurrentEnvt()).getValue(); } IMatchingResult matcher = keywordParameters.get(kwLabel); matcher.initMatch(ResultFactory.makeResult(kwFormals.get(kwLabel), subjectParam, ctx)); @@ -179,7 +179,7 @@ else if (this.subject.mayHaveKeywordParameters()) { IValue subjectParam = kwArgs.get(entry.getKey()); if (subjectParam == null && func != null) { // we may have a default available - subjectParam = func.computeDefaultKeywordParameter(entry.getKey(), (IConstructor) subject.getValue()).getValue(); + subjectParam = func.computeDefaultKeywordParameter(entry.getKey(), (IConstructor) subject.getValue(), ctx.getCurrentEnvt()).getValue(); } if (func != null) { diff --git a/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java b/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java index e433bee8456..7b9566716d4 100644 --- a/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java +++ b/src/org/rascalmpl/interpreter/result/ConcreteConstructorFunction.java @@ -22,7 +22,6 @@ import org.rascalmpl.ast.KeywordFormal; import org.rascalmpl.interpreter.IEvaluator; import org.rascalmpl.interpreter.env.Environment; -import org.rascalmpl.interpreter.env.ModuleEnvironment.GenericKeywordParameters; import org.rascalmpl.types.NonTerminalType; import org.rascalmpl.types.RascalTypeFactory; @@ -41,7 +40,7 @@ public class ConcreteConstructorFunction extends ConstructorFunction { public ConcreteConstructorFunction(AbstractAST ast, Type constructorType, IEvaluator> eval, Environment env) { - super(ast, eval, env, constructorType, Collections.emptyList(), Collections.emptySet()); + super(ast, eval, env, constructorType, Collections.emptyList()); } @Override diff --git a/src/org/rascalmpl/interpreter/result/ConstructorFunction.java b/src/org/rascalmpl/interpreter/result/ConstructorFunction.java index b47d4470866..0b7fa2d8631 100644 --- a/src/org/rascalmpl/interpreter/result/ConstructorFunction.java +++ b/src/org/rascalmpl/interpreter/result/ConstructorFunction.java @@ -48,9 +48,8 @@ public class ConstructorFunction extends NamedFunction { protected final Type constructorType; private Type kwTypes = null; // cache private final List initializers; - private final Set genericKwps; - public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Environment env, Type constructorType, List initializers, Set genericKwps) { + public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Environment env, Type constructorType, List initializers) { super(ast, eval, TF.functionType(constructorType.getAbstractDataType(), constructorType.getFieldTypes(), TypeDeclarationEvaluator.computeKeywordParametersType(initializers, eval)), @@ -64,7 +63,6 @@ public ConstructorFunction(AbstractAST ast, IEvaluator> eval, Env ); this.constructorType = constructorType; this.initializers = initializers; - this.genericKwps = genericKwps; } public Type getConstructorType() { @@ -73,12 +71,12 @@ public Type getConstructorType() { @Override public ConstructorFunction cloneInto(Environment env) { - return new ConstructorFunction(getAst(), getEval(), env, constructorType, initializers, genericKwps); + return new ConstructorFunction(getAst(), getEval(), env, constructorType, initializers); } // TODO: refactor and make small. For now this does the job. - public Result computeDefaultKeywordParameter(String label, IConstructor value) { - Set kws = genericKwps; + public Result computeDefaultKeywordParameter(String label, IConstructor value, Environment callerEnvironment) { + Set kws = callerEnvironment.lookupGenericKeywordParameters(constructorType.getAbstractDataType()); IWithKeywordParameters wkw = value.asWithKeywordParameters(); Environment old = ctx.getCurrentEnvt(); Environment resultEnv = new Environment(declarationEnvironment, URIUtil.rootLocation("initializer"), "keyword parameter initializer"); diff --git a/src/org/rascalmpl/interpreter/result/ConstructorResult.java b/src/org/rascalmpl/interpreter/result/ConstructorResult.java index 0e589083013..f8577dee587 100644 --- a/src/org/rascalmpl/interpreter/result/ConstructorResult.java +++ b/src/org/rascalmpl/interpreter/result/ConstructorResult.java @@ -157,7 +157,7 @@ public Result keywordFieldAccess(Type consType, String nam ConstructorFunction cons = ctx.getCurrentEnvt().getConstructorFunction(consType); if (cons != null) { - return (Result) cons.computeDefaultKeywordParameter(name, getValue()); + return (Result) cons.computeDefaultKeywordParameter(name, getValue(), ctx.getCurrentEnvt()); } else { // The constructor is not in scope, but there might be a generic keyword parameter in scope nevertheless From 281130502a549ada8b23898d8657be2d1fa36c51 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 18 Sep 2025 11:40:22 +0200 Subject: [PATCH 3/4] added generic keyword parameter cache as an experiment --- .../interpreter/env/ModuleEnvironment.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java index f5244081013..1683ac4c066 100644 --- a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java @@ -78,10 +78,11 @@ public class ModuleEnvironment extends Environment { private boolean bootstrap; private String deprecated; protected Map resourceImporters; + protected Map> cachedGeneralKeywordParameters; protected static final TypeFactory TF = TypeFactory.getInstance(); - public final static String SHELL_MODULE = "$shell$"; + public final static String SHELL_MODULE = "$"; public ModuleEnvironment(String name, GlobalEnvironment heap) { super(ValueFactoryFactory.getValueFactory().sourceLocation(URIUtil.assumeCorrect("main", name, "")), name); @@ -95,6 +96,7 @@ public ModuleEnvironment(String name, GlobalEnvironment heap) { this.syntaxDefined = false; this.bootstrap = false; this.resourceImporters = new HashMap(); + this.cachedGeneralKeywordParameters = null; } /** @@ -113,14 +115,15 @@ protected ModuleEnvironment(ModuleEnvironment env) { this.syntaxDefined = env.syntaxDefined; this.bootstrap = env.bootstrap; this.resourceImporters = env.resourceImporters; + this.cachedGeneralKeywordParameters = null; this.deprecated = env.deprecated; } @Override public void reset() { super.reset(); - this.importedModules = new HashSet(); - this.concreteSyntaxTypes = new HashMap(); + this.importedModules = new HashSet<>(); + this.concreteSyntaxTypes = new HashMap<>(); this.typeStore = new TypeStore(); this.productions = new HashSet(); this.initialized = false; @@ -128,10 +131,11 @@ public void reset() { this.bootstrap = false; this.extended = new HashSet(); this.deprecated = null; + this.generalKeywordParameters = new HashMap<>(); + this.cachedGeneralKeywordParameters = null; } public void extend(ModuleEnvironment other) { -// super.extend(other); extendNameFlags(other); // First extend the imports before functions and variables @@ -710,6 +714,16 @@ public List getFormals() { @Override public Set lookupGenericKeywordParameters(Type adt) { + if (cachedGeneralKeywordParameters != null) { + var result = cachedGeneralKeywordParameters.get(adt); + if (result != null) { + return result; + } + } + else { + cachedGeneralKeywordParameters = io.usethesource.capsule.Map.Transient.of(); + } + Set result = new HashSet<>(); List list = generalKeywordParameters.get(adt); if (list != null) { @@ -725,6 +739,9 @@ public Set lookupGenericKeywordParameters(Type adt) { } } + // save the result for next time + cachedGeneralKeywordParameters.put(adt, result); + return result; } From beb2078c3b5cf8d5e9a227c91d432d0ecc62cd3a Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 18 Sep 2025 17:29:29 +0200 Subject: [PATCH 4/4] clear kwparam cache when reloading --- src/org/rascalmpl/interpreter/env/ModuleEnvironment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java index 1683ac4c066..90760faf6a6 100644 --- a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java @@ -390,10 +390,12 @@ public void unImport(String moduleName) { typeStore.unimportStores(new TypeStore[] { old.getStore() }); } } + cachedGeneralKeywordParameters = null; } public void unExtend(String moduleName) { extended.remove(moduleName); + cachedGeneralKeywordParameters = null; } @Override