2222import com .microsoft .z3 .BoolExpr ;
2323import com .microsoft .z3 .Context ;
2424import com .microsoft .z3 .Expr ;
25+ import com .microsoft .z3 .FPExpr ;
2526import com .microsoft .z3 .FuncDecl ;
2627import com .microsoft .z3 .IntExpr ;
2728import com .microsoft .z3 .Quantifier ;
@@ -82,6 +83,9 @@ final class CelAstToZ3Translator {
8283 private static final String EMPTY_LIST_PREFIX = "!empty_list" ;
8384 private static final String EMPTY_MAP_PREFIX = "!empty_map" ;
8485 private static final String NULL_VALUE_FIELD = "null_value" ;
86+ private static final String SECONDS_FIELD = "seconds" ;
87+ private static final long MAX_UINT32 = 0xFFFFFFFFL ;
88+ private static final int MIN_DURATION_NANOS = -999_999_999 ;
8589 private final Context ctx ;
8690 private final CelZ3TypeSystem typeSystem ;
8791 private final CelZ3OperatorTranslator operatorTranslator ;
@@ -401,6 +405,12 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
401405 if (isJsonWkt (createStruct .messageName ())) {
402406 return translateJsonWktStruct (celExpr , createStruct , ast );
403407 }
408+ if (CelTypes .isWrapperType (createStruct .messageName ())) {
409+ return translateWrapperWktStruct (celExpr , createStruct , ast );
410+ }
411+ if (isTimeWkt (createStruct .messageName ())) {
412+ return translateTimeWktStruct (celExpr , createStruct , ast );
413+ }
404414
405415 // Bypass SMT when the struct is empty (return the cached SMT default pointer)
406416 if (createStruct .entries ().isEmpty ()) {
@@ -517,7 +527,12 @@ private TranslatedValue translateJsonWktStruct(
517527 && entry .fieldKey ().equals (NULL_VALUE_FIELD );
518528
519529 if (entry .optionalEntry ()) {
520- Expr <?> optRef = typeSystem .getOptionalRef (finalVal );
530+ Expr <?> checkedVal =
531+ typeSystem .withRuntimeError (finalVal , ctx .mkNot (typeSystem .isOptional (finalVal )));
532+ entryTv =
533+ TranslatedValue .create (
534+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
535+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
521536 BoolExpr hasValue = typeSystem .optHasValue (optRef );
522537
523538 Expr <?> unpackedVal = typeSystem .getOptionalValue (optRef );
@@ -533,6 +548,129 @@ private TranslatedValue translateJsonWktStruct(
533548 ctx , typeSystem , finalVal , celExpr , ImmutableList .of (entryTv ));
534549 }
535550
551+ private TranslatedValue translateWrapperWktStruct (
552+ CelExpr celExpr , CelExpr .CelStruct createStruct , CelAbstractSyntaxTree ast ) {
553+ String typeName = createStruct .messageName ();
554+ CelType wrapperType =
555+ CelTypes .getWellKnownCelType (typeName )
556+ .orElseThrow (
557+ () -> new IllegalArgumentException ("Unexpected wrapper type: " + typeName ));
558+ CelType targetType = ((NullableType ) wrapperType ).targetType ();
559+ Expr <?> currentVal = getDefaultValueForType (targetType );
560+
561+ List <TranslatedValue > entryTvs = new ArrayList <>();
562+ for (CelExpr .CelStruct .Entry entry : createStruct .entries ()) {
563+ TranslatedValue entryTv = translateExpr (entry .value (), ast );
564+ Expr <?> val = entryTv .z3Expr ();
565+ if (entry .optionalEntry ()) {
566+ Expr <?> checkedVal =
567+ typeSystem .withRuntimeError (val , ctx .mkNot (typeSystem .isOptional (val )));
568+ entryTv =
569+ TranslatedValue .create (
570+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
571+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
572+ BoolExpr hasValue = typeSystem .optHasValue (optRef );
573+ currentVal = ctx .mkITE (hasValue , typeSystem .getOptionalValue (optRef ), currentVal );
574+ } else {
575+ currentVal = val ;
576+ }
577+ entryTvs .add (entryTv );
578+ }
579+ BoolExpr validValue = createTypeConstraintForType (currentVal , targetType );
580+ if (typeName .equals (CelTypes .INT32_WRAPPER_MESSAGE )) {
581+ IntExpr intVal = typeSystem .getInt (currentVal );
582+ validValue =
583+ ctx .mkAnd (
584+ validValue ,
585+ ctx .mkGe (intVal , ctx .mkInt (Integer .MIN_VALUE )),
586+ ctx .mkLe (intVal , ctx .mkInt (Integer .MAX_VALUE )));
587+ } else if (typeName .equals (CelTypes .UINT32_WRAPPER_MESSAGE )) {
588+ IntExpr uintVal = typeSystem .getUint (currentVal );
589+ validValue =
590+ ctx .mkAnd (
591+ validValue ,
592+ ctx .mkGe (uintVal , ctx .mkInt (0 )),
593+ ctx .mkLe (uintVal , ctx .mkInt (MAX_UINT32 )));
594+ } else if (typeName .equals (CelTypes .FLOAT_WRAPPER_MESSAGE )) {
595+ FPExpr doubleVal = typeSystem .getDouble (currentVal );
596+ FPExpr narrowedFloat =
597+ ctx .mkFPToFP (ctx .mkFPRoundNearestTiesToEven (), doubleVal , ctx .mkFPSortSingle ());
598+ FPExpr widenedDouble =
599+ ctx .mkFPToFP (ctx .mkFPRoundNearestTiesToEven (), narrowedFloat , ctx .mkFPSortDouble ());
600+ currentVal = typeSystem .wrapDouble (widenedDouble );
601+ }
602+ currentVal = typeSystem .withRuntimeError (currentVal , ctx .mkNot (validValue ));
603+ return TranslatedValue .propagateStrict (ctx , typeSystem , currentVal , celExpr , entryTvs );
604+ }
605+
606+ private static boolean isTimeWkt (String messageName ) {
607+ return messageName .equals (CelTypes .TIMESTAMP_MESSAGE )
608+ || messageName .equals (CelTypes .DURATION_MESSAGE );
609+ }
610+
611+ private TranslatedValue translateTimeWktStruct (
612+ CelExpr celExpr , CelExpr .CelStruct createStruct , CelAbstractSyntaxTree ast ) {
613+ boolean isTimestamp = createStruct .messageName ().equals (CelTypes .TIMESTAMP_MESSAGE );
614+ Expr <?> secondsVal = typeSystem .mkInt (0 );
615+ Expr <?> nanosVal = typeSystem .mkInt (0 );
616+ List <TranslatedValue > entryTvs = new ArrayList <>();
617+
618+ for (CelExpr .CelStruct .Entry entry : createStruct .entries ()) {
619+ TranslatedValue entryTv = translateExpr (entry .value (), ast );
620+ Expr <?> val = entryTv .z3Expr ();
621+ Expr <?> unpackedVal = val ;
622+ BoolExpr hasValue = ctx .mkTrue ();
623+ if (entry .optionalEntry ()) {
624+ Expr <?> checkedVal =
625+ typeSystem .withRuntimeError (val , ctx .mkNot (typeSystem .isOptional (val )));
626+ entryTv =
627+ TranslatedValue .create (
628+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
629+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
630+ hasValue = typeSystem .optHasValue (optRef );
631+ unpackedVal = typeSystem .getOptionalValue (optRef );
632+ }
633+ if (entry .fieldKey ().equals (SECONDS_FIELD )) {
634+ secondsVal = ctx .mkITE (hasValue , unpackedVal , secondsVal );
635+ } else {
636+ nanosVal = ctx .mkITE (hasValue , unpackedVal , nanosVal );
637+ }
638+ entryTvs .add (entryTv );
639+ }
640+
641+ IntExpr seconds = typeSystem .getInt (secondsVal );
642+ IntExpr nanos = typeSystem .getInt (nanosVal );
643+ IntExpr totalNanos =
644+ (IntExpr )
645+ ctx .mkAdd (ctx .mkMul (seconds , ctx .mkInt (CelZ3TypeSystem .NANOS_PER_SECOND )), nanos );
646+
647+ BoolExpr typeError =
648+ ctx .mkOr (ctx .mkNot (typeSystem .isInt (secondsVal )), ctx .mkNot (typeSystem .isInt (nanosVal )));
649+ BoolExpr rangeError ;
650+ if (isTimestamp ) {
651+ BoolExpr nanosOutOfRange =
652+ ctx .mkOr (
653+ ctx .mkLt (nanos , ctx .mkInt (0 )),
654+ ctx .mkGe (nanos , ctx .mkInt (CelZ3TypeSystem .NANOS_PER_SECOND )));
655+ rangeError = ctx .mkOr (nanosOutOfRange , typeSystem .checkTimestampOverflow (totalNanos ));
656+ } else {
657+ BoolExpr nanosOutOfRange =
658+ ctx .mkOr (
659+ ctx .mkLt (nanos , ctx .mkInt (MIN_DURATION_NANOS )),
660+ ctx .mkGe (nanos , ctx .mkInt (CelZ3TypeSystem .NANOS_PER_SECOND )));
661+ BoolExpr signMismatch =
662+ ctx .mkOr (
663+ ctx .mkAnd (ctx .mkGt (seconds , ctx .mkInt (0 )), ctx .mkLt (nanos , ctx .mkInt (0 ))),
664+ ctx .mkAnd (ctx .mkLt (seconds , ctx .mkInt (0 )), ctx .mkGt (nanos , ctx .mkInt (0 ))));
665+ rangeError =
666+ ctx .mkOr (nanosOutOfRange , signMismatch , typeSystem .checkDurationOverflow (totalNanos ));
667+ }
668+ Expr <?> wrapped =
669+ isTimestamp ? typeSystem .wrapTimestamp (totalNanos ) : typeSystem .wrapDuration (totalNanos );
670+ Expr <?> result = typeSystem .withRuntimeError (wrapped , typeError , rangeError );
671+ return TranslatedValue .propagateStrict (ctx , typeSystem , result , celExpr , entryTvs );
672+ }
673+
536674 private Expr <?> getDefaultValueForType (CelType type ) {
537675 if (type instanceof NullableType ) {
538676 return typeSystem .mkNull ();
@@ -1326,14 +1464,14 @@ private BoolExpr createTypeConstraintForType(Expr<?> val, CelType type) {
13261464 return typeSystem .isBytes (val );
13271465 }
13281466 if (type .equals (SimpleType .TIMESTAMP )) {
1329- IntExpr seconds = typeSystem .getTimestamp (val );
1467+ IntExpr nanos = typeSystem .getTimestamp (val );
13301468 return ctx .mkAnd (
1331- typeSystem .isTimestamp (val ), ctx .mkNot (typeSystem .checkTimestampOverflow (seconds )));
1469+ typeSystem .isTimestamp (val ), ctx .mkNot (typeSystem .checkTimestampOverflow (nanos )));
13321470 }
13331471 if (type .equals (SimpleType .DURATION )) {
1334- IntExpr seconds = typeSystem .getDuration (val );
1472+ IntExpr nanos = typeSystem .getDuration (val );
13351473 return ctx .mkAnd (
1336- typeSystem .isDuration (val ), ctx .mkNot (typeSystem .checkDurationOverflow (seconds )));
1474+ typeSystem .isDuration (val ), ctx .mkNot (typeSystem .checkDurationOverflow (nanos )));
13371475 }
13381476
13391477 if (type instanceof ListType ) {
0 commit comments