packages feed

liquidhaskell-boot-0.9.14.1: src/Language/Haskell/Liquid/Transforms/QuestionMark.hs

{-# LANGUAGE PatternGuards #-}
-- | Eliminate applications of the '?' operator from Core after ANF.
--
-- After ANF, @e ? s@ becomes:
--
-- @
-- let a1 = e
-- let a2 = s
-- ... (?) \@A \@B a1 a2
-- @
--
-- This pass replaces @(?) \@A \@B a1 a2@ with just @a1@, so that no KVars
-- are introduced in the signature of @?@. The binding @a2 = s@ remains in
-- scope, so the lemma's postcondition still enters the environment during
-- constraint generation.

module Language.Haskell.Liquid.Transforms.QuestionMark (eliminateQuestionMark) where

import Liquid.GHC.API as Ghc

-- | Look up the '?' operator in the 'GlobalRdrEnv'. If found, eliminate all
-- its applications from the Core program. If not found (module doesn't import
-- ProofCombinators), return the bindings unchanged.
eliminateQuestionMark :: GlobalRdrEnv -> [CoreBind] -> [CoreBind]
eliminateQuestionMark rdrEnv cbs =
  case lookupQuestionMark rdrEnv of
    Nothing   -> cbs
    Just name -> map (goBind name) cbs

-- | Find the 'Name' of '?' from @Language.Haskell.Liquid.ProofCombinators@
-- in the renamer environment.
lookupQuestionMark :: GlobalRdrEnv -> Maybe Name
lookupQuestionMark rdrEnv =
  case lookupGRE rdrEnv (LookupRdrName rdrName SameNameSpace) of
    [gre] -> Just (greName gre)
    _     -> Nothing
  where
    rdrName = mkRdrQual (mkModuleName "Language.Haskell.Liquid.ProofCombinators")
                        (mkVarOcc "?")

goBind :: Name -> CoreBind -> CoreBind
goBind n (NonRec x e) = NonRec x (goExpr n e)
goBind n (Rec xes)    = Rec [(x, goExpr n e) | (x, e) <- xes]

goExpr :: Name -> CoreExpr -> CoreExpr
goExpr n e
  | Just firstArg <- isQuestionMarkApp n e = goExpr n firstArg
goExpr n (Lam x e)         = Lam x (goExpr n e)
goExpr n (Let b e)          = Let (goBind n b) (goExpr n e)
goExpr n (Case s x t alts) = Case (goExpr n s) x t [goAlt n a | a <- alts]
goExpr n (Cast e co)        = Cast (goExpr n e) co
goExpr n (Tick t e)         = Tick t (goExpr n e)
goExpr n (App f a)          = App (goExpr n f) (goExpr n a)
goExpr _ e                  = e -- Var, Lit, Type, Coercion

goAlt :: Name -> CoreAlt -> CoreAlt
goAlt n (Alt con bs e) = Alt con bs (goExpr n e)

-- | Detect a fully-saturated application of '?': four arguments total
-- (two types + two values), possibly wrapped in ticks.
-- Returns @Just arg1@ (the first value argument).
isQuestionMarkApp :: Name -> CoreExpr -> Maybe CoreExpr
isQuestionMarkApp name expr =
  case collectArgsTicks (const True) expr of
    (Var v, args, _ticks)
      | varName v == name
      , [_tA, _tB, a, _b] <- args
      -> Just a
    _ -> Nothing