Skip to content

Commit 2dc0e07

Browse files
l46kokcopybara-github
authored andcommitted
Fix comprehension variable mangling scopes in AstMutator
PiperOrigin-RevId: 1005927619
1 parent b7096de commit 2dc0e07

3 files changed

Lines changed: 60 additions & 39 deletions

File tree

‎optimizer/src/main/java/dev/cel/optimizer/AstMutator.java‎

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -677,55 +677,43 @@ private CelMutableExpr mangleIdentsInComprehensionExpr(
677677
String originalAccuVar,
678678
MangledComprehensionName mangledComprehensionName) {
679679
CelMutableComprehension comprehension = comprehensionExpr.comprehension();
680-
replaceIdentName(
681-
comprehension.loopStep(), originalIterVar, mangledComprehensionName.iterVarName());
682-
replaceIdentName(comprehensionExpr, originalAccuVar, mangledComprehensionName.resultName());
683-
680+
// Iteration variables are in scope of the loop condition and step, the accumulator also of the
681+
// result. Neither is in scope of the iteration range or the accumulator initializer.
682+
renameInLoop(comprehension, originalIterVar, mangledComprehensionName.iterVarName());
684683
comprehension.setIterVar(mangledComprehensionName.iterVarName());
685-
686-
// Most standard macros set accu_var as @result, but not all (ex: cel.bind).
687-
if (comprehension.accuVar().equals(originalAccuVar)) {
688-
comprehension.setAccuVar(mangledComprehensionName.resultName());
689-
}
690-
691684
if (!originalIterVar2.isEmpty()) {
685+
renameInLoop(comprehension, originalIterVar2, mangledComprehensionName.iterVar2Name());
692686
comprehension.setIterVar2(mangledComprehensionName.iterVar2Name());
693-
replaceIdentName(
694-
comprehension.loopStep(), originalIterVar2, mangledComprehensionName.iterVar2Name());
695687
}
688+
String resultName = mangledComprehensionName.resultName();
689+
renameInLoop(comprehension, originalAccuVar, resultName);
690+
comprehension.setResult(replaceIdentName(comprehension.result(), originalAccuVar, resultName));
691+
comprehension.setAccuVar(resultName);
696692

697693
return mutateExpr(NO_OP_ID_GENERATOR, root, comprehensionExpr, comprehensionExpr.id());
698694
}
699695

