Skip to content

Commit b66cca9

Browse files
committed
Add some additional tactics to assist proving inductive facts.
Reimplement `goal_insert` based on cut. This tactic was removed in an earlier phase, but here we can add it back fairly easily. Add the ability to apply local hypotheses in addition to theorems, via `goal_apply_hyp`. Add the ability to specialize a local hypothesis via `goal_specialize_hyp`. This is especially useful for specializing an inductive hypothesis in the (unfortunatly common) case where solvers cannot figure out the correct instantiations. The `split_goal` tactic now works on hypotheses that represent SAWCore implications (i.e., nondependent functions between Props), which provides the standard modus ponens rule.
1 parent 09b0edb commit b66cca9

File tree

3 files changed

+131
-31
lines changed

3 files changed

+131
-31
lines changed

src/SAWScript/Builtins.hs

+11-11
Original file line numberDiff line numberDiff line change
@@ -838,25 +838,25 @@ goal_revert_hyp i =
838838
do sc <- SV.scriptTopLevel getSharedContext
839839
execTactic (tacticRevertHyp sc i)
840840

841-
{-
842-
goal_assume :: ProofScript Theorem
843-
goal_assume =
844-
do sc <- SV.scriptTopLevel getSharedContext
845-
pos <- SV.scriptTopLevel SV.getPosition
846-
execTactic (tacticAssume sc pos)
847-
-}
848-
849841
goal_intro :: Text -> ProofScript TypedTerm
850842
goal_intro s =
851843
do sc <- SV.scriptTopLevel getSharedContext
852844
execTactic (tacticIntro sc s)
853845

854-
{-
855846
goal_insert :: Theorem -> ProofScript ()
856847
goal_insert thm =
857848
do sc <- SV.scriptTopLevel getSharedContext
858-
execTactic (tacticCut sc thm)
859-
-}
849+
execTactic (tacticInsert sc thm)
850+
851+
goal_specialize_hyp :: [TypedTerm] -> ProofScript ()
852+
goal_specialize_hyp ts =
853+
do sc <- SV.scriptTopLevel getSharedContext
854+
execTactic (tacticSpecializeHyp sc (map ttTerm ts))
855+
856+
goal_apply_hyp :: Integer -> ProofScript ()
857+
goal_apply_hyp n =
858+
do sc <- SV.scriptTopLevel getSharedContext
859+
execTactic (tacticApplyHyp sc n)
860860

861861
goal_num_when :: Int -> ProofScript () -> ProofScript ()
862862
goal_num_when n script =

src/SAWScript/Interpreter.hs

+13-8
Original file line numberDiff line numberDiff line change
@@ -1706,19 +1706,24 @@ primitives = Map.fromList
17061706
Experimental
17071707
[ "TODO "]
17081708

1709-
{-
1710-
, prim "goal_assume" "ProofScript Theorem"
1711-
(pureVal goal_assume)
1712-
Experimental
1713-
[ "Convert the first hypothesis in the current proof goal into a"
1714-
, "local Theorem."
1715-
]
17161709
, prim "goal_insert" "Theorem -> ProofScript ()"
17171710
(pureVal goal_insert)
17181711
Experimental
17191712
[ "Insert a Theorem as a new hypothesis in the current proof goal."
17201713
]
1721-
-}
1714+
1715+
, prim "goal_apply_hyp" "Int -> ProofScript ()"
1716+
(pureVal goal_apply_hyp)
1717+
Experimental
1718+
[ "Apply the numbered local hypothesis to the focused conclusion." ]
1719+
1720+
, prim "goal_specialize_hyp" "[Term] -> ProofScript ()"
1721+
(pureVal goal_specialize_hyp)
1722+
Experimental
1723+
[ "Specialize the focused local hypothesis by supplying the values"
1724+
, "for universal quantifiers. A new specialized hypothesis will be"
1725+
, "added to the sequent."
1726+
]
17221727

17231728
, prim "goal_intro" "String -> ProofScript Term"
17241729
(pureVal goal_intro)

src/SAWScript/Proof.hs

+107-12
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ module SAWScript.Proof
9292
, withFirstGoal
9393
, tacticIntro
9494
, tacticApply
95+
, tacticApplyHyp
9596
, tacticSplit
9697
, tacticCut
9798
, tacticTrivial
@@ -101,6 +102,8 @@ module SAWScript.Proof
101102
, tacticExact
102103
, tacticIntroHyps
103104
, tacticRevertHyp
105+
, tacticInsert
106+
, tacticSpecializeHyp
104107

105108
, Quantification(..)
106109
, predicateToProp
@@ -324,7 +327,13 @@ splitSequent sc sqt =
324327
return (Just ( HypFocusedSequent (FB hs1 x (hs2 ++ [b])) gs
325328
, HypFocusedSequent (FB hs1 y (hs2 ++ [nb])) gs
326329
))
327-
Nothing -> return Nothing
330+
Nothing ->
331+
splitImpl sc h >>= \case
332+
Just (x, y) ->
333+
return (Just ( HypFocusedSequent (FB hs1 y hs2) gs
334+
, GoalFocusedSequent (hs1 ++ [h] ++ hs2) (FB gs x [])
335+
))
336+
Nothing -> return Nothing
328337

