diff options
author | RyanGlScott <ryan.gl.scott@gmail.com> | 2015-10-03 19:21:37 +0200 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2015-10-03 20:03:15 +0200 |
commit | 6cde981a8788b225819be28659caddc35b77972d (patch) | |
tree | f78cd8be5a0549a654e523345bbde48a80493120 | |
parent | a96f1acc59f425062e6192b4cd2a19e1ef987f4a (diff) | |
download | haskell-6cde981a8788b225819be28659caddc35b77972d.tar.gz |
Make GHC generics capable of handling unboxed types
This adds a data family (`URec`) and six data family instances (`UAddr`,
`UChar`, `UDouble`, `UFloat`, `UInt`, and `UWord`) which a `deriving
Generic(1)` clause will generate if it sees `Addr#`, `Char#`, `Double#`,
`Float#`, `Int#`, or `Word#`, respectively. The programmer can then
provide instances for these data family instances to provide custom
implementations for unboxed types, similar to how derived `Eq`, `Ord`,
and `Show` instances currently special-case unboxed types.
Fixes #10868.
Test Plan: ./validate
Reviewers: goldfire, dreixel, bgamari, austin, hvr, kosmikus
Reviewed By: dreixel, kosmikus
Subscribers: simonpj, thomie
Differential Revision: https://phabricator.haskell.org/D1239
GHC Trac Issues: #10868
-rw-r--r-- | compiler/prelude/PrelNames.hs | 65 | ||||
-rw-r--r-- | compiler/typecheck/TcGenGenerics.hs | 132 | ||||
-rw-r--r-- | docs/users_guide/7.12.1-notes.rst | 3 | ||||
-rw-r--r-- | docs/users_guide/glasgow_exts.rst | 42 | ||||
-rw-r--r-- | libraries/base/GHC/Generics.hs | 108 | ||||
-rw-r--r-- | libraries/base/changelog.md | 4 | ||||
-rw-r--r-- | testsuite/tests/generics/GEq/GEq1.hs | 33 | ||||
-rw-r--r-- | testsuite/tests/generics/GEq/GEq1.stdout | 2 | ||||
-rw-r--r-- | testsuite/tests/generics/GEq/GEq1A.hs | 22 | ||||
-rw-r--r-- | testsuite/tests/generics/GShow/GShow.hs | 32 | ||||
-rw-r--r-- | testsuite/tests/generics/GShow/GShow1.stdout | 1 | ||||
-rw-r--r-- | testsuite/tests/generics/GShow/Main.hs | 10 | ||||
-rw-r--r-- | testsuite/tests/generics/T8468.stderr | 2 |
13 files changed, 387 insertions, 69 deletions
diff --git a/compiler/prelude/PrelNames.hs b/compiler/prelude/PrelNames.hs index 10d8747b73..f1212a38f1 100644 --- a/compiler/prelude/PrelNames.hs +++ b/compiler/prelude/PrelNames.hs @@ -370,7 +370,9 @@ genericTyConNames = [ compTyConName, rTyConName, pTyConName, dTyConName, cTyConName, sTyConName, rec0TyConName, par0TyConName, d1TyConName, c1TyConName, s1TyConName, noSelTyConName, - repTyConName, rep1TyConName + repTyConName, rep1TyConName, uRecTyConName, + uAddrTyConName, uCharTyConName, uDoubleTyConName, + uFloatTyConName, uIntTyConName, uWordTyConName ] {- @@ -687,7 +689,11 @@ u1DataCon_RDR, par1DataCon_RDR, rec1DataCon_RDR, conName_RDR, conFixity_RDR, conIsRecord_RDR, noArityDataCon_RDR, arityDataCon_RDR, selName_RDR, prefixDataCon_RDR, infixDataCon_RDR, leftAssocDataCon_RDR, - rightAssocDataCon_RDR, notAssocDataCon_RDR :: RdrName + rightAssocDataCon_RDR, notAssocDataCon_RDR, + uAddrDataCon_RDR, uCharDataCon_RDR, uDoubleDataCon_RDR, + uFloatDataCon_RDR, uIntDataCon_RDR, uWordDataCon_RDR, + uAddrHash_RDR, uCharHash_RDR, uDoubleHash_RDR, + uFloatHash_RDR, uIntHash_RDR, uWordHash_RDR :: RdrName u1DataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "U1") par1DataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "Par1") @@ -728,6 +734,19 @@ leftAssocDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "LeftAssociative") rightAssocDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "RightAssociative") notAssocDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "NotAssociative") +uAddrDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UAddr") +uCharDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UChar") +uDoubleDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UDouble") +uFloatDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UFloat") +uIntDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UInt") +uWordDataCon_RDR = dataQual_RDR gHC_GENERICS (fsLit "UWord") + +uAddrHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uAddr#") +uCharHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uChar#") +uDoubleHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uDouble#") +uFloatHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uFloat#") +uIntHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uInt#") +uWordHash_RDR = varQual_RDR gHC_GENERICS (fsLit "uWord#") fmap_RDR, pure_RDR, ap_RDR, foldable_foldr_RDR, foldMap_RDR, traverse_RDR, mempty_RDR, mappend_RDR :: RdrName @@ -789,7 +808,9 @@ v1TyConName, u1TyConName, par1TyConName, rec1TyConName, compTyConName, rTyConName, pTyConName, dTyConName, cTyConName, sTyConName, rec0TyConName, par0TyConName, d1TyConName, c1TyConName, s1TyConName, noSelTyConName, - repTyConName, rep1TyConName :: Name + repTyConName, rep1TyConName, uRecTyConName, + uAddrTyConName, uCharTyConName, uDoubleTyConName, + uFloatTyConName, uIntTyConName, uWordTyConName :: Name v1TyConName = tcQual gHC_GENERICS (fsLit "V1") v1TyConKey u1TyConName = tcQual gHC_GENERICS (fsLit "U1") u1TyConKey @@ -818,6 +839,14 @@ noSelTyConName = tcQual gHC_GENERICS (fsLit "NoSelector") noSelTyConKey repTyConName = tcQual gHC_GENERICS (fsLit "Rep") repTyConKey rep1TyConName = tcQual gHC_GENERICS (fsLit "Rep1") rep1TyConKey +uRecTyConName = tcQual gHC_GENERICS (fsLit "URec") uRecTyConKey +uAddrTyConName = tcQual gHC_GENERICS (fsLit "UAddr") uAddrTyConKey +uCharTyConName = tcQual gHC_GENERICS (fsLit "UChar") uCharTyConKey +uDoubleTyConName = tcQual gHC_GENERICS (fsLit "UDouble") uDoubleTyConKey +uFloatTyConName = tcQual gHC_GENERICS (fsLit "UFloat") uFloatTyConKey +uIntTyConName = tcQual gHC_GENERICS (fsLit "UInt") uIntTyConKey +uWordTyConName = tcQual gHC_GENERICS (fsLit "UWord") uWordTyConKey + -- Base strings Strings unpackCStringName, unpackCStringFoldrName, unpackCStringUtf8Name, eqStringName, stringTyConName :: Name @@ -1469,7 +1498,9 @@ v1TyConKey, u1TyConKey, par1TyConKey, rec1TyConKey, compTyConKey, rTyConKey, pTyConKey, dTyConKey, cTyConKey, sTyConKey, rec0TyConKey, par0TyConKey, d1TyConKey, c1TyConKey, s1TyConKey, noSelTyConKey, - repTyConKey, rep1TyConKey :: Unique + repTyConKey, rep1TyConKey, uRecTyConKey, + uAddrTyConKey, uCharTyConKey, uDoubleTyConKey, + uFloatTyConKey, uIntTyConKey, uWordTyConKey :: Unique v1TyConKey = mkPreludeTyConUnique 135 u1TyConKey = mkPreludeTyConUnique 136 @@ -1498,21 +1529,29 @@ noSelTyConKey = mkPreludeTyConUnique 154 repTyConKey = mkPreludeTyConUnique 155 rep1TyConKey = mkPreludeTyConUnique 156 +uRecTyConKey = mkPreludeTyConUnique 157 +uAddrTyConKey = mkPreludeTyConUnique 158 +uCharTyConKey = mkPreludeTyConUnique 159 +uDoubleTyConKey = mkPreludeTyConUnique 160 +uFloatTyConKey = mkPreludeTyConUnique 161 +uIntTyConKey = mkPreludeTyConUnique 162 +uWordTyConKey = mkPreludeTyConUnique 163 + -- Type-level naturals typeNatKindConNameKey, typeSymbolKindConNameKey, typeNatAddTyFamNameKey, typeNatMulTyFamNameKey, typeNatExpTyFamNameKey, typeNatLeqTyFamNameKey, typeNatSubTyFamNameKey , typeSymbolCmpTyFamNameKey, typeNatCmpTyFamNameKey :: Unique -typeNatKindConNameKey = mkPreludeTyConUnique 160 -typeSymbolKindConNameKey = mkPreludeTyConUnique 161 -typeNatAddTyFamNameKey = mkPreludeTyConUnique 162 -typeNatMulTyFamNameKey = mkPreludeTyConUnique 163 -typeNatExpTyFamNameKey = mkPreludeTyConUnique 164 -typeNatLeqTyFamNameKey = mkPreludeTyConUnique 165 -typeNatSubTyFamNameKey = mkPreludeTyConUnique 166 -typeSymbolCmpTyFamNameKey = mkPreludeTyConUnique 167 -typeNatCmpTyFamNameKey = mkPreludeTyConUnique 168 +typeNatKindConNameKey = mkPreludeTyConUnique 164 +typeSymbolKindConNameKey = mkPreludeTyConUnique 165 +typeNatAddTyFamNameKey = mkPreludeTyConUnique 166 +typeNatMulTyFamNameKey = mkPreludeTyConUnique 167 +typeNatExpTyFamNameKey = mkPreludeTyConUnique 168 +typeNatLeqTyFamNameKey = mkPreludeTyConUnique 169 +typeNatSubTyFamNameKey = mkPreludeTyConUnique 170 +typeSymbolCmpTyFamNameKey = mkPreludeTyConUnique 171 +typeNatCmpTyFamNameKey = mkPreludeTyConUnique 172 ntTyConKey:: Unique ntTyConKey = mkPreludeTyConUnique 174 diff --git a/compiler/typecheck/TcGenGenerics.hs b/compiler/typecheck/TcGenGenerics.hs index 6ea541c384..9a2b988b9f 100644 --- a/compiler/typecheck/TcGenGenerics.hs +++ b/compiler/typecheck/TcGenGenerics.hs @@ -30,6 +30,7 @@ import IfaceEnv ( newGlobalBinder ) import Name hiding ( varName ) import RdrName import BasicTypes +import TysPrim import TysWiredIn import PrelNames import InstEnv @@ -47,6 +48,7 @@ import FastString import Util import Control.Monad (mplus,forM) +import Data.Maybe (isJust) #include "HsVersions.h" @@ -278,14 +280,19 @@ canDoGenerics tc tc_args -- it relies on instantiating *polymorphic* sum and product types -- at the argument types of the constructors bad_con dc = if (any bad_arg_type (dataConOrigArgTys dc)) - then (NotValid (ppr dc <+> text "must not have unlifted or polymorphic arguments")) + then (NotValid (ppr dc <+> text + "must not have exotic unlifted or polymorphic arguments")) else (if (not (isVanillaDataCon dc)) then (NotValid (ppr dc <+> text "must be a vanilla data constructor")) else IsValid) -- Nor can we do the job if it's an existential data constructor, -- Nor if the args are polymorphic types (I don't think) - bad_arg_type ty = isUnLiftedType ty || not (isTauTy ty) + bad_arg_type ty = (isUnLiftedType ty && not (allowedUnliftedTy ty)) + || not (isTauTy ty) + +allowedUnliftedTy :: Type -> Bool +allowedUnliftedTy = isJust . unboxedRepRDRs mergeErrors :: [Validity] -> Validity mergeErrors [] = IsValid @@ -586,23 +593,29 @@ tc_mkRepTy :: -- Gen0_ or Gen1_, for Rep or Rep1 -> TcM Type tc_mkRepTy gk_ tycon metaDts = do - d1 <- tcLookupTyCon d1TyConName - c1 <- tcLookupTyCon c1TyConName - s1 <- tcLookupTyCon s1TyConName - nS1 <- tcLookupTyCon noSelTyConName - rec0 <- tcLookupTyCon rec0TyConName - rec1 <- tcLookupTyCon rec1TyConName - par1 <- tcLookupTyCon par1TyConName - u1 <- tcLookupTyCon u1TyConName - v1 <- tcLookupTyCon v1TyConName - plus <- tcLookupTyCon sumTyConName - times <- tcLookupTyCon prodTyConName - comp <- tcLookupTyCon compTyConName + d1 <- tcLookupTyCon d1TyConName + c1 <- tcLookupTyCon c1TyConName + s1 <- tcLookupTyCon s1TyConName + nS1 <- tcLookupTyCon noSelTyConName + rec0 <- tcLookupTyCon rec0TyConName + rec1 <- tcLookupTyCon rec1TyConName + par1 <- tcLookupTyCon par1TyConName + u1 <- tcLookupTyCon u1TyConName + v1 <- tcLookupTyCon v1TyConName + plus <- tcLookupTyCon sumTyConName + times <- tcLookupTyCon prodTyConName + comp <- tcLookupTyCon compTyConName + uAddr <- tcLookupTyCon uAddrTyConName + uChar <- tcLookupTyCon uCharTyConName + uDouble <- tcLookupTyCon uDoubleTyConName + uFloat <- tcLookupTyCon uFloatTyConName + uInt <- tcLookupTyCon uIntTyConName + uWord <- tcLookupTyCon uWordTyConName let mkSum' a b = mkTyConApp plus [a,b] mkProd a b = mkTyConApp times [a,b] mkComp a b = mkTyConApp comp [a,b] - mkRec0 a = mkTyConApp rec0 [a] + mkRec0 a = mkBoxTy uAddr uChar uDouble uFloat uInt uWord rec0 a mkRec1 a = mkTyConApp rec1 [a] mkPar1 = mkTyConTy par1 mkD a = mkTyConApp d1 [metaDTyCon, sumP (tyConDataCons a)] @@ -650,6 +663,28 @@ tc_mkRepTy gk_ tycon metaDts = return (mkD tycon) +-- Given the TyCons for each URec-related type synonym, check to see if the +-- given type is an unlifted type that generics understands. If so, return +-- its representation type. Otherwise, return Rec0. +-- See Note [Generics and unlifted types] +mkBoxTy :: TyCon -- UAddr + -> TyCon -- UChar + -> TyCon -- UDouble + -> TyCon -- UFloat + -> TyCon -- UInt + -> TyCon -- UWord + -> TyCon -- Rec0 + -> Type + -> Type +mkBoxTy uAddr uChar uDouble uFloat uInt uWord rec0 ty + | ty == addrPrimTy = mkTyConTy uAddr + | ty == charPrimTy = mkTyConTy uChar + | ty == doublePrimTy = mkTyConTy uDouble + | ty == floatPrimTy = mkTyConTy uFloat + | ty == intPrimTy = mkTyConTy uInt + | ty == wordPrimTy = mkTyConTy uWord + | otherwise = mkTyConApp rec0 [ty] + -------------------------------------------------------------------------------- -- Meta-information -------------------------------------------------------------------------------- @@ -781,22 +816,22 @@ mk1Sum gk_ us i n datacon = (from_alt, to_alt) from_alt = (nlConVarPat datacon_rdr datacon_vars, from_alt_rhs) from_alt_rhs = mkM1_E (genLR_E i n (mkProd_E gk_ us' datacon_varTys)) - to_alt = (mkM1_P (genLR_P i n (mkProd_P gk us' datacon_vars)), to_alt_rhs) - -- These M1s are meta-information for the datatype + to_alt = ( mkM1_P (genLR_P i n (mkProd_P gk us' datacon_varTys)) + , to_alt_rhs + ) -- These M1s are meta-information for the datatype to_alt_rhs = case gk_ of Gen0_DC -> nlHsVarApps datacon_rdr datacon_vars Gen1_DC argVar -> nlHsApps datacon_rdr $ map argTo datacon_varTys where argTo (var, ty) = converter ty `nlHsApp` nlHsVar var where converter = argTyFold argVar $ ArgTyAlg - {ata_rec0 = const $ nlHsVar unK1_RDR, + {ata_rec0 = nlHsVar . unboxRepRDR, ata_par1 = nlHsVar unPar1_RDR, ata_rec1 = const $ nlHsVar unRec1_RDR, ata_comp = \_ cnv -> (nlHsVar fmap_RDR `nlHsApp` cnv) `nlHsCompose` nlHsVar unComp1_RDR} - -- Generates the L1/R1 sum pattern genLR_P :: Int -> Int -> LPat RdrName -> LPat RdrName genLR_P i n p @@ -832,35 +867,54 @@ mkProd_E gk_ _ varTys = mkM1_E (foldBal prod appVars) prod a b = prodDataCon_RDR `nlHsApps` [a,b] wrapArg_E :: GenericKind_DC -> (RdrName, Type) -> LHsExpr RdrName -wrapArg_E Gen0_DC (var, _) = mkM1_E (k1DataCon_RDR `nlHsVarApps` [var]) +wrapArg_E Gen0_DC (var, ty) = mkM1_E $ + boxRepRDR ty `nlHsVarApps` [var] -- This M1 is meta-information for the selector -wrapArg_E (Gen1_DC argVar) (var, ty) = mkM1_E $ converter ty `nlHsApp` nlHsVar var +wrapArg_E (Gen1_DC argVar) (var, ty) = mkM1_E $ + converter ty `nlHsApp` nlHsVar var -- This M1 is meta-information for the selector where converter = argTyFold argVar $ ArgTyAlg - {ata_rec0 = const $ nlHsVar k1DataCon_RDR, + {ata_rec0 = nlHsVar . boxRepRDR, ata_par1 = nlHsVar par1DataCon_RDR, ata_rec1 = const $ nlHsVar rec1DataCon_RDR, ata_comp = \_ cnv -> nlHsVar comp1DataCon_RDR `nlHsCompose` (nlHsVar fmap_RDR `nlHsApp` cnv)} +boxRepRDR :: Type -> RdrName +boxRepRDR = maybe k1DataCon_RDR fst . unboxedRepRDRs + +unboxRepRDR :: Type -> RdrName +unboxRepRDR = maybe unK1_RDR snd . unboxedRepRDRs +-- Retrieve the RDRs associated with each URec data family instance +-- constructor. See Note [Generics and unlifted types] +unboxedRepRDRs :: Type -> Maybe (RdrName, RdrName) +unboxedRepRDRs ty + | ty == addrPrimTy = Just (uAddrDataCon_RDR, uAddrHash_RDR) + | ty == charPrimTy = Just (uCharDataCon_RDR, uCharHash_RDR) + | ty == doublePrimTy = Just (uDoubleDataCon_RDR, uDoubleHash_RDR) + | ty == floatPrimTy = Just (uFloatDataCon_RDR, uFloatHash_RDR) + | ty == intPrimTy = Just (uIntDataCon_RDR, uIntHash_RDR) + | ty == wordPrimTy = Just (uWordDataCon_RDR, uWordHash_RDR) + | otherwise = Nothing -- Build a product pattern -mkProd_P :: GenericKind -- Gen0 or Gen1 - -> US -- Base for unique names - -> [RdrName] -- List of variables to match - -> LPat RdrName -- Resulting product pattern -mkProd_P _ _ [] = mkM1_P (nlNullaryConPat u1DataCon_RDR) -mkProd_P gk _ vars = mkM1_P (foldBal prod appVars) +mkProd_P :: GenericKind -- Gen0 or Gen1 + -> US -- Base for unique names + -> [(RdrName, Type)] -- List of variables to match, + -- along with their types + -> LPat RdrName -- Resulting product pattern +mkProd_P _ _ [] = mkM1_P (nlNullaryConPat u1DataCon_RDR) +mkProd_P gk _ varTys = mkM1_P (foldBal prod appVars) -- These M1s are meta-information for the constructor where - appVars = map (wrapArg_P gk) vars + appVars = unzipWith (wrapArg_P gk) varTys prod a b = prodDataCon_RDR `nlConPat` [a,b] -wrapArg_P :: GenericKind -> RdrName -> LPat RdrName -wrapArg_P Gen0 v = mkM1_P (k1DataCon_RDR `nlConVarPat` [v]) +wrapArg_P :: GenericKind -> RdrName -> Type -> LPat RdrName +wrapArg_P Gen0 v ty = mkM1_P (boxRepRDR ty `nlConVarPat` [v]) -- This M1 is meta-information for the selector -wrapArg_P Gen1 v = m1DataCon_RDR `nlConVarPat` [v] +wrapArg_P Gen1 v _ = m1DataCon_RDR `nlConVarPat` [v] mkGenericLocal :: US -> RdrName mkGenericLocal u = mkVarUnqual (mkFastString ("g" ++ show u)) @@ -883,3 +937,17 @@ foldBal' _ x [] = x foldBal' _ _ [y] = y foldBal' op x l = let (a,b) = splitAt (length l `div` 2) l in foldBal' op x a `op` foldBal' op x b + +{- +Note [Generics and unlifted types] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Normally, all constants are marked with K1/Rec0. The exception to this rule is +when a data constructor has an unlifted argument (e.g., Int#, Char#, etc.). In +that case, we must use a data family instance of URec (from GHC.Generics) to +mark it. As a result, before we can generate K1 or unK1, we must first check +to see if the type is actually one of the unlifted types for which URec has a +data family instance; if so, we generate that instead. + +See wiki:Commentary/Compiler/GenericDeriving#Handlingunliftedtypes for more +details on why URec is implemented the way it is. +-} diff --git a/docs/users_guide/7.12.1-notes.rst b/docs/users_guide/7.12.1-notes.rst index 188daa9217..dc87c59c9e 100644 --- a/docs/users_guide/7.12.1-notes.rst +++ b/docs/users_guide/7.12.1-notes.rst @@ -74,6 +74,9 @@ Language - Due to a :ghc-ticket:`security issue <10826>`, Safe Haskell now forbids annotations in programs marked as ``-XSafe``. +- Generic instances can be derived for data types whose constructors have + arguments with certain unlifted types. See :ref:`generic-programming` for + more details. Compiler ~~~~~~~~ diff --git a/docs/users_guide/glasgow_exts.rst b/docs/users_guide/glasgow_exts.rst index e2dd28e9f7..bc9e0233f3 100644 --- a/docs/users_guide/glasgow_exts.rst +++ b/docs/users_guide/glasgow_exts.rst @@ -12004,6 +12004,48 @@ we show generic serialization: Typically this class will not be exported, as it only makes sense to have instances for the representation types. +Unlifted representation types +----------------------------- + +The data family ``URec`` is provided to enable generic programming over +datatypes with certain unlifted arguments. There are six instances corresponding +to common unlifted types: :: + + data family URec a p + + data instance URec (Ptr ()) p = UAddr { uAddr# :: Addr# } + data instance URec Char p = UChar { uChar# :: Char# } + data instance URec Double p = UDouble { uDouble# :: Double# } + data instance URec Int p = UInt { uInt# :: Int# } + data instance URec Float p = UFloat { uFloat# :: Float# } + data instance URec Word p = UWord { uWord# :: Word# } + +Six type synonyms are provided for convenience: :: + + type UAddr = URec (Ptr ()) + type UChar = URec Char + type UDouble = URec Double + type UFloat = URec Float + type UInt = URec Int + type UWord = URec Word + +As an example, this data declaration: :: + + data IntHash = IntHash Int# + deriving Generic + +results in the following ``Generic`` instance: :: + + instance Generic IntHash where + type Rep IntHash = + D1 D1IntHash + (C1 C1_0IntHash + (S1 NoSelector UInt)) + +A user could provide, for example, a ``GSerialize UInt`` instance so that a +``Serialize IntHash`` instance could be easily defined in terms of +``GSerialize``. + Generic defaults ---------------- diff --git a/libraries/base/GHC/Generics.hs b/libraries/base/GHC/Generics.hs index d98533b5b2..3e38930261 100644 --- a/libraries/base/GHC/Generics.hs +++ b/libraries/base/GHC/Generics.hs @@ -8,6 +8,7 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE PolyKinds #-} +{-# LANGUAGE MagicHash #-} ----------------------------------------------------------------------------- -- | @@ -532,6 +533,65 @@ module GHC.Generics ( -- @ -- newtype (':.:') f g p = 'Comp1' { 'unComp1' :: f (g p) } -- @ + +-- *** Representation of unlifted types +-- +-- | +-- +-- If one were to attempt to derive a Generic instance for a datatype with an +-- unlifted argument (for example, 'Int#'), one might expect the occurrence of +-- the 'Int#' argument to be marked with @'Rec0' 'Int#'@. This won't work, +-- though, since 'Int#' is of kind @#@ and 'Rec0' expects a type of kind @*@. +-- In fact, polymorphism over unlifted types is disallowed completely. +-- +-- One solution would be to represent an occurrence of 'Int#' with 'Rec0 Int' +-- instead. With this approach, however, the programmer has no way of knowing +-- whether the 'Int' is actually an 'Int#' in disguise. +-- +-- Instead of reusing 'Rec0', a separate data family 'URec' is used to mark +-- occurrences of common unlifted types: +-- +-- @ +-- data family URec a p +-- +-- data instance 'URec' ('Ptr' ()) p = 'UAddr' { 'uAddr#' :: 'Addr#' } +-- data instance 'URec' 'Char' p = 'UChar' { 'uChar#' :: 'Char#' } +-- data instance 'URec' 'Double' p = 'UDouble' { 'uDouble#' :: 'Double#' } +-- data instance 'URec' 'Int' p = 'UFloat' { 'uFloat#' :: 'Float#' } +-- data instance 'URec' 'Float' p = 'UInt' { 'uInt#' :: 'Int#' } +-- data instance 'URec' 'Word' p = 'UWord' { 'uWord#' :: 'Word#' } +-- @ +-- +-- Several type synonyms are provided for convenience: +-- +-- @ +-- type 'UAddr' = 'URec' ('Ptr' ()) +-- type 'UChar' = 'URec' 'Char' +-- type 'UDouble' = 'URec' 'Double' +-- type 'UFloat' = 'URec' 'Float' +-- type 'UInt' = 'URec' 'Int' +-- type 'UWord' = 'URec' 'Word' +-- @ +-- +-- The declaration +-- +-- @ +-- data IntHash = IntHash Int# +-- deriving 'Generic' +-- @ +-- +-- yields +-- +-- @ +-- instance 'Generic' IntHash where +-- type 'Rep' IntHash = +-- 'D1' D1IntHash +-- ('C1' C1_0IntHash +-- ('S1' 'NoSelector' 'UInt')) +-- @ +-- +-- Currently, only the six unlifted types listed above are generated, but this +-- may be extended to encompass more unlifted types in the future. #if 0 -- *** Limitations -- @@ -548,6 +608,11 @@ module GHC.Generics ( V1, U1(..), Par1(..), Rec1(..), K1(..), M1(..) , (:+:)(..), (:*:)(..), (:.:)(..) + -- ** Unboxed representation types + , URec(..) + , type UAddr, type UChar, type UDouble + , type UFloat, type UInt, type UWord + -- ** Synonyms for convenience , Rec0, Par0, R, P , D1, C1, S1, D, C, S @@ -562,6 +627,8 @@ module GHC.Generics ( ) where -- We use some base types +import GHC.Prim ( Addr#, Char#, Double#, Float#, Int#, Word# ) +import GHC.Ptr ( Ptr ) import GHC.Types import Data.Maybe ( Maybe(..) ) import Data.Either ( Either(..) ) @@ -614,6 +681,46 @@ infixr 7 :.: newtype (:.:) f (g :: * -> *) (p :: *) = Comp1 { unComp1 :: f (g p) } deriving (Eq, Ord, Read, Show, Generic) +-- | Constants of kind @#@ +data family URec (a :: *) (p :: *) + +-- | Used for marking occurrences of 'Addr#' +data instance URec (Ptr ()) p = UAddr { uAddr# :: Addr# } + deriving (Eq, Ord, Generic) + +-- | Used for marking occurrences of 'Char#' +data instance URec Char p = UChar { uChar# :: Char# } + deriving (Eq, Ord, Show, Generic) + +-- | Used for marking occurrences of 'Double#' +data instance URec Double p = UDouble { uDouble# :: Double# } + deriving (Eq, Ord, Show, Generic) + +-- | Used for marking occurrences of 'Float#' +data instance URec Float p = UFloat { uFloat# :: Float# } + deriving (Eq, Ord, Show, Generic) + +-- | Used for marking occurrences of 'Int#' +data instance URec Int p = UInt { uInt# :: Int# } + deriving (Eq, Ord, Show, Generic) + +-- | Used for marking occurrences of 'Word#' +data instance URec Word p = UWord { uWord# :: Word# } + deriving (Eq, Ord, Show, Generic) + +-- | Type synonym for 'URec': 'Addr#' +type UAddr = URec (Ptr ()) +-- | Type synonym for 'URec': 'Char#' +type UChar = URec Char +-- | Type synonym for 'URec': 'Double#' +type UDouble = URec Double +-- | Type synonym for 'URec': 'Float#' +type UFloat = URec Float +-- | Type synonym for 'URec': 'Int#' +type UInt = URec Int +-- | Type synonym for 'URec': 'Word#' +type UWord = URec Word + -- | Tag for K1: recursion (of kind *) data R -- | Tag for K1: parameters (other than the last) @@ -642,7 +749,6 @@ type C1 = M1 C -- | Type synonym for encoding meta-information for record selectors type S1 = M1 S - -- | Class for datatypes that represent datatypes class Datatype (d :: *) where -- | The name of the datatype (unqualified) diff --git a/libraries/base/changelog.md b/libraries/base/changelog.md index b40bfefe91..4874808d2d 100644 --- a/libraries/base/changelog.md +++ b/libraries/base/changelog.md @@ -62,6 +62,10 @@ super-class of `Monoid` in the future). These modules were provided by the `semigroups` package previously. (#10365) + * Add `URec`, `UAddr`, `UChar`, `UDouble`, `UFloat`, `UInt`, and `UWord` to + `GHC.Generics` as part of making GHC generics capable of handling + unlifted types (#10868) + ## 4.8.1.0 *Jul 2015* * Bundled with GHC 7.10.2 diff --git a/testsuite/tests/generics/GEq/GEq1.hs b/testsuite/tests/generics/GEq/GEq1.hs index 164535cddb..d6ca0b057e 100644 --- a/testsuite/tests/generics/GEq/GEq1.hs +++ b/testsuite/tests/generics/GEq/GEq1.hs @@ -1,7 +1,9 @@ -{-# LANGUAGE TypeOperators, DeriveGeneric, TypeFamilies, FlexibleInstances #-} +{-# LANGUAGE TypeOperators, DeriveGeneric, TypeFamilies, + FlexibleInstances, MagicHash #-} module Main where +import GHC.Exts import GHC.Generics hiding (C, D) import GEq1A @@ -20,6 +22,13 @@ data family F a b :: * -> * data instance F Int b c = F b Int c deriving Generic +data U a = U a Addr# Char# Double# Float# Int# Word# + deriving Generic + +data family UF a b :: * -> * +data instance UF Int b c = UF b c Addr# Char# Double# Float# Int# Word# + deriving Generic + -- Example values c0 = C0 c1 = C1 @@ -35,17 +44,27 @@ f1 :: F Int Float Char f1 = F 0.0 3 'h' f2 = F 0.0 4 'h' +u0 :: U Int +u0 = U 1 "1"# '1'# 1.0## 1.0# 1# 1## + +uf0 :: UF Int Int Int +uf0 = UF 2 2 "1"# '2'# 2.0## 2.0# 2# 2## + -- Generic instances instance GEq C instance (GEq a) => GEq (D a) instance (GEq a, GEq b) => GEq (a :**: b) instance (GEq b, GEq c) => GEq (F Int b c) +instance (GEq a) => GEq (U a) +instance (GEq b, GEq c) => GEq (UF Int b c) -- Tests -teq0 = geq c0 c1 -teq1 = geq d0 d1 -teq2 = geq d0 d0 -teq3 = geq p1 p1 -teq4 = geq f1 f2 +teq0 = geq c0 c1 +teq1 = geq d0 d1 +teq2 = geq d0 d0 +teq3 = geq p1 p1 +teq4 = geq f1 f2 +teq5 = geq u0 u0 +teq6 = geq uf0 uf0 -main = mapM_ print [teq0, teq1, teq2, teq3, teq4] +main = mapM_ print [teq0, teq1, teq2, teq3, teq4, teq5, teq6] diff --git a/testsuite/tests/generics/GEq/GEq1.stdout b/testsuite/tests/generics/GEq/GEq1.stdout index 3ce45b831d..e590e50cd2 100644 --- a/testsuite/tests/generics/GEq/GEq1.stdout +++ b/testsuite/tests/generics/GEq/GEq1.stdout @@ -3,3 +3,5 @@ False True True False +True +True diff --git a/testsuite/tests/generics/GEq/GEq1A.hs b/testsuite/tests/generics/GEq/GEq1A.hs index 7bdfbebe54..9a91e8040b 100644 --- a/testsuite/tests/generics/GEq/GEq1A.hs +++ b/testsuite/tests/generics/GEq/GEq1A.hs @@ -1,7 +1,9 @@ -{-# LANGUAGE TypeOperators, DefaultSignatures, FlexibleContexts, FlexibleInstances #-} +{-# LANGUAGE TypeOperators, DefaultSignatures, + FlexibleContexts, FlexibleInstances, MagicHash #-} module GEq1A where +import GHC.Exts import GHC.Generics class GEq' f where @@ -26,13 +28,25 @@ instance (GEq' a, GEq' b) => GEq' (a :+: b) where instance (GEq' a, GEq' b) => GEq' (a :*: b) where geq' (a1 :*: b1) (a2 :*: b2) = geq' a1 a2 && geq' b1 b2 - -class GEq a where +-- Unboxed types +instance GEq' UAddr where + geq' (UAddr a1) (UAddr a2) = isTrue# (eqAddr# a1 a2) +instance GEq' UChar where + geq' (UChar c1) (UChar c2) = isTrue# (eqChar# c1 c2) +instance GEq' UDouble where + geq' (UDouble d1) (UDouble d2) = isTrue# (d1 ==## d2) +instance GEq' UFloat where + geq' (UFloat f1) (UFloat f2) = isTrue# (eqFloat# f1 f2) +instance GEq' UInt where + geq' (UInt i1) (UInt i2) = isTrue# (i1 ==# i2) +instance GEq' UWord where + geq' (UWord w1) (UWord w2) = isTrue# (eqWord# w1 w2) + +class GEq a where geq :: a -> a -> Bool default geq :: (Generic a, GEq' (Rep a)) => a -> a -> Bool geq x y = geq' (from x) (from y) - -- Base types instances (ad-hoc) instance GEq Char where geq = (==) instance GEq Int where geq = (==) diff --git a/testsuite/tests/generics/GShow/GShow.hs b/testsuite/tests/generics/GShow/GShow.hs index 3c8f2591ef..6cdda282d8 100644 --- a/testsuite/tests/generics/GShow/GShow.hs +++ b/testsuite/tests/generics/GShow/GShow.hs @@ -5,13 +5,14 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE IncoherentInstances #-} -- :-/ {-# LANGUAGE DefaultSignatures #-} +{-# LANGUAGE MagicHash #-} module GShow ( -- * Generic show class GShow(..) ) where - +import GHC.Exts import GHC.Generics -------------------------------------------------------------------------------- @@ -36,10 +37,10 @@ instance (GShow c) => GShow' (K1 i c) where -- No instances for P or Rec because gshow is only applicable to types of kind * instance (GShow' a, Constructor c) => GShow' (M1 C c a) where - gshowsPrec' _ n c@(M1 x) = + gshowsPrec' _ n c@(M1 x) = case (fixity, conIsTuple c) of - (Prefix,False) -> showParen (n > 10 && not (isNullary x)) - ( showString (conName c) + (Prefix,False) -> showParen (n > 10 && not (isNullary x)) + ( showString (conName c) . if (isNullary x) then id else showChar ' ' . showBraces t (gshowsPrec' t 10 x)) (Prefix,True) -> showParen (n > 10) (showBraces t (gshowsPrec' t 10 x)) @@ -58,7 +59,7 @@ instance (GShow' a, Constructor c) => GShow' (M1 C c a) where conIsTuple c = case conName c of ('(':',':_) -> True otherwise -> False - + isNullary (M1 x) = isNullary x instance (Selector s, GShow' a) => GShow' (M1 S s a) where @@ -85,12 +86,23 @@ instance (GShow' a, GShow' b) => GShow' (a :*: b) where gshowsPrec' t n a . showChar ',' . gshowsPrec' t n b gshowsPrec' t@Pref n (a :*: b) = gshowsPrec' t (n+1) a . showChar ' ' . gshowsPrec' t (n+1) b - + -- If we have a product then it is not a nullary constructor isNullary _ = False - -class GShow a where +-- Unboxed instances +instance GShow' UChar where + gshowsPrec' _ _ (UChar c) = showsPrec 0 (C# c) . showChar '#' +instance GShow' UDouble where + gshowsPrec' _ _ (UDouble d) = showsPrec 0 (D# d) . showString "##" +instance GShow' UFloat where + gshowsPrec' _ _ (UFloat f) = showsPrec 0 (F# f) . showChar '#' +instance GShow' UInt where + gshowsPrec' _ _ (UInt i) = showsPrec 0 (I# i) . showChar '#' +instance GShow' UWord where + gshowsPrec' _ _ (UWord w) = showsPrec 0 (W# w) . showString "##" + +class GShow a where gshowsPrec :: Int -> a -> ShowS default gshowsPrec :: (Generic a, GShow' (Rep a)) => Int -> a -> ShowS gshowsPrec n = gshowsPrec' Pref n . from @@ -100,13 +112,15 @@ class GShow a where gshow :: a -> String gshow x = gshows x "" - + -- Base types instances instance GShow Char where gshowsPrec = showsPrec +instance GShow Double where gshowsPrec = showsPrec instance GShow Int where gshowsPrec = showsPrec instance GShow Float where gshowsPrec = showsPrec instance GShow String where gshowsPrec = showsPrec +instance GShow Word where gshowsPrec = showsPrec instance GShow Bool where gshowsPrec = showsPrec intersperse :: a -> [a] -> [a] diff --git a/testsuite/tests/generics/GShow/GShow1.stdout b/testsuite/tests/generics/GShow/GShow1.stdout index 6109e446a5..71e1299245 100644 --- a/testsuite/tests/generics/GShow/GShow1.stdout +++ b/testsuite/tests/generics/GShow/GShow1.stdout @@ -1,3 +1,4 @@ D0 D1 {d11 = Just 'p', d12 = D0} D1 {d11 = (3,0.14), d12 = D0} +U (1) ('1'#) (-1.0##) (-1.0#) (-1#) (1##) diff --git a/testsuite/tests/generics/GShow/Main.hs b/testsuite/tests/generics/GShow/Main.hs index 81768ed647..952602e54d 100644 --- a/testsuite/tests/generics/GShow/Main.hs +++ b/testsuite/tests/generics/GShow/Main.hs @@ -1,12 +1,14 @@ -{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveGeneric, MagicHash #-} module Main where +import GHC.Exts import GHC.Generics hiding (C, D) import GShow -- We should be able to generate a generic representation for these types data D a = D0 | D1 { d11 :: a, d12 :: (D a) } deriving Generic +data U a = U a Char# Double# Float# Int# Word# deriving Generic -- Example values d0 :: D Char @@ -16,8 +18,12 @@ d1 = D1 (Just 'p') D0 d2 :: D (Int,Float) d2 = D1 (3,0.14) D0 +u0 :: U Int +u0 = U 1 '1'# -1.0## -1.0# -1# 1## + -- Generic instances instance (GShow a) => GShow (D a) +instance (GShow a) => GShow (U a) -- Tests -main = mapM_ putStrLn [gshow d0, gshow d1, gshow d2] +main = mapM_ putStrLn [gshow d0, gshow d1, gshow d2, gshow u0] diff --git a/testsuite/tests/generics/T8468.stderr b/testsuite/tests/generics/T8468.stderr index 62536cec69..aaf68b9d5a 100644 --- a/testsuite/tests/generics/T8468.stderr +++ b/testsuite/tests/generics/T8468.stderr @@ -1,5 +1,5 @@ T8468.hs:6:42: Can't make a derived instance of ‘Generic1 Array’: - Array must not have unlifted or polymorphic arguments + Array must not have exotic unlifted or polymorphic arguments In the data declaration for ‘Array’ |