{- Tests for the th-desugar package (c) Richard Eisenberg 2013 rae@cs.brynmawr.edu -} {-# LANGUAGE TemplateHaskell, UnboxedTuples, ParallelListComp, CPP, RankNTypes, TypeFamilies, DataKinds, ConstraintKinds, PolyKinds, MultiParamTypeClasses, FlexibleInstances, ExistentialQuantification, ScopedTypeVariables, GADTs, ViewPatterns, TupleSections, TypeOperators, PartialTypeSignatures, PatternSynonyms, TypeApplications #-} {-# OPTIONS -Wno-incomplete-patterns -Wno-overlapping-patterns -Wno-unused-matches -Wno-type-defaults -Wno-missing-signatures -Wno-unused-do-bind -Wno-missing-fields -Wno-incomplete-record-updates -Wno-partial-type-signatures -Wno-redundant-constraints #-} #if __GLASGOW_HASKELL__ >= 805 {-# LANGUAGE DerivingVia #-} {-# LANGUAGE QuantifiedConstraints #-} #endif #if __GLASGOW_HASKELL__ >= 809 {-# LANGUAGE StandaloneKindSignatures #-} #endif #if __GLASGOW_HASKELL__ >= 906 {-# LANGUAGE TypeData #-} #endif module Main where import Prelude hiding ( exp ) import Test.HUnit import Test.Hspec hiding ( runIO ) -- import Test.Hspec.HUnit import Splices import qualified DsDec import qualified Dec import Dec ( RecordSel ) import ReifyTypeCUSKs import ReifyTypeSigs import T159Decs ( t159A, t159B ) import Language.Haskell.TH.Desugar import qualified Language.Haskell.TH.Desugar.OSet as OS import Language.Haskell.TH.Desugar.Expand ( expandUnsoundly ) import Language.Haskell.TH import qualified Language.Haskell.TH.Syntax as Syn ( NameSpace(..), lift ) import Control.Exception ( ErrorCall ) import Control.Monad import qualified Data.Map as M import Data.Proxy #if __GLASGOW_HASKELL__ >= 900 import Prelude as P #endif -- | -- Convert a HUnit test suite to a spec. This can be used to run existing -- HUnit tests with Hspec. fromHUnitTest :: Test -> Spec -- copied from https://github.com/hspec/hspec/blob/master/hspec-contrib/src/Test/Hspec/Contrib/HUnit.hs fromHUnitTest t = case t of TestList xs -> mapM_ go xs x -> go x where go :: Test -> Spec go t_ = case t_ of TestLabel s (TestCase e) -> it s e TestLabel s (TestList xs) -> describe s (mapM_ go xs) TestLabel s x -> describe s (go x) TestList xs -> describe "" (mapM_ go xs) TestCase e -> it "" e tests :: Test tests = test [ "sections" ~: $test1_sections @=? $(dsSplice test1_sections) , "lampats" ~: $test2_lampats @=? $(dsSplice test2_lampats) , "lamcase" ~: $test3_lamcase @=? $(dsSplice test3_lamcase) -- Must fix nested pattern-matching for this to work. Argh. -- , "tuples" ~: $test4_tuples @=? $(dsSplice test4_tuples) , "ifs" ~: $test5_ifs @=? $(dsSplice test5_ifs) , "ifs2" ~: $test6_ifs2 @=? $(dsSplice test6_ifs2) , "let" ~: $test7_let @=? $(dsSplice test7_let) , "case" ~: $test8_case @=? $(dsSplice test8_case) , "do" ~: $test9_do @=? $(dsSplice test9_do) , "comp" ~: $test10_comp @=? $(dsSplice test10_comp) , "parcomp" ~: $test11_parcomp @=? $(dsSplice test11_parcomp) , "parcomp2" ~: $test12_parcomp2 @=? $(dsSplice test12_parcomp2) , "sig" ~: $test13_sig @=? $(dsSplice test13_sig) , "record" ~: $test14_record @=? $(dsSplice test14_record) , "litp" ~: $test15_litp @=? $(dsSplice test15_litp) , "tupp" ~: $test16_tupp @=? $(dsSplice test16_tupp) , "infixp" ~: $test17_infixp @=? $(dsSplice test17_infixp) , "tildep" ~: $test18_tildep @=? $(dsSplice test18_tildep) , "bangp" ~: $test19_bangp @=? $(dsSplice test19_bangp) , "asp" ~: $test20_asp @=? $(dsSplice test20_asp) , "wildp" ~: $test21_wildp @=? $(dsSplice test21_wildp) , "listp" ~: $test22_listp @=? $(dsSplice test22_listp) #if __GLASGOW_HASKELL__ >= 801 , "sigp" ~: $test23_sigp @=? $(dsSplice test23_sigp) #endif , "fun" ~: $test24_fun @=? $(dsSplice test24_fun) , "fun2" ~: $test25_fun2 @=? $(dsSplice test25_fun2) , "forall" ~: $test26_forall @=? $(dsSplice test26_forall) , "kisig" ~: $test27_kisig @=? $(dsSplice test27_kisig) , "tupt" ~: $test28_tupt @=? $(dsSplice test28_tupt) , "listt" ~: $test29_listt @=? $(dsSplice test29_listt) , "promoted" ~: $test30_promoted @=? $(dsSplice test30_promoted) , "constraint" ~: $test31_constraint @=? $(dsSplice test31_constraint) , "tylit" ~: $test32_tylit @=? $(dsSplice test32_tylit) , "tvbs" ~: $test33_tvbs @=? $(dsSplice test33_tvbs) , "let_as" ~: $test34_let_as @=? $(dsSplice test34_let_as) , "pred" ~: $test37_pred @=? $(dsSplice test37_pred) , "pred2" ~: $test38_pred2 @=? $(dsSplice test38_pred2) , "eq" ~: $test39_eq @=? $(dsSplice test39_eq) , "wildcard" ~: $test40_wildcards@=? $(dsSplice test40_wildcards) #if __GLASGOW_HASKELL__ >= 801 , "typeapps" ~: $test41_typeapps @=? $(dsSplice test41_typeapps) , "scoped_tvs" ~: $test42_scoped_tvs @=? $(dsSplice test42_scoped_tvs) , "ubx_sums" ~: $test43_ubx_sums @=? $(dsSplice test43_ubx_sums) #endif , "let_pragma" ~: $test44_let_pragma @=? $(dsSplice test44_let_pragma) -- , "empty_rec" ~: $test45_empty_record_con @=? $(dsSplice test45_empty_record_con) -- This one can't be tested by this means, because it contains an "undefined" #if __GLASGOW_HASKELL__ >= 803 , "over_label" ~: $test46_overloaded_label @=? $(dsSplice test46_overloaded_label) #endif , "do_partial_match" ~: $test47_do_partial_match @=? $(dsSplice test47_do_partial_match) #if __GLASGOW_HASKELL__ >= 805 , "quantified_constraints" ~: $test48_quantified_constraints @=? $(dsSplice test48_quantified_constraints) #endif #if __GLASGOW_HASKELL__ >= 807 , "implicit_params" ~: $test49_implicit_params @=? $(dsSplice test49_implicit_params) , "vka" ~: $test50_vka @=? $(dsSplice test50_vka) #endif #if __GLASGOW_HASKELL__ >= 809 , "tuple_sections" ~: $test51_tuple_sections @=? $(dsSplice test51_tuple_sections) #endif #if __GLASGOW_HASKELL__ >= 900 , "qual_do" ~: $test52_qual_do @=? $(dsSplice test52_qual_do) #endif #if __GLASGOW_HASKELL__ >= 901 , "vta_in_con_pats" ~: $test53_vta_in_con_pats @=? $(dsSplice test53_vta_in_con_pats) #endif #if __GLASGOW_HASKELL__ >= 902 , "overloaded_record_dot" ~: $test54_overloaded_record_dot @=? $(dsSplice test54_overloaded_record_dot) #endif #if __GLASGOW_HASKELL__ >= 903 , "opaque_pragma" ~: $test55_opaque_pragma @=? $(dsSplice test55_opaque_pragma) , "lambda_cases" ~: $test56_lambda_cases @=? $(dsSplice test56_lambda_cases) #endif ] test35a = $test35_expand test35b = $(test35_expand >>= dsExp >>= expand >>= return . expToTH) test36a = $test36_expand test36b = $(test36_expand >>= dsExp >>= expand >>= return . expToTH) test_e3a = $test_expand3 test_e3b = $(test_expand3 >>= dsExp >>= expand >>= return . expToTH) test_e4a = $test_expand4 test_e4b = $(test_expand4 >>= dsExp >>= expand >>= return . expToTH) test_e5a = $test_expand5 test_e5b = $(test_expand5 >>= dsExp >>= expand >>= return . expToTH) test_e6a = $test_expand6 test_e6b = $(test_expand6 >>= dsExp >>= expand >>= return . expToTH) test_e7a = $test_expand7 test_e7b = $(test_expand7 >>= dsExp >>= expand >>= return . expToTH) test_e7c = $(test_expand7 >>= dsExp >>= expandUnsoundly >>= return . expToTH) #if __GLASGOW_HASKELL__ < 801 test_e8a = $(test_expand8 >>= dsExp >>= expand >>= return . expToTH) -- This won't expand on recent GHCs now that GHC Trac #8953 is fixed for -- closed type families. #endif test_e8b = $(test_expand8 >>= dsExp >>= expandUnsoundly >>= return . expToTH) test_e9a = $test_expand9 -- requires GHC #9262 test_e9b = $(test_expand9 >>= dsExp >>= expand >>= return . expToTH) test_e10a = $test_expand10 test_e10b = $(test_expand10 >>= dsExp >>= expand >>= return . expToTH) hasSameType :: a -> a -> Bool hasSameType _ _ = True test_expand :: Bool test_expand = and [ hasSameType test35a test35b , hasSameType test36a test36b , hasSameType test_e3a test_e3b , hasSameType test_e4a test_e4b , hasSameType test_e5a test_e5b , hasSameType test_e6a test_e6b , hasSameType test_e7a test_e7b , hasSameType test_e7a test_e7c #if __GLASGOW_HASKELL__ < 801 , hasSameType test_e8a test_e8a #endif , hasSameType test_e8b test_e8b , hasSameType test_e9a test_e9b , hasSameType test_e10a test_e10b ] test_dec :: [Bool] test_dec = $(do bools <- mapM testDecSplice dec_test_nums return $ ListE bools) $( do fuzzType <- mkTypeName "Fuzz" fuzzData <- mkDataName "Fuzz" let tySynDecs = TySynD (mkName "FuzzSyn") [] (ConT fuzzType) dataSynDecs = TySynD (mkName "FuzzDataSyn") [] (ConT fuzzData) fuzzDecs <- [d| data Fuzz = Fuzz |] return $ tySynDecs : dataSynDecs : fuzzDecs ) test_mkName :: Bool test_mkName = and [ hasSameType (Proxy :: Proxy FuzzSyn) (Proxy :: Proxy Fuzz) , hasSameType (Proxy :: Proxy FuzzDataSyn) (Proxy :: Proxy 'Fuzz) ] test_bug8884 :: Bool test_bug8884 = $(do info <- reify ''Poly dinfo@(DTyConI (DOpenTypeFamilyD (DTypeFamilyHead _name _tvbs (DKindSig resK) _ann)) (Just [DTySynInstD (DTySynEqn _ lhs _rhs)])) <- dsInfo info let isTypeKind (DConT n) = isTypeKindName n isTypeKind _ = False case (isTypeKind resK, lhs) of (True, _ `DAppT` DSigT (DVarT _) (DVarT _)) -> [| True |] _ -> do runIO $ do putStrLn "Failed bug8884 test:" putStrLn $ show dinfo [| False |] ) flatten_dvald :: Bool flatten_dvald = let s1 = $(flatten_dvald_test) s2 = $(do expr <- flatten_dvald_test DLetE ddecs dexpr <- dsExp expr flattened <- fmap concat $ mapM flattenDValD ddecs return $ expToTH $ DLetE flattened dexpr ) in s1 == s2 test_rec_sels :: Bool test_rec_sels = and $(do bools <- mapM testRecSelTypes [1..rec_sel_test_num_sels] return $ ListE bools) test_standalone_deriving :: Bool test_standalone_deriving = (MkBlarggie 5 'x') == (MkBlarggie 5 'x') test_deriving_strategies :: Bool #if __GLASGOW_HASKELL__ >= 801 test_deriving_strategies = compare (MkBlarggie 5 'x') (MkBlarggie 5 'x') == EQ #else test_deriving_strategies = True #endif test_local_tyfam_expansion :: Bool test_local_tyfam_expansion = $(do fam_name <- newName "Fam" let orig_ty = DConT fam_name exp_ty <- withLocalDeclarations (decsToTH [ DOpenTypeFamilyD (DTypeFamilyHead fam_name [] DNoSig Nothing) , DTySynInstD (DTySynEqn Nothing (DConT fam_name) (DConT ''Int)) ]) (expandType orig_ty) orig_ty `eqTHSplice` exp_ty) test_stuck_tyfam_expansion :: Bool test_stuck_tyfam_expansion = $(do fam_name <- newName "F" x <- newName "x" k <- newName "k" let orig_ty = DConT fam_name `DAppT` DConT '() -- F '() exp_ty <- withLocalDeclarations (decsToTH [ -- type family F (x :: k) :: k DOpenTypeFamilyD (DTypeFamilyHead fam_name [DKindedTV x () (DVarT k)] (DKindSig (DVarT k)) Nothing) -- type instance F (x :: ()) = x , DTySynInstD (DTySynEqn Nothing (DConT fam_name `DAppT` DSigT (DVarT x) (DConT ''())) (DVarT x)) ]) (expandType orig_ty) orig_ty `eqTHSplice` exp_ty) test_t85 :: Bool test_t85 = $(do let orig_ty = (DConT ''Constant `DAppT` DConT ''Int `DAppT` DConT 'True) `DSigT` (DConT ''Constant `DAppT` DConT ''Char `DAppT` DConT ''Bool) expected_ty = DConT 'True `DSigT` DConT ''Bool expanded_ty <- expandType orig_ty expected_ty `eqTHSplice` expanded_ty) test_t92 :: Bool test_t92 = $(do a <- newName "a" f <- newName "f" let t = DForallT (DForallInvis [DPlainTV f SpecifiedSpec]) (DVarT f `DAppT` DVarT a) toposortTyVarsOf [t] `eqTHSplice` [DPlainTV a ()]) test_t97 :: Bool test_t97 = $(do a <- newName "a" k <- newName "k" let orig_ty = DForallT (DForallInvis [DKindedTV a SpecifiedSpec (DConT ''Constant `DAppT` DConT ''Int `DAppT` DVarT k)]) (DVarT a) expected_ty = DForallT (DForallInvis [DKindedTV a SpecifiedSpec (DVarT k)]) (DVarT a) expanded_ty <- expandType orig_ty expected_ty `eqTHSplice` expanded_ty) test_getDataD_kind_sig :: Bool test_getDataD_kind_sig = 3 == $(do data_name <- newName "TestData" a <- newName "a" let type_kind = DConT typeKindName data_kind_sig = DArrowT `DAppT` type_kind `DAppT` (DArrowT `DAppT` type_kind `DAppT` type_kind) (_, tvbs, _) <- withLocalDeclarations [decToTH (DDataD Data [] data_name [DPlainTV a ()] (Just data_kind_sig) [] [])] (getDataD "th-desugar: Impossible" data_name) [| $(Syn.lift (length tvbs)) |]) test_t100 :: Bool test_t100 = $(do decs <- [d| data T b where MkT :: forall a. { unT :: a } -> T a |] info <- withLocalDeclarations decs (dsReify (mkName "unT")) let -- forall a. T a -> a exp_ty = DForallT (DForallInvis [DPlainTV (mkName "a") SpecifiedSpec]) $ DArrowT `DAppT` (DConT (mkName "T") `DAppT` DVarT (mkName "a")) `DAppT` DVarT (mkName "a") case info of Just (DVarI _ actual_ty _) -> exp_ty `eqTHSplice` actual_ty _ -> [| False |]) test_t102 :: [Bool] test_t102 = $(do decs1 <- [d| data Foo x where MkFoo :: forall a. { unFoo :: a } -> Foo a |] let b1 = withLocalDeclarations decs1 $ do [DDataD _ _ _ _ _ cons1 _] <- dsDecs decs1 recs1 <- getRecordSelectors cons1 (length recs1 `div` 2) `eqTHSplice` 1 decs2 <- [d| data HList l where Nil :: HList '[] (:>) :: { hhead :: x, htail :: HList xs } -> HList (x ': xs) |] let b2 = withLocalDeclarations decs2 $ do [DDataD _ _ _ _ _ cons2 _] <- dsDecs decs2 recs2 <- getRecordSelectors cons2 (length recs2 `div` 2) `eqTHSplice` 2 [| [$b1, $b2] |]) test_t103 :: Bool test_t103 = $(do decs <- [d| data P (a :: k) = MkP |] [DDataD _ _ _ _ _ [DCon tvbs _ _ _ _] _] <- dsDecs decs case tvbs of [DPlainTV k SpecifiedSpec, DKindedTV a SpecifiedSpec (DVarT k')] | nameBase k == "k" , nameBase a == "a" , k == k' -> [| True |] | otherwise -> [| False |]) test_t112 :: [Bool] test_t112 = $(do a <- newName "a" b <- newName "b" let aVar = DVarT a bVar = DVarT b aTvb = DPlainTV a () bTvb = DPlainTV b () fvsABExpected = [aTvb, bTvb] fvsABActual = toposortTyVarsOf [aVar, bVar] fvsBAExpected = [bTvb, aTvb] fvsBAActual = toposortTyVarsOf [bVar, aVar] eqAB = fvsABExpected `eqTH` fvsABActual eqBA = fvsBAExpected `eqTH` fvsBAActual [| [eqAB, eqBA] |]) test_t132 :: Bool test_t132 = $(do let c = mkName "C" m = mkName "m" a = mkName "a" fixity = Fixity 5 InfixR -- Defines a class with a fixity declaration inside, i.e., -- -- class C a where -- infixr 5 `m` -- m :: a -- -- We define this by hand to avoid GHC#17608 on pre-9.0 GHCs. decs = sweeten [ DClassD [] c [DPlainTV a ()] [] [ DLetDec (DInfixD fixity m) , DLetDec (DSigD m (DVarT a)) ] ] expected = Just fixity actual <- withLocalDeclarations decs (reifyFixityWithLocals m) expected `eqTHSplice` actual) #if __GLASGOW_HASKELL__ >= 801 -- Test local reification of pattern synonym record selectors. test_t137 :: [Bool] test_t137 = $(do a <- newName "a" b <- newName "b" let aVarT = DVarT a aVarP = DVarP a bVarT = DVarT b bVarP = DVarP b aTvb = DPlainTV a SpecifiedSpec bTvb = DPlainTV b SpecifiedSpec p1 = mkName "P1" unP1a = mkName "unP1a" unP1b = mkName "unP1b" p2 = mkName "P2" unP2a = mkName "unP2a" unP2b = mkName "unP2b" p3 = mkName "P3" unP3a = mkName "unP3a" unP3b = mkName "unP3b" tupleTy = DConT (tupleTypeName 2) `DAppT` aVarT `DAppT` bVarT showCxt = [DConT ''Show `DAppT` aVarT] patSynSigDBodyTy = DArrowT `DAppT` aVarT `DAppT` (DArrowT `DAppT` bVarT `DAppT` tupleTy) -- pattern P{unPa, unPb} = (unPa, unPb) mkPatSynD :: Name -> Name -> Name -> DDec mkPatSynD p unPa unPb = DPatSynD p (RecordPatSyn [unPa, unPb]) DImplBidir (DConP (tupleDataName 2) [] [aVarP, bVarP]) decs :: [Dec] decs = sweeten [ -- pattern P1 :: a -> b -> (a, b) DPatSynSigD p1 patSynSigDBodyTy , mkPatSynD p1 unP1a unP1b -- pattern P2 :: Show a => a -> b -> (a, b) , DPatSynSigD p2 $ DConstrainedT showCxt patSynSigDBodyTy , mkPatSynD p2 unP2a unP2b -- pattern P3 :: forall b a. Show a => a -> b -> (a, b) , DPatSynSigD p3 $ DForallT (DForallInvis [bTvb, aTvb]) $ DConstrainedT showCxt patSynSigDBodyTy , mkPatSynD p3 unP3a unP3b ] -- Pair each pattern synonym record selector name with the type that -- local reification should produce. expecteds :: [(Name, DType)] expecteds = [ (unP1a, DForallT (DForallInvis [aTvb, bTvb]) $ DArrowT `DAppT` tupleTy `DAppT` aVarT) , (unP1b, DForallT (DForallInvis [aTvb, bTvb]) $ DArrowT `DAppT` tupleTy `DAppT` bVarT) -- The reified types below use (DForallInvis []) due to the way -- that ForallT is desugared. -- See Note [Desugaring and sweetening ForallT] in -- Language.Haskell.TH.Desugar.Core. , (unP2a, DForallT (DForallInvis []) $ DConstrainedT showCxt $ DArrowT `DAppT` tupleTy `DAppT` aVarT) , (unP2b, DForallT (DForallInvis []) $ DConstrainedT showCxt $ DArrowT `DAppT` tupleTy `DAppT` bVarT) , (unP3a, DForallT (DForallInvis [bTvb, aTvb]) $ DConstrainedT showCxt $ DArrowT `DAppT` tupleTy `DAppT` aVarT) , (unP3b, DForallT (DForallInvis [bTvb, aTvb]) $ DConstrainedT showCxt $ DArrowT `DAppT` tupleTy `DAppT` bVarT) ] expected_eq_actual :: (Name, DType) -> DsM Q Bool expected_eq_actual (sel_name, expected_ty) = do let expected_info = Just $ DVarI sel_name expected_ty Nothing actual_info <- dsReify sel_name pure $ expected_info `eqTH` actual_info bs <- withLocalDeclarations decs $ mapM expected_eq_actual expecteds Syn.lift bs) #endif test_t154 :: Bool test_t154 = $(do decs <- [d| data T where (:$$:) :: Int -> Int -> T |] ddecs <- dsDecs decs let mb_is_infix = case ddecs of [DDataD _ _ _ _ _ [DCon _ _ _ (DNormalC is_infix _) _] _] -> Just is_infix _ -> Nothing mb_is_infix `eqTHSplice` Just False) -- Regression test for #159 which ensures that non-exhaustive functions throw -- a runtime error before forcing their arguments. test_t159 :: Expectation test_t159 = do -- NB: Catch ErrorCall here, not PatternMatchFail. This is because we desugar -- non-exhaustive patterns into a custom `error` expression. let testOne f = f (let x = x in x) `shouldThrow` \(_ :: ErrorCall) -> True testOne t159A testOne t159B #if __GLASGOW_HASKELL__ >= 906 test_t170 :: [Bool] test_t170 = $(do decs <- [d| type data TyData = MkTyData |] let test_TypeData_NameSpace nameStr = withLocalDeclarations decs $ do Just name <- lookupTypeNameWithLocals nameStr mbNS <- reifyNameSpace name mbNS `eqTHSplice` Just Syn.TcClsName let b1 = test_TypeData_NameSpace "TyData" let b2 = test_TypeData_NameSpace "MkTyData" [| [$b1, $b2] |]) #endif test_t171 :: Bool test_t171 = $(do a <- newName "a" b <- newName "b" c <- newName "c" x <- newName "x" y <- newName "y" let aVarT = DVarT a bVarT = DVarT b cVarT = DVarT c aTvb = DPlainTV a SpecifiedSpec bTvb = DPlainTV b SpecifiedSpec cTvb = DPlainTV c SpecifiedSpec t = mkName "T" mkT = mkName "mkT" getT1 = mkName "getT1" getT2 = mkName "getT2" dec = -- data T x y where -- MkT :: forall b a c. { getT1 :: b, getT2 :: c } -> T a b DDataD Data [] t [DPlainTV x (), DPlainTV y ()] Nothing [ DCon [bTvb, aTvb, cTvb] [] mkT (DRecC [ ( getT1 , Bang NoSourceUnpackedness NoSourceStrictness , bVarT ) , ( getT2 , Bang NoSourceUnpackedness NoSourceStrictness , cVarT ) ]) res_ty ] [] res_ty = DConT t `DAppT` aVarT `DAppT` bVarT expected_ty = DForallT (DForallInvis [bTvb, aTvb]) $ DArrowT `DAppT` res_ty `DAppT` bVarT withLocalDeclarations (sweeten [dec]) $ do Just (DVarI _ actual_ty _) <- dsReify getT1 expected_ty `eqTHSplice` actual_ty) -- Unit tests for functions that compute free variables (e.g., fvDType) test_fvs :: [Bool] test_fvs = $(do a <- newName "a" let -- (Show a => Show (Maybe a)) => String ty1 = DConstrainedT [DConstrainedT [DConT ''Show `DAppT` DVarT a] (DConT ''Show `DAppT` (DConT ''Maybe `DAppT` DVarT a))] (DConT ''String) b1 = fvDType ty1 `eqTH` OS.singleton a -- #93 [| [b1] |]) test_kind_substitution :: [Bool] test_kind_substitution = $(do a <- newName "a" b <- newName "b" c <- newName "c" k <- newName "k" let subst = M.singleton a (DVarT b) -- (Nothing :: Maybe a) ty1 = DSigT (DConT 'Nothing) (DConT ''Maybe `DAppT` DVarT a) -- forall (c :: a). c ty2 = DForallT (DForallInvis [DKindedTV c SpecifiedSpec (DVarT a)]) (DVarT c) -- forall a (c :: a). c ty3 = DForallT (DForallInvis [ DPlainTV a SpecifiedSpec , DKindedTV c SpecifiedSpec (DVarT a) ]) (DVarT c) -- forall (a :: k) k (b :: k). Proxy b -> Proxy a ty4 = DForallT (DForallInvis [ DKindedTV a SpecifiedSpec (DVarT k) , DPlainTV k SpecifiedSpec , DKindedTV b SpecifiedSpec (DVarT k) ]) (DArrowT `DAppT` (DConT ''Proxy `DAppT` DVarT b) `DAppT` (DConT ''Proxy `DAppT` DVarT a)) substTy1 <- substTy subst ty1 substTy2 <- substTy subst ty2 substTy3 <- substTy subst ty3 substTy4 <- substTy subst ty4 let freeVars1 = fvDType substTy1 freeVars2 = fvDType substTy2 freeVars3 = fvDType substTy3 freeVars4 = fvDType substTy4 b1 = freeVars1 `eqTH` OS.singleton b b2 = freeVars2 `eqTH` OS.singleton b b3 = freeVars3 `eqTH` OS.empty b4 = freeVars4 `eqTH` OS.singleton k [| [b1, b2, b3, b4] |]) test_lookup_value_type_names :: [Bool] test_lookup_value_type_names = $(do let nameStr = "***" valName <- newName nameStr typeName <- newName nameStr let tyDec = DTySynD typeName [] (DConT ''Bool) decs = decsToTH [ DLetDec (DSigD valName (DConT ''Bool)) , DLetDec (DValD (DVarP valName) (DConE 'False)) , tyDec ] lookupReify lookup_fun = withLocalDeclarations decs $ do Just n <- lookup_fun nameStr Just i <- dsReify n return i reifiedVal <- lookupReify lookupValueNameWithLocals reifiedType <- lookupReify lookupTypeNameWithLocals let b1 = reifiedVal `eqTH` DVarI valName (DConT ''Bool) Nothing let b2 = reifiedType `eqTH` DTyConI tyDec Nothing [| [b1, b2] |]) local_reifications :: [String] local_reifications = $(do decs <- reifyDecs m_infos <- withLocalDeclarations decs $ mapM reifyWithLocals_maybe reifyDecsNames let m_infos' = assumeStarT m_infos ListE <$> mapM (Syn.lift . show) (unqualify m_infos')) type T123G = Either () () type T123F = Either T123G T123G type T123E = Either T123F T123F type T123D = Either T123E T123E type T123C = Either T123D T123D type T123B = Either T123C T123C type T123A = Either T123B T123B $reifyDecs $(return []) -- somehow, this is necessary to get the staging correct for the -- reifications below. Weird. normal_reifications :: [String] normal_reifications = $(do infos <- mapM reify reifyDecsNames ListE <$> mapM (Syn.lift . show . Just) (dropTrailing0s $ delinearize $ unqualify infos)) zipWith3M :: Monad m => (a -> b -> c -> m d) -> [a] -> [b] -> [c] -> m [d] zipWith3M f (a:as) (b:bs) (c:cs) = liftM2 (:) (f a b c) (zipWith3M f as bs cs) zipWith3M _ _ _ _ = return [] simplCase :: [Bool] simplCase = $( do exps <- sequence simplCaseTests dexps <- mapM dsExp exps sexps <- mapM scExp dexps bools <- zipWithM (\e1 e2 -> [| $(return e1) == $(return e2) |]) exps (map sweeten sexps) return $ ListE bools ) test_roundtrip :: [Bool] test_roundtrip = $( do exprs <- sequence test_exprs ds_exprs1 <- mapM dsExp exprs let th_exprs1 = map expToTH ds_exprs1 ds_exprs2 <- mapM dsExp th_exprs1 let th_exprs2 = map expToTH ds_exprs2 ds_exprs3 <- mapM dsExp th_exprs2 let bools = zipWith eqTH ds_exprs2 ds_exprs3 Syn.lift bools ) test_matchTy :: [Bool] test_matchTy = [ matchTy NoIgnore (DVarT a) (DConT ''Bool) == Just (M.singleton a (DConT ''Bool)) , matchTy NoIgnore (DVarT a) (DVarT a) == Just (M.singleton a (DVarT a)) , matchTy NoIgnore (DVarT a) (DVarT b) == Just (M.singleton a (DVarT b)) , matchTy NoIgnore (DConT ''Either `DAppT` DVarT a `DAppT` DVarT b) (DConT ''Either `DAppT` DConT ''Int `DAppT` DConT ''Bool) == Just (M.fromList [(a, DConT ''Int), (b, DConT ''Bool)]) , matchTy NoIgnore (DConT ''Either `DAppT` DVarT a `DAppT` DVarT a) (DConT ''Either `DAppT` DConT ''Int `DAppT` DConT ''Int) == Just (M.singleton a (DConT ''Int)) , matchTy NoIgnore (DConT ''Either `DAppT` DVarT a `DAppT` DVarT a) (DConT ''Either `DAppT` DConT ''Int `DAppT` DConT ''Bool) == Nothing , matchTy NoIgnore (DConT ''Int) (DConT ''Bool) == Nothing , matchTy NoIgnore (DConT ''Int) (DConT ''Int) == Just M.empty , matchTy NoIgnore (DConT ''Int) (DVarT a) == Nothing , matchTy NoIgnore (DVarT a `DSigT` DConT ''Bool) (DConT ''Int) == Nothing , matchTy YesIgnore (DVarT a `DSigT` DConT ''Bool) (DConT ''Int) == Just (M.singleton a (DConT ''Int)) ] where a = mkName "a" b = mkName "b" -- Test that type synonym expansion is efficient test_t123 :: () test_t123 = $(do _ <- expand (DConT ''T123A) [| () |]) main :: IO () main = hspec $ do describe "th-desugar library" $ do it "compiles" $ True it "expands" $ test_expand zipWithM (\num success -> it ("passes dec test " ++ show num) success) dec_test_nums test_dec -- instance test 1 is part of dectest 6. it "passes instance test" $ $(do ty <- [t| Int -> Bool |] [inst1, inst2] <- reifyInstances ''Show [ty] inst1 `eqTHSplice` inst2) it "makes type names" $ test_mkName it "fixes bug 8884" $ test_bug8884 it "flattens DValDs" $ flatten_dvald it "extracts record selectors" $ test_rec_sels it "works with standalone deriving" $ test_standalone_deriving it "works with deriving strategies" $ test_deriving_strategies it "doesn't expand local type families" $ test_local_tyfam_expansion it "doesn't crash on a stuck type family application" $ test_stuck_tyfam_expansion it "expands type synonyms in kinds" $ test_t85 it "toposorts free variables in polytypes" $ test_t92 it "expands type synonyms in type variable binders" $ test_t97 it "reifies GADT record selectors correctly" $ test_t100 zipWithM (\b n -> it ("collects GADT record selectors correctly" ++ show n) b) test_t102 [1..] it "quantifies kind variables in desugared ADT constructors" $ test_t103 it "reifies data type return kinds accurately" $ test_getDataD_kind_sig zipWithM (\b n -> it ("toposorts free variables deterministically " ++ show n) b) test_t112 [1..] it "reifies fixity declarations inside of classes" $ test_t132 #if __GLASGOW_HASKELL__ >= 801 zipWithM (\b n -> it ("reifies local pattern synonym record selectors " ++ show n) b) test_t137 [1..] #endif zipWithM (\b n -> it ("computes free variables correctly " ++ show n) b) test_fvs [1..] it "desugars non-infix GADT constructors with symbolic names correctly" $ test_t154 it "desugars non-exhaustive expressions into code that errors at runtime" $ test_t159 #if __GLASGOW_HASKELL__ >= 906 zipWithM (\b n -> it ("looks up TypeData names in the type namespace correctly " ++ show n) b) test_t170 [1..] #endif it "locally reifies GADT record selector types with explicit foralls correctly" $ test_t171 -- Remove map pprints here after switch to th-orphans zipWithM (\t t' -> it ("can do Type->DType->Type of " ++ t) $ t == t') $(sequence round_trip_types >>= Syn.lift . map pprint) $(sequence round_trip_types >>= mapM (\ t -> withLocalDeclarations [] (dsType t >>= expandType >>= return . typeToTH)) >>= Syn.lift . map pprint) zipWith3M (\a b n -> it ("reifies local definition " ++ show n) $ a == b) local_reifications normal_reifications [1..] zipWithM (\b n -> it ("works on simplCase test " ++ show n) b) simplCase [1..] zipWithM (\b n -> it ("round-trip successfully on case " ++ show n) b) test_roundtrip [1..] zipWithM (\b n -> it ("lookups up local value and type names " ++ show n) b) test_lookup_value_type_names [1..] zipWithM (\b n -> it ("substitutes tyvar binder kinds " ++ show n) b) test_kind_substitution [1..] zipWithM (\b n -> it ("matches types " ++ show n) b) test_matchTy [1..] zipWithM (\b n -> it ("reifies kinds of declarations with CUSKs " ++ show n) b) test_reify_type_cusks [1..] zipWithM (\b n -> it ("reifies kinds of declarations without CUSKs " ++ show n) b) test_reify_type_no_cusks [1..] zipWithM (\b n -> it ("reifies the kinds of declarations with signatures " ++ show n) b) test_reify_kind_sigs [1..] fromHUnitTest tests