329338
UnfocusedSequent _ _ -> fail "split tactic: focus required"
330339

@@ -857,11 +866,13 @@ data Evidence
857866
-- current goal.
858867
| ApplyEvidence !Theorem ![Either Term Evidence]
859868

860-
-- | This type of evidence is used to prove an implication. The included
861-
-- proposition must match the hypothesis of the goal, and the included
862-
-- evidence must match the conclusion of the goal. The proposition is
863-
-- allowed to appear inside the evidence as a local assumption.
864-
-- | AssumeEvidence TheoremNonce Prop Evidence
869+
-- | This type of evidence is produced when a local hypothesis is applied
870+
-- via backward reasoning to prove a goal. Pi-quantified variables
871+
-- of the hypothesis may be specialized either by giving an explicit @Term@ to
872+
-- instantiate the variable, or by giving @Evidence@ for @Prop@ hypotheses.
873+
-- After specializing the given @Theorem@ the result must match the
874+
-- current goal.
875+
| ApplyHypEvidence Integer ![Either Term Evidence]
865876

866877
-- | This type of evidence is used to prove a universally-quantified statement.
867878
| IntroEvidence !(ExtCns Term) !Evidence
@@ -971,6 +982,14 @@ cutEvidence :: Prop -> [Evidence] -> IO Evidence
971982
cutEvidence p [e1,e2] = pure (CutEvidence p e1 e2)
972983
cutEvidence _ _ = fail "cutEvidence: expected two evidence values"
973984

