packages feed

hls-tactics-plugin-1.3.0.0: test/UnificationSpec.hs

{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module UnificationSpec where

import           Control.Arrow
import           Data.Bool (bool)
import           Data.Functor ((<&>))
import           Data.Maybe (mapMaybe)
import qualified Data.Set as S
import           Data.Traversable
import           Data.Tuple (swap)
import           TcType (substTy, tcGetTyVar_maybe)
import           Test.Hspec
import           Test.QuickCheck
import           Type (mkTyVarTy)
import           TysPrim (alphaTyVars)
import           TysWiredIn (mkBoxedTupleTy)
import           Wingman.GHC
import           Wingman.Types


spec :: Spec
spec = describe "unification" $ do
  it "should be able to unify univars with skolems on either side of the equality" $ do
    property $ do
      -- Pick some number of unification vars and skolem
      n <- choose (1, 1)
      let (skolems, take n -> univars) = splitAt n $ fmap mkTyVarTy alphaTyVars
      -- Randomly pair them
      skolem_uni_pairs <-
        for (zip skolems univars) randomSwap
      let (lhs, rhs)
            = mkBoxedTupleTy *** mkBoxedTupleTy
            $ unzip skolem_uni_pairs
      pure $
        counterexample (show skolems) $
        counterexample (show lhs) $
        counterexample (show rhs) $
          case tryUnifyUnivarsButNotSkolems
                 (S.fromList $ mapMaybe tcGetTyVar_maybe skolems)
                 (CType lhs)
                 (CType rhs) of
            Just subst ->
              -- For each pair, running the unification over the univar should
              -- result in the skolem
              conjoin $ zip univars skolems <&> \(uni, skolem) ->
                let substd = substTy subst uni
                 in counterexample (show substd) $
                    counterexample (show skolem) $
                      CType substd === CType skolem
            Nothing -> True === False


randomSwap :: (a, a) -> Gen (a, a)
randomSwap ab = do
  which <- arbitrary
  pure $ bool swap id which ab