scyther-proof-0.3.0: src/Scyther/Sequent.hs
{-# LANGUAGE DeriveDataTypeable #-}
module Scyther.Sequent (
-- * Datatype
Sequent(..)
, seProto
-- ** Logically safe construction
, wellTypedCases
, saturate
, frule
, fruleInst
, chainRule
, splitEq
, exploitTyping
, uniqueTIDQuantifiers
) where
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Data
import Control.Arrow
import Control.Monad
import qualified Scyther.Equalities as E
import Scyther.Facts
import Scyther.Formula
------------------------------------------------------------------------------
-- Sequents
------------------------------------------------------------------------------
-- | A sequent with a conjunction of a set of facts as the premise and a single
-- formula as the conclusion denoting a statement about a reachable state of
-- a protocol.
data Sequent = Sequent {
sePrem :: Facts
, seConcl :: Formula
}
deriving( Eq, Show, Ord, Data, Typeable )
-- | The protocol of a sequent.
seProto :: Sequent -> Protocol
seProto = protocol . sePrem
-- Construction
---------------
-- | Make all thread identifiers occurring in the sequent unique by
-- consistently relabeling the thread identifiers in the conclusion.
uniqueTIDQuantifiers :: Sequent -> Sequent
uniqueTIDQuantifiers (Sequent prem concl) =
Sequent prem (relabelTIDs [nextTID prem..] concl)
-- | Apply a function to the premise, but return only the updated sequent if
-- the premise was changed.
changePrem :: MonadPlus m => (Facts -> m Facts) -> Sequent -> m Sequent
changePrem f se = do
let prem0 = sePrem se
prem1 <- f prem0
guard (prem0 /= prem1)
return $ se { sePrem = prem1 }
-- | The named list of sequents which need to be proven in order to prove that
-- the given protocol is well typed or weakly-atomic
--
-- PRE: The conclusion of the sequent must be typing atom.
--
-- Uses 'fail' for error reporting.
wellTypedCases :: MonadPlus m => Sequent -> m [(String, Sequent)]
wellTypedCases se = case seConcl se of
FAtom (ATyping someTyp) ->
return $ protoRoles (seProto se) >>= roleProofs someTyp
_ -> mzero
where
roleProofs someTyp role = proveRecvs S.empty (roleSteps role)
where
proveRecvs _ [] = []
proveRecvs recv ( Send _ _ : steps) = proveRecvs recv steps
proveRecvs recv ((Recv _ (PMVar lid)) : steps) =
-- don't prove single reiceves as they are handled directly by the tactic
-- on the Isabelle side.
proveRecvs (S.insert lid recv) steps
proveRecvs recv (step@(Recv _ pt) : steps) =
let mvars = patFMV pt
in (S.toList mvars >>= proveVar) `mplus`
(proveRecvs (recv `S.union` mvars) steps)
where
proveVar v
| v `S.member` recv = fail "proveVar: not first receive"
| otherwise = do
return (name, Sequent prem concl)
where
name = roleName role ++ "_" ++ stepLabel step ++ "_" ++ getId v
(tid, prem0) = freshTID (sePrem se)
mv = MVar (LocalId (v, tid))
premErr = error $ "wellTypedCases: could not add thread " ++ show tid ++ ". This should not happen."
prem1 = maybe premErr saturateFacts . join $
conjoinAtoms [AEv (Step tid step), AEq (E.TIDRoleEq (tid, role))] prem0
prem = fromMaybe (error "failed to set typing") $ setTyping someTyp prem1
concl = FAtom $
case someTyp of
WeaklyAtomic -> AHasType mv Nothing
Typing typ -> case M.lookup (v, role) typ of
Just ty -> AHasType mv (Just ty)
Nothing -> error $
"wellTypedCases: no type given for '"++show v++"' in role '"++roleName role++"'"
-- | Emulate a variant Isabelle's 'frule' tactic. It works only if the given
-- maping of free variables of the rule makes the premise of the rule provable
-- under the given proof state. Then, the conclusion of the rule with free
-- variables mapped accordingly is added to premises of the proof state. The
-- last step works currently only for conclusions being false of pure
-- conclusions.
--
-- NOTE that 'frule' works only for rules that contain no existential
-- quantifiers in the conclusion.
fruleInst :: MonadPlus m
=> Sequent -- ^ rule
-> E.Mapping -- ^ mapping of free variables of rule to proof state
-> Sequent -- ^ proof state
-> m (Maybe Sequent) -- ^ some result if resolution worked. Nothing
-- denotes that False was derived. Just means that
-- premises of proof state were extended.
--
-- mzero if rule could not be applied
fruleInst rule mapping state = do
atoms <- conjunctionToAtoms $ seConcl rule
let statePrem = sePrem state
guard (proveFacts statePrem (sePrem rule) mapping)
optStatePrem' <- conjoinAtoms (map (substAtom (E.getMappingEqs mapping)) atoms) statePrem
case optStatePrem' of
Nothing -> do return Nothing
Just statePrem' -> do guard (statePrem /= statePrem')
return . Just $ Sequent statePrem' (seConcl state)
-- | Like 'fruleInst' but tries all mappings.
frule :: MonadPlus m
=> Sequent -- ^ rule
-> Sequent -- ^ proof state
-> m (E.Mapping, Maybe Sequent)
-- ^ some result if resolution worked. Nothing denotes that False was
-- derived. Just means that premises of proof state were extended.
--
-- mzero if rule could not be applied
frule rule state = case resolutions of
[] -> mzero
res : _ -> return res
where
resolutions = do
mapping <- freeVariableMappings (sePrem rule) (sePrem state)
((,) mapping) `liftM` fruleInst rule mapping state
{-
-- | Emulate Isabelle's 'frule' tactic; i.e. the first sequent is the rule that
-- is used for resolution.
--
-- NOTE that 'frule' works only for rules that contain no existential
-- quantifiers in the conclusion.
frule :: Sequent -- ^ Rule to use for resolution.
-> Sequent -- ^ Proof state that this rule is resolved against.
-> [(Mapping, Maybe Sequent)]
-- ^ The mapping and no resulting proof state
-- if the resolution solved this subgoal;
-- otherwise the new subgoal provided it is
-- differnt from the old one.
frule rule state = do
atoms <- conjunctionToAtoms $ seConcl rule
let prem0 = sePrem state
mapping <- resolve (sePrem rule) prem0
optPrem1 <- conjoinAtoms (map (substAtom (getMappingEqs mapping)) atoms) prem0
case optPrem1 of
Nothing -> do return (mapping, Nothing)
Just prem1 -> do guard (prem1 /= prem0)
return (mapping, Just $ Sequent prem1 (seConcl state))
-}
-- | Try to saturate a sequent, if possible and leading to new facts.
saturate :: MonadPlus m => Sequent -> m Sequent
saturate = changePrem (return . saturateFacts)
-- | Try to use the chain rule.
--
-- MonadPlus is used to report a failure to apply the rule.
--
chainRule :: MonadPlus m
=> Sequent -> Message
-> m [((String, [Either TID AgentId]), Sequent)]
chainRule se m =
map (second mkSequent) `liftM` chainRuleFacts m (sePrem se)
where
mkSequent prem = Sequent prem (seConcl se)
-- | Try to exploit the typing. Fails if no new facts could be derived.
exploitTyping :: MonadPlus m => Sequent -> m Sequent
exploitTyping = changePrem exploitTypingFacts
-- | Split a splittable equality.
-- splitting can be done.
splitEq :: E.MsgEq -> Sequent -> [Maybe Sequent]
splitEq eq se
| eq `elem` splittableEqs prems = map (fmap updPrem) $ splitEqFacts eq prems
| otherwise = error $ "splitEq: equality not present"
where
prems = sePrem se
updPrem prem' = se {sePrem = prem'}