Skip to content

Commit 7a67241

Browse files
l46kokcopybara-github
authored andcommitted
Support partial evaluation via unknowns in planner
PiperOrigin-RevId: 877616092
1 parent 62c1f11 commit 7a67241

39 files changed

Lines changed: 1306 additions & 82 deletions

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ java_library(
3838
"//runtime:interpreter_util",
3939
"//runtime:lite_runtime",
4040
"//runtime:lite_runtime_factory",
41+
"//runtime:partial_vars",
42+
"//runtime:unknown_attributes",
4143
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
4244
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
4345
"@cel_spec//proto/cel/expr/conformance/test:simple_java_proto",

extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
import dev.cel.expr.conformance.proto3.TestAllTypes.NestedMessage;
5050
import dev.cel.parser.CelMacro;
5151
import dev.cel.parser.CelStandardMacro;
52+
import dev.cel.runtime.CelAttributePattern;
5253
import dev.cel.runtime.CelEvaluationException;
5354
import dev.cel.runtime.CelFunctionBinding;
5455
import dev.cel.runtime.CelRuntime;
5556
import dev.cel.runtime.InterpreterUtil;
57+
import dev.cel.runtime.PartialVars;
5658
import java.time.Duration;
5759
import java.time.Instant;
5860
import java.util.List;
@@ -897,14 +899,12 @@ public void optionalIndex_onMap_returnsOptionalValue() throws Exception {
897899
@TestParameters("{source: '{?x: x}'}")
898900
public void optionalIndex_onMapWithUnknownInput_returnsUnknownResult(String source)
899901
throws Exception {
900-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
901-
// TODO: Uncomment once unknowns is implemented
902-
return;
903-
}
904902
Cel cel = newCelBuilder().addVar("x", OptionalType.create(SimpleType.INT)).build();
905903
CelAbstractSyntaxTree ast = compile(cel, source);
906904

907-
Object result = cel.createProgram(ast).eval();
905+
Object result =
906+
cel.createProgram(ast)
907+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("x")));
908908

909909
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
910910
}
@@ -987,18 +987,16 @@ public void optionalIndex_onOptionalList_returnsOptionalValue() throws Exception
987987

988988
@Test
989989
public void optionalIndex_onListWithUnknownInput_returnsUnknownResult() throws Exception {
990-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
991-
// TODO: Uncomment once unknowns is implemented
992-
return;
993-
}
994990
Cel cel =
995991
newCelBuilder()
996992
.addVar("x", OptionalType.create(SimpleType.INT))
997993
.setResultType(ListType.create(SimpleType.INT))
998994
.build();
999995
CelAbstractSyntaxTree ast = compile(cel, "[?x]");
1000996

1001-
Object result = cel.createProgram(ast).eval();
997+
Object result =
998+
cel.createProgram(ast)
999+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("x")));
10021000

10031001
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
10041002
}
@@ -1026,18 +1024,16 @@ public void traditionalIndex_onOptionalList_returnsOptionalEmpty() throws Except
10261024
@TestParameters("{expression: 'optional.none().orValue(optx)'}")
10271025
public void optionalChainedFunctions_lhsIsUnknown_returnsUnknown(String expression)
10281026
throws Exception {
1029-
if (testMode.equals(TestMode.PLANNER_CHECKED) || testMode.equals(TestMode.PLANNER_PARSE_ONLY)) {
1030-
// TODO: Uncomment once unknowns is implemented
1031-
return;
1032-
}
10331027
Cel cel =
10341028
newCelBuilder()
10351029
.addVar("optx", OptionalType.create(SimpleType.INT))
10361030
.addVar("x", SimpleType.INT)
10371031
.build();
10381032
CelAbstractSyntaxTree ast = compile(cel, expression);
10391033

1040-
Object result = cel.createProgram(ast).eval();
1034+
Object result =
1035+
cel.createProgram(ast)
1036+
.eval(PartialVars.of(CelAttributePattern.fromQualifiedIdentifier("optx")));
10411037

10421038
assertThat(InterpreterUtil.isUnknown(result)).isTrue();
10431039
}

