{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE BangPatterns #-} -- | This module contains the functions related to @Error@ type, -- in particular, to @tidyError@ using a solution, and @pprint@ errors. module Language.Haskell.Liquid.UX.Errors ( tidyError ) where import Control.Arrow (second) import qualified Data.HashMap.Strict as M import qualified Data.HashSet as S import qualified Data.List as L import Data.Hashable import Data.Maybe (maybeToList) import qualified Language.Fixpoint.Types as F import Language.Haskell.Liquid.Types.RefType import Language.Haskell.Liquid.Transforms.Simplify import Language.Haskell.Liquid.UX.Tidy import Language.Haskell.Liquid.Types import qualified Language.Haskell.Liquid.GHC.Misc as GM import qualified Language.Haskell.Liquid.Misc as Misc import qualified Language.Fixpoint.Misc as Misc -- import Debug.Trace type Ctx = M.HashMap F.Symbol SpecType type CtxM = M.HashMap F.Symbol (WithModel SpecType) ------------------------------------------------------------------------ tidyError :: Config -> F.FixSolution -> Error -> Error ------------------------------------------------------------------------ tidyError cfg sol = fmap (tidySpecType tidy) . tidyErrContext tidy sol where tidy = configTidy cfg configTidy :: Config -> F.Tidy configTidy cfg | shortNames cfg = F.Lossy | otherwise = F.Full tidyErrContext :: F.Tidy -> F.FixSolution -> Error -> Error tidyErrContext k _ e@(ErrSubType {}) = e { ctx = c', tact = F.subst θ tA, texp = F.subst θ tE } where (θ, c') = tidyCtx k xs (ctx e) xs = F.syms tA ++ F.syms tE tA = tact e tE = texp e tidyErrContext _ _ e@(ErrSubTypeModel {}) = e { ctxM = c', tactM = fmap (F.subst θ) tA, texp = fmap (F.subst θ) tE } where (θ, c') = tidyCtxM xs $ ctxM e xs = F.syms tA ++ F.syms tE tA = tactM e tE = texp e tidyErrContext k _ e@(ErrAssType {}) = e { ctx = c', cond = F.subst θ p } where m = ctx e (θ, c') = tidyCtx k xs m xs = F.syms p p = cond e tidyErrContext _ _ e = e -------------------------------------------------------------------------------- tidyCtx :: F.Tidy -> [F.Symbol] -> Ctx -> (F.Subst, Ctx) -------------------------------------------------------------------------------- tidyCtx k xs m = (θ1 `mappend` θ2, M.fromList yts) where yts = [tBind x (tidySpecType k t) | (x, t) <- xt2s] (θ2, xt2s) = tidyREnv xt1s (θ1, xt1s) = tidyTemps xts xts = sliceREnv xs m tBind x t = (x', shiftVV t x') where x' = F.tidySymbol x tidyCtxM :: [F.Symbol] -> CtxM -> (F.Subst, CtxM) tidyCtxM xs m = (θ, M.fromList yts) where yts = [tBind x t | (x, t) <- xts] (θ, xts) = tidyTemps $ second (fmap stripReft) <$> tidyREnvM xs m tBind x t = (x', fmap (\t -> shiftVV t x') t) where x' = F.tidySymbol x tidyREnv :: [(F.Symbol, SpecType)] -> (F.Subst, [(F.Symbol, SpecType)]) tidyREnv xts = (θ, second (F.subst θ) <$> zts) where θ = expandVarDefs yes (yes, zts) = Misc.mapEither isInline xts -- | 'expandVarDefs [(x1, e1), ... ,(xn, en)] returns a `Subst` that -- contains the fully substituted definitions for each `xi`. For example, -- expandVarDefs [(x1, 'x2 + x3'), (x5, 'x1 + 1')] -- should return -- [x1 := 'x2 + x3, x5 := (x2 + x3) + 1] expandVarDefs :: [(F.Symbol, F.Expr)] -> F.Subst expandVarDefs = go mempty where go !su xes | null yes = su `mappend` (F.mkSubst xes) | otherwise = go (su `mappend` su') xes'' where xes'' = [(z, F.subst su' e) | (z, e) <- zes] xs = S.fromList [x | (x, _) <- xes] su' = F.mkSubst yes (yes, zes) = L.partition (isDef xs . snd) xes isDef xs e = all (not . (`S.member` xs)) (F.syms e) isInline :: (a, SpecType) -> Either (a, F.Expr) (a, SpecType) isInline (x, t) = either (Left . (x,)) (Right . (x,)) (isInline' t) isInline' :: SpecType -> Either F.Expr SpecType isInline' t = case ro of Nothing -> Right t' Just rr -> case F.isSingletonReft (ur_reft rr) of Just e -> Left e Nothing -> Right (strengthen t' rr) where (t', ro) = stripRType t stripReft :: SpecType -> SpecType stripReft t = maybe t' (strengthen t') ro where (t', ro) = stripRType t stripRType :: SpecType -> (SpecType, Maybe RReft) stripRType st = (t', ro) where t' = fmap (const (uTop mempty)) t ro = stripRTypeBase t t = simplifyBounds st sliceREnv :: [F.Symbol] -> Ctx -> [(F.Symbol, SpecType)] sliceREnv xs m = [(x, t) | x <- xs', t <- maybeToList (M.lookup x m), ok t] where xs' = expandFix deps xs deps y = maybe [] (F.syms . rTypeReft) (M.lookup y m) ok = not . isFunTy tidyREnvM :: [F.Symbol] -> CtxM -> [(F.Symbol, WithModel SpecType)] tidyREnvM xs m = [(x, t) | x <- xs', t <- maybeToList (M.lookup x m), ok t] where xs' = expandFix deps xs deps y = maybe [] (F.syms . rTypeReft . dropModel) (M.lookup y m) ok = not . isFunTy . dropModel expandFix :: (Eq a, Hashable a) => (a -> [a]) -> [a] -> [a] expandFix f = S.toList . go S.empty where go seen [] = seen go seen (x:xs) | x `S.member` seen = go seen xs | otherwise = go (S.insert x seen) (f x ++ xs) tidyTemps :: (F.Subable t) => [(F.Symbol, t)] -> (F.Subst, [(F.Symbol, t)]) tidyTemps xts = (θ, [(txB x, txTy t) | (x, t) <- xts]) where txB x = M.lookupDefault x x m txTy = F.subst θ m = M.fromList yzs θ = F.mkSubst [(y, F.EVar z) | (y, z) <- yzs] yzs = zip ys niceTemps ys = [ x | (x,_) <- xts, GM.isTmpSymbol x] niceTemps :: [F.Symbol] niceTemps = mkSymbol <$> xs ++ ys where mkSymbol = F.symbol . ('?' :) xs = Misc.single <$> ['a' .. 'z'] ys = ("a" ++) <$> [show n | n <- [0 ..]]