typelet-0.1.4: src/TypeLet/Plugin.hs
module TypeLet.Plugin (plugin) where
import Prelude hiding (cycle)
import Data.Traversable (forM)
import GHC.Plugins (Plugin(..), defaultPlugin, purePlugin)
import TypeLet.Plugin.Constraints
import TypeLet.Plugin.GhcTcPluginAPI
import TypeLet.Plugin.NameResolution
import TypeLet.Plugin.Substitution
{-------------------------------------------------------------------------------
Top-level plumbing
-------------------------------------------------------------------------------}
plugin :: Plugin
plugin = defaultPlugin {
pluginRecompile = purePlugin
, tcPlugin = \_cmdline -> Just . mkTcPlugin $ TcPlugin {
tcPluginInit = resolveNames
, tcPluginSolve = solve
, tcPluginRewrite = \_st -> emptyUFM
, tcPluginStop = \_st -> return ()
}
}
{-------------------------------------------------------------------------------
Constraint resolution
General approach: regard @Let@ constraints as defining a substitution, and
then resolve @Equal@ constraints by /applying/ that substitution and
simplifying to a derived equality constraint (derived instead of a new wanted
constraint, because we don't actually need the evidence).
-------------------------------------------------------------------------------}
-- | Main interface to constraint resolution
--
-- NOTE: For now we are completely ignoring the derived constraints.
solve :: ResolvedNames -> TcPluginSolver
solve rn given wanted
| null wanted = simplifyGivens rn given
| otherwise = simplifyWanteds rn given wanted
-- | Simplify givens
--
-- We (currently?) never simplify any givens, so we just two empty lists,
-- indicating that there no constraints were removed and none got added.
simplifyGivens ::
ResolvedNames -- ^ Result of name resolution (during init)
-> [Ct] -- ^ Given constraints
-> TcPluginM 'Solve TcPluginSolveResult
simplifyGivens _st _given = return $ TcPluginOk [] []
-- | Simplify wanteds
--
-- This function provides the key functionality of the plugin.
--
-- We resolve 'Equal' constraints to /nominal/ equality constraints: we want
-- 'cast' to resolve @Let@ bindings, but not additionally work as 'coerce'.
simplifyWanteds ::
ResolvedNames -- ^ Result of name resolution (during init)
-> [Ct] -- ^ Given constraints
-> [Ct] -- ^ Wanted constraints
-> TcPluginM 'Solve TcPluginSolveResult
simplifyWanteds rn given wanted = do
case parseAll (parseLet rn) given of
Left err ->
errWith $ formatInvalidLet <$> err
Right lets -> do
case letsToSubst lets of
Left cycle ->
errWith $ formatLetCycle cycle
Right subst -> do
(solved, new) <- fmap unzip $
forM (parseAll' (withOrig (parseEqual rn)) wanted) $
uncurry (solveEqual subst)
return $ TcPluginOk solved new
where
-- Work-around bug in ghc, making sure the location is set correctly
newWanted' :: CtLoc -> PredType -> TcPluginM 'Solve CtEvidence
newWanted' l w = setCtLocM l $ newWanted l w
errWith ::
GenLocated CtLoc TcPluginErrorMessage
-> TcPluginM 'Solve TcPluginSolveResult
errWith (L l err) = do
errAsTyp <- mkTcPluginErrorTy err
mkErr <$> newWanted' l errAsTyp
where
mkErr :: CtEvidence -> TcPluginSolveResult
mkErr = TcPluginContradiction . (:[]) . mkNonCanonical
-- Solve an Equal constraint by applying the substitution and turning it
-- into a nominal equality constraint
solveEqual ::
Subst
-> Ct -- Original Equal constraint
-> GenLocated CtLoc CEqual -- Parsed Equal constraint
-> TcPluginM 'Solve ((EvTerm, Ct), Ct)
solveEqual subst orig (L l parsed) = do
ev <- newWanted' l $
mkPrimEqPredRole
Nominal
(substTy subst (equalLHS parsed))
(substTy subst (equalRHS parsed))
return (
(evidenceEqual rn parsed, orig)
, mkNonCanonical ev
)