Skip to content

Commit e13bec5

Browse files
l46kokcopybara-github
authored andcommitted
Add block_ext to conformance test suite, refactor to consolidate cel.block overload declaration
PiperOrigin-RevId: 964402265
1 parent e5c1466 commit e13bec5

7 files changed

Lines changed: 146 additions & 23 deletions

File tree

conformance/src/test/java/dev/cel/conformance/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ java_library(
2020
"//common:compiler_common",
2121
"//common:container",
2222
"//common:options",
23+
"//common/ast",
24+
"//common/ast:cel_block",
25+
"//common/types",
2326
"//common/types:cel_proto_types",
2427
"//compiler",
2528
"//compiler:compiler_builder",
2629
"//extensions",
30+
"//extensions:bindings",
2731
"//extensions:optional_library",
2832
"//parser:macro",
2933
"//parser:parser_builder",
@@ -75,6 +79,7 @@ java_library(
7579
_ALL_TESTS = [
7680
"@cel_spec//tests/simple:testdata/basic.textproto",
7781
"@cel_spec//tests/simple:testdata/bindings_ext.textproto",
82+
"@cel_spec//tests/simple:testdata/block_ext.textproto",
7883
"@cel_spec//tests/simple:testdata/comparisons.textproto",
7984
"@cel_spec//tests/simple:testdata/conversions.textproto",
8085
"@cel_spec//tests/simple:testdata/dynamic.textproto",

conformance/src/test/java/dev/cel/conformance/ConformanceTest.java

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,28 @@
3030
import com.google.protobuf.ExtensionRegistry;
3131
import com.google.protobuf.TypeRegistry;
3232
import dev.cel.checker.CelChecker;
33+
import dev.cel.checker.CelCheckerBuilder;
3334
import dev.cel.common.CelContainer;
35+
import dev.cel.common.CelIssue;
3436
import dev.cel.common.CelOptions;
3537
import dev.cel.common.CelValidationResult;
38+
import dev.cel.common.CelVarDecl;
39+
import dev.cel.common.ast.CelBlock;
40+
import dev.cel.common.ast.CelConstant;
41+
import dev.cel.common.ast.CelExpr;
3642
import dev.cel.common.types.CelProtoTypes;
43+
import dev.cel.common.types.SimpleType;
3744
import dev.cel.compiler.CelCompilerFactory;
3845
import dev.cel.compiler.CelCompilerLibrary;
3946
import dev.cel.expr.conformance.test.SimpleTest;
47+
import dev.cel.extensions.CelBindingsExtensions;
4048
import dev.cel.extensions.CelExtensions;
4149
import dev.cel.extensions.CelOptionalLibrary;
50+
import dev.cel.parser.CelMacro;
51+
import dev.cel.parser.CelMacroExpander;
52+
import dev.cel.parser.CelMacroExprFactory;
4253
import dev.cel.parser.CelParser;
54+
import dev.cel.parser.CelParserBuilder;
4355
import dev.cel.parser.CelParserFactory;
4456
import dev.cel.parser.CelStandardMacro;
4557
import dev.cel.runtime.CelEvaluationException;
@@ -50,6 +62,7 @@
5062
import dev.cel.runtime.CelRuntimeImpl;
5163
import dev.cel.runtime.CelRuntimeLibrary;
5264
import java.util.Map;
65+
import java.util.Optional;
5366
import org.junit.runners.model.Statement;
5467

5568
// Qualifying proto2/proto3 TestAllTypes makes it less clear.
@@ -73,7 +86,8 @@ public final class ConformanceTest extends Statement {
7386
CelExtensions.protos(),
7487
CelExtensions.sets(OPTIONS),
7588
CelExtensions.strings(),
76-
CelOptionalLibrary.INSTANCE);
89+
CelOptionalLibrary.INSTANCE,
90+
new ConformanceBlockLibrary());
7791

7892
private static final ImmutableList<CelRuntimeLibrary> CANONICAL_RUNTIME_EXTENSIONS =
7993
ImmutableList.of(
@@ -206,11 +220,11 @@ public boolean shouldSkip() {
206220
@Override
207221
public void evaluate() throws Throwable {
208222
CelValidationResult response = getParser(test).parse(test.getExpr(), test.getName());
209-
assertThat(response.hasError()).isFalse();
223+
assertThat(response.getErrors()).isEmpty();
210224
if (!test.getDisableCheck()) {
211225
response = getChecker(test).check(response.getAst());
212226
}
213-
assertThat(response.hasError()).isFalse();
227+
assertThat(response.getErrors()).isEmpty();
214228
Type resultType = CelProtoTypes.celTypeToType(response.getAst().getResultType());
215229

216230
if (test.getCheckOnly()) {
@@ -262,4 +276,103 @@ public void evaluate() throws Throwable {
262276
String.format("Unexpected matcher kind: %s", test.getResultMatcherCase()));
263277
}
264278
}
279+
280+
/**
281+
* Conformance-only library providing macros for the {@code block_ext} test suite.
282+
*
283+
* <p>These macros ({@code cel.block}, {@code cel.index}, {@code cel.iterVar}, and {@code
284+
* cel.accuVar}) are strictly used for conformance testing to represent block expressions in text
285+
* form. In production, AST optimization passes (such as common subexpression elimination)
286+
* directly generate the {@code cel.@block} call and {@code @index} / {@code @it} / {@code @ac}
287+
* variable nodes without going through these macros.
288+
*/
289+
private static final class ConformanceBlockLibrary implements CelCompilerLibrary {
290+
private static final int MAX_INDICES = 30;
291+
292+
@Override
293+
public void setParserOptions(CelParserBuilder parserBuilder) {
294+
parserBuilder.addMacros(
295+
CelMacro.newReceiverMacro("block", 2, ConformanceBlockLibrary::expandBlock),
296+
CelMacro.newReceiverMacro("index", 1, ConformanceBlockLibrary::expandIndex),
297+
CelMacro.newReceiverMacro("iterVar", 2, expandCompreVar("cel.iterVar", "@it")),
298+
CelMacro.newReceiverMacro("accuVar", 2, expandCompreVar("cel.accuVar", "@ac")));
299+
}
300+
301+
@Override
302+
public void setCheckerOptions(CelCheckerBuilder checkerBuilder) {
303+
checkerBuilder.addFunctionDeclarations(CelBindingsExtensions.CEL_BLOCK_FUNCTION_DECL);
304+
for (int i = 0; i < MAX_INDICES; i++) {
305+
checkerBuilder.addVarDeclarations(
306+
CelVarDecl.newVarDeclaration(CelBlock.INDEX_PREFIX + i, SimpleType.DYN));
307+
}
308+
}
309+
310+
private static Optional<CelExpr> expandBlock(
311+
CelMacroExprFactory exprFactory, CelExpr target, ImmutableList<CelExpr> args) {
312+
if (!isCelNamespace(target)) {
313+
return Optional.empty();
314+
}
315+
CelExpr bindings = args.get(0);
316+
if (!bindings.exprKind().getKind().equals(CelExpr.ExprKind.Kind.LIST)) {
317+
return Optional.of(
318+
exprFactory.reportError(
319+
CelIssue.formatError(
320+
exprFactory.getSourceLocation(bindings),
321+
"cel.block requires the first arg to be a list literal")));
322+
}
323+
return Optional.of(exprFactory.newGlobalCall(CelBlock.FUNCTION_NAME, args));
324+
}
325+
326+
private static Optional<CelExpr> expandIndex(
327+
CelMacroExprFactory exprFactory, CelExpr target, ImmutableList<CelExpr> args) {
328+
if (!isCelNamespace(target)) {
329+
return Optional.empty();
330+
}
331+
CelExpr index = args.get(0);
332+
if (!isNonNegativeInt(index)) {
333+
return Optional.of(
334+
exprFactory.reportError(
335+
CelIssue.formatError(
336+
exprFactory.getSourceLocation(index),
337+
"cel.index requires a single non-negative int constant arg")));
338+
}
339+
return Optional.of(
340+
exprFactory.newIdentifier(CelBlock.INDEX_PREFIX + index.constant().int64Value()));
341+
}
342+
343+
private static CelMacroExpander expandCompreVar(String macroName, String prefix) {
344+
return (exprFactory, target, args) -> {
345+
if (!isCelNamespace(target)) {
346+
return Optional.empty();
347+
}
348+
for (CelExpr arg : args) {
349+
if (!isNonNegativeInt(arg)) {
350+
return Optional.of(
351+
exprFactory.reportError(
352+
CelIssue.formatError(
353+
exprFactory.getSourceLocation(arg),
354+
macroName + " requires two non-negative int constant args")));
355+
}
356+
}
357+
return Optional.of(
358+
exprFactory.newIdentifier(
359+
String.format(
360+
"%s:%d:%d",
361+
prefix,
362+
args.get(0).constant().int64Value(),
363+
args.get(1).constant().int64Value())));
364+
};
365+
}
366+
367+
private static boolean isNonNegativeInt(CelExpr expr) {
368+
return expr.exprKind().getKind().equals(CelExpr.ExprKind.Kind.CONSTANT)
369+
&& expr.constant().getKind().equals(CelConstant.Kind.INT64_VALUE)
370+
&& expr.constant().int64Value() >= 0;
371+
}
372+
373+
private static boolean isCelNamespace(CelExpr target) {
374+
return target.exprKind().getKind().equals(CelExpr.ExprKind.Kind.IDENT)
375+
&& target.ident().name().equals("cel");
376+
}
377+
}
265378
}

extensions/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ java_library(
6161
name = "native",
6262
exports = ["//extensions/src/main/java/dev/cel/extensions:native"],
6363
)
64+
65+
java_library(
66+
name = "bindings",
67+
visibility = ["//:internal"],
68+
exports = ["//extensions/src/main/java/dev/cel/extensions:bindings"],
69+
)

extensions/src/main/java/dev/cel/extensions/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ java_library(
145145
deps = [
146146
"//common:compiler_common",
147147
"//common/ast",
148+
"//common/ast:cel_block",
148149
"//common/types",
149150
"//compiler:compiler_builder",
150151
"//extensions:extension_library",

extensions/src/main/java/dev/cel/extensions/CelBindingsExtensions.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import dev.cel.common.CelFunctionDecl;
2424
import dev.cel.common.CelIssue;
2525
import dev.cel.common.CelOverloadDecl;
26+
import dev.cel.common.ast.CelBlock;
2627
import dev.cel.common.ast.CelExpr;
2728
import dev.cel.common.types.ListType;
2829
import dev.cel.common.types.SimpleType;
@@ -59,6 +60,15 @@ static CelExtensionLibrary<CelBindingsExtensions> library() {
5960
return LIBRARY;
6061
}
6162

63+
public static final CelFunctionDecl CEL_BLOCK_FUNCTION_DECL =
64+
CelFunctionDecl.newFunctionDeclaration(
65+
CelBlock.FUNCTION_NAME,
66+
CelOverloadDecl.newGlobalOverload(
67+
"cel_block_list",
68+
TypeParamType.create("T"),
69+
ListType.create(SimpleType.DYN),
70+
TypeParamType.create("T")));
71+
6272
@Override
6373
public int version() {
6474
return 0;
@@ -67,14 +77,7 @@ public int version() {
6777
@Override
6878
public ImmutableSet<CelFunctionDecl> functions() {
6979
// TODO: Add bindings for block once decorator support is available.
70-
return ImmutableSet.of(
71-
CelFunctionDecl.newFunctionDeclaration(
72-
"cel.@block",
73-
CelOverloadDecl.newGlobalOverload(
74-
"cel_block_list",
75-
TypeParamType.create("T"),
76-
ListType.create(SimpleType.DYN),
77-
TypeParamType.create("T"))));
80+
return ImmutableSet.of(CEL_BLOCK_FUNCTION_DECL);
7881
}
7982

8083
@Override

optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ java_library(
7373
"//common/navigation:mutable_navigation",
7474
"//common/types",
7575
"//common/types:type_providers",
76+
"//extensions:bindings",
7677
"//optimizer:ast_optimizer",
7778
"//optimizer:mutable_ast",
7879
"@maven//:com_google_errorprone_error_prone_annotations",

optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import dev.cel.common.CelFunctionDecl;
3535
import dev.cel.common.CelMutableAst;
3636
import dev.cel.common.CelMutableSource;
37-
import dev.cel.common.CelOverloadDecl;
3837
import dev.cel.common.CelSource;
3938
import dev.cel.common.CelSource.Extension;
4039
import dev.cel.common.CelSource.Extension.Component;
@@ -55,8 +54,8 @@
5554
import dev.cel.common.navigation.CelNavigableMutableExpr;
5655
import dev.cel.common.navigation.TraversalOrder;
5756
import dev.cel.common.types.CelType;
58-
import dev.cel.common.types.ListType;
5957
import dev.cel.common.types.SimpleType;
58+
import dev.cel.extensions.CelBindingsExtensions;
6059
import dev.cel.optimizer.AstMutator;
6160
import dev.cel.optimizer.AstMutator.MangledComprehensionAst;
6261
import dev.cel.optimizer.CelAstOptimizer;
@@ -98,8 +97,6 @@ public final class SubexpressionOptimizer implements CelAstOptimizer {
9897
private static final SubexpressionOptimizer INSTANCE =
9998
new SubexpressionOptimizer(SubexpressionOptimizerOptions.newBuilder().build());
10099
private static final String BIND_IDENTIFIER_PREFIX = "@r";
101-
private static final String CEL_BLOCK_FUNCTION = "cel.@block";
102-
private static final String BLOCK_INDEX_PREFIX = "@index";
103100
private static final Extension CEL_BLOCK_AST_EXTENSION_TAG =
104101
Extension.create("cel_block", Version.of(1L, 1L), Component.COMPONENT_RUNTIME);
105102

@@ -165,7 +162,7 @@ private OptimizationResult optimizeUsingCelBlock(CelAbstractSyntaxTree ast, Cel
165162
CelMutableExpr targetCseShape = normalizeForEquality(cseCandidates.get(0));
166163
subexpressions.add(cseCandidates.get(0));
167164

168-
String blockIdentifier = BLOCK_INDEX_PREFIX + blockIdentifierIndex++;
165+
String blockIdentifier = CelBlock.INDEX_PREFIX + blockIdentifierIndex++;
169166

170167
// Replace all CSE candidates with new block index identifier
171168
astToModify =
@@ -217,7 +214,7 @@ private OptimizationResult optimizeUsingCelBlock(CelAbstractSyntaxTree ast, Cel
217214

218215
// Wrap the optimized expression in cel.block
219216
astToModify =
220-
astMutator.wrapAstWithNewCelBlock(CEL_BLOCK_FUNCTION, astToModify, subexpressions);
217+
astMutator.wrapAstWithNewCelBlock(CelBlock.FUNCTION_NAME, astToModify, subexpressions);
221218
astToModify = astMutator.renumberIdsConsecutively(astToModify);
222219

223220
// Tag the AST with cel.block designated as an extension
@@ -226,7 +223,7 @@ private OptimizationResult optimizeUsingCelBlock(CelAbstractSyntaxTree ast, Cel
226223
return OptimizationResult.create(
227224
optimizedAst,
228225
newVarDecls.build(),
229-
ImmutableList.of(newCelBlockFunctionDecl(ast.getResultType())));
226+
ImmutableList.of(CelBindingsExtensions.CEL_BLOCK_FUNCTION_DECL));
230227
}
231228

232229
/**
@@ -595,11 +592,8 @@ private CelMutableExpr normalizeForEquality(CelMutableExpr mutableExpr) {
595592
}
596593

597594
@VisibleForTesting
598-
static CelFunctionDecl newCelBlockFunctionDecl(CelType resultType) {
599-
return CelFunctionDecl.newFunctionDeclaration(
600-
CEL_BLOCK_FUNCTION,
601-
CelOverloadDecl.newGlobalOverload(
602-
"cel_block_list", resultType, ListType.create(SimpleType.DYN), resultType));
595+
static CelFunctionDecl newCelBlockFunctionDecl(CelType unusedResultType) {
596+
return CelBindingsExtensions.CEL_BLOCK_FUNCTION_DECL;
603597
}
604598

605599
/** Options to configure how Common Subexpression Elimination behave. */

0 commit comments

Comments
 (0)