ghc-typelits-natnormalise 0.7.2 → 0.7.3
raw patch · 7 files changed
+184/−31 lines, 7 filesdep +ghc-bignumdep −sybdep ~ghcPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-bignum
Dependencies removed: syb
Dependency ranges changed: ghc
API changes (from Hackage documentation)
Files
- CHANGELOG.md +3/−0
- ghc-typelits-natnormalise.cabal +7/−5
- src/GHC/TypeLits/Normalise.hs +34/−6
- src/GHC/TypeLits/Normalise/SOP.hs +8/−1
- src/GHC/TypeLits/Normalise/Unify.hs +63/−17
- tests/ErrorTests.hs +48/−0
- tests/Tests.hs +21/−2
CHANGELOG.md view
@@ -1,5 +1,8 @@ # Changelog for the [`ghc-typelits-natnormalise`](http://hackage.haskell.org/package/ghc-typelits-natnormalise) package +## 0.7.3 *January 1st 2021*+* Build on GHC 9.0.1-rc1+ ## 0.7.2 *March 9 2020* * Fixes [#44](https://github.com/clash-lang/ghc-typelits-natnormalise/issues/44) infinite loop due to boxed equality
ghc-typelits-natnormalise.cabal view
@@ -1,5 +1,5 @@ name: ghc-typelits-natnormalise-version: 0.7.2+version: 0.7.3 synopsis: GHC typechecker plugin for types of kind GHC.TypeLits.Nat description: A type checker plugin for GHC that can solve /equalities/ and /inequalities/@@ -49,7 +49,7 @@ CHANGELOG.md cabal-version: >=1.10 tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,- GHC == 8.8.1, GHC == 8.10.1+ GHC == 8.8.4, GHC == 8.10.3, GHC == 9.0.1 source-repository head type: git@@ -67,11 +67,13 @@ GHC.TypeLits.Normalise.Unify build-depends: base >=4.9 && <5, containers >=0.5.7.1 && <0.7,- ghc >=8.0.1 && <8.11,+ ghc >=8.0.1 && <9.2, ghc-tcplugins-extra >=0.3.1,- integer-gmp >=1.0 && <1.1,- syb >=0.7.1 && <0.8, transformers >=0.5.2.0 && < 0.6+ if impl(ghc >= 9.0.0)+ build-depends: ghc-bignum >=1.0 && <1.1+ else+ build-depends: integer-gmp >=1.0 && <1.1 hs-source-dirs: src default-language: Haskell2010 other-extensions: CPP
src/GHC/TypeLits/Normalise.hs view
@@ -164,6 +164,7 @@ #endif import Control.Monad.Trans.Writer.Strict import Data.Either (partitionEithers, rights)+import Data.IORef import Data.List (intersect, partition, stripPrefix, find) import Data.Maybe (mapMaybe, catMaybes) import Data.Set (Set, empty, toList, notMember, fromList, union)@@ -175,6 +176,31 @@ import Text.Read (readMaybe) -- GHC API+#if MIN_VERSION_ghc(9,0,0)+import GHC.Builtin.Names (knownNatClassName, eqTyConKey, heqTyConKey, hasKey)+import GHC.Builtin.Types (promotedFalseDataCon, promotedTrueDataCon, typeNatKind)+import GHC.Builtin.Types.Literals+ (typeNatAddTyCon, typeNatExpTyCon, typeNatLeqTyCon, typeNatMulTyCon,+ typeNatSubTyCon)+import GHC.Core (Expr (..))+import GHC.Core.Coercion (CoercionHole, Role (..), mkUnivCo)+import GHC.Core.Predicate+ (EqRel (NomEq), Pred (EqPred), classifyPredType, getEqPredTys, mkClassPred,+ mkPrimEqPred, isEqPred, isEqPrimPred)+import GHC.Core.TyCo.Rep (Type (..), UnivCoProvenance (..))+import GHC.Core.Type+ (Kind, PredType, eqType, mkTyVarTy, tyConAppTyCon_maybe, typeKind)+import GHC.Driver.Plugins (Plugin (..), defaultPlugin, purePlugin)+import GHC.Tc.Plugin+ (TcPluginM, newCoercionHole, tcLookupClass, tcPluginTrace, tcPluginIO)+import GHC.Tc.Types (TcPlugin (..), TcPluginResult (..))+import GHC.Tc.Types.Constraint+ (Ct, CtEvidence (..), CtLoc, TcEvDest (..), ShadowInfo (WDeriv), ctEvidence,+ ctLoc, ctLocSpan, isGiven, isWanted, mkNonCanonical, setCtLoc, setCtLocSpan,+ isWantedCt, ctEvLoc, ctEvPred, ctEvExpr)+import GHC.Tc.Types.Evidence (EvTerm (..), evCast)+import GHC.Utils.Outputable (Outputable (..), (<+>), ($$), text)+#else #if MIN_VERSION_ghc(8,5,0) import CoreSyn (Expr (..)) #endif@@ -207,7 +233,6 @@ import TcTypeNats (typeNatLeqTyCon) import TysWiredIn (promotedFalseDataCon, promotedTrueDataCon)-import Data.IORef #if MIN_VERSION_ghc(8,10,0) import Constraint@@ -248,6 +273,7 @@ #if MIN_VERSION_ghc(8,10,0) import TcType (isEqPrimPred) #endif+#endif -- internal import GHC.TypeLits.Normalise.SOP@@ -479,11 +505,13 @@ _ -> do tcPluginTrace ("simplifyNats(backtrack: " ++ show (length fancyGivens) ++ ")") (ppr varEqs)- foldr findFirstSimpliedWanted (Simplified []) <$>- mapM (\v -> do let eqs = v ++ eqsW- tcPluginTrace "simplifyNats" (ppr eqs)- simples [] [] [] [] eqs)- fancyGivens++ allSimplified <- forM fancyGivens $ \v -> do+ let eqs = v ++ eqsW+ tcPluginTrace "simplifyNats" (ppr eqs)+ simples [] [] [] [] eqs++ pure (foldr findFirstSimpliedWanted (Simplified []) allSimplified) where simples :: [CoreUnify] -> [((EvTerm, Ct), [Ct])]
src/GHC/TypeLits/Normalise/SOP.hs view
@@ -73,6 +73,9 @@ x^(y*z) @ -}++{-# LANGUAGE CPP #-}+ module GHC.TypeLits.Normalise.SOP ( -- * SOP types Symbol (..)@@ -94,7 +97,11 @@ import Data.List (sort) -- GHC API-import Outputable (Outputable (..), (<+>), text, hcat, integer, punctuate)+#if MIN_VERSION_ghc(9,0,0)+import GHC.Utils.Outputable (Outputable (..), (<+>), text, hcat, integer, punctuate)+#else+import Outputable (Outputable (..), (<+>), text, hcat, integer, punctuate)+#endif data Symbol v c = I Integer -- ^ Integer constant
src/GHC/TypeLits/Normalise/Unify.hs view
@@ -48,13 +48,11 @@ -- External import Control.Arrow (first, second)-import Control.Monad.Trans.Maybe import Control.Monad.Trans.Writer.Strict import Data.Function (on)-import Data.List ((\\), intersect, mapAccumL, nub)-import Data.Maybe (fromMaybe, mapMaybe)+import Data.List ((\\), intersect, nub)+import Data.Maybe (fromMaybe, mapMaybe, isJust) import Data.Set (Set)-import Data.Generics (mkM, everywhereM) import qualified Data.Set as Set import GHC.Base (isTrue#,(==#))@@ -62,10 +60,26 @@ import GHC.Integer.Logarithms (integerLogBase#) -- GHC API+#if MIN_VERSION_ghc(9,0,0)+import GHC.Builtin.Types (boolTy, promotedTrueDataCon, typeNatKind)+import GHC.Builtin.Types.Literals+ (typeNatAddTyCon, typeNatExpTyCon, typeNatLeqTyCon, typeNatMulTyCon, typeNatSubTyCon)+import GHC.Core.Predicate (EqRel (NomEq), Pred (EqPred), classifyPredType, mkPrimEqPred)+import GHC.Core.TyCon (TyCon)+import GHC.Core.Type+ (PredType, TyVar, coreView, eqType, mkNumLitTy, mkTyConApp, mkTyVarTy, nonDetCmpType, typeKind)+import GHC.Core.TyCo.Rep (Kind, Type (..), TyLit (..))+import GHC.Tc.Plugin (TcPluginM, tcPluginTrace)+import GHC.Tc.Types.Constraint (Ct, ctEvidence, ctEvId, ctEvPred, isGiven)+import GHC.Types.Unique.Set+ (UniqSet, unionManyUniqSets, emptyUniqSet, unionUniqSets, unitUniqSet)+import GHC.Utils.Outputable (Outputable (..), (<+>), ($$), text)+#else import Outputable (Outputable (..), (<+>), ($$), text) import TcPluginM (TcPluginM, tcPluginTrace) import TcTypeNats (typeNatAddTyCon, typeNatExpTyCon, typeNatMulTyCon, typeNatSubTyCon, typeNatLeqTyCon)+import TyCon (TyCon) import Type (TyVar, coreView, eqType, mkNumLitTy, mkTyConApp, mkTyVarTy, nonDetCmpType, PredType, typeKind)@@ -82,6 +96,7 @@ import TcRnTypes (ctEvPred) import Type (EqRel (NomEq), PredTree (EqPred), classifyPredType, mkPrimEqPred) #endif+#endif -- Internal import GHC.TypeLits.Normalise.SOP@@ -123,19 +138,50 @@ | tc == typeNatExpTyCon = normaliseExp <$> normaliseNat x <*> normaliseNat y normaliseNat t = return (S [P [C (CType t)]]) --- | Applies 'normaliseNat' and 'simplifySOP' to type or predicats--- to reduce any occurence of sub-terms--- of /kind/ 'GHC.TypeLits.Nat'.--- If the result is the same as input, returns @'Nothing'@.-normaliseNatEverywhere- :: Type -> Writer [(Type, Type)] (Maybe Type)-normaliseNatEverywhere ty- | typeKind ty `eqType` typeNatKind = do- ty' <- normaliseSimplifyNat ty- return $ if ty `eqType` ty' then Nothing else Just ty'- | otherwise = do- ty' <- everywhereM (mkM normaliseSimplifyNat) ty- return $ if ty `eqType` ty' then Nothing else Just ty'+-- | Runs writer action. If the result /Nothing/ writer actions will be+-- discarded.+maybeRunWriter+ :: Monoid a+ => Writer a (Maybe b)+ -> Writer a (Maybe b)+maybeRunWriter w =+ case runWriter w of+ (Nothing, _) -> pure Nothing+ (b, a) -> tell a >> pure b++-- | Applies 'normaliseNat' and 'simplifySOP' to type or predicates to reduce+-- any occurrences of sub-terms of /kind/ 'GHC.TypeLits.Nat'. If the result is+-- the same as input, returns @'Nothing'@.+normaliseNatEverywhere :: Type -> Writer [(Type, Type)] (Maybe Type)+normaliseNatEverywhere ty0+ | TyConApp tc _fields <- ty0+ , tc `elem` knownTyCons = do+ -- Normalize under current type constructor application. 'go' skips all+ -- known type constructors.+ ty1M <- maybeRunWriter (go ty0)+ let ty1 = fromMaybe ty0 ty1M++ -- Normalize (subterm-normalized) type given to 'normaliseNatEverywhere'+ ty2 <- normaliseSimplifyNat ty1+ -- TODO: 'normaliseNat' could keep track whether it changed anything. That's+ -- TODO: probably cheaper than checking for equality here.+ pure (if ty2 `eqType` ty1 then ty1M else Just ty2)+ | otherwise = go ty0+ where+ knownTyCons :: [TyCon]+ knownTyCons = [typeNatExpTyCon, typeNatMulTyCon, typeNatSubTyCon, typeNatAddTyCon]++ -- Normalize given type, but ignore all top-level+ go :: Type -> Writer [(Type, Type)] (Maybe Type)+ go (TyConApp tc_ fields0_) = do+ fields1_ <- mapM (maybeRunWriter . cont) fields0_+ if any isJust fields1_ then+ pure (Just (TyConApp tc_ (zipWith fromMaybe fields0_ fields1_)))+ else+ pure Nothing+ where+ cont = if tc_ `elem` knownTyCons then go else normaliseNatEverywhere+ go _ = pure Nothing normaliseSimplifyNat :: Type -> Writer [(Type, Type)] Type normaliseSimplifyNat ty
tests/ErrorTests.hs view
@@ -29,9 +29,15 @@ testProxy1 = id testProxy1Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy (x + 1) -> Proxy (2 + x)"+ ," Actual: Proxy (x + 1) -> Proxy (x + 1)"+ ]+#else ["Expected type: Proxy (x + 1) -> Proxy (2 + x)" ,"Actual type: Proxy (2 + x) -> Proxy (2 + x)" ]+#endif type family GCD (x :: Nat) (y :: Nat) :: Nat type instance GCD 6 8 = 2@@ -41,9 +47,15 @@ testProxy2 = id testProxy2Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy (GCD 6 8 + x) -> Proxy (x + GCD 9 6)"+ ," Actual: Proxy (2 + x) -> Proxy (2 + x)"+ ]+#else ["Expected type: Proxy (GCD 6 8 + x) -> Proxy (x + GCD 9 6)" ,"Actual type: Proxy (x + 3) -> Proxy (x + 3)" ]+#endif proxyFun3 :: Proxy (x + x + x) -> () proxyFun3 = const ()@@ -52,9 +64,15 @@ testProxy3 = proxyFun3 testProxy3Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy 8 -> ()"+ ," Actual: Proxy ((x0 + x0) + x0) -> ()"+ ]+#else ["Expected type: Proxy 8 -> ()" ,"Actual type: Proxy ((x0 + x0) + x0) -> ()" ]+#endif proxyFun4 :: Proxy ((2*y)+4) -> () proxyFun4 = const ()@@ -63,17 +81,29 @@ testProxy4 = proxyFun4 testProxy4Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy 2 -> ()"+ ," Actual: Proxy ((2 * y0) + 4) -> ()"+ ]+#else ["Expected type: Proxy 2 -> ()" ,"Actual type: Proxy ((2 * y0) + 4) -> ()" ]+#endif testProxy5 :: Proxy 7 -> () testProxy5 = proxyFun4 testProxy5Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy 7 -> ()"+ ," Actual: Proxy ((2 * y1) + 4) -> ()"+ ]+#else ["Expected type: Proxy 7 -> ()" ,"Actual type: Proxy ((2 * y1) + 4) -> ()" ]+#endif proxyFun6 :: Proxy (2^k) -> Proxy (2^k) proxyFun6 = const Proxy@@ -82,9 +112,15 @@ testProxy6 = proxyFun6 (Proxy :: Proxy 7) testProxy6Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy (2 ^ k0)"+ ," Actual: Proxy 7"+ ]+#else ["Expected type: Proxy (2 ^ k0)" ,"Actual type: Proxy 7" ]+#endif proxyFun7 :: Proxy (2^k) -> Proxy k proxyFun7 = const Proxy@@ -93,9 +129,15 @@ testProxy8 = id testProxy8Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy x -> Proxy (y + x)"+ ," Actual: Proxy x -> Proxy x"+ ]+#else ["Expected type: Proxy x -> Proxy (y + x)" ,"Actual type: Proxy x -> Proxy x" ]+#endif proxyInEq :: (a <= b) => Proxy a -> Proxy b -> () proxyInEq _ _ = ()@@ -170,9 +212,15 @@ testProxy15 = id testProxy15Errors =+#if __GLASGOW_HASKELL__ >= 900+ ["Expected: Proxy n -> Proxy (n + d)"+ ," Actual: Proxy n -> Proxy n"+ ]+#else ["Expected type: Proxy n -> Proxy (n + d)" ,"Actual type: Proxy n -> Proxy n" ]+#endif data Fin (n :: Nat) where FZ :: Fin (n + 1)
tests/Tests.hs view
@@ -282,7 +282,7 @@ -> (forall (m :: Nat) . f (m + k) -> r) -- ^ Function with the @(n + k)@ constraint -> r-leToPlus _ a f = f @ (n-k) a+leToPlus _ a f = f @(n-k) a data BNat :: Nat -> Type where BT :: BNat 0@@ -340,6 +340,25 @@ -> Proxy x proxyEq3 _ _ x = x +-- Would yield (b <=? c) ~ 'True+proxyEq4+ :: forall a b c+ . (KnownNat a, c <= b, b <= a)+ => Proxy b+ -> Proxy c+ -> Proxy a+ -> Proxy (((a - b) + c) + (b - c))+proxyEq4 = theProxy+ where+ theProxy+ :: forall a b c+ . (KnownNat (((a - b) + c) + (b - c)), c <= b, b <= a)+ => Proxy b+ -> Proxy c+ -> Proxy a+ -> Proxy (((a - b) + c) + (b - c))+ theProxy _ _ = id+ proxyInEqImplication :: (2 <= (2 ^ (n + d))) => Proxy d -> Proxy n@@ -557,7 +576,7 @@ , testCase "2a <=? 4a ~ False" $ testProxy14 `throws` testProxy14Errors , testCase "Show (Boo n) => Show (Boo (n - 1 + 1))" $ testProxy17 `throws` test17Errors- , testCase "1 <= m, m <= rp implies 1 <= rp - m" $ (testProxy19 (Proxy @ 1) (Proxy @ 1)) `throws` test19Errors+ , testCase "1 <= m, m <= rp implies 1 <= rp - m" $ (testProxy19 (Proxy @1) (Proxy @1)) `throws` test19Errors ] ] ]