packages feed

alms-0.5.0: src/Coercion.hs

-- | Converts coercion expressions to dynamic checks.
{-# LANGUAGE
      PatternGuards,
      QuasiQuotes,
      ViewPatterns #-}
module Coercion  (
  coerceExpression,
  translate, translateDecls, TEnv, tenv0
) where

import Loc
import Meta.Quasi
import Ppr ()
import qualified Syntax
import qualified Syntax.Expr
import qualified Syntax.Notable
import qualified Syntax.Patt
import Syntax hiding (Type, Type'(..))
import Type
import TypeRel ()
import Util
import ErrorMessage

import qualified Data.Map as M
import qualified Control.Monad.State as CMS

-- | The translation environment.  This currently doesn't carry
--   any information, but we keep it in the interface for later use.
type TEnv = ()

-- | The initial translation environment
tenv0 :: TEnv
tenv0  = ()

-- | Translate a whole program
translate :: TEnv -> Prog Renamed -> Prog Renamed
translate _ = id

-- | Location to use for constructed code
_loc :: Loc
_loc  = mkBogus "<coercion>"

-- | Translation a sequence of declarations in the context
--   of a translation environment, returning a new translation
--   environment
translateDecls :: TEnv -> [Decl Renamed] -> (TEnv, [Decl Renamed])
translateDecls tenv decls = (tenv, decls)

coerceExpression :: AlmsMonad m =>
                    Expr Renamed -> Type -> Type -> m (Expr Renamed)
coerceExpression e tfrom tto = do
  prj <- CMS.evalStateT (build True M.empty tfrom tto) 0
  return $ exApp (exApp prj (exPair (exStr neg) (exStr pos))) e
  where
  neg = "context at " ++ show (getLoc e)
  pos = "value at " ++ show (getLoc e)

build :: AlmsMonad m =>
         Bool -> M.Map (TyVarR, TyVarR) (Maybe (Lid Renamed)) ->
         Type -> Type -> CMS.StateT Integer m (Expr Renamed)
build b recs tfrom tto
  | (tvs,  TyFun qd  t1  t2)  <- vtQus Forall tfrom,
    (tvs', TyFun qd' t1' t2') <- vtQus Forall tto,
    length tvs == length tvs'
    = do
        let which = case (qConstBound qd, qConstBound qd') of
              (Qa, Qu) -> [$ex|+ INTERNALS.Contract.affunc |]
              (Qu, _ ) -> [$ex|+ INTERNALS.Contract.func[U] |]
              (_ , Qa) -> [$ex|+ INTERNALS.Contract.func[A] |]
            recs' = foldr2
                      M.insert
                      (shadow tvs tvs' recs)
                      (zip tvs tvs')
                      (repeat Nothing)
        dom <- build (not b) recs' t1' t1
        cod <- build b recs' t2 t2'
        let body = [$ex|+ $which $dom $cod |]
        return $ if null tvs
          then body
          else absContract $
               exAbsVar' (lid "f") (typeToStx' tfrom) $
               foldr (\tv0 acc -> exTAbs tv0 . acc) id tvs $
               exAbsVar' (lid "x") (typeToStx' t1') $
               instContract body `exApp`
               foldl (\acc tv0 -> exTApp acc (Syntax.tyVar tv0))
                     (exBVar (lid "f")) tvs `exApp`
               exBVar (lid "x")
build b recs (view -> TyQu Exists tv t) (view -> TyQu Exists tv' t') = do
  let recs' = M.insert (tv, tv') Nothing (shadow [tv] [tv'] recs)
  body <- instContract `liftM` build b recs' t t'
  let tv''  = freshTyVar tv (ftv (tv, tv'))
      tstx  = typeToStx' t
      tstx' = typeToStx' t'
  return $
    absContract $
      [$ex|+ fun (Pack('$tv'', e) : ex '$tv. $tstx) ->
               Pack[ex '$tv'. $tstx']('$tv'', $body e) |]
build b recs (view -> TyMu tv t) (view -> TyMu tv' t') = do
  l    <- freshLid
  let recs' = M.insert (tv, tv') (Just l) (shadow [tv] [tv'] recs)
  body <- build b recs' t t'
  let tstx  = typeToStx' t
      tstx' = typeToStx' t'
  return $
    [$ex|+
      let rec $lid:l
              (parties : string * string)
                       : (mu '$tv. $tstx) -> mu '$tv'. $tstx'
          = $body parties
       in $lid:l
    |]
build b recs (view -> TyVar tv) (view -> TyVar tv')
  | Just (Just l) <- M.lookup (if b then (tv, tv') else (tv', tv)) recs
    = return [$ex|+ $lid:l |]
  | Just Nothing <- M.lookup (if b then (tv, tv') else (tv', tv)) recs
    = return [$ex|+ INTERNALS.Contract.any ['$tv'] |]
build _ _    t t' =
  if t <: t'
    then let tstx' = typeToStx' t' in
         return [$ex|+ INTERNALS.Contract.any [$tstx'] |]
    else CMS.lift . throwAlms $ AlmsException StaticsPhase bogus [$msg|
        <dl>
          <dt>from type: <dd>$t
          <dt>to type:   <dd>$t'.
        </dl>
    |]

shadow :: [TyVarR] -> [TyVarR] ->
          M.Map (TyVarR, TyVarR) a -> M.Map (TyVarR, TyVarR) a
shadow tvs tvs' = M.filterWithKey
                    (\(tv, tv') _ -> tv `notElem` tvs && tv' `notElem` tvs')

absContract :: Expr Renamed -> Expr Renamed
absContract body =
  [$ex|+ fun (neg: string, pos: string) -> $body |]

instContract :: Expr Renamed -> Expr Renamed
instContract con = [$ex|+ $con (neg, pos) |]

freshLid :: Monad m => CMS.StateT Integer m (Lid Renamed)
freshLid = do
  n <- CMS.get
  CMS.put (n + 1)
  return (lid ("c" ++ show n))