@@ -842,10 +842,25 @@ applyEquation term rule =
842
842
-- check required constraints from lhs.
843
843
-- Reaction on false/indeterminate varies depending on the equation's type (function/simplification),
844
844
-- see @handleSimplificationEquation@ and @handleFunctionEquation@
845
- checkRequires subst
846
845
847
- -- check ensured conditions, filter any true ones, prune if any is false
848
- ensuredConditions <- checkEnsures subst
846
+ -- instantiate the requires clause with the obtained substitution
847
+ let rawRequired =
848
+ concatMap
849
+ splitBoolPredicates
850
+ rule. requires
851
+ -- required = map (coerce . substituteInTerm subst . coerce) rawRequired
852
+ knownPredicates <- (. predicates) <$> lift getState
853
+
854
+ let (syntacticRequires, restRequires) = case rule. attributes. syntacticClauses of
855
+ SyntacticClauses [] -> ([] , rawRequired)
856
+ SyntacticClauses _syntactic -> ([head rawRequired], tail rawRequired) -- TODO should use _syntactic to select
857
+
858
+ -- check required conditions
859
+ withContext CtxConstraint $
860
+ checkRequires subst (Set. toList knownPredicates) syntacticRequires restRequires
861
+
862
+ -- check all ensured conditions together with the path condition
863
+ ensuredConditions <- checkEnsures rule subst
849
864
lift $ pushConstraints $ Set. fromList ensuredConditions
850
865
851
866
-- when a new path condition is added, invalidate the equation cache
@@ -856,46 +871,9 @@ applyEquation term rule =
856
871
\ s -> s{cache = s. cache{equations = mempty }}
857
872
pure $ substituteInTerm subst rule. rhs
858
873
where
859
- filterOutKnownConstraints :: Set Predicate -> [Predicate ] -> EquationT io [Predicate ]
860
- filterOutKnownConstraints priorKnowledge constraitns = do
861
- let (knownTrue, toCheck) = partition (`Set.member` priorKnowledge) constraitns
862
- unless (null knownTrue) $
863
- getPrettyModifiers >>= \ case
864
- ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) ->
865
- logMessage $
866
- renderOneLineText $
867
- " Known true side conditions (won't check):"
868
- <+> hsep (intersperse " ," $ map (pretty' @ mods ) knownTrue)
869
- pure toCheck
870
-
871
- -- Simplify given predicate in a nested EquationT execution.
872
- -- Call 'whenBottom' if it is Bottom, return Nothing if it is Top,
873
- -- otherwise return the simplified remaining predicate.
874
- checkConstraint whenBottom (Predicate p) = withContext CtxConstraint $ do
875
- let fallBackToUnsimplifiedOrBottom :: EquationFailure -> EquationT io Term
876
- fallBackToUnsimplifiedOrBottom = \ case
877
- UndefinedTerm {} -> pure FalseBool
878
- _ -> pure p
879
- -- exceptions need to be handled differently in the recursion,
880
- -- falling back to the unsimplified constraint instead of aborting.
881
- simplified <-
882
- lift $ simplifyConstraint' True p `catch_` fallBackToUnsimplifiedOrBottom
883
- case simplified of
884
- FalseBool -> do
885
- throwE . whenBottom $ coerce p
886
- TrueBool -> pure Nothing
887
- other -> pure . Just $ coerce other
888
-
889
874
allMustBeConcrete (AllConstrained Concrete ) = True
890
875
allMustBeConcrete _ = False
891
876
892
- checkConcreteness ::
893
- Concreteness ->
894
- Map Variable Term ->
895
- ExceptT
896
- ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
897
- (EquationT io )
898
- ()
899
877
checkConcreteness Unconstrained _ = pure ()
900
878
checkConcreteness (AllConstrained constrained) subst =
901
879
mapM_ (\ (var, t) -> mkCheck (toPair var) constrained t) $ Map. assocs subst
@@ -942,74 +920,246 @@ applyEquation term rule =
942
920
check
943
921
$ Map. lookup Variable {variableSort = SortApp sortName [] , variableName} subst
944
922
945
- checkRequires ::
946
- Substitution ->
947
- ExceptT
948
- ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
949
- (EquationT io )
950
- ()
951
- checkRequires matchingSubst = do
952
- ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
953
- -- instantiate the requires clause with the obtained substitution
954
- let required =
955
- concatMap
956
- (splitBoolPredicates . coerce . substituteInTerm matchingSubst . coerce)
957
- rule. requires
958
- -- If the required condition is _syntactically_ present in
959
- -- the prior (known constraints), we don't check it.
960
- knownPredicates <- (. predicates) <$> lift getState
961
- toCheck <- lift $ filterOutKnownConstraints knownPredicates required
923
+ filterOutKnownConstraints ::
924
+ forall io .
925
+ LoggerMIO io =>
926
+ Set Predicate ->
927
+ [Predicate ] ->
928
+ EquationT io [Predicate ]
929
+ filterOutKnownConstraints priorKnowledge constraitns = do
930
+ let (knownTrue, toCheck) = partition (`Set.member` priorKnowledge) constraitns
931
+ unless (null knownTrue) $
932
+ getPrettyModifiers >>= \ case
933
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) ->
934
+ logMessage $
935
+ renderOneLineText $
936
+ " Known true side conditions (won't check):"
937
+ <+> hsep (intersperse " ," $ map (pretty' @ mods ) knownTrue)
938
+ pure toCheck
962
939
963
- -- check the filtered requires clause conditions
964
- unclearConditions <-
965
- catMaybes
966
- <$> mapM
967
- ( checkConstraint $ \ p -> (\ ctxt -> ctxt $ logMessage (" Condition simplified to #Bottom." :: Text ), ConditionFalse p)
940
+ checkRequires ::
941
+ forall io .
942
+ LoggerMIO io =>
943
+ Substitution ->
944
+ [Predicate ] ->
945
+ [Predicate ] ->
946
+ [Predicate ] ->
947
+ ExceptT
948
+ ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
949
+ (EquationT io )
950
+ ()
951
+ checkRequires currentSubst knownPredicates syntacticRequires restRequires' = do
952
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
953
+ case syntacticRequires of
954
+ [] -> checkRequiresSemantically currentSubst (Set. fromList knownPredicates) restRequires'
955
+ _ ->
956
+ withContext CtxSyntactic $
957
+ checkRequiresSyntactically
958
+ syntacticRequires
959
+ currentSubst
960
+ knownPredicates
961
+ restRequires'
962
+
963
+ checkRequiresSyntactically ::
964
+ forall io .
965
+ LoggerMIO io =>
966
+ [Predicate ] ->
967
+ Substitution ->
968
+ [Predicate ] ->
969
+ [Predicate ] ->
970
+ ExceptT
971
+ ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
972
+ (EquationT io )
973
+ ()
974
+ checkRequiresSyntactically syntacticRequires currentSubst knownPredicates restRequires' = do
975
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
976
+ case syntacticRequires of
977
+ [] ->
978
+ unless (null restRequires') $
979
+ -- no more @syntacticRequires@, but unresolved conditions remain: abort
980
+ throwRemainingRequires currentSubst restRequires'
981
+ (headSyntacticRequires : restSyntacticRequires) -> do
982
+ koreDef <- (. definition) <$> lift getConfig
983
+ case knownPredicates of
984
+ [] ->
985
+ unless (null restRequires') $
986
+ -- no more @knownPredicates@, but unresolved conditions remain: abort
987
+ throwRemainingRequires currentSubst restRequires'
988
+ (c : cs) -> case matchTermsWithSubst Eval koreDef currentSubst (coerce headSyntacticRequires) (coerce c) of
989
+ MatchFailed {} -> checkRequiresSyntactically syntacticRequires currentSubst cs restRequires'
990
+ MatchIndeterminate {} -> checkRequiresSyntactically syntacticRequires currentSubst cs restRequires'
991
+ MatchSuccess newSubst ->
992
+ -- we got a substitution from matching @headSyntacticRequires@ against the clause @c@ of the path condition,
993
+ -- try applying it to @syntacticRequires <> restRequires'@, simplifying that with LLVM,
994
+ -- and filtering from the path condition
995
+ ( do
996
+ withContext CtxSubstitution
997
+ $ logMessage
998
+ $ WithJsonMessage
999
+ (object [" substitution" .= (bimap (externaliseTerm . Var ) externaliseTerm <$> Map. toList newSubst)])
1000
+ $ renderOneLineText
1001
+ $ " Substitution:"
1002
+ <+> ( hsep $
1003
+ intersperse " ," $
1004
+ map (\ (k, v) -> pretty' @ mods k <+> " ->" <+> pretty' @ mods v) $
1005
+ Map. toList newSubst
1006
+ )
1007
+
1008
+ let substitited = map (coerce . substituteInTerm newSubst . coerce) (syntacticRequires <> restRequires')
1009
+ simplified <- coerce <$> mapM simplifyWithLLVM substitited
1010
+ stillUnclear <- lift $ filterOutKnownConstraints (Set. fromList knownPredicates) simplified
1011
+ case stillUnclear of
1012
+ [] -> pure () -- done
1013
+ _ -> do
1014
+ logMessage $
1015
+ renderOneLineText
1016
+ ( " Uncertain about conditions in syntactic simplification rule: "
1017
+ <+> hsep (intersperse " ," $ map (pretty' @ mods ) stillUnclear)
1018
+ )
1019
+ -- @newSubst@ does not solve the conditions completely, repeat the process for @restSyntacticRequires@
1020
+ -- using the learned @newSubst@
1021
+ checkRequiresSyntactically restSyntacticRequires newSubst knownPredicates restRequires'
1022
+ )
1023
+ `catchE` ( \ case
1024
+ (_, IndeterminateCondition {}) -> checkRequiresSyntactically syntacticRequires currentSubst cs restRequires'
1025
+ (_, ConditionFalse {}) -> checkRequiresSyntactically syntacticRequires currentSubst cs restRequires'
1026
+ err -> throwE err
1027
+ )
1028
+ where
1029
+ simplifyWithLLVM term = do
1030
+ simplified <-
1031
+ lift $
1032
+ simplifyConstraint' False term
1033
+ `catch_` ( \ case
1034
+ UndefinedTerm {} -> pure FalseBool
1035
+ _ -> pure term
1036
+ )
1037
+ case simplified of
1038
+ FalseBool -> do
1039
+ throwE
1040
+ ( \ ctxt -> ctxt $ logMessage (" Condition simplified to #Bottom." :: Text )
1041
+ , ConditionFalse . coerce $ term
968
1042
)
969
- toCheck
1043
+ other -> pure other
970
1044
971
- -- unclear conditions may have been simplified and
972
- -- could now be syntactically present in the path constraints, filter again
973
- stillUnclear <- lift $ filterOutKnownConstraints knownPredicates unclearConditions
1045
+ throwRemainingRequires subst preds = do
1046
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
1047
+ let substintuted = map (substituteInPredicate subst) preds
1048
+ throwE
1049
+ ( \ ctx ->
1050
+ ctx . logMessage $
1051
+ WithJsonMessage (object [" conditions" .= map (externaliseTerm . coerce) preds]) $
1052
+ renderOneLineText
1053
+ ( " Uncertain about conditions in syntactic simplification rule: "
1054
+ <+> hsep (intersperse " ," $ map (pretty' @ mods ) substintuted)
1055
+ )
1056
+ , IndeterminateCondition restRequires'
1057
+ )
974
1058
975
- solver :: SMT. SMTContext <- (. smtSolver) <$> lift getConfig
1059
+ checkRequiresSemantically ::
1060
+ forall io .
1061
+ LoggerMIO io =>
1062
+ Substitution ->
1063
+ Set Predicate ->
1064
+ [Predicate ] ->
1065
+ ExceptT
1066
+ ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
1067
+ (EquationT io )
1068
+ ()
1069
+ checkRequiresSemantically currentSubst knownPredicates restRequires' = do
1070
+ ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
1071
+
1072
+ -- apply current substitution to restRequires
1073
+ let restRequires = map (coerce . substituteInTerm currentSubst . coerce) restRequires'
1074
+ toCheck <- lift $ filterOutKnownConstraints knownPredicates restRequires
1075
+ unclearRequires <-
1076
+ catMaybes
1077
+ <$> mapM
1078
+ ( checkConstraint $ \ p -> (\ ctxt -> ctxt $ logMessage (" Condition simplified to #Bottom." :: Text ), ConditionFalse p)
1079
+ )
1080
+ toCheck
976
1081
977
- -- check any conditions that are still unclear with the SMT solver
978
- -- (or abort if no solver is being used), abort if still unclear after
979
- unless (null stillUnclear) $
980
- lift (SMT. checkPredicates solver knownPredicates mempty (Set. fromList stillUnclear)) >>= \ case
981
- SMT. IsUnknown {} -> do
982
- -- no solver or still unclear: abort
983
- throwE
984
- ( \ ctx ->
985
- ctx . logMessage $
986
- WithJsonMessage (object [" conditions" .= map (externaliseTerm . coerce) stillUnclear]) $
987
- renderOneLineText
988
- ( " Uncertain about conditions in rule: " <+> hsep (intersperse " ," $ map (pretty' @ mods ) stillUnclear)
989
- )
990
- , IndeterminateCondition stillUnclear
991
- )
992
- SMT. IsInvalid -> do
993
- -- actually false given path condition: fail
994
- let failedP = Predicate $ foldl1' AndTerm $ map coerce stillUnclear
995
- throwE
996
- ( \ ctx ->
997
- ctx . logMessage $
998
- WithJsonMessage (object [" conditions" .= map (externaliseTerm . coerce) stillUnclear]) $
999
- renderOneLineText (" Required condition found to be false: " <> pretty' @ mods failedP)
1000
- , ConditionFalse failedP
1001
- )
1002
- SMT. IsValid {} -> do
1003
- -- can proceed
1004
- pure ()
1082
+ -- unclear conditions may have been simplified and
1083
+ -- could now be syntactically present in the path constraints, filter again
1084
+ stillUnclear <- lift $ filterOutKnownConstraints knownPredicates unclearRequires
1005
1085
1006
- checkEnsures ::
1007
- Substitution ->
1008
- ExceptT
1009
- ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
1010
- (EquationT io )
1011
- [Predicate ]
1012
- checkEnsures matchingSubst = do
1086
+ -- check unclear requires-clauses in the context of known constraints (prior)
1087
+ solver :: SMT. SMTContext <- (. smtSolver) <$> lift getConfig
1088
+
1089
+ -- check any conditions that are still unclear with the SMT solver
1090
+ -- (or abort if no solver is being used), abort if still unclear after
1091
+ unless (null stillUnclear) $
1092
+ lift (SMT. checkPredicates solver knownPredicates mempty (Set. fromList stillUnclear)) >>= \ case
1093
+ SMT. IsUnknown {} -> do
1094
+ -- no solver or still unclear: abort
1095
+ throwE
1096
+ ( \ ctx ->
1097
+ ctx . logMessage $
1098
+ WithJsonMessage (object [" conditions" .= map (externaliseTerm . coerce) stillUnclear]) $
1099
+ renderOneLineText
1100
+ ( " Uncertain about conditions in rule: " <+> hsep (intersperse " ," $ map (pretty' @ mods ) stillUnclear)
1101
+ )
1102
+ , IndeterminateCondition stillUnclear
1103
+ )
1104
+ SMT. IsInvalid -> do
1105
+ -- actually false given path condition: fail
1106
+ let failedP = Predicate $ foldl1' AndTerm $ map coerce stillUnclear
1107
+ throwE
1108
+ ( \ ctx ->
1109
+ ctx . logMessage $
1110
+ WithJsonMessage (object [" conditions" .= map (externaliseTerm . coerce) stillUnclear]) $
1111
+ renderOneLineText (" Required condition found to be false: " <> pretty' @ mods failedP)
1112
+ , ConditionFalse failedP
1113
+ )
1114
+ SMT. IsValid {} -> do
1115
+ -- can proceed
1116
+ pure ()
1117
+
1118
+ -- Simplify given predicate in a nested EquationT execution.
1119
+ -- Call 'whenBottom' if it is Bottom, return Nothing if it is Top,
1120
+ -- otherwise return the simplified remaining predicate.
1121
+ checkConstraint ::
1122
+ forall io .
1123
+ LoggerMIO io =>
1124
+ ( Predicate ->
1125
+ ( (EquationT io () -> EquationT io () ) -> EquationT io ()
1126
+ , ApplyEquationFailure
1127
+ )
1128
+ ) ->
1129
+ Predicate ->
1130
+ ExceptT
1131
+ ( (EquationT io () -> EquationT io () ) -> EquationT io ()
1132
+ , ApplyEquationFailure
1133
+ )
1134
+ (EquationT io )
1135
+ (Maybe Predicate )
1136
+ checkConstraint whenBottom (Predicate p) = withContext CtxConstraint $ do
1137
+ let fallBackToUnsimplifiedOrBottom :: EquationFailure -> EquationT io Term
1138
+ fallBackToUnsimplifiedOrBottom = \ case
1139
+ UndefinedTerm {} -> pure FalseBool
1140
+ _ -> pure p
1141
+ -- exceptions need to be handled differently in the recursion,
1142
+ -- falling back to the unsimplified constraint instead of aborting.
1143
+ simplified <-
1144
+ lift $ simplifyConstraint' True p `catch_` fallBackToUnsimplifiedOrBottom
1145
+ case simplified of
1146
+ FalseBool -> do
1147
+ throwE . whenBottom $ coerce p
1148
+ TrueBool -> pure Nothing
1149
+ other -> pure . Just $ coerce other
1150
+
1151
+ checkEnsures ::
1152
+ forall io tag .
1153
+ LoggerMIO io =>
1154
+ RewriteRule tag ->
1155
+ Substitution ->
1156
+ ExceptT
1157
+ ((EquationT io () -> EquationT io () ) -> EquationT io () , ApplyEquationFailure )
1158
+ (EquationT io )
1159
+ [Predicate ]
1160
+ checkEnsures
1161
+ rule
1162
+ matchingSubst = do
1013
1163
ModifiersRep (_ :: FromModifiersT mods => Proxy mods ) <- getPrettyModifiers
1014
1164
let ensured =
1015
1165
concatMap
0 commit comments