{-# LANGUAGE PatternGuards, ViewPatterns, FlexibleContexts, ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module GHC.Util.Unify(
Subst', fromSubst',
validSubst', substitute',
unifyExp'
) where
import Control.Monad
import Data.Generics.Uniplate.Operations
import Data.Char
import Data.List.Extra
import Data.Data
import Data.Tuple.Extra
import Util
import HsSyn
import SrcLoc as GHC
import Outputable hiding ((<>))
import RdrName
import OccName
import GHC.Util.Outputable
import GHC.Util.HsExpr
import GHC.Util.Pat
import GHC.Util.RdrName
import GHC.Util.View
isUnifyVar :: String -> Bool
isUnifyVar [x] = x == '?' || isAlpha x
isUnifyVar [] = False
isUnifyVar xs = all (== '?') xs
newtype Subst' a = Subst' [(String, a)]
deriving (Semigroup, Monoid)
fromSubst' :: Subst' a -> [(String, a)]
fromSubst' (Subst' xs) = xs
instance Functor Subst' where
fmap f (Subst' xs) = Subst' $ map (second f) xs
instance Outputable a => Show (Subst' a) where
show (Subst' xs) = unlines [a ++ " = " ++ unsafePrettyPrint b | (a,b) <- xs]
validSubst' :: (a -> a -> Bool) -> Subst' a -> Maybe (Subst' a)
validSubst' eq = fmap Subst' . mapM f . groupSort . fromSubst'
where f (x, y : ys) | all (eq y) ys = Just (x, y)
f _ = Nothing
substitute' :: Subst' (LHsExpr GhcPs) -> LHsExpr GhcPs -> LHsExpr GhcPs
substitute' (Subst' bind) = transformBracketOld' exp . transformBi pat . transformBi typ
where
exp :: LHsExpr GhcPs -> Maybe (LHsExpr GhcPs)
exp (LL _ (HsVar _ x)) = lookup (rdrNameStr' x) bind
exp (LL loc (OpApp _ lhs (LL _ (HsVar _ x)) rhs))
| Just y <- lookup (rdrNameStr' x) bind = Just (cL loc (OpApp noExt lhs y rhs))
exp (LL loc (SectionL _ exp (LL _ (HsVar _ x))))
| Just y <- lookup (rdrNameStr' x) bind = Just (cL loc (SectionL noExt exp y))
exp (LL loc (SectionR _ (LL _ (HsVar _ x)) exp))
| Just y <- lookup (rdrNameStr' x) bind = Just (cL loc (SectionR noExt y exp))
exp _ = Nothing
pat :: LPat GhcPs -> LPat GhcPs
pat (LL _ (VarPat _ x))
| Just y@(LL _ HsVar{}) <- lookup (rdrNameStr' x) bind = strToPat' (varToStr' y)
pat x = x :: LPat GhcPs
typ :: LHsType GhcPs -> LHsType GhcPs
typ (LL _ (HsTyVar _ _ x))
| Just (LL _ (HsAppType _ _ (HsWC _ y))) <- lookup (rdrNameStr' x) bind = y
typ x = x :: LHsType GhcPs
type NameMatch' = Located RdrName -> Located RdrName -> Bool
unify' :: Data a => NameMatch' -> Bool -> a -> a -> Maybe (Subst' (LHsExpr GhcPs))
unify' nm root x y
| Just (x, y) <- cast (x, y) = unifyExp' nm root x y
| Just (x, y) <- cast (x, y) = unifyPat' nm x y
| Just (x, y) <- cast (x, y) = unifyType' nm x y
| Just (x :: GHC.SrcSpan) <- cast x = Just mempty
| otherwise = unifyDef' nm x y
unifyDef' :: Data a => NameMatch' -> a -> a -> Maybe (Subst' (LHsExpr GhcPs))
unifyDef' nm x y = fmap mconcat . sequence =<< gzip (unify' nm False) x y
unifyExp' :: NameMatch' -> Bool -> LHsExpr GhcPs -> LHsExpr GhcPs -> Maybe (Subst' (LHsExpr GhcPs) )
unifyExp' nm root x y | not root, isPar' x, not $ isPar' y = unifyExp' nm root (fromParen' x) y
unifyExp' nm root (LL _ (HsVar _ (rdrNameStr' -> v))) y | isUnifyVar v, not $ isTypeApp' y = Just $ Subst' [(v, y)]
unifyExp' nm root (LL _ (HsVar _ x)) (LL _ (HsVar _ y)) | nm x y = Just mempty
unifyExp' nm root (LL _ (OpApp _ lhs1 (LL _ (HsVar _ (rdrNameStr' -> v))) rhs1))
(LL _ (OpApp _ lhs2 (LL _ (HsVar _ (rdrNameStr' -> op2))) rhs2))
| isUnifyVar v =
(Subst' [(v, strToVar' op2)] <>) <$>
liftM2 (<>) (unifyExp' nm False lhs1 lhs2) (unifyExp' nm False rhs1 rhs2)
unifyExp' nm root (LL _ (SectionL _ exp1 (LL _ (HsVar _ (rdrNameStr' -> v)))))
(LL _ (SectionL _ exp2 (LL _ (HsVar _ (rdrNameStr' -> op2)))))
| isUnifyVar v = (Subst' [(v, strToVar' op2)] <>) <$> unifyExp' nm False exp1 exp2
unifyExp' nm root (LL _ (SectionR _ (LL _ (HsVar _ (rdrNameStr' -> v))) exp1))
(LL _ (SectionR _ (LL _ (HsVar _ (rdrNameStr' -> op2))) exp2))
| isUnifyVar v = (Subst' [(v, strToVar' op2)] <>) <$> unifyExp' nm False exp1 exp2
unifyExp' nm root x@(LL _ (HsApp _ x1 x2)) (LL _ (HsApp _ y1 y2)) =
liftM2 (<>) (unifyExp' nm False x1 y1) (unifyExp' nm False x2 y2) `mplus`
(do guard $ not root
(LL _ (OpApp _ y11 dot y12)) <- return $ fromParen' y1
guard $ isDot' dot
unifyExp' nm root x (noLoc (HsApp noExt y11 (noLoc (HsApp noExt y12 y2))))
)
unifyExp' nm root x (LL _ (OpApp _ lhs2 op2@(LL _ (HsVar _ op2')) rhs2))
| (LL _ (OpApp _ lhs1 op1@(LL _ (HsVar _ op1')) rhs1)) <- x = guard (nm op1' op2') >> liftM2 (<>) (unifyExp' nm False lhs1 lhs2) (unifyExp' nm False rhs1 rhs2)
| isDol' op2 = unifyExp' nm root x $ noLoc (HsApp noExt lhs2 rhs2)
| otherwise = unifyExp' nm root x $ noLoc (HsApp noExt (noLoc (HsApp noExt op2 lhs2)) rhs2)
unifyExp' nm root x y | isOther x, isOther y = unifyDef' nm x y
where
{-# INLINE isOther #-}
isOther :: LHsExpr GhcPs -> Bool
isOther (LL _ HsVar{}) = False
isOther (LL _ HsApp{}) = False
isOther (LL _ OpApp{}) = False
isOther _ = True
unifyExp' _ _ _ _ = Nothing
unifyPat' :: NameMatch' -> LPat GhcPs -> LPat GhcPs -> Maybe (Subst' (LHsExpr GhcPs))
unifyPat' nm (LL _ (VarPat _ x)) (LL _ (VarPat _ y)) =
Just $ Subst' [(rdrNameStr' x, strToVar'(rdrNameStr' y))]
unifyPat' nm (LL _ (VarPat _ x)) (LL _ (WildPat _)) =
let s = rdrNameStr' x in Just $ Subst' [(s, strToVar'("_" ++ s))]
unifyPat' nm (LL _ (ConPatIn x _)) (LL _ (ConPatIn y _)) | rdrNameStr' x /= rdrNameStr' y =
Nothing
unifyPat' nm x y =
unifyDef' nm x y
unifyType' :: NameMatch' -> LHsType GhcPs -> LHsType GhcPs -> Maybe (Subst' (LHsExpr GhcPs))
unifyType' nm (LL loc (HsTyVar _ _ x)) y =
let wc = HsWC noExt y :: LHsWcType (NoGhcTc GhcPs)
unused = noLoc (HsVar noExt (noLoc $ mkRdrUnqual (mkVarOcc "__unused__"))) :: LHsExpr GhcPs
appType = cL loc (HsAppType noExt unused wc) :: LHsExpr GhcPs
in Just $ Subst' [(rdrNameStr' x, appType)]
unifyType' nm x y = unifyDef' nm x y