700-
private void replaceIdentName(
701-
CelMutableExpr comprehensionExpr, String originalIdentName, String newIdentName) {
702-
int iterCount;
703-
for (iterCount = 0; iterCount < iterationLimit; iterCount++) {
704-
CelMutableExpr identToMangle =
705-
CelNavigableMutableExpr.fromExpr(comprehensionExpr)
706-
.descendants()
707-
.map(CelNavigableMutableExpr::expr)
708-
.filter(
709-
node ->
710-
node.getKind().equals(ExprKind.Kind.IDENT)
711-
&& node.ident().name().equals(originalIdentName))
712-
.findAny()
713-
.orElse(null);
714-
if (identToMangle == null) {
715-
break;
716-
}
717-
718-
comprehensionExpr =
719-
mutateExpr(
720-
NO_OP_ID_GENERATOR,
721-
comprehensionExpr,
722-
CelMutableExpr.ofIdent(newIdentName),
723-
identToMangle.id());
724-
}
696+
private void renameInLoop(CelMutableComprehension comprehension, String from, String to) {
697+
comprehension.setLoopCondition(replaceIdentName(comprehension.loopCondition(), from, to));
698+
comprehension.setLoopStep(replaceIdentName(comprehension.loopStep(), from, to));
699+
}
725700

726-
if (iterCount >= iterationLimit) {
727-
throw new IllegalStateException("Max iteration count reached.");
701+
// Returns the renamed expression, which is a new node if `expr` itself is a matching identifier.
702+
private CelMutableExpr replaceIdentName(
703+
CelMutableExpr expr, String originalIdentName, String newIdentName) {
704+
ImmutableList<Long> identIds =
705+
CelNavigableMutableExpr.fromExpr(expr)
706+
.allNodes()
707+
.filter(
708+
node ->
709+
node.getKind().equals(ExprKind.Kind.IDENT)
710+
&& node.expr().ident().name().equals(originalIdentName))
711+
.map(CelNavigableMutableExpr::id)
712+
.collect(toImmutableList());
713+
for (long id : identIds) {
714+
expr = mutateExpr(NO_OP_ID_GENERATOR, expr, CelMutableExpr.ofIdent(newIdentName), id);
728715
}
716+
return expr;
729717
}
730718

731719
private CelMutableSource mangleIdentsInMacroSource(

‎optimizer/src/test/java/dev/cel/optimizer/AstMutatorTest.java‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,23 @@ public void mangleComprehensionVariable_nonIdentComprehensionResult() throws Exc
846846
assertConsistentMacroCalls(mangledAst);
847847
}
848848

849+
@Test
850+
public void mangleComprehensionVariable_bindInitReferencesOuterVariable() throws Exception {
851+
// The `x` in the initializer refers to the outer variable, not to the one being bound.
852+
CelAbstractSyntaxTree ast = CEL.compile("cel.bind(x, x, x + 1)").getAst();
853+
854+
CelAbstractSyntaxTree mangledAst =
855+
AST_MUTATOR
856+
.mangleComprehensionIdentifierNames(CelMutableAst.fromCelAst(ast), "@it", "@it2", "@ac")
857+
.mutableAst()
858+
.toParsedAst();
859+
860+
assertThat(CEL_UNPARSER.unparse(mangledAst)).isEqualTo("cel.bind(x, x, @ac:0:0 + 1)");
861+
assertThat(mangledAst.getExpr().comprehension().loopStep().ident().name()).isEqualTo("@ac:0:0");
862+
assertThat(CEL.createProgram(CEL.check(mangledAst).getAst()).eval(ImmutableMap.of("x", 5L)))
863+
.isEqualTo(6L);
864+
}
865+
849866
@Test
850867
public void mangleComprehensionVariable_macroSourceDisabled_macroCallMapIsEmpty()
851868
throws Exception {

‎optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,22 @@ public void cse_comprehensionReferencingOuterBindVar_notExtractedOutOfBindScope(
368368
+ " cel.bind(t, 99, @index0.exists(@it:0:0, @it:0:0 == @ac:1:0)))");
369369
}
370370

371+
@Test
372+
@TestParameters("{source: '[cel.bind(x, x, x + 1), cel.bind(x, x, x + 1)]', expected: '[6, 6]'}")
373+
@TestParameters(
374+
"{source: '[[1, 2].map(k, cel.bind(k, k, k + x)), [1, 2].map(k, cel.bind(k, k, k + x))]',"
375+
+ " expected: '[[6, 7], [6, 7]]'}")
376+
public void cse_bindInitReferencesOuterVariable_evaluatesCorrectly(String source, String expected)
377+
throws Exception {
378+
CelAbstractSyntaxTree ast = cel.compile(source).getAst();
379+
CelOptimizer celOptimizer = newCseOptimizer(SubexpressionOptimizerOptions.newBuilder().build());
380+
381+
CelAbstractSyntaxTree optimizedAst = celOptimizer.optimize(ast);
382+
383+
assertThat(cel.createProgram(optimizedAst).eval(ImmutableMap.of("x", 5L)))
384+
.isEqualTo(cel.createProgram(cel.compile(expected).getAst()).eval());
385+
}
386+
371387
@Test
372388
public void celBlock_astExtensionTagged() throws Exception {
373389
CelAbstractSyntaxTree ast = cel.compile("size(x) + size(x)").getAst();

0 commit comments

Comments
 (0)