2323import com .microsoft .z3 .Expr ;
2424import com .microsoft .z3 .FuncDecl ;
2525import com .microsoft .z3 .IntExpr ;
26- import com .microsoft .z3 .Pattern ;
2726import com .microsoft .z3 .Quantifier ;
2827import com .microsoft .z3 .SeqExpr ;
2928import com .microsoft .z3 .Sort ;
@@ -284,11 +283,24 @@ private TranslatedValue translateList(CelExpr celExpr, CelAbstractSyntaxTree ast
284283 // check to a trivial identity check (e.g., `list_ref_0 == list_ref_0`).
285284 if (listRef == null ) {
286285 SeqExpr seq = ctx .mkEmptySeq (ctx .mkSeqSort (typeSystem .celValueSort ()));
287- for (CelExpr element : createList .elements ()) {
286+ ImmutableList <Integer > optionalIndices = createList .optionalIndices ();
287+ ImmutableList <CelExpr > elements = createList .elements ();
288+ for (int i = 0 ; i < elements .size (); i ++) {
289+ CelExpr element = elements .get (i );
288290 TranslatedValue elem = translateExpr (element , ast );
289291 elementsTv .add (elem );
290292
291- seq = typeSystem .mkConcatSafe (seq , ctx .mkUnit (elem .z3Expr ()));
293+ if (optionalIndices .contains (i )) {
294+ Expr <?> optRef = typeSystem .getOptionalRef (elem .z3Expr ());
295+ seq =
296+ (SeqExpr )
297+ ctx .mkITE (
298+ typeSystem .optHasValue (optRef ),
299+ typeSystem .mkConcatSafe (seq , ctx .mkUnit (typeSystem .getOptionalValue (optRef ))),
300+ seq );
301+ } else {
302+ seq = typeSystem .mkConcatSafe (seq , ctx .mkUnit (elem .z3Expr ()));
303+ }
292304 }
293305 listRef = typeSystem .mkListRefConst (LIST_REF_PREFIX );
294306 typeConstraints .add (ctx .mkEq (typeSystem .getSeq (listRef ), seq ));
@@ -318,12 +330,24 @@ private TranslatedValue translateMap(CelExpr celExpr, CelAbstractSyntaxTree ast)
318330 Expr <?> value = valueTv .z3Expr ();
319331 elementsTv .add (valueTv );
320332
333+ Expr <?> finalValue = value ;
334+ BoolExpr finalPresence = ctx .mkTrue ();
335+ if (entryAst .optionalEntry ()) {
336+ Expr <?> optRef = typeSystem .getOptionalRef (value );
337+ finalPresence = typeSystem .optHasValue (optRef );
338+ finalValue = typeSystem .getOptionalValue (optRef );
339+ }
340+
321341 BoolExpr keyAlreadyPresent = (BoolExpr ) ctx .mkSelect (mapPresence , key );
342+ BoolExpr shouldInsertKey = ctx .mkAnd (ctx .mkNot (keyAlreadyPresent ), finalPresence );
322343 keysSeq =
323- ctx .mkITE (keyAlreadyPresent , keysSeq , typeSystem .mkConcatSafe (keysSeq , ctx .mkUnit (key )));
344+ ctx .mkITE (shouldInsertKey , typeSystem .mkConcatSafe (keysSeq , ctx .mkUnit (key )), keysSeq );
324345
325- mapValues = ctx .mkStore (mapValues , key , value );
326- mapPresence = ctx .mkStore (mapPresence , key , ctx .mkTrue ());
346+ mapValues =
347+ (ArrayExpr ) ctx .mkITE (finalPresence , ctx .mkStore (mapValues , key , finalValue ), mapValues );
348+ mapPresence =
349+ (ArrayExpr )
350+ ctx .mkITE (finalPresence , ctx .mkStore (mapPresence , key , ctx .mkTrue ()), mapPresence );
327351 }
328352
329353 typeConstraints .add (ctx .mkEq (typeSystem .getMapValues (mapRef ), mapValues ));
@@ -371,6 +395,14 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
371395 .orElseGet (() -> extractAstTypeOrDefault (ast , entryAst .value ().id ()));
372396 Expr <?> defaultVal = getDefaultValueForType (fieldType );
373397
398+ Expr <?> finalValue = value ;
399+ BoolExpr optionalHasValue = ctx .mkTrue ();
400+ if (entryAst .optionalEntry ()) {
401+ Expr <?> optRef = typeSystem .getOptionalRef (value );
402+ optionalHasValue = typeSystem .optHasValue (optRef );
403+ finalValue = typeSystem .getOptionalValue (optRef );
404+ }
405+
374406 // Canonicalization Trick:
375407 //
376408 // We avoid storing explicit default values (e.g. `single_int32: 0`)
@@ -379,11 +411,13 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
379411 // (`msg1 == msg2`) to work without using quantifiers (which avoids MBQI loops).
380412 // Because proto3 singular primitives do not have field presence, we also skip setting
381413 // `msgPresence`.
382- BoolExpr shouldBypass =
383- fieldType .kind ().isPrimitive () ? ctx .mkEq (value , defaultVal ) : ctx .mkFalse ();
414+ BoolExpr isDefaultPrimitive =
415+ fieldType .kind ().isPrimitive () ? ctx .mkEq (finalValue , defaultVal ) : ctx .mkFalse ();
416+
417+ BoolExpr shouldBypass = ctx .mkOr (ctx .mkNot (optionalHasValue ), isDefaultPrimitive );
384418
385419 msgValues =
386- (ArrayExpr ) ctx .mkITE (shouldBypass , msgValues , ctx .mkStore (msgValues , key , value ));
420+ (ArrayExpr ) ctx .mkITE (shouldBypass , msgValues , ctx .mkStore (msgValues , key , finalValue ));
387421
388422 msgPresence =
389423 (ArrayExpr )
@@ -655,7 +689,8 @@ private TranslatedValue translateComprehension(CelExpr celExpr, CelAbstractSynta
655689 List <Expr <?>> allRangeElems = new ArrayList <>();
656690
657691 // For statically known list/map literals, unroll them exactly.
658- if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .LIST ) {
692+ if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .LIST
693+ && iterRangeExpr .list ().optionalIndices ().isEmpty ()) {
659694 ImmutableList <CelExpr > elements = iterRangeExpr .list ().elements ();
660695 for (int i = 0 ; i < elements .size (); i ++) {
661696 TranslatedValue valueTv = translateExpr (elements .get (i ), ast );
@@ -664,7 +699,8 @@ private TranslatedValue translateComprehension(CelExpr celExpr, CelAbstractSynta
664699 iterationElements .add (new IterationElement (typeSystem .mkInt (i ), value ));
665700 allRangeElems .add (value );
666701 }
667- } else if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .MAP ) {
702+ } else if (iterRangeExpr .exprKind ().getKind () == ExprKind .Kind .MAP
703+ && iterRangeExpr .map ().entries ().stream ().noneMatch (CelExpr .CelMap .Entry ::optionalEntry )) {
668704 for (CelExpr .CelMap .Entry entry : iterRangeExpr .map ().entries ()) {
669705 TranslatedValue keyTv = translateExpr (entry .key (), ast );
670706 Expr <?> key = keyTv .z3Expr ();
@@ -782,36 +818,18 @@ private void applyBoundedMapBijection(
782818 }
783819 }
784820
785- Expr <?> kVar = ctx .mkFreshConst (MAP_BIJECTION_PREFIX , typeSystem .celValueSort ());
786- BoolExpr isValidKey =
787- ctx .mkOr (
788- typeSystem .isInt (kVar ), typeSystem .isUint (kVar ),
789- typeSystem .isBool (kVar ), typeSystem .isString (kVar ));
790- BoolExpr inMap = (BoolExpr ) ctx .mkSelect (mapPresence , kVar );
821+ BoolExpr isNotTruncated = ctx .mkLe (lengthExpr , ctx .mkInt (comprehensionUnrollLimit ));
791822
792- List < BoolExpr > inSeqMatches = new ArrayList <>( );
823+ ArrayExpr seqMap = ctx . mkConstArray ( typeSystem . celValueSort (), ctx . mkFalse () );
793824 for (int i = 0 ; i < comprehensionUnrollLimit ; i ++) {
794- BoolExpr match =
795- ctx .mkAnd (
796- ctx .mkLt (ctx .mkInt (i ), lengthExpr ), ctx .mkEq (kVar , ctx .mkNth (seq , ctx .mkInt (i ))));
797- inSeqMatches .add (match );
825+ seqMap =
826+ (ArrayExpr )
827+ ctx .mkITE (
828+ ctx .mkLt (ctx .mkInt (i ), lengthExpr ),
829+ ctx .mkStore (seqMap , ctx .mkNth (seq , ctx .mkInt (i )), ctx .mkTrue ()),
830+ seqMap );
798831 }
799- BoolExpr inSeq = CelZ3TypeSystem .mkOrFlattened (ctx , inSeqMatches );
800-
801- BoolExpr isNotTruncated = ctx .mkLe (lengthExpr , ctx .mkInt (comprehensionUnrollLimit ));
802-
803- Pattern inMapPattern = ctx .mkPattern (inMap );
804-
805- BoolExpr completeness =
806- ctx .mkForall (
807- new Expr <?>[] {kVar },
808- ctx .mkImplies (ctx .mkAnd (isNotTruncated , isValidKey , inMap ), inSeq ),
809- 1 ,
810- new Pattern [] {inMapPattern },
811- null ,
812- null ,
813- null );
814- typeConstraints .add (completeness );
832+ typeConstraints .add (ctx .mkImplies (isNotTruncated , ctx .mkEq (mapPresence , seqMap )));
815833 }
816834
817835 private TranslatedValue [] evaluateLoopCondAndStep (
0 commit comments