runtime/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,9 @@ java_library(
351351
"//runtime/src/main/java/dev/cel/runtime:runtime_planner_impl",
352352
],
353353
)
354+
355+
java_library(
356+
name = "partial_vars",
357+
visibility = ["//:internal"],
358+
exports = ["//runtime/src/main/java/dev/cel/runtime:partial_vars"],
359+
)

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ java_library(
826826
":evaluation_listener",
827827
":function_binding",
828828
":function_resolver",
829+
":partial_vars",
829830
":program",
830831
":proto_message_runtime_equality",
831832
":runtime",
@@ -938,6 +939,7 @@ java_library(
938939
":function_resolver",
939940
":interpretable",
940941
":interpreter",
942+
":partial_vars",
941943
":program",
942944
":proto_message_activation_factory",
943945
":runtime_equality",
@@ -955,7 +957,6 @@ java_library(
955957
"@maven//:com_google_errorprone_error_prone_annotations",
956958
"@maven//:com_google_guava_guava",
957959
"@maven//:com_google_protobuf_protobuf_java",
958-
"@maven//:org_jspecify_jspecify",
959960
],
960961
)
961962

@@ -1014,6 +1015,7 @@ java_library(
10141015
":evaluation_exception",
10151016
":function_resolver",
10161017
":interpretable",
1018+
":partial_vars",
10171019
":program",
10181020
":variable_resolver",
10191021
"//:auto_value",
@@ -1029,6 +1031,7 @@ cel_android_library(
10291031
":evaluation_exception",
10301032
":function_resolver_android",
10311033
":interpretable_android",
1034+
":partial_vars_android",
10321035
":program_android",
10331036
":variable_resolver",
10341037
"//:auto_value",
@@ -1318,6 +1321,34 @@ cel_android_library(
13181321
],
13191322
)
13201323

1324+
java_library(
1325+
name = "partial_vars",
1326+
srcs = ["PartialVars.java"],
1327+
tags = [
1328+
],
1329+
deps = [
1330+
":variable_resolver",
1331+
"//:auto_value",
1332+
"//runtime:unknown_attributes",
1333+
"@maven//:com_google_errorprone_error_prone_annotations",
1334+
"@maven//:com_google_guava_guava",
1335+
],
1336+
)
1337+
1338+
cel_android_library(
1339+
name = "partial_vars_android",
1340+
srcs = ["PartialVars.java"],
1341+
tags = [
1342+
],
1343+
deps = [
1344+
":variable_resolver",
1345+
"//:auto_value",
1346+
"//runtime:unknown_attributes_android",
1347+
"@maven//:com_google_errorprone_error_prone_annotations",
1348+
"@maven_android//:com_google_guava_guava",
1349+
],
1350+
)
1351+
13211352
java_library(
13221353
name = "program",
13231354
srcs = ["Program.java"],
@@ -1326,6 +1357,7 @@ java_library(
13261357
deps = [
13271358
":evaluation_exception",
13281359
":function_resolver",
1360+
":partial_vars",
13291361
":variable_resolver",
13301362
"@maven//:com_google_errorprone_error_prone_annotations",
13311363
],
@@ -1339,8 +1371,8 @@ cel_android_library(
13391371
deps = [
13401372
":evaluation_exception",
13411373
":function_resolver_android",
1374+
":partial_vars_android",
13421375
":variable_resolver",
1343-
"//:auto_value",
13441376
"@maven//:com_google_errorprone_error_prone_annotations",
13451377
],
13461378
)

runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public Object eval(
134134
return program.eval(resolver, lateBoundFunctionResolver);
135135
}
136136

137+
@Override
138+
public Object eval(PartialVars partialVars) throws CelEvaluationException {
139+
return program.eval(partialVars);
140+
}
141+
137142
@Override
138143
public Object trace(CelEvaluationListener listener) throws CelEvaluationException {
139144
throw new UnsupportedOperationException("Trace is not yet supported.");

runtime/src/main/java/dev/cel/runtime/CelUnknownSet.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static CelUnknownSet create(Iterable<Long> unknownExprIds) {
5959
return create(ImmutableSet.of(), ImmutableSet.copyOf(unknownExprIds));
6060
}
6161

62-
static CelUnknownSet create(
62+
public static CelUnknownSet create(
6363
ImmutableSet<CelAttribute> attributes, ImmutableSet<Long> unknownExprIds) {
6464
return new AutoValue_CelUnknownSet(attributes, unknownExprIds);
6565
}

runtime/src/main/java/dev/cel/runtime/LiteProgramImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public Object eval(CelVariableResolver resolver) throws CelEvaluationException {
5252
throw new UnsupportedOperationException("To be implemented");
5353
}
5454

55+
@Override
56+
public Object eval(PartialVars partialVars) throws CelEvaluationException {
57+
// TODO: Wire in program planner
58+
throw new UnsupportedOperationException("To be implemented");
59+
}
60+
5561
static Program plan(Interpretable interpretable) {
5662
return new AutoValue_LiteProgramImpl(interpretable);
5763
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.runtime;
16+
17+
import com.google.auto.value.AutoValue;
18+
import com.google.common.collect.ImmutableList;
19+
import java.util.Map;
20+
import java.util.Optional;
21+
22+
/**
23+
* A holder for a {@link CelVariableResolver} and a set of {@link CelAttributePattern}s that
24+
* indicate variables or parts of variables whose value are not yet known.
25+
*/
26+
@AutoValue
27+
public abstract class PartialVars {
28+
29+
/** The resolver to use for resolving evaluation variables. */
30+
public abstract CelVariableResolver resolver();
31+
32+
/**
33+
* A list of attribute patterns specifying which missing attribute paths should be tracked as
34+
* unknown values.
35+
*/
36+
public abstract ImmutableList<CelAttributePattern> unknowns();
37+
38+
/** Constructs a new {@code PartialVars} from one or more {@link CelAttributePattern}s. */
39+
public static PartialVars of(CelAttributePattern... unknowns) {
40+
return of((unused) -> Optional.empty(), ImmutableList.copyOf(unknowns));
41+
}
42+
43+
/**
44+
* Constructs a new {@code PartialVars} from a {@link CelVariableResolver} and a list of {@link
45+
* CelAttributePattern}s.
46+
*/
47+
public static PartialVars of(
48+
CelVariableResolver resolver, Iterable<CelAttributePattern> unknowns) {
49+
return new AutoValue_PartialVars(resolver, ImmutableList.copyOf(unknowns));
50+
}
51+
52+
/**
53+
* Constructs a new {@code PartialVars} from a map of variables and an array of {@link
54+
* CelAttributePattern}s.
55+
*/
56+
public static PartialVars of(Map<String, ?> variables, CelAttributePattern... unknowns) {
57+
return of(
58+
(name) -> variables.containsKey(name) ? Optional.of(variables.get(name)) : Optional.empty(),
59+
unknowns);
60+
}
61+
62+
/**
63+
* Constructs a new {@code PartialVars} from a {@link CelVariableResolver} and an array of {@link
64+
* CelAttributePattern}s.
65+
*/
66+
public static PartialVars of(CelVariableResolver resolver, CelAttributePattern... unknowns) {
67+
return of(resolver, ImmutableList.copyOf(unknowns));
68+
}
69+
}

runtime/src/main/java/dev/cel/runtime/Program.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ Object eval(Map<String, ?> mapValue, CelFunctionResolver lateBoundFunctionResolv
4343
*/
4444
Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver)
4545
throws CelEvaluationException;
46+
47+
/** Evaluate a compiled program with unknown attribute patterns {@code partialVars}. */
48+
Object eval(PartialVars partialVars) throws CelEvaluationException;
4649
}

runtime/src/main/java/dev/cel/runtime/ProgramImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ public Object eval(Map<String, ?> mapValue, CelFunctionResolver lateBoundFunctio
6060
return evalInternal(Activation.copyOf(mapValue), lateBoundFunctionResolver);
6161
}
6262

63+
@Override
64+
public Object eval(PartialVars partialVars) throws CelEvaluationException {
65+
return evalInternal(
66+
UnknownContext.create(partialVars.resolver(), partialVars.unknowns()),
67+
Optional.empty(),
68+
Optional.empty());
69+
}
70+
6371
@Override
6472
public Object trace(CelEvaluationListener listener) throws CelEvaluationException {
6573
return evalInternal(Activation.EMPTY, listener);

0 commit comments

Comments
 (0)