From dd4c16c7caddd94123642e7aad1c9560382b94f7 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Thu, 16 Jul 2026 16:28:59 +0200 Subject: [PATCH 1/2] Resolve checked identifiers directly Plan undecorated checked top-level variables as direct activation lookups instead of absolute attributes. Select roots and decorated programs keep the attribute shape so qualifier planning, partial unknowns, and custom decorators retain their existing behavior. --- .../cel/interpreter/Interpretable.java | 45 +++++++++++++++++++ .../cel/interpreter/InterpretablePlanner.java | 28 +++++++++++- .../cel/interpreter/InterpreterTest.java | 22 +++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java index 3e332db8..78aa621e 100644 --- a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java +++ b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java @@ -19,9 +19,11 @@ import static org.projectnessie.cel.common.types.BoolT.True; import static org.projectnessie.cel.common.types.Err.isError; import static org.projectnessie.cel.common.types.Err.newErr; +import static org.projectnessie.cel.common.types.Err.noSuchAttributeException; import static org.projectnessie.cel.common.types.Err.noSuchOverload; import static org.projectnessie.cel.common.types.Err.valOrErr; import static org.projectnessie.cel.common.types.UnknownT.isUnknown; +import static org.projectnessie.cel.common.types.UnknownT.unknownOf; import static org.projectnessie.cel.common.types.Util.isUnknownOrError; import static org.projectnessie.cel.interpreter.Activation.emptyActivation; import static org.projectnessie.cel.interpreter.Coster.Cost.OneOne; @@ -54,6 +56,7 @@ import org.projectnessie.cel.common.types.traits.Receiver; import org.projectnessie.cel.common.types.traits.Sizer; import org.projectnessie.cel.common.types.traits.Trait; +import org.projectnessie.cel.interpreter.Activation.PartialActivation; import org.projectnessie.cel.interpreter.Activation.VarActivation; import org.projectnessie.cel.interpreter.AttributeFactory.Attribute; import org.projectnessie.cel.interpreter.AttributeFactory.ConditionalAttribute; @@ -143,6 +146,48 @@ interface InterpretableCall extends Interpretable { // Core Interpretable implementations used during the program planning phase. + /** evalIdent evaluates a checked top-level variable directly from the activation. */ + final class EvalIdent extends AbstractEval implements Coster { + private final String name; + private final TypeAdapter adapter; + + EvalIdent(long id, String name, TypeAdapter adapter) { + super(id); + this.name = Objects.requireNonNull(name); + this.adapter = Objects.requireNonNull(adapter); + } + + /** Eval implements the Interpretable interface method. */ + @Override + public Val eval(Activation ctx) { + if (ctx instanceof PartialActivation) { + for (AttributePattern pattern : ((PartialActivation) ctx).unknownAttributePatterns()) { + if (pattern.variableMatches(name)) { + return unknownOf(id); + } + } + } + + ResolvedValue value = ctx.resolveName(name); + if (!value.present()) { + RuntimeException err = noSuchAttributeException("id: " + id + ", names: [" + name + "]"); + return newErr(err, err.toString()); + } + return adapter.nativeToValue(value.value()); + } + + /** Cost implements the Coster interface method. */ + @Override + public Cost cost() { + return OneOne; + } + + @Override + public String toString() { + return "EvalIdent{" + "id=" + id + ", name='" + name + '\'' + '}'; + } + } + final class EvalTestOnly implements Interpretable, Coster { private final long id; private final Interpretable op; diff --git a/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java b/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java index 25c522ed..3e6d1007 100644 --- a/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java +++ b/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java @@ -55,6 +55,7 @@ import org.projectnessie.cel.interpreter.Interpretable.EvalBinary; import org.projectnessie.cel.interpreter.Interpretable.EvalEq; import org.projectnessie.cel.interpreter.Interpretable.EvalFold; +import org.projectnessie.cel.interpreter.Interpretable.EvalIdent; import org.projectnessie.cel.interpreter.Interpretable.EvalList; import org.projectnessie.cel.interpreter.Interpretable.EvalListFold; import org.projectnessie.cel.interpreter.Interpretable.EvalMap; @@ -238,7 +239,11 @@ Interpretable planCheckedIdent(long id, Reference identRef) { return newConstValue(id, cVal); } - // Otherwise, return the attribute for the resolved identifier name. + // Otherwise, evaluate the checked top-level variable directly for ordinary plans. Decorated + // programs keep the attribute shape because custom decorators may inspect attributes. + if (decorators.length == 0) { + return new EvalIdent(id, identRef.getName(), adapter); + } return new EvalAttr(adapter, attrFactory.absoluteAttribute(id, identRef.getName())); } @@ -261,7 +266,7 @@ Interpretable planSelect(Expr expr) { Select sel = expr.getSelectExpr(); // Plan the operand evaluation. - Interpretable op = plan(sel.getOperand()); + Interpretable op = planSelectOperand(sel.getOperand()); // Determine the field type if this is a proto message type. FieldType fieldType = null; @@ -314,6 +319,25 @@ Interpretable planSelect(Expr expr) { return relAttr; } + private Interpretable planSelectOperand(Expr operand) { + if (operand.getExprKindCase() != Expr.ExprKindCase.IDENT_EXPR) { + return plan(operand); + } + + Reference identRef = refMap.get(operand.getId()); + if (identRef == null || identRef.getValue() != Reference.getDefaultInstance().getValue()) { + return plan(operand); + } + + Type cType = typeMap.get(operand.getId()); + if (cType != null && cType.getType() != Type.getDefaultInstance()) { + return plan(operand); + } + + return new EvalAttr( + adapter, attrFactory.absoluteAttribute(operand.getId(), identRef.getName())); + } + private FieldType findFieldType(String messageType, String fieldName) { String key = messageType + '\n' + fieldName; FieldType ft = fieldTypes.get(key); diff --git a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java index beb18580..f89981df 100644 --- a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java +++ b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java @@ -1579,6 +1579,28 @@ void missingIdentInSelect() { assertThat(result).isInstanceOf(Err.class); } + @Test + void checkedTopLevelIdentPreservesActivationSemantics() { + Source src = newTextSource("x"); + ParseResult parsed = Parser.parseAllMacros(src); + assertThat(parsed.hasErrors()).withFailMessage(parsed.getErrors()::toDisplayString).isFalse(); + + Container cont = testContainer("test"); + TypeRegistry reg = newRegistry(); + CheckerEnv env = newStandardCheckerEnv(cont, reg); + env.add(Decls.newVar("x", Decls.Bool)); + CheckResult checkResult = Checker.Check(parsed, src, env); + + AttributeFactory attrs = newPartialAttributeFactory(cont, reg, reg); + Interpreter interp = newStandardInterpreter(cont, reg, reg, attrs); + Interpretable i = interp.newInterpretable(checkResult.getCheckedExpr()); + + assertThat(i.eval(newActivation(mapOf("x", true)))).isSameAs(True); + assertThat(i.eval(emptyActivation())).isInstanceOf(Err.class); + assertThat(i.eval(newPartialActivation(mapOf("x", true), newAttributePattern("x")))) + .isInstanceOf(UnknownT.class); + } + @Test void equalityWithUnknownOperandStaysUnknown() { assertThat(evalWithUnknownStringX("x == 'foo'")).isInstanceOf(UnknownT.class); From 6d272bbb324a1afaf176ee4599124a3cb21f577a Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 22 Jul 2026 20:05:12 +0200 Subject: [PATCH 2/2] fix --- .../cel/interpreter/InterpretablePlanner.java | 3 ++- .../cel/interpreter/InterpreterTest.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java b/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java index 3e6d1007..6fee2a25 100644 --- a/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java +++ b/core/src/main/java/org/projectnessie/cel/interpreter/InterpretablePlanner.java @@ -266,7 +266,8 @@ Interpretable planSelect(Expr expr) { Select sel = expr.getSelectExpr(); // Plan the operand evaluation. - Interpretable op = planSelectOperand(sel.getOperand()); + Interpretable op = + decorators.length == 0 ? planSelectOperand(sel.getOperand()) : plan(sel.getOperand()); // Determine the field type if this is a proto message type. FieldType fieldType = null; diff --git a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java index f89981df..9cc589bb 100644 --- a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java +++ b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java @@ -1601,6 +1601,32 @@ void checkedTopLevelIdentPreservesActivationSemantics() { .isInstanceOf(UnknownT.class); } + @Test + void checkedTopLevelIdentSelectPreservesTrackedState() { + Source src = newTextSource("a.b"); + ParseResult parsed = Parser.parseAllMacros(src); + assertThat(parsed.hasErrors()).withFailMessage(parsed.getErrors()::toDisplayString).isFalse(); + + Container cont = testContainer("test"); + TypeRegistry reg = newRegistry(); + CheckerEnv env = newStandardCheckerEnv(cont, reg); + env.add(Decls.newVar("a", Decls.newMapType(Decls.String, Decls.Bool))); + CheckResult checkResult = Checker.Check(parsed, src, env); + + Expr select = checkResult.getCheckedExpr().getExpr(); + long selectId = select.getId(); + long operandId = select.getSelectExpr().getOperand().getId(); + EvalState state = newEvalState(); + AttributeFactory attrs = newAttributeFactory(cont, reg, reg); + Interpreter interp = newStandardInterpreter(cont, reg, reg, attrs); + Interpretable i = interp.newInterpretable(checkResult.getCheckedExpr(), trackState(state)); + + assertThat(i.eval(newActivation(mapOf("a", mapOf("b", true))))).isSameAs(True); + assertThat(state.ids()).containsExactlyInAnyOrder(operandId, selectId); + assertThat(state.value(operandId)).isSameAs(True); + assertThat(state.value(selectId)).isSameAs(True); + } + @Test void equalityWithUnknownOperandStaysUnknown() { assertThat(evalWithUnknownStringX("x == 'foo'")).isInstanceOf(UnknownT.class);