@@ -88,7 +88,8 @@ final class CelAstToZ3Translator {
8888 private final ImmutableSet <String > unknownIdentifiers ;
8989 private final int comprehensionUnrollLimit ;
9090 private final CelTypeProvider typeProvider ;
91- private final List <BoolExpr > truncationConditions ;
91+ private final Set <BoolExpr > truncationConditions ;
92+ private final Set <SeqExpr <?>> boundedMapBijections ;
9293 private final Map <String , Expr <?>> emptyMessageCache ;
9394 private final Map <Object , Expr <?>> listLiteralCache ;
9495 private Expr <?> emptyListCache ;
@@ -102,7 +103,7 @@ Set<BoolExpr> getTypeConstraints() {
102103 }
103104
104105 BoolExpr hasTruncation () {
105- return CelZ3TypeSystem .mkOrFlattened (ctx , truncationConditions );
106+ return CelZ3TypeSystem .mkOrFlattened (ctx , new ArrayList <>( truncationConditions ) );
106107 }
107108
108109 CelZ3TypeSystem getTypeSystem () {
@@ -284,11 +285,23 @@ private TranslatedValue translateList(CelExpr celExpr, CelAbstractSyntaxTree ast
284285 // check to a trivial identity check (e.g., `list_ref_0 == list_ref_0`).
285286 if (listRef == null ) {
286287 SeqExpr seq = ctx .mkEmptySeq (ctx .mkSeqSort (typeSystem .celValueSort ()));
287- for (CelExpr element : createList .elements ()) {
288+ ImmutableSet <Integer > optionalIndices = ImmutableSet .copyOf (createList .optionalIndices ());
289+ for (int i = 0 ; i < createList .elements ().size (); i ++) {
290+ CelExpr element = createList .elements ().get (i );
288291 TranslatedValue elem = translateExpr (element , ast );
289292 elementsTv .add (elem );
290293
291- seq = typeSystem .mkConcatSafe (seq , ctx .mkUnit (elem .z3Expr ()));
294+ boolean isOptional = optionalIndices .contains (i );
295+ OptionalUnwrap opt = unwrapOptional (elem .z3Expr (), isOptional );
296+ SeqExpr optSeq =
297+ isOptional
298+ ? (SeqExpr )
299+ ctx .mkITE (
300+ opt .isPresent ,
301+ ctx .mkUnit (opt .effectiveValue ),
302+ ctx .mkEmptySeq (ctx .mkSeqSort (typeSystem .celValueSort ())))
303+ : ctx .mkUnit (opt .effectiveValue );
304+ seq = typeSystem .mkConcatSafe (seq , optSeq );
292305 }
293306 listRef = typeSystem .mkListRefConst (LIST_REF_PREFIX );
294307 typeConstraints .add (ctx .mkEq (typeSystem .getSeq (listRef ), seq ));
@@ -297,7 +310,9 @@ private TranslatedValue translateList(CelExpr celExpr, CelAbstractSyntaxTree ast
297310 }
298311
299312 Expr <?> result = typeSystem .wrapList (listRef );
300- return TranslatedValue .propagateStrict (ctx , typeSystem , result , celExpr , elementsTv );
313+ BoolExpr baseTaint = ctx .mkFalse ();
314+ return TranslatedValue .propagateStrict (
315+ ctx , typeSystem , result , Optional .of (celExpr ), baseTaint , elementsTv );
301316 }
302317
303318 private TranslatedValue translateMap (CelExpr celExpr , CelAbstractSyntaxTree ast ) {
@@ -318,20 +333,31 @@ private TranslatedValue translateMap(CelExpr celExpr, CelAbstractSyntaxTree ast)
318333 Expr <?> value = valueTv .z3Expr ();
319334 elementsTv .add (valueTv );
320335
321- BoolExpr keyAlreadyPresent = (BoolExpr ) ctx .mkSelect (mapPresence , key );
322- keysSeq =
323- ctx .mkITE (keyAlreadyPresent , keysSeq , typeSystem .mkConcatSafe (keysSeq , ctx .mkUnit (key )));
336+ OptionalUnwrap opt = unwrapOptional (value , entryAst .optionalEntry ());
324337
325- mapValues = ctx .mkStore (mapValues , key , value );
326- mapPresence = ctx .mkStore (mapPresence , key , ctx .mkTrue ());
338+ BoolExpr keyAlreadyPresent = (BoolExpr ) ctx .mkSelect (mapPresence , key );
339+ BoolExpr shouldAddKey = ctx .mkAnd (opt .isPresent , ctx .mkNot (keyAlreadyPresent ));
340+
341+ SeqExpr keyOptSeq =
342+ (SeqExpr )
343+ ctx .mkITE (
344+ shouldAddKey ,
345+ ctx .mkUnit (key ),
346+ ctx .mkEmptySeq (ctx .mkSeqSort (typeSystem .celValueSort ())));
347+
348+ keysSeq = typeSystem .mkConcatSafe (keysSeq , keyOptSeq );
349+ mapValues = storeIf (opt .isPresent , mapValues , key , opt .effectiveValue );
350+ mapPresence = storeIf (opt .isPresent , mapPresence , key , ctx .mkTrue ());
327351 }
328352
329353 typeConstraints .add (ctx .mkEq (typeSystem .getMapValues (mapRef ), mapValues ));
330354 typeConstraints .add (ctx .mkEq (typeSystem .getMapPresence (mapRef ), mapPresence ));
331355 typeConstraints .add (ctx .mkEq (typeSystem .getMapKeys (mapRef ), keysSeq ));
332356
333357 Expr <?> result = typeSystem .wrapMap (mapRef );
334- return TranslatedValue .propagateStrict (ctx , typeSystem , result , celExpr , elementsTv );
358+ BoolExpr baseTaint = ctx .mkFalse ();
359+ return TranslatedValue .propagateStrict (
360+ ctx , typeSystem , result , Optional .of (celExpr ), baseTaint , elementsTv );
335361 }
336362
337363 private TranslatedValue translateStruct (CelExpr celExpr , CelAbstractSyntaxTree ast ) {
@@ -379,15 +405,15 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
379405 // (`msg1 == msg2`) to work without using quantifiers (which avoids MBQI loops).
380406 // Because proto3 singular primitives do not have field presence, we also skip setting
381407 // `msgPresence`.
408+ OptionalUnwrap opt = unwrapOptional (value , entryAst .optionalEntry ());
409+
382410 BoolExpr shouldBypass =
383- fieldType .kind ().isPrimitive () ? ctx .mkEq (value , defaultVal ) : ctx .mkFalse ();
411+ fieldType .kind ().isPrimitive () ? ctx .mkEq (opt . effectiveValue , defaultVal ) : ctx .mkFalse ();
384412
385- msgValues =
386- (ArrayExpr ) ctx .mkITE (shouldBypass , msgValues , ctx .mkStore (msgValues , key , value ));
413+ BoolExpr shouldStore = ctx .mkAnd (opt .isPresent , ctx .mkNot (shouldBypass ));
387414
388- msgPresence =
389- (ArrayExpr )
390- ctx .mkITE (shouldBypass , msgPresence , ctx .mkStore (msgPresence , key , ctx .mkTrue ()));
415+ msgValues = storeIf (shouldStore , msgValues , key , opt .effectiveValue );
416+ msgPresence = storeIf (shouldStore , msgPresence , key , ctx .mkTrue ());
391417 }
392418
393419 typeConstraints .add (
@@ -396,7 +422,9 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
396422 typeConstraints .add (ctx .mkEq (typeSystem .getMsgPresence (msgRef ), msgPresence ));
397423
398424 Expr <?> result = typeSystem .wrapMessage (msgRef );
399- return TranslatedValue .propagateStrict (ctx , typeSystem , result , celExpr , elementsTv );
425+ BoolExpr baseTaint = ctx .mkFalse ();
426+ return TranslatedValue .propagateStrict (
427+ ctx , typeSystem , result , Optional .of (celExpr ), baseTaint , elementsTv );
400428 }
401429
402430 private Expr <?> getDefaultValueForType (CelType type ) {
@@ -472,6 +500,28 @@ private Expr<?> getDefaultValueForType(CelType type) {
472500 return typeSystem .mkUnknown ();
473501 }
474502
503+ private static final class OptionalUnwrap {
504+ final BoolExpr isPresent ;
505+ final Expr <?> effectiveValue ;
506+
507+ OptionalUnwrap (BoolExpr isPresent , Expr <?> effectiveValue ) {
508+ this .isPresent = isPresent ;
509+ this .effectiveValue = effectiveValue ;
510+ }
511+ }
512+
513+ private OptionalUnwrap unwrapOptional (Expr <?> value , boolean isOptional ) {
514+ if (!isOptional ) {
515+ return new OptionalUnwrap (ctx .mkTrue (), value );
516+ }
517+ Expr <?> optRef = typeSystem .getOptionalRef (value );
518+ return new OptionalUnwrap (typeSystem .optHasValue (optRef ), typeSystem .getOptionalValue (optRef ));
519+ }
520+
521+ private ArrayExpr storeIf (BoolExpr condition , ArrayExpr array , Expr <?> key , Expr <?> value ) {
522+ return (ArrayExpr ) ctx .mkITE (condition , ctx .mkStore (array , key , value ), array );
523+ }
524+
475525 private static final class FieldAccess {
476526 final Expr <?> presence ;
477527 final Expr <?> value ;
@@ -657,24 +707,53 @@ private TranslatedValue translateComprehension(CelExpr celExpr, CelAbstractSynta
657707 // For statically known list/map literals, unroll them exactly.
658708 if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .LIST ) {
659709 ImmutableList <CelExpr > elements = iterRangeExpr .list ().elements ();
710+ ImmutableList <Integer > optionalIndices = iterRangeExpr .list ().optionalIndices ();
660711 for (int i = 0 ; i < elements .size (); i ++) {
661712 TranslatedValue valueTv = translateExpr (elements .get (i ), ast );
662- Expr <?> value = valueTv .z3Expr ();
663- taints .add (valueTv .isApproximate ());
664- iterationElements .add (new IterationElement (typeSystem .mkInt (i ), value ));
665- allRangeElems .add (value );
713+ boolean isOptional = optionalIndices .contains (i );
714+ OptionalUnwrap opt = unwrapOptional (valueTv .z3Expr (), isOptional );
715+ if (isOptional && ctx .mkFalse ().equals (opt .isPresent )) {
716+ continue ;
717+ }
718+ taints .add (
719+ isOptional
720+ ? CelZ3TypeSystem .mkAndFlattened (ctx , opt .isPresent , valueTv .isApproximate ())
721+ : valueTv .isApproximate ());
722+ iterationElements .add (
723+ new IterationElement (
724+ typeSystem .mkInt (i ),
725+ opt .effectiveValue ,
726+ isOptional ? Optional .of (opt .isPresent ) : Optional .empty ()));
727+ allRangeElems .add (
728+ isOptional
729+ ? ctx .mkITE (opt .isPresent , opt .effectiveValue , typeSystem .mkInt (0 ))
730+ : opt .effectiveValue );
666731 }
667732 } else if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .MAP ) {
668733 for (CelExpr .CelMap .Entry entry : iterRangeExpr .map ().entries ()) {
669734 TranslatedValue keyTv = translateExpr (entry .key (), ast );
670735 Expr <?> key = keyTv .z3Expr ();
671736 taints .add (keyTv .isApproximate ());
672737 TranslatedValue valueTv = translateExpr (entry .value (), ast );
673- Expr <?> value = valueTv .z3Expr ();
674- taints .add (valueTv .isApproximate ());
675- iterationElements .add (new IterationElement (key , value ));
738+ boolean isOptional = entry .optionalEntry ();
739+ OptionalUnwrap opt = unwrapOptional (valueTv .z3Expr (), isOptional );
740+ if (isOptional && ctx .mkFalse ().equals (opt .isPresent )) {
741+ continue ;
742+ }
743+ taints .add (
744+ isOptional
745+ ? CelZ3TypeSystem .mkAndFlattened (ctx , opt .isPresent , valueTv .isApproximate ())
746+ : valueTv .isApproximate ());
747+ iterationElements .add (
748+ new IterationElement (
749+ key ,
750+ opt .effectiveValue ,
751+ isOptional ? Optional .of (opt .isPresent ) : Optional .empty ()));
676752 allRangeElems .add (key );
677- allRangeElems .add (value );
753+ allRangeElems .add (
754+ isOptional
755+ ? ctx .mkITE (opt .isPresent , opt .effectiveValue , typeSystem .mkInt (0 ))
756+ : opt .effectiveValue );
678757 }
679758 } else {
680759 return translateDynamicComprehension (celExpr , ast );
@@ -693,13 +772,23 @@ private TranslatedValue translateComprehension(CelExpr celExpr, CelAbstractSynta
693772 comp , ast , iterElem .keyOrIndex , iterElem .value , currentAccu , isMap , isTwoVar );
694773 Expr <?> condition = condAndStep [0 ].z3Expr ();
695774 Expr <?> step = condAndStep [1 ].z3Expr ();
696- taints .add (condAndStep [1 ].isApproximate ());
775+ if (iterElem .hasValue .isPresent ()) {
776+ taints .add (
777+ CelZ3TypeSystem .mkAndFlattened (
778+ ctx , iterElem .hasValue .get (), condAndStep [1 ].isApproximate ()));
779+ } else {
780+ taints .add (condAndStep [1 ].isApproximate ());
781+ }
697782
698783 Expr <?> stepVal = ctx .mkITE ((BoolExpr ) typeSystem .unwrapBool (condition ), step , currentAccu );
699784 Expr <?> typeErrorOrStep =
700785 typeSystem .withRuntimeError (stepVal , ctx .mkNot (typeSystem .isBool (condition )));
701786
702- accu = typeSystem .propagateErrorAndUnknown (typeErrorOrStep , condition );
787+ Expr <?> updatedAccu = typeSystem .propagateErrorAndUnknown (typeErrorOrStep , condition );
788+ accu =
789+ iterElem .hasValue .isPresent ()
790+ ? ctx .mkITE (iterElem .hasValue .get (), updatedAccu , currentAccu )
791+ : updatedAccu ;
703792 }
704793
705794 TranslatedValue resultTv =
@@ -773,6 +862,9 @@ private TranslatedValue translateDynamicComprehension(
773862
774863 private void applyBoundedMapBijection (
775864 ArrayExpr mapPresence , SeqExpr <?> seq , ArithExpr lengthExpr ) {
865+ if (!boundedMapBijections .add (seq )) {
866+ return ;
867+ }
776868 for (int i = 0 ; i < comprehensionUnrollLimit ; i ++) {
777869 for (int j = i + 1 ; j < comprehensionUnrollLimit ; j ++) {
778870 BoolExpr validPair = ctx .mkLt (ctx .mkInt (j ), lengthExpr );
@@ -1223,16 +1315,23 @@ private BoolExpr createTypeConstraintForType(Expr<?> val, CelType type) {
12231315 this .emptyMessageCache = new HashMap <>();
12241316 this .listLiteralCache = new HashMap <>();
12251317 this .typeProvider = typeProvider ;
1226- this .truncationConditions = new ArrayList <>();
1318+ this .truncationConditions = new LinkedHashSet <>();
1319+ this .boundedMapBijections = new LinkedHashSet <>();
12271320 }
12281321
12291322 private static class IterationElement {
12301323 final Expr <?> keyOrIndex ;
12311324 final Expr <?> value ;
1325+ final Optional <BoolExpr > hasValue ;
12321326
12331327 IterationElement (Expr <?> keyOrIndex , Expr <?> value ) {
1328+ this (keyOrIndex , value , Optional .empty ());
1329+ }
1330+
1331+ IterationElement (Expr <?> keyOrIndex , Expr <?> value , Optional <BoolExpr > hasValue ) {
12341332 this .keyOrIndex = keyOrIndex ;
12351333 this .value = value ;
1334+ this .hasValue = hasValue ;
12361335 }
12371336 }
12381337
@@ -1249,6 +1348,7 @@ private Optional<Object> toCacheKey(CelExpr expr) {
12491348 }
12501349 builder .add (elemKey .get ());
12511350 }
1351+ builder .add (expr .list ().optionalIndices ());
12521352 return Optional .of (builder .build ());
12531353 default :
12541354 return Optional .empty ();
0 commit comments