1515package dev .cel .verifier .axioms ;
1616
1717import com .google .common .collect .ImmutableList ;
18+ import com .microsoft .z3 .BoolExpr ;
19+ import com .microsoft .z3 .Context ;
1820import com .microsoft .z3 .Expr ;
21+ import com .microsoft .z3 .FPExpr ;
22+ import com .microsoft .z3 .SeqExpr ;
1923import dev .cel .common .CelFunctionDecl ;
2024import dev .cel .extensions .CelOptionalLibrary ;
2125import dev .cel .extensions .CelOptionalLibrary .Function ;
26+ import dev .cel .verifier .CelZ3TypeSystem ;
2227import java .util .Optional ;
2328
2429/** Axiomatization for CEL's optional library functions. */
30+ @ SuppressWarnings ({"unchecked" , "rawtypes" }) // Z3 Java API uses raw types.
2531final 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 ()))
0 commit comments