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 | |
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
-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 | ||||
-rw-r--r-- | docs/users_guide/7.12.1-notes.xml | 7 | ||||
-rw-r--r-- | docs/users_guide/flags.xml | 6 | ||||
-rw-r--r-- | docs/users_guide/glasgow_exts.xml | 149 | ||||
-rw-r--r-- | libraries/template-haskell/Language/Haskell/TH/Syntax.hs | 23 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_compile/T1830.hs | 6 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_compile/all.T | 1 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_fail/T1830.hs | 5 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_fail/T1830.stderr | 5 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_fail/all.T | 1 | ||||
-rw-r--r-- | testsuite/tests/driver/T4437.hs | 3 | ||||
-rw-r--r-- | testsuite/tests/th/T1830.hs | 15 | ||||
-rw-r--r-- | testsuite/tests/th/T1830.stdout | 7 | ||||
-rw-r--r-- | testsuite/tests/th/T1830a.hs | 47 | ||||
-rw-r--r-- | testsuite/tests/th/all.T | 4 |
19 files changed, 469 insertions, 10 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 diff --git a/docs/users_guide/7.12.1-notes.xml b/docs/users_guide/7.12.1-notes.xml index d1310869fa..b23e3d8e2e 100644 --- a/docs/users_guide/7.12.1-notes.xml +++ b/docs/users_guide/7.12.1-notes.xml @@ -223,6 +223,13 @@ <literal>$(...)</literal>. This behavior has been preserved under the new implementation, and is now recognized and documented in <xref linkend="th-syntax"/>. + </para> + </listitem> + <listitem> + <para> + The <literal>Lift</literal> class is now derivable via + the <option>-XDeriveLift</option> extension. See + <xref linkend="deriving-lift"/> for more information. </para> </listitem> </itemizedlist> diff --git a/docs/users_guide/flags.xml b/docs/users_guide/flags.xml index c357f25a20..7bf8246b75 100644 --- a/docs/users_guide/flags.xml +++ b/docs/users_guide/flags.xml @@ -905,6 +905,12 @@ <entry>7.2.1</entry> </row> <row> + <entry><option>-XDeriveLift</option></entry> + <entry>Enable <link linkend="deriving-lift">deriving for the Lift class</link>.</entry> + <entry>dynamic</entry> + <entry><option>-XNoDeriveLift</option></entry> + </row> + <row> <entry><option>-XDeriveTraversable</option></entry> <entry>Enable <link linkend="deriving-extra">deriving for the Traversable class</link>. Implies <option>-XDeriveFunctor</option> and <option>-XDeriveFoldable</option>.</entry> diff --git a/docs/users_guide/glasgow_exts.xml b/docs/users_guide/glasgow_exts.xml index 7aaf1a80a6..7554c4d064 100644 --- a/docs/users_guide/glasgow_exts.xml +++ b/docs/users_guide/glasgow_exts.xml @@ -4222,6 +4222,13 @@ instance dictates the instances of <literal>Functor</literal> and <option>-XDeriveFunctor</option> and <option>-XDeriveFoldable</option>. See <xref linkend="deriving-traversable"/>. </para></listitem> + +<listitem><para> With <option>-XDeriveLift</option>, you can derive instances +of the class <literal>Lift</literal>, defined in the +<literal>Language.Haskell.TH.Syntax</literal> module of the +<literal>template-haskell</literal> package. +See <xref linkend="deriving-lift"/>. +</para></listitem> </itemizedlist> You can also use a standalone deriving declaration instead (see <xref linkend="stand-alone-deriving"/>). @@ -4546,6 +4553,84 @@ instance Typeable "Hello" -- Type-level symbols </sect2> +<sect2 id="deriving-lift"> +<title>Deriving <literal>Lift</literal> instances</title> + +<para>The class <literal>Lift</literal>, unlike other derivable classes, lives +in <literal>template-haskell</literal> instead of <literal>base</literal>. +Having a data type be an instance of <literal>Lift</literal> permits its values +to be promoted to Template Haskell expressions (of type +<literal>ExpQ</literal>), which can then be spliced into Haskell source code. +</para> + +<para>Here is an example of how one can derive <literal>Lift</literal>: + +<programlisting> +{-# LANGUAGE DeriveLift #-} +module Bar where + +import Language.Haskell.TH.Syntax + +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" "Bar" "Foo")) + (lift a) + lift (u :^: v) + = infixApp + (lift u) + (conE + (mkNameG_d "package-name" "Bar" ":^:")) + (lift v) +-} + +----- +{-# LANGUAGE TemplateHaskell #-} +module Baz where + +import Bar +import Language.Haskell.TH.Lift + +foo :: Foo String +foo = $(lift $ Foo "foo") + +fooExp :: Lift a => Foo a -> Q Exp +fooExp f = [| f |] +</programlisting> + +<option>-XDeriveLift</option> also works for certain unboxed types +(<literal>Addr#</literal>, <literal>Char#</literal>, +<literal>Double#</literal>, <literal>Float#</literal>, +<literal>Int#</literal>, and <literal>Word#</literal>): + +<programlisting> +{-# LANGUAGE DeriveLift, MagicHash #-} +module Unboxed where + +import GHC.Exts +import Language.Haskell.TH.Syntax + +data IntHash = IntHash Int# deriving Lift + +{- +instance Lift IntHash where + lift (IntHash i) + = appE + (conE + (mkNameG_d "package-name" "Unboxed" "IntHash")) + (litE + (intPrimL (toInteger (I# i)))) +-} +</programlisting> + +</para> + +</sect2> + <sect2 id="newtype-deriving"> <title>Generalised derived instances for newtypes</title> @@ -10042,6 +10127,70 @@ Wiki page</ulink>. </para> </listitem> + <listitem> + <para> + It is possible for a splice to expand to an expression that contain + names which are not in scope at the site of the splice. As an + example, consider the following code: + +<programlisting> +module Bar where + +import Language.Haskell.TH + +add1 :: Int -> Q Exp +add1 x = [| x + 1 |] +</programlisting> + + Now consider a splice using <literal>add1</literal> in a separate + module: + +<programlisting> +module Foo where + +import Bar + +two :: Int +two = $(add1 1) +</programlisting> + + Template Haskell cannot know what the argument to + <literal>add1</literal> will be at the function's definition site, so + a lifting mechanism is used to promote <literal>x</literal> into a + value of type <literal>Q Exp</literal>. This functionality is exposed + to the user as the <literal>Lift</literal> typeclass in the + <literal>Language.Haskell.TH.Syntax</literal> module. If a type has a + <literal>Lift</literal> instance, then any of its values can be + lifted to a Template Haskell expression: + +<programlisting> +class Lift t where + lift :: t -> Q Exp +</programlisting> + + In general, if GHC sees an expression within Oxford brackets (e.g., + <literal>[| foo bar |]</literal>, then GHC looks up each name within + the brackets. If a name is global (e.g., suppose + <literal>foo</literal> comes from an import or a top-level + declaration), then the fully qualified name is used directly in the + quotation. If the name is local (e.g., suppose <literal>bar</literal> + is bound locally in the function definition + <literal>mkFoo bar = [| foo bar |]</literal>), then GHC uses + <literal>lift</literal> on it (so GHC pretends + <literal>[| foo bar |]</literal> actually contains + <literal>[| foo $(lift bar) |]</literal>). Local names, which are not + in scope at splice locations, are actually evaluated when the + quotation is processed. + + The <literal>template-haskell</literal> library provides + <literal>Lift</literal> instances for many common data types. + Furthermore, it is possible to derive <literal>Lift</literal> + instances automatically by using the <option>-XDeriveLift</option> + language extension. See <xref linkend="deriving-lift" /> for more + information. + </para> + </listitem> + <listitem><para> You may omit the <literal>$(...)</literal> in a top-level declaration splice. Simply writing an expression (rather than a declaration) implies a splice. For example, you can write <programlisting> diff --git a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs index 48f3f96cbf..b64dfffb93 100644 --- a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs +++ b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs @@ -470,7 +470,30 @@ sequenceQ = sequence -- ----------------------------------------------------- +-- | A 'Lift' instance can have any of its values turned into a Template +-- Haskell expression. This is needed when a value used within a Template +-- Haskell quotation is bound outside the Oxford brackets (@[| ... |]@) but not +-- at the top level. As an example: +-- +-- > add1 :: Int -> Q Exp +-- > add1 x = [| x + 1 |] +-- +-- Template Haskell has no way of knowing what value @x@ will take on at +-- splice-time, so it requires the type of @x@ to be an instance of 'Lift'. +-- +-- 'Lift' instances can be derived automatically by use of the @-XDeriveLift@ +-- GHC language extension: +-- +-- > {-# LANGUAGE DeriveLift #-} +-- > module Foo where +-- > +-- > import Language.Haskell.TH.Syntax +-- > +-- > data Bar a = Bar1 a (Bar a) | Bar2 String +-- > deriving Lift class Lift t where + -- | Turn a value into a Template Haskell expression, suitable for use in + -- a splice. lift :: t -> Q Exp default lift :: Data t => t -> Q Exp lift = liftData diff --git a/testsuite/tests/deriving/should_compile/T1830.hs b/testsuite/tests/deriving/should_compile/T1830.hs new file mode 100644 index 0000000000..edaff7b546 --- /dev/null +++ b/testsuite/tests/deriving/should_compile/T1830.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE DeriveLift #-} +module T1830 where + +import Language.Haskell.TH.Syntax (Lift) + +data Nothing deriving Lift diff --git a/testsuite/tests/deriving/should_compile/all.T b/testsuite/tests/deriving/should_compile/all.T index a01a5149b2..ec81cc3c05 100644 --- a/testsuite/tests/deriving/should_compile/all.T +++ b/testsuite/tests/deriving/should_compile/all.T @@ -16,6 +16,7 @@ test('drv015', normal, compile, ['']) test('drv020', normal, compile, ['']) test('drv022', normal, compile, ['']) test('deriving-1935', normal, compile, ['']) +test('T1830', normal, compile, ['']) test('T2378', normal, compile, ['']) test('T2856', normal, compile, ['']) test('T3057', extra_clean(['T3057A.o', 'T3057A.hi']), multimod_compile, ['T3057', '-v0']) diff --git a/testsuite/tests/deriving/should_fail/T1830.hs b/testsuite/tests/deriving/should_fail/T1830.hs new file mode 100644 index 0000000000..8108d7342a --- /dev/null +++ b/testsuite/tests/deriving/should_fail/T1830.hs @@ -0,0 +1,5 @@ +module T1830 where + +import Language.Haskell.TH.Syntax (Lift) + +data Foo a = Foo a deriving Lift diff --git a/testsuite/tests/deriving/should_fail/T1830.stderr b/testsuite/tests/deriving/should_fail/T1830.stderr new file mode 100644 index 0000000000..9c4209161f --- /dev/null +++ b/testsuite/tests/deriving/should_fail/T1830.stderr @@ -0,0 +1,5 @@ + +T1830.hs:5:29: error: + Can't make a derived instance of ‘Lift (Foo a)’: + You need DeriveLift to derive an instance for this class + In the data declaration for ‘Foo’ diff --git a/testsuite/tests/deriving/should_fail/all.T b/testsuite/tests/deriving/should_fail/all.T index 94120d2912..d65961268f 100644 --- a/testsuite/tests/deriving/should_fail/all.T +++ b/testsuite/tests/deriving/should_fail/all.T @@ -16,6 +16,7 @@ test('drvfail016', extra_clean(['drvfail016.hi-boot', 'drvfail016.o-boot']), run_command, ['$MAKE --no-print-directory -s drvfail016']) +test('T1830', normal, compile_fail, ['']) test('T2394', normal, compile_fail, ['']) # T2604 was removed as it was out of date re: fixing #9858 test('T2701', normal, compile_fail, ['']) diff --git a/testsuite/tests/driver/T4437.hs b/testsuite/tests/driver/T4437.hs index c197cbd5dc..9d5202e357 100644 --- a/testsuite/tests/driver/T4437.hs +++ b/testsuite/tests/driver/T4437.hs @@ -35,7 +35,8 @@ expectedGhcOnlyExtensions = ["RelaxedLayout", "AlternativeLayoutRuleTransitional", "StaticPointers", "StrictData", - "ApplicativeDo"] -- TODO add this to Cabal + "ApplicativeDo", + "DeriveLift"] -- TODO add this to Cabal expectedCabalOnlyExtensions :: [String] expectedCabalOnlyExtensions = ["Generics", diff --git a/testsuite/tests/th/T1830.hs b/testsuite/tests/th/T1830.hs new file mode 100644 index 0000000000..a119ec515b --- /dev/null +++ b/testsuite/tests/th/T1830.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE TemplateHaskell #-} +module Main where + +import Language.Haskell.TH.Syntax (lift) +import T1830a + +main :: IO () +main = do + print ($(lift algDT1) == algDT1) + print ($(lift algDT2) == algDT2) + print ($(lift algDT3) == algDT3) + print ($(lift prim) == prim) + print ($(lift df1) == df1) + print ($(lift df2) == df2) + print ($(lift df3) == df3) diff --git a/testsuite/tests/th/T1830.stdout b/testsuite/tests/th/T1830.stdout new file mode 100644 index 0000000000..672e08f95c --- /dev/null +++ b/testsuite/tests/th/T1830.stdout @@ -0,0 +1,7 @@ +True +True +True +True +True +True +True diff --git a/testsuite/tests/th/T1830a.hs b/testsuite/tests/th/T1830a.hs new file mode 100644 index 0000000000..5012acd117 --- /dev/null +++ b/testsuite/tests/th/T1830a.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE DeriveLift #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE TypeFamilies #-} +module T1830a where + +import GHC.Exts +import Language.Haskell.TH.Syntax (Lift(..)) + +data AlgDT a b c = NormalCon a b + | RecCon { recCon1 :: a, recCon2 :: b} + | a :^: b + deriving (Eq, Lift) + +data Prim = Prim Char# Double# Int# Float# Word# + deriving (Eq, Lift) + +-- We can't test this for equality easily due to the unstable nature of +-- primitive string literal equality. We include this anyway to ensure that +-- deriving Lift for datatypes with Addr# in them does in fact work. +data AddrHash = AddrHash Addr# + deriving Lift + +data Empty deriving Lift + +data family DataFam a b c + +data instance DataFam Int b c = DF1 Int | DF2 b + deriving (Eq, Lift) + +newtype instance DataFam Char b c = DF3 Char + deriving (Eq, Lift) + +algDT1, algDT2, algDT3 :: AlgDT Int String () +algDT1 = NormalCon 1 "foo" +algDT2 = RecCon 2 "bar" +algDT3 = 3 :^: "baz" + +prim :: Prim +prim = Prim 'a'# 1.0## 1# 1.0# 1## + +df1, df2 :: DataFam Int Char () +df1 = DF1 1 +df2 = DF2 'a' + +df3 :: DataFam Char () () +df3 = DF3 'b' diff --git a/testsuite/tests/th/all.T b/testsuite/tests/th/all.T index bad0a0e161..0bb4aa4bb0 100644 --- a/testsuite/tests/th/all.T +++ b/testsuite/tests/th/all.T @@ -127,6 +127,10 @@ test('TH_ghci1', normal, ghci_script, ['TH_ghci1.script']) test('TH_linePragma', normal, compile_fail, ['-v0']) +test('T1830', + extra_clean(['T1830a.o','T1830a.hi']), + multimod_compile_and_run, + ['T1830', '-v0']) test('T2700', normal, compile, ['-v0']) test('T2817', normal, compile, ['-v0']) test('T2713', normal, compile_fail, ['-v0']) |