Skip to content

Commit f60c78d

Browse files
l46kokcopybara-github
authored andcommitted
Add an axiom for optional.ofZeroValue for verifier
PiperOrigin-RevId: 952524537
1 parent 22e84f7 commit f60c78d

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

verifier/src/main/java/dev/cel/verifier/axioms/OptionalAxioms.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@
1515
package dev.cel.verifier.axioms;
1616

1717
import com.google.common.collect.ImmutableList;
18+
import com.microsoft.z3.BoolExpr;
19+
import com.microsoft.z3.Context;
1820
import com.microsoft.z3.Expr;
21+
import com.microsoft.z3.FPExpr;
22+
import com.microsoft.z3.SeqExpr;
1923
import dev.cel.common.CelFunctionDecl;
2024
import dev.cel.extensions.CelOptionalLibrary;
2125
import dev.cel.extensions.CelOptionalLibrary.Function;
26+
import dev.cel.verifier.CelZ3TypeSystem;
2227
import java.util.Optional;
2328

2429
/** Axiomatization for CEL's optional library functions. */
30+
@SuppressWarnings({"unchecked", "rawtypes"}) // Z3 Java API uses raw types.
2531
final class OptionalAxioms {
2632

2733
static final ImmutableList<CelZ3FunctionAxiom> ALL_AXIOMS =
@@ -40,6 +46,17 @@ final class OptionalAxioms {
4046
sink.accept(ts.optHasValue(optRef));
4147
return Optional.of(ts.mkOptionalOf(optRef));
4248
}),
49+
createUnaryAxiom(
50+
Function.OPTIONAL_OF_NON_ZERO_VALUE,
51+
"optional_ofNonZeroValue",
52+
(ctx, ts, sink, value) -> {
53+
Expr<?> optRef = ctx.mkApp(ts.optionalOfRefFunc(), value);
54+
BoolExpr isZero = isZeroValue(ctx, ts, value);
55+
sink.accept(
56+
ctx.mkImplies(ctx.mkNot(isZero), ctx.mkEq(ts.getOptionalValue(optRef), value)));
57+
sink.accept(ctx.mkImplies(ctx.mkNot(isZero), ts.optHasValue(optRef)));
58+
return Optional.of(ctx.mkITE(isZero, ts.mkOptionalNone(), ts.mkOptionalOf(optRef)));
59+
}),
4360
createUnaryAxiom(
4461
Function.HAS_VALUE,
4562
"optional_hasValue",
@@ -69,6 +86,27 @@ final class OptionalAxioms {
6986
return Optional.of(ctx.mkITE(ts.optHasValue(optRef), val, other));
7087
}));
7188

