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,11 @@ 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 String SUBSECOND_FUNC_SUFFIX = "_subsecond" ;
88+ private static final long MAX_UINT32 = 0xFFFFFFFFL ;
89+ private static final int NANOS_PER_SECOND = 1_000_000_000 ;
90+ private static final int MIN_DURATION_NANOS = -999_999_999 ;
8591 private final Context ctx ;
8692 private final CelZ3TypeSystem typeSystem ;
8793 private final CelZ3OperatorTranslator operatorTranslator ;
@@ -401,6 +407,12 @@ private TranslatedValue translateStruct(CelExpr celExpr, CelAbstractSyntaxTree a
401407 if (isJsonWkt (createStruct .messageName ())) {
402408 return translateJsonWktStruct (celExpr , createStruct , ast );
403409 }
410+ if (CelTypes .isWrapperType (createStruct .messageName ())) {
411+ return translateWrapperWktStruct (celExpr , createStruct , ast );
412+ }
413+ if (isTimeWkt (createStruct .messageName ())) {
414+ return translateTimeWktStruct (celExpr , createStruct , ast );
415+ }
404416
405417 // Bypass SMT when the struct is empty (return the cached SMT default pointer)
406418 if (createStruct .entries ().isEmpty ()) {
@@ -517,7 +529,12 @@ private TranslatedValue translateJsonWktStruct(
517529 && entry .fieldKey ().equals (NULL_VALUE_FIELD );
518530
519531 if (entry .optionalEntry ()) {
520- Expr <?> optRef = typeSystem .getOptionalRef (finalVal );
532+ Expr <?> checkedVal =
533+ typeSystem .withRuntimeError (finalVal , ctx .mkNot (typeSystem .isOptional (finalVal )));
534+ entryTv =
535+ TranslatedValue .create (
536+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
537+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
521538 BoolExpr hasValue = typeSystem .optHasValue (optRef );
522539
523540 Expr <?> unpackedVal = typeSystem .getOptionalValue (optRef );
@@ -533,6 +550,144 @@ private TranslatedValue translateJsonWktStruct(
533550 ctx , typeSystem , finalVal , celExpr , ImmutableList .of (entryTv ));
534551 }
535552
553+ private TranslatedValue translateWrapperWktStruct (
554+ CelExpr celExpr , CelExpr .CelStruct createStruct , CelAbstractSyntaxTree ast ) {
555+ String typeName = createStruct .messageName ();
556+ CelType wrapperType =
557+ CelTypes .getWellKnownCelType (typeName )
558+ .orElseThrow (
559+ () -> new IllegalArgumentException ("Unexpected wrapper type: " + typeName ));
560+ CelType targetType = ((NullableType ) wrapperType ).targetType ();
561+ Expr <?> currentVal = getDefaultValueForType (targetType );
562+
563+ List <TranslatedValue > entryTvs = new ArrayList <>();
564+ for (CelExpr .CelStruct .Entry entry : createStruct .entries ()) {
565+ TranslatedValue entryTv = translateExpr (entry .value (), ast );
566+ Expr <?> val = entryTv .z3Expr ();
567+ if (entry .optionalEntry ()) {
568+ Expr <?> checkedVal =
569+ typeSystem .withRuntimeError (val , ctx .mkNot (typeSystem .isOptional (val )));
570+ entryTv =
571+ TranslatedValue .create (
572+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
573+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
574+ BoolExpr hasValue = typeSystem .optHasValue (optRef );
575+ currentVal = ctx .mkITE (hasValue , typeSystem .getOptionalValue (optRef ), currentVal );
576+ } else {
577+ currentVal = val ;
578+ }
579+ entryTvs .add (entryTv );
580+ }
581+ BoolExpr validValue = createTypeConstraintForType (currentVal , targetType );
582+ if (typeName .equals (CelTypes .INT32_WRAPPER_MESSAGE )) {
583+ IntExpr intVal = typeSystem .getInt (currentVal );
584+ validValue =
585+ ctx .mkAnd (
586+ validValue ,
587+ ctx .mkGe (intVal , ctx .mkInt (Integer .MIN_VALUE )),
588+ ctx .mkLe (intVal , ctx .mkInt (Integer .MAX_VALUE )));
589+ } else if (typeName .equals (CelTypes .UINT32_WRAPPER_MESSAGE )) {
590+ IntExpr uintVal = typeSystem .getUint (currentVal );
591+ validValue =
592+ ctx .mkAnd (
593+ validValue ,
594+ ctx .mkGe (uintVal , ctx .mkInt (0 )),
595+ ctx .mkLe (uintVal , ctx .mkInt (MAX_UINT32 )));
596+ } else if (typeName .equals (CelTypes .FLOAT_WRAPPER_MESSAGE )) {
597+ FPExpr floatVal = typeSystem .getDouble (currentVal );
598+ validValue =
599+ ctx .mkAnd (
600+ validValue ,
601+ ctx .mkFPGEq (floatVal , typeSystem .mkFpDouble (-Float .MAX_VALUE )),
602+ ctx .mkFPLEq (floatVal , typeSystem .mkFpDouble (Float .MAX_VALUE )));
603+ }
604+ currentVal = typeSystem .withRuntimeError (currentVal , ctx .mkNot (validValue ));
605+ return TranslatedValue .propagateStrict (ctx , typeSystem , currentVal , celExpr , entryTvs );
606+ }
607+
608+ private static boolean isTimeWkt (String messageName ) {
609+ return messageName .equals (CelTypes .TIMESTAMP_MESSAGE )
610+ || messageName .equals (CelTypes .DURATION_MESSAGE );
611+ }
612+
613+ private TranslatedValue translateTimeWktStruct (
614+ CelExpr celExpr , CelExpr .CelStruct createStruct , CelAbstractSyntaxTree ast ) {
615+ boolean isTimestamp = createStruct .messageName ().equals (CelTypes .TIMESTAMP_MESSAGE );
616+ Expr <?> secondsVal = typeSystem .mkInt (0 );
617+ Expr <?> nanosVal = typeSystem .mkInt (0 );
618+ List <TranslatedValue > entryTvs = new ArrayList <>();
619+
620+ for (CelExpr .CelStruct .Entry entry : createStruct .entries ()) {
621+ TranslatedValue entryTv = translateExpr (entry .value (), ast );
622+ Expr <?> val = entryTv .z3Expr ();
623+ Expr <?> unpackedVal = val ;
624+ BoolExpr hasValue = ctx .mkTrue ();
625+ if (entry .optionalEntry ()) {
626+ Expr <?> checkedVal =
627+ typeSystem .withRuntimeError (val , ctx .mkNot (typeSystem .isOptional (val )));
628+ entryTv =
629+ TranslatedValue .create (
630+ checkedVal , entry .value (), typeSystem , entryTv .isApproximate ());
631+ Expr <?> optRef = typeSystem .getOptionalRef (checkedVal );
632+ hasValue = typeSystem .optHasValue (optRef );
633+ unpackedVal = typeSystem .getOptionalValue (optRef );
634+ }
635+ if (entry .fieldKey ().equals (SECONDS_FIELD )) {
636+ secondsVal = ctx .mkITE (hasValue , unpackedVal , secondsVal );
637+ } else {
638+ nanosVal = ctx .mkITE (hasValue , unpackedVal , nanosVal );
639+ }
640+ entryTvs .add (entryTv );
641+ }
642+
643+ IntExpr seconds = typeSystem .getInt (secondsVal );
644+ BoolExpr secondsError =
645+ ctx .mkOr (
646+ ctx .mkNot (typeSystem .isInt (secondsVal )),
647+ isTimestamp
648+ ? typeSystem .checkTimestampOverflow (seconds )
649+ : typeSystem .checkDurationOverflow (seconds ));
650+
651+ IntExpr nanos = typeSystem .getInt (nanosVal );
652+ BoolExpr nanosError ;
653+ if (isTimestamp ) {
654+ nanosError =
655+ ctx .mkOr (
656+ ctx .mkNot (typeSystem .isInt (nanosVal )),
657+ ctx .mkLt (nanos , ctx .mkInt (0 )),
658+ ctx .mkGe (nanos , ctx .mkInt (NANOS_PER_SECOND )));
659+ } else {
660+ BoolExpr nanosOutOfRange =
661+ ctx .mkOr (
662+ ctx .mkLt (nanos , ctx .mkInt (MIN_DURATION_NANOS )),
663+ ctx .mkGe (nanos , ctx .mkInt (NANOS_PER_SECOND )));
664+ BoolExpr signMismatch =
665+ ctx .mkOr (
666+ ctx .mkAnd (ctx .mkGt (seconds , ctx .mkInt (0 )), ctx .mkLt (nanos , ctx .mkInt (0 ))),
667+ ctx .mkAnd (ctx .mkLt (seconds , ctx .mkInt (0 )), ctx .mkGt (nanos , ctx .mkInt (0 ))));
668+ nanosError = ctx .mkOr (ctx .mkNot (typeSystem .isInt (nanosVal )), nanosOutOfRange , signMismatch );
669+ }
670+ BoolExpr nanosNonZero = ctx .mkNot (ctx .mkEq (nanos , ctx .mkInt (0 )));
671+ BoolExpr subSecondApprox = ctx .mkAnd (ctx .mkNot (nanosError ), nanosNonZero );
672+ FuncDecl <?> subSecondOffset =
673+ typeSystem .internFuncDecl (
674+ createStruct .messageName () + SUBSECOND_FUNC_SUFFIX ,
675+ new Sort [] {ctx .getIntSort (), ctx .getIntSort ()},
676+ ctx .getIntSort ());
677+ IntExpr subSecondTime = (IntExpr ) ctx .mkApp (subSecondOffset , seconds , nanos );
678+ IntExpr encodedTime = (IntExpr ) ctx .mkITE (subSecondApprox , subSecondTime , seconds );
679+ Expr <?> wrapped =
680+ isTimestamp ? typeSystem .wrapTimestamp (encodedTime ) : typeSystem .wrapDuration (encodedTime );
681+ Expr <?> result = typeSystem .withRuntimeError (wrapped , secondsError , nanosError );
682+ TranslatedValue strictResult =
683+ TranslatedValue .propagateStrict (ctx , typeSystem , result , celExpr , entryTvs );
684+ return TranslatedValue .create (
685+ strictResult .z3Expr (),
686+ celExpr ,
687+ typeSystem ,
688+ CelZ3TypeSystem .mkOrFlattened (ctx , strictResult .isApproximate (), subSecondApprox ));
689+ }
690+
536691 private Expr <?> getDefaultValueForType (CelType type ) {
537692 if (type instanceof NullableType ) {
538693 return typeSystem .mkNull ();
0 commit comments