diff options
author | RyanGlScott <ryan.gl.scott@gmail.com> | 2015-09-21 21:50:55 -0500 |
---|---|---|
committer | Austin Seipp <austin@well-typed.com> | 2015-09-21 21:50:56 -0500 |
commit | 089b72f524a6a7564346baca9595fcd07081ec40 (patch) | |
tree | 2354366bd18fe44ddbcbe4953e172f345a374b66 /compiler | |
parent | d4d34a73aacc225a8f28d7138137bf548c9e51cc (diff) | |
download | haskell-089b72f524a6a7564346baca9595fcd07081ec40.tar.gz |
DeriveLift extension (#1830)
Summary:
This implements -XDeriveLift, which allows for automatic derivation
of the Lift class from template-haskell. The implementation is based
off of Ian Lynagh's th-lift library
(http://hackage.haskell.org/package/th-lift).
Test Plan: ./validate
Reviewers: hvr, simonpj, bgamari, goldfire, austin
Reviewed By: goldfire, austin
Subscribers: osa1, thomie
Differential Revision: https://phabricator.haskell.org/D1168
GHC Trac Issues: #1830
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/main/DynFlags.hs | 2 | ||||
-rw-r--r-- | compiler/prelude/PrelNames.hs | 9 | ||||
-rw-r--r-- | compiler/prelude/THNames.hs | 49 | ||||
-rw-r--r-- | compiler/typecheck/TcDeriv.hs | 13 | ||||
-rw-r--r-- | compiler/typecheck/TcGenDeriv.hs | 127 |
5 files changed, 191 insertions, 9 deletions
diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs index 802f264e36..0978c1132c 100644 --- a/compiler/main/DynFlags.hs +++ b/compiler/main/DynFlags.hs @@ -613,6 +613,7 @@ data ExtensionFlag | Opt_DeriveGeneric -- Allow deriving Generic/1 | Opt_DefaultSignatures -- Allow extra signatures for defmeths | Opt_DeriveAnyClass -- Allow deriving any class + | Opt_DeriveLift -- Allow deriving Lift | Opt_TypeSynonymInstances | Opt_FlexibleContexts @@ -3133,6 +3134,7 @@ xFlags = [ flagSpec "DeriveFoldable" Opt_DeriveFoldable, flagSpec "DeriveFunctor" Opt_DeriveFunctor, flagSpec "DeriveGeneric" Opt_DeriveGeneric, + flagSpec "DeriveLift" Opt_DeriveLift, flagSpec "DeriveTraversable" Opt_DeriveTraversable, flagSpec "DisambiguateRecordFields" Opt_DisambiguateRecordFields, flagSpec "DoAndIfThenElse" Opt_DoAndIfThenElse, diff --git a/compiler/prelude/PrelNames.hs b/compiler/prelude/PrelNames.hs index 1684a2f3e0..a6eb834641 100644 --- a/compiler/prelude/PrelNames.hs +++ b/compiler/prelude/PrelNames.hs @@ -599,6 +599,11 @@ minus_RDR = nameRdrName minusName times_RDR = varQual_RDR gHC_NUM (fsLit "*") plus_RDR = varQual_RDR gHC_NUM (fsLit "+") +toInteger_RDR, toRational_RDR, fromIntegral_RDR :: RdrName +toInteger_RDR = nameRdrName toIntegerName +toRational_RDR = nameRdrName toRationalName +fromIntegral_RDR = nameRdrName fromIntegralName + fromString_RDR :: RdrName fromString_RDR = nameRdrName fromStringName @@ -1305,6 +1310,10 @@ knownSymbolClassNameKey = mkPreludeClassUnique 43 ghciIoClassKey :: Unique ghciIoClassKey = mkPreludeClassUnique 44 +---------------- Template Haskell ------------------- +-- USES ClassUniques 200-299 +----------------------------------------------------- + {- ************************************************************************ * * diff --git a/compiler/prelude/THNames.hs b/compiler/prelude/THNames.hs index 9367d4b85a..d3deb49ba2 100644 --- a/compiler/prelude/THNames.hs +++ b/compiler/prelude/THNames.hs @@ -9,7 +9,8 @@ module THNames where import PrelNames( mk_known_key_name ) import Module( Module, mkModuleNameFS, mkModule, thPackageKey ) import Name( Name ) -import OccName( tcName, dataName, varName ) +import OccName( tcName, clsName, dataName, varName ) +import RdrName( RdrName, nameRdrName ) import Unique import FastString @@ -122,6 +123,9 @@ templateHaskellNames = [ -- AnnTarget valueAnnotationName, typeAnnotationName, moduleAnnotationName, + -- The type classes + liftClassName, + -- And the tycons qTyConName, nameTyConName, patTyConName, fieldPatTyConName, matchQTyConName, clauseQTyConName, expQTyConName, fieldExpTyConName, predTyConName, @@ -143,15 +147,19 @@ qqLib = mkTHModule (fsLit "Language.Haskell.TH.Quote") mkTHModule :: FastString -> Module mkTHModule m = mkModule thPackageKey (mkModuleNameFS m) -libFun, libTc, thFun, thTc, thCon, qqFun :: FastString -> Unique -> Name +libFun, libTc, thFun, thTc, thCls, thCon, qqFun :: FastString -> Unique -> Name libFun = mk_known_key_name OccName.varName thLib libTc = mk_known_key_name OccName.tcName thLib thFun = mk_known_key_name OccName.varName thSyn thTc = mk_known_key_name OccName.tcName thSyn +thCls = mk_known_key_name OccName.clsName thSyn thCon = mk_known_key_name OccName.dataName thSyn qqFun = mk_known_key_name OccName.varName qqLib -------------------- TH.Syntax ----------------------- +liftClassName :: Name +liftClassName = thCls (fsLit "Lift") liftClassKey + qTyConName, nameTyConName, fieldExpTyConName, patTyConName, fieldPatTyConName, expTyConName, decTyConName, typeTyConName, tyVarBndrTyConName, matchTyConName, clauseTyConName, funDepTyConName, @@ -512,6 +520,12 @@ quotePatName = qqFun (fsLit "quotePat") quotePatKey quoteDecName = qqFun (fsLit "quoteDec") quoteDecKey quoteTypeName = qqFun (fsLit "quoteType") quoteTypeKey +-- ClassUniques available: 200-299 +-- Check in PrelNames if you want to change this + +liftClassKey :: Unique +liftClassKey = mkPreludeClassUnique 200 + -- TyConUniques available: 200-299 -- Check in PrelNames if you want to change this @@ -873,3 +887,34 @@ valueAnnotationIdKey, typeAnnotationIdKey, moduleAnnotationIdKey :: Unique valueAnnotationIdKey = mkPreludeMiscIdUnique 490 typeAnnotationIdKey = mkPreludeMiscIdUnique 491 moduleAnnotationIdKey = mkPreludeMiscIdUnique 492 + +{- +************************************************************************ +* * + RdrNames +* * +************************************************************************ +-} + +lift_RDR, mkNameG_dRDR, mkNameG_vRDR :: RdrName +lift_RDR = nameRdrName liftName +mkNameG_dRDR = nameRdrName mkNameG_dName +mkNameG_vRDR = nameRdrName mkNameG_vName + +-- data Exp = ... +conE_RDR, litE_RDR, appE_RDR, infixApp_RDR :: RdrName +conE_RDR = nameRdrName conEName +litE_RDR = nameRdrName litEName +appE_RDR = nameRdrName appEName +infixApp_RDR = nameRdrName infixAppName + +-- data Lit = ... +stringL_RDR, intPrimL_RDR, wordPrimL_RDR, floatPrimL_RDR, + doublePrimL_RDR, stringPrimL_RDR, charPrimL_RDR :: RdrName +stringL_RDR = nameRdrName stringLName +intPrimL_RDR = nameRdrName intPrimLName +wordPrimL_RDR = nameRdrName wordPrimLName +floatPrimL_RDR = nameRdrName floatPrimLName +doublePrimL_RDR = nameRdrName doublePrimLName +stringPrimL_RDR = nameRdrName stringPrimLName +charPrimL_RDR = nameRdrName charPrimLName diff --git a/compiler/typecheck/TcDeriv.hs b/compiler/typecheck/TcDeriv.hs index d76302fc37..58aeb2edc9 100644 --- a/compiler/typecheck/TcDeriv.hs +++ b/compiler/typecheck/TcDeriv.hs @@ -54,6 +54,7 @@ import TcType import Var import VarSet import PrelNames +import THNames ( liftClassKey ) import SrcLoc import Util import Outputable @@ -1170,6 +1171,9 @@ sideConditions mtheta cls | cls_key == gen1ClassKey = Just (checkFlag Opt_DeriveGeneric `andCond` cond_vanilla `andCond` cond_Representable1Ok) + | cls_key == liftClassKey = Just (checkFlag Opt_DeriveLift `andCond` + cond_vanilla `andCond` + cond_args cls) | otherwise = Nothing where cls_key = getUnique cls @@ -1257,6 +1261,7 @@ cond_args cls (_, tc, _) | cls_key == eqClassKey = check_in arg_ty ordOpTbl | cls_key == ordClassKey = check_in arg_ty ordOpTbl | cls_key == showClassKey = check_in arg_ty boxConTbl + | cls_key == liftClassKey = check_in arg_ty litConTbl | otherwise = False -- Read, Ix etc check_in :: Type -> [(Type,a)] -> Bool @@ -1355,20 +1360,20 @@ std_class_via_coercible :: Class -> Bool -- because giving so gives the same results as generating the boilerplate std_class_via_coercible clas = classKey clas `elem` [eqClassKey, ordClassKey, ixClassKey, boundedClassKey] - -- Not Read/Show because they respect the type + -- Not Read/Show/Lift because they respect the type -- Not Enum, because newtypes are never in Enum non_coercible_class :: Class -> Bool --- *Never* derive Read, Show, Typeable, Data, Generic, Generic1 by Coercible, --- even with -XGeneralizedNewtypeDeriving +-- *Never* derive Read, Show, Typeable, Data, Generic, Generic1, Lift +-- by Coercible, even with -XGeneralizedNewtypeDeriving -- Also, avoid Traversable, as the Coercible-derived instance and the "normal"-derived -- instance behave differently if there's a non-lawful Applicative out there. -- Besides, with roles, Coercible-deriving Traversable is ill-roled. non_coercible_class cls = classKey cls `elem` ([ readClassKey, showClassKey, dataClassKey , genClassKey, gen1ClassKey, typeableClassKey - , traversableClassKey ]) + , traversableClassKey, liftClassKey ]) new_dfun_name :: Class -> TyCon -> TcM Name new_dfun_name clas tycon -- Just a simple wrapper diff --git a/compiler/typecheck/TcGenDeriv.hs b/compiler/typecheck/TcGenDeriv.hs index 5f6a021a4c..b60fc8c032 100644 --- a/compiler/typecheck/TcGenDeriv.hs +++ b/compiler/typecheck/TcGenDeriv.hs @@ -25,7 +25,7 @@ module TcGenDeriv ( mkCoerceClassMethEqn, gen_Newtype_binds, genAuxBinds, - ordOpTbl, boxConTbl, + ordOpTbl, boxConTbl, litConTbl, mkRdrFunBind ) where @@ -44,6 +44,9 @@ import PrelInfo import FamInstEnv( FamInst ) import MkCore ( eRROR_ID ) import PrelNames hiding (error_RDR) +import THNames +import Module ( moduleName, moduleNameString + , modulePackageKey, packageKeyString ) import MkId ( coerceId ) import PrimOp import SrcLoc @@ -130,8 +133,8 @@ genDerivedBinds dflags fix_env clas loc tycon , (dataClassKey, gen_Data_binds dflags) , (functorClassKey, gen_Functor_binds) , (foldableClassKey, gen_Foldable_binds) - , (traversableClassKey, gen_Traversable_binds) ] - + , (traversableClassKey, gen_Traversable_binds) + , (liftClassKey, gen_Lift_binds) ] -- Nothing: we can (try to) derive it via Generics -- Just s: we can't, reason s @@ -1887,6 +1890,90 @@ gen_Traversable_binds loc tycon {- ************************************************************************ * * + Lift instances +* * +************************************************************************ + +Example: + + data Foo a = Foo a | a :^: a deriving Lift + + ==> + + instance (Lift a) => Lift (Foo a) where + lift (Foo a) + = appE + (conE + (mkNameG_d "package-name" "ModuleName" "Foo")) + (lift a) + lift (u :^: v) + = infixApp + (lift u) + (conE + (mkNameG_d "package-name" "ModuleName" ":^:")) + (lift v) + +Note that (mkNameG_d "package-name" "ModuleName" "Foo") is equivalent to what +'Foo would be when using the -XTemplateHaskell extension. To make sure that +-XDeriveLift can be used on stage-1 compilers, however, we expliticly invoke +makeG_d. +-} + +gen_Lift_binds :: SrcSpan -> TyCon -> (LHsBinds RdrName, BagDerivStuff) +gen_Lift_binds loc tycon + | null data_cons = (unitBag (L loc $ mkFunBind (L loc lift_RDR) + [mkMatch [nlWildPat] errorMsg_Expr emptyLocalBinds]) + , emptyBag) + | otherwise = (unitBag lift_bind, emptyBag) + where + errorMsg_Expr = nlHsVar error_RDR `nlHsApp` nlHsLit + (mkHsString $ "Can't lift value of empty datatype " ++ tycon_str) + + lift_bind = mk_FunBind loc lift_RDR (map pats_etc data_cons) + data_cons = tyConDataCons tycon + tycon_str = occNameString . nameOccName . tyConName $ tycon + + pats_etc data_con + = ([con_pat], lift_Expr) + where + con_pat = nlConVarPat data_con_RDR as_needed + data_con_RDR = getRdrName data_con + con_arity = dataConSourceArity data_con + as_needed = take con_arity as_RDRs + lifted_as = zipWithEqual "mk_lift_app" mk_lift_app + tys_needed as_needed + tycon_name = tyConName tycon + is_infix = dataConIsInfix data_con + tys_needed = dataConOrigArgTys data_con + + mk_lift_app ty a + | not (isUnLiftedType ty) = nlHsApp (nlHsVar lift_RDR) + (nlHsVar a) + | otherwise = nlHsApp (nlHsVar litE_RDR) + (primLitOp (mkBoxExp (nlHsVar a))) + where (primLitOp, mkBoxExp) = primLitOps "Lift" tycon ty + + pkg_name = packageKeyString . modulePackageKey + . nameModule $ tycon_name + mod_name = moduleNameString . moduleName . nameModule $ tycon_name + con_name = occNameString . nameOccName . dataConName $ data_con + + conE_Expr = nlHsApp (nlHsVar conE_RDR) + (nlHsApps mkNameG_dRDR + (map (nlHsLit . mkHsString) + [pkg_name, mod_name, con_name])) + + lift_Expr + | is_infix = nlHsApps infixApp_RDR [a1, conE_Expr, a2] + | otherwise = foldl mk_appE_app conE_Expr lifted_as + (a1:a2:_) = lifted_as + +mk_appE_app :: LHsExpr RdrName -> LHsExpr RdrName -> LHsExpr RdrName +mk_appE_app a b = nlHsApps appE_RDR [a, b] + +{- +************************************************************************ +* * Newtype-deriving instances * * ************************************************************************ @@ -2106,6 +2193,20 @@ primOrdOps :: String -- The class involved -- See Note [Deriving and unboxed types] in TcDeriv primOrdOps str tycon ty = assoc_ty_id str tycon ordOpTbl ty +primLitOps :: String -- The class involved + -> TyCon -- The tycon involved + -> Type -- The type + -> ( LHsExpr RdrName -> LHsExpr RdrName -- Constructs a Q Exp value + , LHsExpr RdrName -> LHsExpr RdrName -- Constructs a boxed value + ) +primLitOps str tycon ty = ( assoc_ty_id str tycon litConTbl ty + , \v -> nlHsVar boxRDR `nlHsApp` v + ) + where + boxRDR + | ty == addrPrimTy = unpackCString_RDR + | otherwise = assoc_ty_id str tycon boxConTbl ty + ordOpTbl :: [(Type, (RdrName, RdrName, RdrName, RdrName, RdrName))] ordOpTbl = [(charPrimTy , (ltChar_RDR , leChar_RDR , eqChar_RDR , geChar_RDR , gtChar_RDR )) @@ -2134,6 +2235,26 @@ postfixModTbl ,(doublePrimTy, "##") ] +litConTbl :: [(Type, LHsExpr RdrName -> LHsExpr RdrName)] +litConTbl + = [(charPrimTy , nlHsApp (nlHsVar charPrimL_RDR)) + ,(intPrimTy , nlHsApp (nlHsVar intPrimL_RDR) + . nlHsApp (nlHsVar toInteger_RDR)) + ,(wordPrimTy , nlHsApp (nlHsVar wordPrimL_RDR) + . nlHsApp (nlHsVar toInteger_RDR)) + ,(addrPrimTy , nlHsApp (nlHsVar stringPrimL_RDR) + . nlHsApp (nlHsApp + (nlHsVar map_RDR) + (compose_RDR `nlHsApps` + [ nlHsVar fromIntegral_RDR + , nlHsVar fromEnum_RDR + ]))) + ,(floatPrimTy , nlHsApp (nlHsVar floatPrimL_RDR) + . nlHsApp (nlHsVar toRational_RDR)) + ,(doublePrimTy, nlHsApp (nlHsVar doublePrimL_RDR) + . nlHsApp (nlHsVar toRational_RDR)) + ] + -- | Lookup `Type` in an association list. assoc_ty_id :: String -- The class involved -> TyCon -- The tycon involved |