985+
insertEvidence :: Theorem -> [Evidence] -> IO Evidence
986+
insertEvidence thm [e] = pure (CutEvidence (_thmProp thm) e (ApplyEvidence thm []))
987+
insertEvidence _ _ = fail "insertEvidence: expected one evidence value"
988+
989+
specializeHypEvidence :: Integer -> Prop -> [Term] -> [Evidence] -> IO Evidence
990+
specializeHypEvidence n h ts [e] = pure (CutEvidence h e (ApplyHypEvidence n (map Left ts)))
991+
specializeHypEvidence _ _ _ _ = fail "specializeHypEvidence: expected one evidence value"
992+
974993
structuralEvidence :: Sequent -> Evidence -> Evidence
975994
structuralEvidence _sqt (StructuralEvidence sqt' e) = StructuralEvidence sqt' e
976995
structuralEvidence sqt e = StructuralEvidence sqt e
@@ -1033,16 +1052,17 @@ constructTheorem sc db p e loc ploc rsn elapsed =
10331052
-- of the given theorem.
10341053
specializeTheorem :: SharedContext -> TheoremDB -> Pos -> Text -> Theorem -> [Term] -> IO Theorem
10351054
specializeTheorem _sc _db _loc _rsn thm [] = return thm
1036-
specializeTheorem sc db loc rsn thm ts0 =
1037-
do let p0 = unProp (_thmProp thm)
1038-
res <- TC.runTCM (loop p0 ts0) sc Nothing []
1055+
specializeTheorem sc db loc rsn thm ts =
1056+
do res <- specializeProp sc (_thmProp thm) ts
10391057
case res of
10401058
Left err -> fail (unlines (["specialize_theorem: failed to specialize"] ++ TC.prettyTCError err))
10411059
Right p' ->
1042-
constructTheorem sc db (Prop p') (ApplyEvidence thm (map Left ts0)) loc Nothing rsn 0
1060+
constructTheorem sc db p' (ApplyEvidence thm (map Left ts)) loc Nothing rsn 0
10431061

1062+
specializeProp :: SharedContext -> Prop -> [Term] -> IO (Either TC.TCError Prop)
1063+
specializeProp sc (Prop p0) ts0 = TC.runTCM (loop p0 ts0) sc Nothing []
10441064
where
1045-
loop p [] = return p
1065+
loop p [] = return (Prop p)
10461066
loop p (t:ts) =
10471067
do prop <- liftIO (scSort sc propSort)
10481068
t' <- TC.typeInferComplete t
@@ -1388,6 +1408,29 @@ checkEvidence sc = \e p -> do nenv <- scGetNamingEnv sc
13881408
d2 <- check nenv e2 sqt2
13891409
return (d1 <> d2)
13901410

1411+
ApplyHypEvidence n es ->
1412+
case sqt of
1413+
GoalFocusedSequent hs (FB gs1 g gs2) ->
1414+
case genericDrop n hs of
1415+
(h:_) ->
1416+
do (d,sy,p') <- checkApply nenv (\g' -> GoalFocusedSequent hs (FB gs1 g' gs2)) h es
1417+
ok <- scConvertible sc False (unProp g) p'
1418+
unless ok $ fail $ unlines
1419+
[ "Apply evidence does not match the required proposition"
1420+
, showTerm (unProp g)
1421+
, showTerm p'
1422+
]
1423+
return (d, sy)
1424+
1425+
_ -> fail $ unlines $
1426+
[ "Not enough hypotheses in apply hypothesis: " ++ show n
1427+
, prettySequent defaultPPOpts nenv sqt
1428+
]
1429+
_ -> fail $ unlines $
1430+
[ "Apply hypothesis evidence requires a goal-focused sequent."
1431+
, prettySequent defaultPPOpts nenv sqt
1432+
]
1433+
13911434
ApplyEvidence thm es ->
13921435
case sequentState sqt of
13931436
GoalFocus p mkSqt ->
@@ -1532,7 +1575,7 @@ finishProof sc db conclProp ps@(ProofState gs (concl,loc,ploc,rsn) stats _ check
15321575
(deps,sy) <- checkEvidence sc e' conclProp
15331576
n <- freshNonce globalNonceGenerator
15341577
end <- getCurrentTime
1535-
thm <- (if recordThm then recordTheorem db else return)
1578+
thm <- (if recordThm then recordTheorem db else return)
15361579
Theorem
15371580
{ _thmProp = conclProp
15381581
, _thmStats = stats
@@ -1814,6 +1857,37 @@ tacticRevertHyp sc i = Tactic \goal ->
18141857
_ -> fail "goal_revert_hyp: conclusion focus required"
18151858

18161859

1860+
-- | Attempt to prove a goal by applying a local hypothesis. Any hypotheses of
1861+
-- the applied proposition will generate additional subgoals.
1862+
tacticApplyHyp :: (F.MonadFail m, MonadIO m) => SharedContext -> Integer -> Tactic m ()
1863+
tacticApplyHyp sc n = Tactic \goal ->
1864+
case goalSequent goal of
1865+
UnfocusedSequent{} -> fail "apply hyp tactic: focus required"
1866+
HypFocusedSequent{} -> fail "apply hyp tactic: cannot apply in a hypothesis"
1867+
GoalFocusedSequent hs (FB gs1 g gs2) ->
1868+
case genericDrop n hs of
1869+
(h:_) ->
1870+
liftIO (goalApply sc h g) >>= \case
1871+
Nothing -> fail "apply hyp tactic: no match"
1872+
Just newterms ->
1873+
let newgoals =
1874+
[ goal{ goalSequent = GoalFocusedSequent hs (FB gs1 p gs2)
1875+
, goalType = goalType goal ++ ".subgoal" ++ show i
1876+
}
1877+
| Right p <- newterms
1878+
| i <- [0::Integer ]
1879+
] in
1880+
return ((), mempty, newgoals, \es -> ApplyHypEvidence n <$> processEvidence newterms es)
1881+
_ -> fail "apply hyp tactic: not enough hypotheses"
1882+
1883+
where
1884+
processEvidence :: [Either Term Prop] -> [Evidence] -> IO [Either Term Evidence]
1885+
processEvidence (Left tm : xs) es = (Left tm :) <$> processEvidence xs es
1886+
processEvidence (Right _ : xs) (e:es) = (Right e :) <$> processEvidence xs es
1887+
processEvidence [] [] = pure []
1888+
processEvidence _ _ = fail "apply hyp tactic failed: evidence mismatch"
1889+
1890+
18171891
-- | Attempt to prove a goal by applying the given theorem. Any hypotheses of
18181892
-- the theorem will generate additional subgoals.
18191893
tacticApply :: (F.MonadFail m, MonadIO m) => SharedContext -> Theorem -> Tactic m ()
@@ -1851,6 +1925,27 @@ tacticSplit sc = Tactic \gl ->
18511925
Nothing -> fail "split tactic failed"
18521926

18531927

1928+
tacticSpecializeHyp ::
1929+
(F.MonadFail m, MonadIO m) => SharedContext -> [Term] -> Tactic m ()
1930+
tacticSpecializeHyp sc ts = Tactic \gl ->
1931+
case goalSequent gl of
1932+
HypFocusedSequent (FB hs1 h hs2) gs ->
1933+
do res <- liftIO (specializeProp sc h ts)
1934+
case res of
1935+
Left err ->
1936+
fail (unlines (["specialize_hyp tactic: failed to specialize"] ++ TC.prettyTCError err))
1937+
Right h' ->
1938+
do let gl' = gl{ goalSequent = HypFocusedSequent (FB hs1 h (hs2++[h'])) gs }
1939+
return ((), mempty, [gl'], specializeHypEvidence (genericLength hs1) h' ts)
1940+
_ -> fail "specialize_hyp tactic failed: requires hypothesis focus"
1941+
1942+
1943+
tacticInsert :: (F.MonadFail m, MonadIO m) => SharedContext -> Theorem -> Tactic m ()
1944+
tacticInsert _sc thm = Tactic \gl ->
1945+
let sqt = addHypothesis (_thmProp thm) (goalSequent gl)
1946+
gl' = gl{ goalSequent = sqt }
1947+
in return ((), mempty, [gl'], insertEvidence thm)
1948+
18541949
tacticCut :: (F.MonadFail m, MonadIO m) => SharedContext -> Prop -> Tactic m ()
18551950
tacticCut _sc p = Tactic \gl ->
18561951
let sqt1 = addHypothesis p (goalSequent gl)

0 commit comments

Comments
 (0)