89+
private static BoolExpr isZeroValue(Context ctx, CelZ3TypeSystem ts, Expr<?> val) {
90+
return ctx.mkOr(
91+
ts.isNull(val),
92+
ctx.mkAnd(ts.isBool(val), ctx.mkEq(ts.unwrapBool(val), ctx.mkFalse())),
93+
ctx.mkAnd(ts.isInt(val), ctx.mkEq(ts.getInt(val), ctx.mkInt(0))),
94+
ctx.mkAnd(ts.isUint(val), ctx.mkEq(ts.getUint(val), ctx.mkInt(0))),
95+
ctx.mkAnd(ts.isDouble(val), ctx.mkFPIsZero((FPExpr) ts.getDouble(val))),
96+
ctx.mkAnd(ts.isString(val), ctx.mkEq(ts.getString(val), ctx.mkString(""))),
97+
ctx.mkAnd(
98+
ts.isBytes(val), ctx.mkEq(ctx.mkLength((SeqExpr) ts.getBytes(val)), ctx.mkInt(0))),
99+
ctx.mkAnd(
100+
ts.isList(val), ctx.mkEq(ctx.mkLength(ts.getSeq(ts.getListRef(val))), ctx.mkInt(0))),
101+
ctx.mkAnd(
102+
ts.isMap(val), ctx.mkEq(ctx.mkLength(ts.getMapKeys(ts.getMapRef(val))), ctx.mkInt(0))),
103+
ctx.mkAnd(
104+
ts.isMessage(val),
105+
ctx.mkEq(
106+
ts.getMsgPresence(ts.getMessageRef(val)),
107+
ctx.mkConstArray(ctx.getStringSort(), ctx.mkFalse()))));
108+
}
109+
72110
private static CelFunctionDecl getDecl(Function funcEnum) {
73111
return CelOptionalLibrary.INSTANCE.functions().stream()
74112
.filter(d -> d.name().equals(funcEnum.getFunction()))

verifier/src/test/java/dev/cel/verifier/CelVerifierZ3ImplTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,31 @@ private enum EquivalenceTestCase {
13931393
OPTIONAL_VALUE_EQUIVALENCE("optional.of(x).value()", "x"),
13941394
OPTIONAL_HAS_VALUE_EQUIVALENCE("optional.of(x).hasValue()", "true"),
13951395
OPTIONAL_NONE_HAS_VALUE_EQUIVALENCE("optional.none().hasValue()", "false"),
1396+
OPTIONAL_OF_NON_ZERO_VALUE_ARITHMETIC_EQUIVALENCE(
1397+
"[optional.ofNonZeroValue(1 + 2 + 3)]", "[optional.of(6)]"),
1398+
OPTIONAL_OF_NON_ZERO_VALUE_INT_ZERO_EQUIVALENCE(
1399+
"optional.ofNonZeroValue(0)", "optional.none()"),
1400+
OPTIONAL_OF_NON_ZERO_VALUE_INT_NON_ZERO_EQUIVALENCE(
1401+
"optional.ofNonZeroValue(5)", "optional.of(5)"),
1402+
OPTIONAL_OF_NON_ZERO_VALUE_STRING_EMPTY_EQUIVALENCE(
1403+
"optional.ofNonZeroValue('')", "optional.none()"),
1404+
OPTIONAL_OF_NON_ZERO_VALUE_STRING_NON_EMPTY_EQUIVALENCE(
1405+
"optional.ofNonZeroValue('hi')", "optional.of('hi')"),
1406+
OPTIONAL_OF_NON_ZERO_VALUE_BOOL_FALSE_EQUIVALENCE(
1407+
"optional.ofNonZeroValue(false)", "optional.none()"),
1408+
OPTIONAL_OF_NON_ZERO_VALUE_BOOL_TRUE_EQUIVALENCE(
1409+
"optional.ofNonZeroValue(true)", "optional.of(true)"),
1410+
OPTIONAL_OF_NON_ZERO_VALUE_DOUBLE_ZERO_EQUIVALENCE(
1411+
"optional.ofNonZeroValue(0.0)", "optional.none()"),
1412+
OPTIONAL_OF_NON_ZERO_VALUE_UINT_ZERO_EQUIVALENCE(
1413+
"optional.ofNonZeroValue(0u)", "optional.none()"),
1414+
OPTIONAL_OF_NON_ZERO_VALUE_LIST_EMPTY_EQUIVALENCE(
1415+
"optional.ofNonZeroValue([])", "optional.none()"),
1416+
OPTIONAL_OF_NON_ZERO_VALUE_MAP_EMPTY_EQUIVALENCE(
1417+
"optional.ofNonZeroValue({})", "optional.none()"),
1418+
OPTIONAL_OF_NON_ZERO_VALUE_BYTES_EMPTY_EQUIVALENCE(
1419+
"optional.ofNonZeroValue(b'')", "optional.none()"),
1420+
OPTIONAL_OF_NON_ZERO_VALUE_NULL_EQUIVALENCE("optional.ofNonZeroValue(null)", "optional.none()"),
13961421
FUNCTIONS("size(\"abc\") == size(role)", "size(role) == size(\"abc\")"),
13971422
NOT_EQUALS("x != y", "!(x == y)"),
13981423
LESS("x < y", "y > x"),

0 commit comments

Comments
 (0)