hermit-0.4.0.0: src/HERMIT/Dictionary/Undefined.hs
{-# LANGUAGE FlexibleContexts #-}
module HERMIT.Dictionary.Undefined
( -- * Working with Undefined Values
-- | Note that many of these operations require 'GHC.Err.undefined' to be explicitly imported if it is not used in the source file.
externals
, verifyStrictT
, mkUndefinedValT
, isUndefinedValT
, replaceCurrentExprWithUndefinedR
, replaceIdWithUndefinedR
, errorToUndefinedR
, undefinedExprR
, undefinedAppR
, undefinedLamR
, undefinedLetR
, undefinedCaseScrutineeR
, undefinedCaseAltsR
, undefinedCaseR
, undefinedCastR
, undefinedTickR
)
where
import Control.Monad ((>=>))
import Data.Monoid
import HERMIT.Context
import HERMIT.Core
import HERMIT.Kure
import HERMIT.GHC hiding ((<>))
import HERMIT.External
import HERMIT.Dictionary.Common
import HERMIT.Dictionary.GHC (substR)
import HERMIT.Dictionary.Reasoning hiding (externals)
------------------------------------------------------------------------
externals :: [External]
externals = map (.+ Unsafe)
[ external "replace-current-expr-with-undefined" (promoteExprR replaceCurrentExprWithUndefinedR :: RewriteH Core)
[ "Set the current expression to \"undefined\"."
] .+ Shallow .+ Context .+ Unsafe
, external "replace-id-with-undefined" (replaceIdWithUndefined :: String -> RewriteH Core)
[ "Replace the specified identifier with \"undefined\"."
] .+ Deep .+ Context .+ Unsafe
, external "error-to-undefined" (promoteExprR errorToUndefinedR :: RewriteH Core)
[ "error ty string ==> undefined ty"
] .+ Shallow .+ Context
, external "is-undefined-val" (promoteExprT isUndefinedValT :: TranslateH Core ())
[ "Succeed if the current expression is an undefined value."
] .+ Shallow .+ Context .+ Predicate
, external "undefined-expr" (promoteExprR undefinedExprR :: RewriteH Core)
[ "undefined-app <+ undefined-lam <+ undefined-let <+ undefined-cast <+ undefined-tick <+ undefined-case"
] .+ Eval .+ Shallow .+ Context
, external "undefined-app" (promoteExprR undefinedAppR :: RewriteH Core)
[ "(undefined ty1) e ==> undefined ty2"
] .+ Eval .+ Shallow .+ Context
, external "undefined-lam" (promoteExprR undefinedLamR :: RewriteH Core)
[ "(\\ v -> undefined ty1) ==> undefined ty2 (where v is not a 'TyVar')"
] .+ Eval .+ Shallow .+ Context
, external "undefined-let" (promoteExprR undefinedLetR :: RewriteH Core)
[ "let bds in (undefined ty) ==> undefined ty"
] .+ Eval .+ Shallow .+ Context
, external "undefined-case" (promoteExprR undefinedCaseR :: RewriteH Core)
[ "case (undefined ty) of alts ==> undefined ty"
, "OR"
, "case e of {pat_1 -> undefined ty ; pat_2 -> undefined ty ; ... ; pat_n -> undefined ty} ==> undefined ty"
] .+ Eval .+ Shallow .+ Context
, external "undefined-cast" (promoteExprR undefinedCastR :: RewriteH Core)
[ "Cast (undefined ty1) co ==> undefined ty2"
] .+ Eval .+ Shallow .+ Context
, external "undefined-tick" (promoteExprR undefinedTickR :: RewriteH Core)
[ "Tick tick (undefined ty1) ==> undefined ty1"
] .+ Eval .+ Shallow .+ Context
]
------------------------------------------------------------------------
undefinedLocation :: String
undefinedLocation = "GHC.Err.undefined"
-- TODO: will crash if 'undefined' is not used (or explicitly imported) in the source file.
findUndefinedIdT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Translate c m a Id
findUndefinedIdT = findIdT undefinedLocation
-- | Check if the current expression is an undefined value.
isUndefinedValT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Translate c m CoreExpr ()
isUndefinedValT = prefixFailMsg "not an undefined value: " $
withPatFailMsg (wrongExprForm "App (Var undefined) (Type ty)") $
do App (Var un) (Type _) <- idR
un' <- findUndefinedIdT
guardMsg (un == un') ("identifier is not " ++ undefinedLocation)
------------------------------------------------------------------------
errorLocation :: String
errorLocation = "GHC.Err.error"
-- TODO: will crash if 'error' is not used (or explicitly imported) in the source file.
findErrorIdT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Translate c m a Id
findErrorIdT = findIdT errorLocation
-- | Check if the current expression is an undefined value.
isErrorValT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Translate c m CoreExpr ()
isErrorValT = prefixFailMsg "not an error value: " $
withPatFailMsg (wrongExprForm "App (App (Var error) (Type ty)) string") $
do App (App (Var er) (Type _)) _ <- idR
er' <- findErrorIdT
guardMsg (er == er') ("identifier is not " ++ errorLocation)
------------------------------------------------------------------------
-- | error ty string ==> undefined ty
errorToUndefinedR :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
errorToUndefinedR = prefixFailMsg "error-to-undefined failed: " (isErrorValT >> replaceCurrentExprWithUndefinedR)
------------------------------------------------------------------------
-- | Make an undefined value of the given type.
mkUndefinedValT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Type -> Translate c m a CoreExpr
mkUndefinedValT ty =
do un <- findUndefinedIdT
return $ App (varToCoreExpr un) (Type ty)
------------------------------------------------------------------------------------------------------
-- | Set the current expression to 'undefined'.
replaceCurrentExprWithUndefinedR :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
replaceCurrentExprWithUndefinedR = contextfreeT exprTypeM >>= mkUndefinedValT
-- | Replace all occurrences of the specified identifier with 'undefined'.
replaceIdWithUndefinedR :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Id -> Rewrite c m Core
replaceIdWithUndefinedR i = mkUndefinedValT (idType i) >>= substR i
replaceIdWithUndefined :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => String -> Rewrite c m Core
replaceIdWithUndefined = findIdT >=> replaceIdWithUndefinedR
------------------------------------------------------------------------------------------------------
-- | undefinedExprR = undefinedAppR <+ undefinedLamR <+ undefinedLetR <+ undefinedCastR <+ undefinedTickR <+ undefinedCaseR
undefinedExprR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedExprR = setFailMsg "undefined-expr failed."
(undefinedAppR <+ undefinedLamR <+ undefinedLetR <+ undefinedCastR <+ undefinedTickR <+ undefinedCaseR)
------------------------------------------------------------------------------------------------------
-- | @(undefined ty1) e@ ==> @undefined ty2@
undefinedAppR :: (BoundVars c, ExtendPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedAppR = prefixFailMsg "undefined-app failed: " $
do appT isUndefinedValT successT (<>)
replaceCurrentExprWithUndefinedR
-- | @(\ v -> undefined ty1)@ ==> @undefined ty2@ (where v is not a 'TyVar')
undefinedLamR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedLamR = prefixFailMsg "undefined-lam failed: " $
do lamT successT isUndefinedValT (<>)
replaceCurrentExprWithUndefinedR
-- | let bds in (undefined ty) ==> undefined ty
undefinedLetR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedLetR = prefixFailMsg "undefined-let failed: " $
do letT successT isUndefinedValT (<>)
replaceCurrentExprWithUndefinedR
-- | Cast (undefined ty1) co ==> undefined ty2
undefinedCastR :: (BoundVars c, ExtendPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedCastR = prefixFailMsg "undefined-cast failed: " $
do castT isUndefinedValT successT (<>)
replaceCurrentExprWithUndefinedR
-- | Tick tick (undefined ty1) ==> undefined ty1
undefinedTickR :: (BoundVars c, ExtendPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedTickR = prefixFailMsg "undefined-tick failed: " $
do tickT successT isUndefinedValT (<>)
replaceCurrentExprWithUndefinedR
-- | undefinedCaseR = undefinedCaseScrutineeR <+ undefinedCaseAltsR
undefinedCaseR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedCaseR = setFailMsg "undefined-case failed" (undefinedCaseScrutineeR <+ undefinedCaseAltsR)
-- | case (undefined ty) of alts ==> undefined ty
undefinedCaseScrutineeR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedCaseScrutineeR = prefixFailMsg "undefined-case failed: " $
do caseT isUndefinedValT successT successT (const successT) (\ _ _ _ _ -> ())
replaceCurrentExprWithUndefinedR
-- | case e of {pat_1 -> undefined ty ; pat_2 -> undefined ty ; ... ; pat_n -> undefined ty} ==> undefined ty
undefinedCaseAltsR :: (AddBindings c, BoundVars c, ExtendPath c Crumb, ReadPath c Crumb, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => Rewrite c m CoreExpr
undefinedCaseAltsR = prefixFailMsg "undefined-case-alts failed: " $
do caseAltT successT successT successT (const (successT,const successT,isUndefinedValT)) (\ _ _ _ _ -> ())
replaceCurrentExprWithUndefinedR
------------------------------------------------------------------------
-- | Verify that the given rewrite is a proof that the given expression is a strict function.
verifyStrictT :: (BoundVars c, HasGlobalRdrEnv c, MonadCatch m, HasDynFlags m, MonadThings m) => CoreExpr -> Rewrite c m CoreExpr -> Translate c m a ()
verifyStrictT f r = prefixFailMsg "strictness verification failed: " $
do (argTy, resTy) <- constT (funExprArgResTypes f)
undefArg <- mkUndefinedValT argTy
rhs <- mkUndefinedValT resTy
let lhs = App f undefArg
verifyEqualityLeftToRightT lhs rhs r
------------------------------------------------------------------------