Teo Camarasu pushed to branch wip/abstract-q at Glasgow Haskell Compiler / GHC
Commits:
-
81baed9d
by Teo Camarasu at 2026-03-14T01:09:55+00:00
-
3fd2d50d
by Teo Camarasu at 2026-03-14T01:09:55+00:00
4 changed files:
- compiler/GHC/Data/IOEnv.hs
- compiler/GHC/Tc/Gen/Splice.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs
- libraries/ghci/GHCi/TH.hs
Changes:
| ... | ... | @@ -29,7 +29,7 @@ module GHC.Data.IOEnv ( |
| 29 | 29 | |
| 30 | 30 | -- I/O operations
|
| 31 | 31 | IORef, newMutVar, readMutVar, writeMutVar, updMutVar,
|
| 32 | - atomicUpdMutVar, atomicUpdMutVar'
|
|
| 32 | + atomicUpdMutVar, atomicUpdMutVar', unliftIOEnv
|
|
| 33 | 33 | ) where
|
| 34 | 34 | |
| 35 | 35 | import GHC.Prelude
|
| ... | ... | @@ -258,3 +258,10 @@ updEnv upd (IOEnv m) = IOEnv (\ env -> m (upd env)) |
| 258 | 258 | updEnvIO :: (env -> IO env') -> IOEnv env' a -> IOEnv env a
|
| 259 | 259 | {-# INLINE updEnvIO #-}
|
| 260 | 260 | updEnvIO upd (IOEnv m) = IOEnv (\ env -> m =<< upd env)
|
| 261 | + |
|
| 262 | +unliftIOEnv :: forall env b. ((forall a. IOEnv env a -> IO a) -> IO b) -> IOEnv env b
|
|
| 263 | +unliftIOEnv k = IOEnv $ \env ->
|
|
| 264 | + let
|
|
| 265 | + unlift :: forall a. IOEnv env a -> IO a
|
|
| 266 | + unlift (IOEnv m) = m env
|
|
| 267 | + in k unlift |
| ... | ... | @@ -138,6 +138,7 @@ import qualified GHC.LanguageExtensions as LangExt |
| 138 | 138 | -- THSyntax gives access to internal functions and data types
|
| 139 | 139 | import qualified GHC.Boot.TH.Syntax as TH
|
| 140 | 140 | import qualified GHC.Boot.TH.Monad as TH
|
| 141 | +import GHC.Boot.TH.Monad (MetaHandlers(..))
|
|
| 141 | 142 | import qualified GHC.Boot.TH.Ppr as TH
|
| 142 | 143 | |
| 143 | 144 | #if defined(HAVE_INTERNAL_INTERPRETER)
|
| ... | ... | @@ -1139,7 +1140,7 @@ convertAnnotationWrapper fhv = do |
| 1139 | 1140 | -}
|
| 1140 | 1141 | |
| 1141 | 1142 | runQuasi :: TH.Q a -> TcM a
|
| 1142 | -runQuasi act = TH.runQ act
|
|
| 1143 | +runQuasi (TH.Q act) = unliftIOEnv $ \runInIO -> liftIO $ act runInIO metaHandlersTcM
|
|
| 1143 | 1144 | |
| 1144 | 1145 | runRemoteModFinalizers :: ThModFinalizers -> TcM ()
|
| 1145 | 1146 | runRemoteModFinalizers (ThModFinalizers finRefs) = do
|
| ... | ... | @@ -1466,68 +1467,12 @@ when showing an error message. |
| 1466 | 1467 | To call runQ in the Tc monad, we need to make TcM an instance of Quasi:
|
| 1467 | 1468 | -}
|
| 1468 | 1469 | |
| 1469 | -instance TH.Quasi TcM where
|
|
| 1470 | - qNewName s = do { u <- newUnique
|
|
| 1471 | - ; let i = toInteger (getKey u)
|
|
| 1472 | - ; return (TH.mkNameU s i) }
|
|
| 1473 | - |
|
| 1474 | - -- 'msg' is forced to ensure exceptions don't escape,
|
|
| 1475 | - -- see Note [Exceptions in TH]
|
|
| 1476 | - qReport True msg = seqList msg $ addErr $ TcRnTHError $ ReportCustomQuasiError True msg
|
|
| 1477 | - qReport False msg = seqList msg $ addDiagnostic $ TcRnTHError $ ReportCustomQuasiError False msg
|
|
| 1478 | - |
|
| 1479 | - qLocation :: TcM TH.Loc
|
|
| 1480 | - qLocation = do { m <- getModule
|
|
| 1481 | - ; l <- getSrcSpanM
|
|
| 1482 | - ; r <- case l of
|
|
| 1483 | - UnhelpfulSpan _ -> pprPanic "qLocation: Unhelpful location"
|
|
| 1484 | - (ppr l)
|
|
| 1485 | - RealSrcSpan s _ -> return s
|
|
| 1486 | - ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile r)
|
|
| 1487 | - , TH.loc_module = moduleNameString (moduleName m)
|
|
| 1488 | - , TH.loc_package = unitString (moduleUnit m)
|
|
| 1489 | - , TH.loc_start = (srcSpanStartLine r, srcSpanStartCol r)
|
|
| 1490 | - , TH.loc_end = (srcSpanEndLine r, srcSpanEndCol r) }) }
|
|
| 1491 | - |
|
| 1492 | - qLookupName = lookupName
|
|
| 1493 | - qReify = reify
|
|
| 1494 | - qReifyFixity nm = lookupThName nm >>= reifyFixity
|
|
| 1495 | - qReifyType = reifyTypeOfThing
|
|
| 1496 | - qReifyInstances = reifyInstances
|
|
| 1497 | - qReifyRoles = reifyRoles
|
|
| 1498 | - qReifyAnnotations = reifyAnnotations
|
|
| 1499 | - qReifyModule = reifyModule
|
|
| 1500 | - qReifyConStrictness nm = do { nm' <- lookupThName nm
|
|
| 1501 | - ; dc <- tcLookupDataCon nm'
|
|
| 1502 | - ; let bangs = dataConImplBangs dc
|
|
| 1503 | - ; return (map reifyDecidedStrictness bangs) }
|
|
| 1504 | - |
|
| 1505 | - -- For qRecover, discard error messages if
|
|
| 1506 | - -- the recovery action is chosen. Otherwise
|
|
| 1507 | - -- we'll only fail higher up.
|
|
| 1508 | - qRecover recover main = tryTcDiscardingErrs recover main
|
|
| 1509 | - |
|
| 1510 | - qGetPackageRoot = do
|
|
| 1511 | - dflags <- getDynFlags
|
|
| 1512 | - return $ fromMaybe "." (workingDirectory dflags)
|
|
| 1513 | - |
|
| 1514 | - qAddDependentFile fp = do
|
|
| 1515 | - ref <- fmap tcg_dependent_files getGblEnv
|
|
| 1516 | - dep_files <- readTcRef ref
|
|
| 1517 | - writeTcRef ref (fp:dep_files)
|
|
| 1518 | - |
|
| 1519 | - qAddDependentDirectory dp = do
|
|
| 1520 | - ref <- fmap tcg_dependent_dirs getGblEnv
|
|
| 1521 | - dep_dirs <- readTcRef ref
|
|
| 1522 | - writeTcRef ref (dp:dep_dirs)
|
|
| 1523 | - |
|
| 1524 | - qAddTempFile suffix = do
|
|
| 1525 | - dflags <- getDynFlags
|
|
| 1526 | - logger <- getLogger
|
|
| 1527 | - tmpfs <- hsc_tmpfs <$> getTopEnv
|
|
| 1528 | - liftIO $ newTempName logger tmpfs (tmpDir dflags) TFL_GhcSession suffix
|
|
| 1529 | - |
|
| 1530 | - qAddTopDecls thds = do
|
|
| 1470 | +report :: Bool -> [Char] -> TcM ()
|
|
| 1471 | +report True msg = seqList msg $ addErr $ TcRnTHError $ ReportCustomQuasiError True msg
|
|
| 1472 | +report False msg = seqList msg $ addDiagnostic $ TcRnTHError $ ReportCustomQuasiError False msg
|
|
| 1473 | + |
|
| 1474 | +addTopDecls :: [TH.Dec] -> TcM ()
|
|
| 1475 | +addTopDecls thds = do
|
|
| 1531 | 1476 | exts <- fmap extensionFlags getDynFlags
|
| 1532 | 1477 | l <- getSrcSpanM
|
| 1533 | 1478 | th_origin <- getThSpliceOrigin
|
| ... | ... | @@ -1555,52 +1500,13 @@ instance TH.Quasi TcM where |
| 1555 | 1500 | bindName :: RdrName -> TcM ()
|
| 1556 | 1501 | bindName (Exact n)
|
| 1557 | 1502 | = do { th_topnames_var <- fmap tcg_th_topnames getGblEnv
|
| 1558 | - ; updTcRef th_topnames_var (\ns -> extendNameSet ns n)
|
|
| 1559 | - }
|
|
| 1503 | + ; updTcRef th_topnames_var (\ns -> extendNameSet ns n)
|
|
| 1504 | + }
|
|
| 1560 | 1505 | |
| 1561 | 1506 | bindName name = addErr $ TcRnTHError $ THNameError $ NonExactName name
|
| 1562 | 1507 | |
| 1563 | - qAddForeignFilePath lang fp = do
|
|
| 1564 | - var <- fmap tcg_th_foreign_files getGblEnv
|
|
| 1565 | - updTcRef var ((lang, fp) :)
|
|
| 1566 | - |
|
| 1567 | - qAddModFinalizer fin = do
|
|
| 1568 | - r <- liftIO $ mkRemoteRef fin
|
|
| 1569 | - fref <- liftIO $ mkForeignRef r (freeRemoteRef r)
|
|
| 1570 | - addModFinalizerRef fref
|
|
| 1571 | - |
|
| 1572 | - qAddCorePlugin plugin = do
|
|
| 1573 | - hsc_env <- getTopEnv
|
|
| 1574 | - let fc = hsc_FC hsc_env
|
|
| 1575 | - let home_unit = hsc_home_unit hsc_env
|
|
| 1576 | - let dflags = hsc_dflags hsc_env
|
|
| 1577 | - let fopts = initFinderOpts dflags
|
|
| 1578 | - r <- liftIO $ findHomeModule fc fopts home_unit (mkModuleName plugin)
|
|
| 1579 | - let err = TcRnTHError $ AddInvalidCorePlugin plugin
|
|
| 1580 | - case r of
|
|
| 1581 | - Found {} -> addErr err
|
|
| 1582 | - FoundMultiple {} -> addErr err
|
|
| 1583 | - _ -> return ()
|
|
| 1584 | - th_coreplugins_var <- tcg_th_coreplugins <$> getGblEnv
|
|
| 1585 | - updTcRef th_coreplugins_var (plugin:)
|
|
| 1586 | - |
|
| 1587 | - qGetQ :: forall a. Typeable a => TcM (Maybe a)
|
|
| 1588 | - qGetQ = do
|
|
| 1589 | - th_state_var <- fmap tcg_th_state getGblEnv
|
|
| 1590 | - th_state <- readTcRef th_state_var
|
|
| 1591 | - -- See #10596 for why we use a scoped type variable here.
|
|
| 1592 | - return (Map.lookup (typeRep (Proxy :: Proxy a)) th_state >>= fromDynamic)
|
|
| 1593 | - |
|
| 1594 | - qPutQ x = do
|
|
| 1595 | - th_state_var <- fmap tcg_th_state getGblEnv
|
|
| 1596 | - updTcRef th_state_var (\m -> Map.insert (typeOf x) (toDyn x) m)
|
|
| 1597 | - |
|
| 1598 | - qIsExtEnabled = xoptM
|
|
| 1599 | - |
|
| 1600 | - qExtsEnabled =
|
|
| 1601 | - EnumSet.toList . extensionFlags . hsc_dflags <$> getTopEnv
|
|
| 1602 | - |
|
| 1603 | - qPutDoc doc_loc s = do
|
|
| 1508 | +putDoc :: TH.DocLoc -> String -> TcM ()
|
|
| 1509 | +putDoc doc_loc s = do
|
|
| 1604 | 1510 | th_doc_var <- tcg_th_docs <$> getGblEnv
|
| 1605 | 1511 | resolved_doc_loc <- resolve_loc doc_loc
|
| 1606 | 1512 | is_local <- checkLocalName resolved_doc_loc
|
| ... | ... | @@ -1622,15 +1528,129 @@ instance TH.Quasi TcM where |
| 1622 | 1528 | checkLocalName (InstDoc n) = nameIsLocalOrFrom <$> getModule <*> pure n
|
| 1623 | 1529 | checkLocalName ModuleDoc = pure True
|
| 1624 | 1530 | |
| 1625 | - |
|
| 1626 | - qGetDoc (TH.DeclDoc n) = lookupThName n >>= lookupDeclDoc
|
|
| 1627 | - qGetDoc (TH.InstDoc t) = lookupThInstName t >>= lookupDeclDoc
|
|
| 1628 | - qGetDoc (TH.ArgDoc n i) = lookupThName n >>= lookupArgDoc i
|
|
| 1629 | - qGetDoc TH.ModuleDoc = do
|
|
| 1531 | +getDoc :: TH.DocLoc -> TcM (Maybe String)
|
|
| 1532 | +getDoc (TH.DeclDoc n) = lookupThName n >>= lookupDeclDoc
|
|
| 1533 | +getDoc (TH.InstDoc t) = lookupThInstName t >>= lookupDeclDoc
|
|
| 1534 | +getDoc (TH.ArgDoc n i) = lookupThName n >>= lookupArgDoc i
|
|
| 1535 | +getDoc TH.ModuleDoc = do
|
|
| 1630 | 1536 | df <- getDynFlags
|
| 1631 | 1537 | docs <- getGblEnv >>= extractDocs df
|
| 1632 | 1538 | return (renderHsDocString . hsDocString <$> (docs_mod_hdr =<< docs))
|
| 1633 | 1539 | |
| 1540 | +getQ :: forall a. Typeable a => TcM (Maybe a)
|
|
| 1541 | +getQ = do
|
|
| 1542 | + th_state_var <- fmap tcg_th_state getGblEnv
|
|
| 1543 | + th_state <- readTcRef th_state_var
|
|
| 1544 | + -- See #10596 for why we use a scoped type variable here.
|
|
| 1545 | + return (Map.lookup (typeRep (Proxy :: Proxy a)) th_state >>= fromDynamic)
|
|
| 1546 | + |
|
| 1547 | +location :: TcM TH.Loc
|
|
| 1548 | +location = do { m <- getModule
|
|
| 1549 | + ; l <- getSrcSpanM
|
|
| 1550 | + ; r <- case l of
|
|
| 1551 | + UnhelpfulSpan _ -> pprPanic "qLocation: Unhelpful location"
|
|
| 1552 | + (ppr l)
|
|
| 1553 | + RealSrcSpan s _ -> return s
|
|
| 1554 | + ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile r)
|
|
| 1555 | + , TH.loc_module = moduleNameString (moduleName m)
|
|
| 1556 | + , TH.loc_package = unitString (moduleUnit m)
|
|
| 1557 | + , TH.loc_start = (srcSpanStartLine r, srcSpanStartCol r)
|
|
| 1558 | + , TH.loc_end = (srcSpanEndLine r, srcSpanEndCol r) }) }
|
|
| 1559 | + |
|
| 1560 | +metaHandlersTcM :: TH.MetaHandlers TcM
|
|
| 1561 | +metaHandlersTcM = TH.MetaHandlers {
|
|
| 1562 | + mNewName = \s -> do { u <- newUnique
|
|
| 1563 | + ; let i = toInteger (getKey u)
|
|
| 1564 | + ; return (TH.mkNameU s i) }
|
|
| 1565 | + |
|
| 1566 | + -- 'msg' is forced to ensure exceptions don't escape,
|
|
| 1567 | + -- see Note [Exceptions in TH]
|
|
| 1568 | + , mReport = report
|
|
| 1569 | + |
|
| 1570 | + , mLocation = location
|
|
| 1571 | + |
|
| 1572 | + , mLookupName = lookupName
|
|
| 1573 | + , mReify = reify
|
|
| 1574 | + , mReifyFixity = \nm -> lookupThName nm >>= reifyFixity
|
|
| 1575 | + , mReifyType = reifyTypeOfThing
|
|
| 1576 | + , mReifyInstances = reifyInstances
|
|
| 1577 | + , mReifyRoles = reifyRoles
|
|
| 1578 | + , mReifyAnnotations = reifyAnnotations
|
|
| 1579 | + , mReifyModule = reifyModule
|
|
| 1580 | + , mReifyConStrictness = \nm -> do { nm' <- lookupThName nm
|
|
| 1581 | + ; dc <- tcLookupDataCon nm'
|
|
| 1582 | + ; let bangs = dataConImplBangs dc
|
|
| 1583 | + ; return (map reifyDecidedStrictness bangs) }
|
|
| 1584 | + |
|
| 1585 | + -- For qRecover, discard error messages if
|
|
| 1586 | + -- the recovery action is chosen. Otherwise
|
|
| 1587 | + -- we'll only fail higher up.
|
|
| 1588 | + -- NB: extremely subtle!!! TODO: write up note
|
|
| 1589 | + -- tryTcDiscardingErrs manipulates the reader env so we need to be careful we don't sneak in the outside env
|
|
| 1590 | + , mRecover = \recover main -> tryTcDiscardingErrs (runQuasi recover) (runQuasi main)
|
|
| 1591 | + |
|
| 1592 | + , mGetPackageRoot = do
|
|
| 1593 | + dflags <- getDynFlags
|
|
| 1594 | + return $ fromMaybe "." (workingDirectory dflags)
|
|
| 1595 | + |
|
| 1596 | + , mAddDependentFile = \fp -> do
|
|
| 1597 | + ref <- fmap tcg_dependent_files getGblEnv
|
|
| 1598 | + dep_files <- readTcRef ref
|
|
| 1599 | + writeTcRef ref (fp:dep_files)
|
|
| 1600 | + |
|
| 1601 | + , mAddDependentDirectory = \dp -> do
|
|
| 1602 | + ref <- fmap tcg_dependent_dirs getGblEnv
|
|
| 1603 | + dep_dirs <- readTcRef ref
|
|
| 1604 | + writeTcRef ref (dp:dep_dirs)
|
|
| 1605 | + |
|
| 1606 | + , mAddTempFile = \suffix -> do
|
|
| 1607 | + dflags <- getDynFlags
|
|
| 1608 | + logger <- getLogger
|
|
| 1609 | + tmpfs <- hsc_tmpfs <$> getTopEnv
|
|
| 1610 | + liftIO $ newTempName logger tmpfs (tmpDir dflags) TFL_GhcSession suffix
|
|
| 1611 | + |
|
| 1612 | + , mAddTopDecls = addTopDecls
|
|
| 1613 | + |
|
| 1614 | + , mAddForeignFilePath = \lang fp -> do
|
|
| 1615 | + var <- fmap tcg_th_foreign_files getGblEnv
|
|
| 1616 | + updTcRef var ((lang, fp) :)
|
|
| 1617 | + |
|
| 1618 | + , mAddModFinalizer = \fin -> do
|
|
| 1619 | + r <- liftIO $ mkRemoteRef fin
|
|
| 1620 | + fref <- liftIO $ mkForeignRef r (freeRemoteRef r)
|
|
| 1621 | + addModFinalizerRef fref
|
|
| 1622 | + |
|
| 1623 | + , mAddCorePlugin = \plugin -> do
|
|
| 1624 | + hsc_env <- getTopEnv
|
|
| 1625 | + let fc = hsc_FC hsc_env
|
|
| 1626 | + let home_unit = hsc_home_unit hsc_env
|
|
| 1627 | + let dflags = hsc_dflags hsc_env
|
|
| 1628 | + let fopts = initFinderOpts dflags
|
|
| 1629 | + r <- liftIO $ findHomeModule fc fopts home_unit (mkModuleName plugin)
|
|
| 1630 | + let err = TcRnTHError $ AddInvalidCorePlugin plugin
|
|
| 1631 | + case r of
|
|
| 1632 | + Found {} -> addErr err
|
|
| 1633 | + FoundMultiple {} -> addErr err
|
|
| 1634 | + _ -> return ()
|
|
| 1635 | + th_coreplugins_var <- tcg_th_coreplugins <$> getGblEnv
|
|
| 1636 | + updTcRef th_coreplugins_var (plugin:)
|
|
| 1637 | + |
|
| 1638 | + , mGetQ = getQ
|
|
| 1639 | + |
|
| 1640 | + , mPutQ = \x -> do
|
|
| 1641 | + th_state_var <- fmap tcg_th_state getGblEnv
|
|
| 1642 | + updTcRef th_state_var (\m -> Map.insert (typeOf x) (toDyn x) m)
|
|
| 1643 | + |
|
| 1644 | + , mIsExtEnabled = xoptM
|
|
| 1645 | + |
|
| 1646 | + , mExtsEnabled =
|
|
| 1647 | + EnumSet.toList . extensionFlags . hsc_dflags <$> getTopEnv
|
|
| 1648 | + |
|
| 1649 | + , mPutDoc = putDoc
|
|
| 1650 | + |
|
| 1651 | + , mGetDoc = getDoc
|
|
| 1652 | + }
|
|
| 1653 | + |
|
| 1634 | 1654 | -- | Looks up documentation for a declaration in first the current module,
|
| 1635 | 1655 | -- otherwise tries to find it in another module via 'hscGetModuleInterface'.
|
| 1636 | 1656 | lookupDeclDoc :: Name -> TcM (Maybe String)
|
| ... | ... | @@ -1795,7 +1815,7 @@ runTH ty fhv = do |
| 1795 | 1815 | -- Remote GHCi, see Note [Remote Template Haskell] in
|
| 1796 | 1816 | -- libraries/ghci/GHCi/TH.hs.
|
| 1797 | 1817 | rstate <- getTHState inst
|
| 1798 | - loc <- TH.qLocation
|
|
| 1818 | + loc <- location
|
|
| 1799 | 1819 | -- run a remote TH request
|
| 1800 | 1820 | r <- liftIO $
|
| 1801 | 1821 | withForeignRef rstate $ \state_hv ->
|
| ... | ... | @@ -1911,32 +1931,32 @@ wrapTHResult tcm = do |
| 1911 | 1931 | |
| 1912 | 1932 | handleTHMessage :: THMessage a -> TcM a
|
| 1913 | 1933 | handleTHMessage msg = case msg of
|
| 1914 | - NewName a -> wrapTHResult $ TH.qNewName a
|
|
| 1915 | - Report b str -> wrapTHResult $ TH.qReport b str
|
|
| 1916 | - LookupName b str -> wrapTHResult $ TH.qLookupName b str
|
|
| 1917 | - Reify n -> wrapTHResult $ TH.qReify n
|
|
| 1918 | - ReifyFixity n -> wrapTHResult $ TH.qReifyFixity n
|
|
| 1919 | - ReifyType n -> wrapTHResult $ TH.qReifyType n
|
|
| 1920 | - ReifyInstances n ts -> wrapTHResult $ TH.qReifyInstances n ts
|
|
| 1921 | - ReifyRoles n -> wrapTHResult $ TH.qReifyRoles n
|
|
| 1934 | + NewName a -> wrapTHResult $ runQuasi $ TH.newName a
|
|
| 1935 | + Report b str -> wrapTHResult $ runQuasi $ TH.report b str
|
|
| 1936 | + LookupName b str -> wrapTHResult $ runQuasi $ TH.lookupName b str
|
|
| 1937 | + Reify n -> wrapTHResult $ runQuasi $ TH.reify n
|
|
| 1938 | + ReifyFixity n -> wrapTHResult $ runQuasi $ TH.reifyFixity n
|
|
| 1939 | + ReifyType n -> wrapTHResult $ runQuasi $ TH.reifyType n
|
|
| 1940 | + ReifyInstances n ts -> wrapTHResult $ runQuasi $ TH.reifyInstances n ts
|
|
| 1941 | + ReifyRoles n -> wrapTHResult $ runQuasi $ TH.reifyRoles n
|
|
| 1922 | 1942 | ReifyAnnotations lookup tyrep ->
|
| 1923 | 1943 | wrapTHResult $ (map B.pack <$> getAnnotationsByTypeRep lookup tyrep)
|
| 1924 | - ReifyModule m -> wrapTHResult $ TH.qReifyModule m
|
|
| 1925 | - ReifyConStrictness nm -> wrapTHResult $ TH.qReifyConStrictness nm
|
|
| 1926 | - GetPackageRoot -> wrapTHResult $ TH.qGetPackageRoot
|
|
| 1927 | - AddDependentFile f -> wrapTHResult $ TH.qAddDependentFile f
|
|
| 1928 | - AddDependentDirectory d -> wrapTHResult $ TH.qAddDependentDirectory d
|
|
| 1929 | - AddTempFile s -> wrapTHResult $ TH.qAddTempFile s
|
|
| 1944 | + ReifyModule m -> wrapTHResult $ runQuasi $ TH.reifyModule m
|
|
| 1945 | + ReifyConStrictness nm -> wrapTHResult $ runQuasi $ TH.reifyConStrictness nm
|
|
| 1946 | + GetPackageRoot -> wrapTHResult $ runQuasi $ TH.getPackageRoot
|
|
| 1947 | + AddDependentFile f -> wrapTHResult $ runQuasi $ TH.addDependentFile f
|
|
| 1948 | + AddDependentDirectory d -> wrapTHResult $ runQuasi $ TH.addDependentDirectory d
|
|
| 1949 | + AddTempFile s -> wrapTHResult $ runQuasi $ TH.addTempFile s
|
|
| 1930 | 1950 | AddModFinalizer r -> do
|
| 1931 | 1951 | interp <- hscInterp <$> getTopEnv
|
| 1932 | 1952 | wrapTHResult $ liftIO (mkFinalizedHValue interp r) >>= addModFinalizerRef
|
| 1933 | - AddCorePlugin str -> wrapTHResult $ TH.qAddCorePlugin str
|
|
| 1934 | - AddTopDecls decs -> wrapTHResult $ TH.qAddTopDecls decs
|
|
| 1935 | - AddForeignFilePath lang str -> wrapTHResult $ TH.qAddForeignFilePath lang str
|
|
| 1936 | - IsExtEnabled ext -> wrapTHResult $ TH.qIsExtEnabled ext
|
|
| 1937 | - ExtsEnabled -> wrapTHResult $ TH.qExtsEnabled
|
|
| 1938 | - PutDoc l s -> wrapTHResult $ TH.qPutDoc l s
|
|
| 1939 | - GetDoc l -> wrapTHResult $ TH.qGetDoc l
|
|
| 1953 | + AddCorePlugin str -> wrapTHResult $ runQuasi $ TH.addCorePlugin str
|
|
| 1954 | + AddTopDecls decs -> wrapTHResult $ runQuasi $ TH.addTopDecls decs
|
|
| 1955 | + AddForeignFilePath lang str -> wrapTHResult $ runQuasi $ TH.addForeignFilePath lang str
|
|
| 1956 | + IsExtEnabled ext -> wrapTHResult $ runQuasi $ TH.isExtEnabled ext
|
|
| 1957 | + ExtsEnabled -> wrapTHResult $ runQuasi $ TH.extsEnabled
|
|
| 1958 | + PutDoc l s -> wrapTHResult $ runQuasi $ TH.putDoc l s
|
|
| 1959 | + GetDoc l -> wrapTHResult $ runQuasi $ TH.getDoc l
|
|
| 1940 | 1960 | FailIfErrs -> wrapTHResult failIfErrsM
|
| 1941 | 1961 | _ -> panic ("handleTHMessage: unexpected message " ++ show msg)
|
| 1942 | 1962 |
| ... | ... | @@ -59,6 +59,7 @@ import GHC.Internal.TH.Syntax |
| 59 | 59 | -----------------------------------------------------
|
| 60 | 60 | |
| 61 | 61 | class (MonadIO m, MonadFail m) => Quasi m where
|
| 62 | + qRunQ :: Q a -> m a
|
|
| 62 | 63 | -- | Fresh names. See 'newName'.
|
| 63 | 64 | qNewName :: String -> m Name
|
| 64 | 65 | |
| ... | ... | @@ -149,6 +150,7 @@ class (MonadIO m, MonadFail m) => Quasi m where |
| 149 | 150 | -- type environment, so reification isn't going to
|
| 150 | 151 | -- work.
|
| 151 | 152 | instance Quasi IO where
|
| 153 | + qRunQ (Q m) = m id metaHandlersIO
|
|
| 152 | 154 | qNewName = newNameIO
|
| 153 | 155 | |
| 154 | 156 | qReport True msg = hPutStrLn stderr ("Template Haskell error: " ++ msg)
|
| ... | ... | @@ -180,9 +182,124 @@ instance Quasi IO where |
| 180 | 182 | qGetDoc _ = badIO "getDoc"
|
| 181 | 183 | qAddDependentDirectory _ = badIO "AddDependentDirectory"
|
| 182 | 184 | |
| 185 | +metaHandlersIO :: MetaHandlers IO
|
|
| 186 | +metaHandlersIO = MetaHandlers {
|
|
| 187 | + mNewName = newNameIO
|
|
| 188 | + , mReport = \b msg ->
|
|
| 189 | + if b then
|
|
| 190 | + hPutStrLn stderr ("Template Haskell error: " ++ msg)
|
|
| 191 | + else
|
|
| 192 | + hPutStrLn stderr ("Template Haskell error: " ++ msg) -- TODO: should this be different from above?
|
|
| 193 | + , mLookupName = \ _ _ -> badIO "lookupName"
|
|
| 194 | + , mReify = \_ -> badIO "reify"
|
|
| 195 | + , mReifyFixity = \_ -> badIO "reifyFixity"
|
|
| 196 | + , mReifyType = \_ -> badIO "reifyFixity"
|
|
| 197 | + , mReifyInstances = \_ _ -> badIO "reifyInstances"
|
|
| 198 | + , mReifyRoles = \_ -> badIO "reifyRoles"
|
|
| 199 | + , mReifyAnnotations = \_ -> badIO "reifyAnnotations"
|
|
| 200 | + , mReifyModule = \_ -> badIO "reifyModule"
|
|
| 201 | + , mReifyConStrictness = \_ -> badIO "reifyConStrictness"
|
|
| 202 | + , mLocation = badIO "currentLocation"
|
|
| 203 | + , mRecover = \_ _ -> badIO "recover" -- Maybe we could fix this?
|
|
| 204 | + , mGetPackageRoot = badIO "getProjectRoot"
|
|
| 205 | + , mAddDependentFile = \_ -> badIO "addDependentFile"
|
|
| 206 | + , mAddTempFile = \_ -> badIO "addTempFile"
|
|
| 207 | + , mAddTopDecls = \_ -> badIO "addTopDecls"
|
|
| 208 | + , mAddForeignFilePath = \_ _ -> badIO "addForeignFilePath"
|
|
| 209 | + , mAddModFinalizer = \_ -> badIO "addModFinalizer"
|
|
| 210 | + , mAddCorePlugin = \_ -> badIO "addCorePlugin"
|
|
| 211 | + , mGetQ = badIO "getQ"
|
|
| 212 | + , mPutQ = \_ -> badIO "putQ"
|
|
| 213 | + , mIsExtEnabled = \_ -> badIO "isExtEnabled"
|
|
| 214 | + , mExtsEnabled = badIO "extsEnabled"
|
|
| 215 | + , mPutDoc = \_ _ -> badIO "putDoc"
|
|
| 216 | + , mGetDoc = \_ -> badIO "getDoc"
|
|
| 217 | + , mAddDependentDirectory = \_ -> badIO "AddDependentDirectory"
|
|
| 218 | + }
|
|
| 219 | + |
|
| 183 | 220 | instance Quote IO where
|
| 184 | 221 | newName = newNameIO
|
| 185 | 222 | |
| 223 | +data MetaHandlers m = MetaHandlers {
|
|
| 224 | + -- | Fresh names. See 'newName'.
|
|
| 225 | + mNewName :: String -> m Name
|
|
| 226 | + |
|
| 227 | + ------- Error reporting and recovery -------
|
|
| 228 | + -- | Report an error (True) or warning (False)
|
|
| 229 | + -- ...but carry on; use 'fail' to stop. See 'report'.
|
|
| 230 | + , mReport :: Bool -> String -> m ()
|
|
| 231 | + |
|
| 232 | + -- | See 'recover'.
|
|
| 233 | + , mRecover :: forall a. Q a -- ^ the error handler
|
|
| 234 | + -> Q a -- ^ action which may fail
|
|
| 235 | + -> m a -- ^ Recover from the monadic 'fail'
|
|
| 236 | + |
|
| 237 | + ------- Inspect the type-checker's environment -------
|
|
| 238 | + -- | True <=> type namespace, False <=> value namespace. See 'lookupName'.
|
|
| 239 | + , mLookupName :: Bool -> String -> m (Maybe Name)
|
|
| 240 | + -- | See 'reify'.
|
|
| 241 | + , mReify :: Name -> m Info
|
|
| 242 | + -- | See 'reifyFixity'.
|
|
| 243 | + , mReifyFixity :: Name -> m (Maybe Fixity)
|
|
| 244 | + -- | See 'reifyType'.
|
|
| 245 | + , mReifyType :: Name -> m Type
|
|
| 246 | + -- | Is (n tys) an instance? Returns list of matching instance Decs (with
|
|
| 247 | + -- empty sub-Decs) Works for classes and type functions. See 'reifyInstances'.
|
|
| 248 | + , mReifyInstances :: Name -> [Type] -> m [Dec]
|
|
| 249 | + -- | See 'reifyRoles'.
|
|
| 250 | + , mReifyRoles :: Name -> m [Role]
|
|
| 251 | + -- | See 'reifyAnnotations'.
|
|
| 252 | + , mReifyAnnotations :: forall a. Data a => AnnLookup -> m [a]
|
|
| 253 | + -- | See 'reifyModule'.
|
|
| 254 | + , mReifyModule :: Module -> m ModuleInfo
|
|
| 255 | + -- | See 'reifyConStrictness'.
|
|
| 256 | + , mReifyConStrictness :: Name -> m [DecidedStrictness]
|
|
| 257 | + |
|
| 258 | + -- | See 'location'.
|
|
| 259 | + , mLocation :: m Loc
|
|
| 260 | + |
|
| 261 | + -- | See 'getPackageRoot'.
|
|
| 262 | + , mGetPackageRoot :: m FilePath
|
|
| 263 | + |
|
| 264 | + -- | See 'addDependentFile'.
|
|
| 265 | + , mAddDependentFile :: FilePath -> m ()
|
|
| 266 | + |
|
| 267 | + -- | See 'addDependentDirectory'.
|
|
| 268 | + , mAddDependentDirectory :: FilePath -> m ()
|
|
| 269 | + |
|
| 270 | + -- | See 'addTempFile'.
|
|
| 271 | + , mAddTempFile :: String -> m FilePath
|
|
| 272 | + |
|
| 273 | + -- | See 'addTopDecls'.
|
|
| 274 | + , mAddTopDecls :: [Dec] -> m ()
|
|
| 275 | + |
|
| 276 | + -- | See 'addForeignFilePath'.
|
|
| 277 | + , mAddForeignFilePath :: ForeignSrcLang -> String -> m ()
|
|
| 278 | + |
|
| 279 | + -- | See 'addModFinalizer'.
|
|
| 280 | + , mAddModFinalizer :: Q () -> m ()
|
|
| 281 | + |
|
| 282 | + -- | See 'addCorePlugin'.
|
|
| 283 | + , mAddCorePlugin :: String -> m ()
|
|
| 284 | + |
|
| 285 | + -- | See 'getQ'.
|
|
| 286 | + , mGetQ :: forall a. Typeable a => m (Maybe a)
|
|
| 287 | + |
|
| 288 | + -- | See 'putQ'.
|
|
| 289 | + , mPutQ :: forall a. Typeable a => a -> m ()
|
|
| 290 | + |
|
| 291 | + -- | See 'isExtEnabled'.
|
|
| 292 | + , mIsExtEnabled :: Extension -> m Bool
|
|
| 293 | + -- | See 'extsEnabled'.
|
|
| 294 | + , mExtsEnabled :: m [Extension]
|
|
| 295 | + |
|
| 296 | + -- | See 'putDoc'.
|
|
| 297 | + , mPutDoc :: DocLoc -> String -> m ()
|
|
| 298 | + -- | See 'getDoc'.
|
|
| 299 | + , mGetDoc :: DocLoc -> m (Maybe String)
|
|
| 300 | + }
|
|
| 301 | + |
|
| 302 | + |
|
| 186 | 303 | newNameIO :: String -> IO Name
|
| 187 | 304 | newNameIO s = do { n <- atomicModifyIORef' counter (\x -> (x + 1, x))
|
| 188 | 305 | ; pure (mkNameU s n) }
|
| ... | ... | @@ -213,7 +330,7 @@ counter = unsafePerformIO (newIORef 0) |
| 213 | 330 | -- inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle),
|
| 214 | 331 | -- providing an abstract interface for the user which is later concretely
|
| 215 | 332 | -- fufilled by an concrete 'Quasi' instance, internal to GHC.
|
| 216 | -newtype Q a = Q { unQ :: forall m. Quasi m => m a }
|
|
| 333 | +newtype Q a = Q { unQ :: forall m. (forall x. m x -> IO x) -> MetaHandlers m -> IO a }
|
|
| 217 | 334 | |
| 218 | 335 | -- | \"Runs\" the 'Q' monad. Normal users of Template Haskell
|
| 219 | 336 | -- should not need this function, as the splice brackets @$( ... )@
|
| ... | ... | @@ -227,22 +344,22 @@ newtype Q a = Q { unQ :: forall m. Quasi m => m a } |
| 227 | 344 | -- simply fail at runtime. Indeed, the only operations guaranteed to succeed
|
| 228 | 345 | -- are 'newName', 'runIO', 'reportError' and 'reportWarning'.
|
| 229 | 346 | runQ :: Quasi m => Q a -> m a
|
| 230 | -runQ (Q m) = m
|
|
| 347 | +runQ = qRunQ
|
|
| 231 | 348 | |
| 232 | 349 | instance Monad Q where
|
| 233 | - Q m >>= k = Q (m >>= \x -> unQ (k x))
|
|
| 350 | + Q m >>= k = Q $ \r h -> (m r h >>= \x -> unQ (k x) r h)
|
|
| 234 | 351 | (>>) = (*>)
|
| 235 | 352 | |
| 236 | 353 | instance MonadFail Q where
|
| 237 | - fail s = report True s >> Q (fail "Q monad failure")
|
|
| 354 | + fail s = report True s >> Q (\_ _ -> fail "Q monad failure")
|
|
| 238 | 355 | |
| 239 | 356 | instance Functor Q where
|
| 240 | - fmap f (Q x) = Q (fmap f x)
|
|
| 357 | + fmap f (Q x) = Q $ \r h -> fmap f (x r h)
|
|
| 241 | 358 | |
| 242 | 359 | instance Applicative Q where
|
| 243 | - pure x = Q (pure x)
|
|
| 244 | - Q f <*> Q x = Q (f <*> x)
|
|
| 245 | - Q m *> Q n = Q (m *> n)
|
|
| 360 | + pure x = Q $ \_ _ -> pure x
|
|
| 361 | + Q f <*> Q x = Q $ \r h -> (f r h <*> x r h)
|
|
| 362 | + Q m *> Q n = Q $ \r h -> (m r h *> n r h)
|
|
| 246 | 363 | |
| 247 | 364 | -- | @since 2.17.0.0
|
| 248 | 365 | instance Semigroup a => Semigroup (Q a) where
|
| ... | ... | @@ -311,8 +428,17 @@ class Monad m => Quote m where |
| 311 | 428 | -}
|
| 312 | 429 | newName :: String -> m Name
|
| 313 | 430 | |
| 431 | +runHandler :: (forall m. MetaHandlers m -> m a) -> Q a
|
|
| 432 | +runHandler op = Q $ \r h -> r (op h)
|
|
| 433 | + |
|
| 434 | +runHandler1 :: (forall m. MetaHandlers m -> a -> m b) -> a -> Q b
|
|
| 435 | +runHandler1 op = \x -> Q $ \r h -> r (op h x)
|
|
| 436 | + |
|
| 437 | +runHandler2 :: (forall m. MetaHandlers m -> a -> b -> m c) -> a -> b -> Q c
|
|
| 438 | +runHandler2 op = \x y -> Q $ \r h -> r (op h x y)
|
|
| 439 | + |
|
| 314 | 440 | instance Quote Q where
|
| 315 | - newName s = Q (qNewName s)
|
|
| 441 | + newName = runHandler1 mNewName
|
|
| 316 | 442 | |
| 317 | 443 | -----------------------------------------------------
|
| 318 | 444 | --
|
| ... | ... | @@ -510,7 +636,7 @@ joinCode = flip bindCode id |
| 510 | 636 | -- | Report an error (True) or warning (False),
|
| 511 | 637 | -- but carry on; use 'fail' to stop.
|
| 512 | 638 | report :: Bool -> String -> Q ()
|
| 513 | -report b s = Q (qReport b s)
|
|
| 639 | +report b s = runHandler2 mReport b s
|
|
| 514 | 640 | {-# DEPRECATED report "Use reportError or reportWarning instead" #-} -- deprecated in 7.6
|
| 515 | 641 | |
| 516 | 642 | -- | Report an error to the user, but allow the current splice's computation to carry on. To abort the computation, use 'fail'.
|
| ... | ... | @@ -525,20 +651,20 @@ reportWarning = report False |
| 525 | 651 | recover :: Q a -- ^ handler to invoke on failure
|
| 526 | 652 | -> Q a -- ^ computation to run
|
| 527 | 653 | -> Q a
|
| 528 | -recover (Q r) (Q m) = Q (qRecover r m)
|
|
| 654 | +recover rec main = Q $ \r h -> r $ mRecover h rec main
|
|
| 529 | 655 | |
| 530 | 656 | -- We don't export lookupName; the Bool isn't a great API
|
| 531 | 657 | -- Instead we export lookupTypeName, lookupValueName
|
| 532 | 658 | lookupName :: Bool -> String -> Q (Maybe Name)
|
| 533 | -lookupName ns s = Q (qLookupName ns s)
|
|
| 659 | +lookupName ns s = runHandler2 mLookupName ns s
|
|
| 534 | 660 | |
| 535 | 661 | -- | Look up the given name in the (type namespace of the) current splice's scope. See "Language.Haskell.TH.Syntax#namelookup" for more details.
|
| 536 | 662 | lookupTypeName :: String -> Q (Maybe Name)
|
| 537 | -lookupTypeName s = Q (qLookupName True s)
|
|
| 663 | +lookupTypeName s = runHandler2 mLookupName True s
|
|
| 538 | 664 | |
| 539 | 665 | -- | Look up the given name in the (value namespace of the) current splice's scope. See "Language.Haskell.TH.Syntax#namelookup" for more details.
|
| 540 | 666 | lookupValueName :: String -> Q (Maybe Name)
|
| 541 | -lookupValueName s = Q (qLookupName False s)
|
|
| 667 | +lookupValueName s = runHandler2 mLookupName False s
|
|
| 542 | 668 | |
| 543 | 669 | {-
|
| 544 | 670 | Note [Name lookup]
|
| ... | ... | @@ -613,7 +739,7 @@ To ensure we get information about @D@-the-value, use 'lookupValueName': |
| 613 | 739 | and to get information about @D@-the-type, use 'lookupTypeName'.
|
| 614 | 740 | -}
|
| 615 | 741 | reify :: Name -> Q Info
|
| 616 | -reify v = Q (qReify v)
|
|
| 742 | +reify v = runHandler1 mReify v
|
|
| 617 | 743 | |
| 618 | 744 | {- | @reifyFixity nm@ attempts to find a fixity declaration for @nm@. For
|
| 619 | 745 | example, if the function @foo@ has the fixity declaration @infixr 7 foo@, then
|
| ... | ... | @@ -622,7 +748,7 @@ example, if the function @foo@ has the fixity declaration @infixr 7 foo@, then |
| 622 | 748 | 'Nothing', so you may assume @bar@ has 'defaultFixity'.
|
| 623 | 749 | -}
|
| 624 | 750 | reifyFixity :: Name -> Q (Maybe Fixity)
|
| 625 | -reifyFixity nm = Q (qReifyFixity nm)
|
|
| 751 | +reifyFixity nm = runHandler1 mReifyFixity nm
|
|
| 626 | 752 | |
| 627 | 753 | {- | @reifyType nm@ attempts to find the type or kind of @nm@. For example,
|
| 628 | 754 | @reifyType 'not@ returns @Bool -> Bool@, and
|
| ... | ... | @@ -630,7 +756,7 @@ reifyFixity nm = Q (qReifyFixity nm) |
| 630 | 756 | This works even if there's no explicit signature and the type or kind is inferred.
|
| 631 | 757 | -}
|
| 632 | 758 | reifyType :: Name -> Q Type
|
| 633 | -reifyType nm = Q (qReifyType nm)
|
|
| 759 | +reifyType nm = runHandler1 mReifyType nm
|
|
| 634 | 760 | |
| 635 | 761 | {- | Template Haskell is capable of reifying information about types and
|
| 636 | 762 | terms defined in previous declaration groups. Top-level declaration splices break up
|
| ... | ... | @@ -722,7 +848,7 @@ has some discussion around this. |
| 722 | 848 | |
| 723 | 849 | -}
|
| 724 | 850 | reifyInstances :: Name -> [Type] -> Q [InstanceDec]
|
| 725 | -reifyInstances cls tys = Q (qReifyInstances cls tys)
|
|
| 851 | +reifyInstances cls tys = runHandler2 mReifyInstances cls tys
|
|
| 726 | 852 | |
| 727 | 853 | {- | @reifyRoles nm@ returns the list of roles associated with the parameters
|
| 728 | 854 | (both visible and invisible) of
|
| ... | ... | @@ -741,20 +867,20 @@ and @reifyRoles Proxy@, we will get @['NominalR', 'PhantomR']@. The 'NominalR' i |
| 741 | 867 | the role of the invisible @k@ parameter. Kind parameters are always nominal.
|
| 742 | 868 | -}
|
| 743 | 869 | reifyRoles :: Name -> Q [Role]
|
| 744 | -reifyRoles nm = Q (qReifyRoles nm)
|
|
| 870 | +reifyRoles nm = runHandler1 mReifyRoles nm
|
|
| 745 | 871 | |
| 746 | 872 | -- | @reifyAnnotations target@ returns the list of annotations
|
| 747 | 873 | -- associated with @target@. Only the annotations that are
|
| 748 | 874 | -- appropriately typed is returned. So if you have @Int@ and @String@
|
| 749 | 875 | -- annotations for the same target, you have to call this function twice.
|
| 750 | 876 | reifyAnnotations :: Data a => AnnLookup -> Q [a]
|
| 751 | -reifyAnnotations an = Q (qReifyAnnotations an)
|
|
| 877 | +reifyAnnotations an = runHandler1 mReifyAnnotations an
|
|
| 752 | 878 | |
| 753 | 879 | -- | @reifyModule mod@ looks up information about module @mod@. To
|
| 754 | 880 | -- look up the current module, call this function with the return
|
| 755 | 881 | -- value of 'Language.Haskell.TH.Lib.thisModule'.
|
| 756 | 882 | reifyModule :: Module -> Q ModuleInfo
|
| 757 | -reifyModule m = Q (qReifyModule m)
|
|
| 883 | +reifyModule m = runHandler1 mReifyModule m
|
|
| 758 | 884 | |
| 759 | 885 | -- | @reifyConStrictness nm@ looks up the strictness information for the fields
|
| 760 | 886 | -- of the constructor with the name @nm@. Note that the strictness information
|
| ... | ... | @@ -769,7 +895,7 @@ reifyModule m = Q (qReifyModule m) |
| 769 | 895 | -- circumstances, but it would return @['DecidedStrict', DecidedStrict]@ if the
|
| 770 | 896 | -- @-XStrictData@ language extension was enabled.
|
| 771 | 897 | reifyConStrictness :: Name -> Q [DecidedStrictness]
|
| 772 | -reifyConStrictness n = Q (qReifyConStrictness n)
|
|
| 898 | +reifyConStrictness n = runHandler1 mReifyConStrictness n
|
|
| 773 | 899 | |
| 774 | 900 | -- | Is the list of instances returned by 'reifyInstances' nonempty?
|
| 775 | 901 | --
|
| ... | ... | @@ -782,7 +908,7 @@ isInstance nm tys = do { decs <- reifyInstances nm tys |
| 782 | 908 | |
| 783 | 909 | -- | The location at which this computation is spliced.
|
| 784 | 910 | location :: Q Loc
|
| 785 | -location = Q qLocation
|
|
| 911 | +location = runHandler mLocation
|
|
| 786 | 912 | |
| 787 | 913 | -- |The 'runIO' function lets you run an I\/O computation in the 'Q' monad.
|
| 788 | 914 | -- Take care: you are guaranteed the ordering of calls to 'runIO' within
|
| ... | ... | @@ -792,7 +918,7 @@ location = Q qLocation |
| 792 | 918 | -- necessarily flushed when the compiler finishes running, so you should
|
| 793 | 919 | -- flush them yourself.
|
| 794 | 920 | runIO :: IO a -> Q a
|
| 795 | -runIO m = Q (qRunIO m)
|
|
| 921 | +runIO m = Q $ \_ _ -> m
|
|
| 796 | 922 | |
| 797 | 923 | -- | Get the package root for the current package which is being compiled.
|
| 798 | 924 | -- This can be set explicitly with the -package-root flag but is normally
|
| ... | ... | @@ -804,7 +930,7 @@ runIO m = Q (qRunIO m) |
| 804 | 930 | -- change directory when compiling files but instead set the -package-root flag
|
| 805 | 931 | -- appropriately.
|
| 806 | 932 | getPackageRoot :: Q FilePath
|
| 807 | -getPackageRoot = Q qGetPackageRoot
|
|
| 933 | +getPackageRoot = runHandler mGetPackageRoot
|
|
| 808 | 934 | |
| 809 | 935 | -- | Record external directories that runIO is using (dependent upon).
|
| 810 | 936 | -- The compiler can then recognize that it should re-compile the Haskell file
|
| ... | ... | @@ -823,7 +949,7 @@ getPackageRoot = Q qGetPackageRoot |
| 823 | 949 | -- * The state of the directory is read at the interface generation time,
|
| 824 | 950 | -- not at the time of the function call.
|
| 825 | 951 | addDependentDirectory :: FilePath -> Q ()
|
| 826 | -addDependentDirectory dp = Q (qAddDependentDirectory dp)
|
|
| 952 | +addDependentDirectory dp = runHandler1 mAddDependentDirectory dp
|
|
| 827 | 953 | |
| 828 | 954 | -- | Record external files that runIO is using (dependent upon).
|
| 829 | 955 | -- The compiler can then recognize that it should re-compile the Haskell file
|
| ... | ... | @@ -837,17 +963,17 @@ addDependentDirectory dp = Q (qAddDependentDirectory dp) |
| 837 | 963 | --
|
| 838 | 964 | -- * The dependency is based on file content, not a modification time
|
| 839 | 965 | addDependentFile :: FilePath -> Q ()
|
| 840 | -addDependentFile fp = Q (qAddDependentFile fp)
|
|
| 966 | +addDependentFile fp = runHandler1 mAddDependentFile fp
|
|
| 841 | 967 | |
| 842 | 968 | -- | Obtain a temporary file path with the given suffix. The compiler will
|
| 843 | 969 | -- delete this file after compilation.
|
| 844 | 970 | addTempFile :: String -> Q FilePath
|
| 845 | -addTempFile suffix = Q (qAddTempFile suffix)
|
|
| 971 | +addTempFile suffix = runHandler1 mAddTempFile suffix
|
|
| 846 | 972 | |
| 847 | 973 | -- | Add additional top-level declarations. The added declarations will be type
|
| 848 | 974 | -- checked along with the current declaration group.
|
| 849 | 975 | addTopDecls :: [Dec] -> Q ()
|
| 850 | -addTopDecls ds = Q (qAddTopDecls ds)
|
|
| 976 | +addTopDecls ds = runHandler1 mAddTopDecls ds
|
|
| 851 | 977 | |
| 852 | 978 | -- | Same as 'addForeignSource', but expects to receive a path pointing to the
|
| 853 | 979 | -- foreign file instead of a 'String' of its contents. Consider using this in
|
| ... | ... | @@ -856,7 +982,7 @@ addTopDecls ds = Q (qAddTopDecls ds) |
| 856 | 982 | -- This is a good alternative to 'addForeignSource' when you are trying to
|
| 857 | 983 | -- directly link in an object file.
|
| 858 | 984 | addForeignFilePath :: ForeignSrcLang -> FilePath -> Q ()
|
| 859 | -addForeignFilePath lang fp = Q (qAddForeignFilePath lang fp)
|
|
| 985 | +addForeignFilePath lang fp = runHandler2 mAddForeignFilePath lang fp
|
|
| 860 | 986 | |
| 861 | 987 | -- | Add a finalizer that will run in the Q monad after the current module has
|
| 862 | 988 | -- been type checked. This only makes sense when run within a top-level splice.
|
| ... | ... | @@ -865,7 +991,7 @@ addForeignFilePath lang fp = Q (qAddForeignFilePath lang fp) |
| 865 | 991 | -- 'reify' is able to find the local definitions when executed inside the
|
| 866 | 992 | -- finalizer.
|
| 867 | 993 | addModFinalizer :: Q () -> Q ()
|
| 868 | -addModFinalizer act = Q (qAddModFinalizer (unQ act))
|
|
| 994 | +addModFinalizer act = runHandler1 mAddModFinalizer act
|
|
| 869 | 995 | |
| 870 | 996 | -- | Adds a core plugin to the compilation pipeline.
|
| 871 | 997 | --
|
| ... | ... | @@ -875,7 +1001,7 @@ addModFinalizer act = Q (qAddModFinalizer (unQ act)) |
| 875 | 1001 | -- to tell the compiler that we needed to compile first a plugin module in the
|
| 876 | 1002 | -- current package.
|
| 877 | 1003 | addCorePlugin :: String -> Q ()
|
| 878 | -addCorePlugin plugin = Q (qAddCorePlugin plugin)
|
|
| 1004 | +addCorePlugin plugin = runHandler1 mAddCorePlugin plugin
|
|
| 879 | 1005 | |
| 880 | 1006 | -- | Get state from the 'Q' monad. The state maintained by 'Q' is isomorphic to
|
| 881 | 1007 | -- a type-indexed finite map. That is,
|
| ... | ... | @@ -889,20 +1015,20 @@ addCorePlugin plugin = Q (qAddCorePlugin plugin) |
| 889 | 1015 | -- Note that the state is local to the Haskell module in which the Template
|
| 890 | 1016 | -- Haskell expression is executed.
|
| 891 | 1017 | getQ :: Typeable a => Q (Maybe a)
|
| 892 | -getQ = Q qGetQ
|
|
| 1018 | +getQ = runHandler mGetQ
|
|
| 893 | 1019 | |
| 894 | 1020 | -- | Replace the state in the 'Q' monad. Note that the state is local to the
|
| 895 | 1021 | -- Haskell module in which the Template Haskell expression is executed.
|
| 896 | 1022 | putQ :: Typeable a => a -> Q ()
|
| 897 | -putQ x = Q (qPutQ x)
|
|
| 1023 | +putQ x = runHandler1 mPutQ x
|
|
| 898 | 1024 | |
| 899 | 1025 | -- | Determine whether the given language extension is enabled in the 'Q' monad.
|
| 900 | 1026 | isExtEnabled :: Extension -> Q Bool
|
| 901 | -isExtEnabled ext = Q (qIsExtEnabled ext)
|
|
| 1027 | +isExtEnabled ext = runHandler1 mIsExtEnabled ext
|
|
| 902 | 1028 | |
| 903 | 1029 | -- | List all enabled language extensions.
|
| 904 | 1030 | extsEnabled :: Q [Extension]
|
| 905 | -extsEnabled = Q qExtsEnabled
|
|
| 1031 | +extsEnabled = runHandler mExtsEnabled
|
|
| 906 | 1032 | |
| 907 | 1033 | -- | Add Haddock documentation to the specified location. This will overwrite
|
| 908 | 1034 | -- any documentation at the location if it already exists. This will reify the
|
| ... | ... | @@ -921,19 +1047,20 @@ extsEnabled = Q qExtsEnabled |
| 921 | 1047 | -- Adding documentation to anything outside of the current module will cause an
|
| 922 | 1048 | -- error.
|
| 923 | 1049 | putDoc :: DocLoc -> String -> Q ()
|
| 924 | -putDoc t s = Q (qPutDoc t s)
|
|
| 1050 | +putDoc t s = runHandler2 mPutDoc t s
|
|
| 925 | 1051 | |
| 926 | 1052 | -- | Retrieves the Haddock documentation at the specified location, if one
|
| 927 | 1053 | -- exists.
|
| 928 | 1054 | -- It can be used to read documentation on things defined outside of the current
|
| 929 | 1055 | -- module, provided that those modules were compiled with the @-haddock@ flag.
|
| 930 | 1056 | getDoc :: DocLoc -> Q (Maybe String)
|
| 931 | -getDoc n = Q (qGetDoc n)
|
|
| 1057 | +getDoc n = runHandler1 mGetDoc n
|
|
| 932 | 1058 | |
| 933 | 1059 | instance MonadIO Q where
|
| 934 | 1060 | liftIO = runIO
|
| 935 | 1061 | |
| 936 | 1062 | instance Quasi Q where
|
| 1063 | + qRunQ = id
|
|
| 937 | 1064 | qNewName = newName
|
| 938 | 1065 | qReport = report
|
| 939 | 1066 | qRecover = recover
|
| ... | ... | @@ -163,56 +163,67 @@ ghcCmd m = GHCiQ $ \s -> do |
| 163 | 163 | instance MonadIO GHCiQ where
|
| 164 | 164 | liftIO m = GHCiQ $ \s -> fmap (,s) m
|
| 165 | 165 | |
| 166 | -instance TH.Quasi GHCiQ where
|
|
| 167 | - qNewName str = ghcCmd (NewName str)
|
|
| 168 | - qReport isError msg = ghcCmd (Report isError msg)
|
|
| 169 | - |
|
| 170 | - -- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice
|
|
| 171 | - qRecover (GHCiQ h) a = GHCiQ $ \s -> mask $ \unmask -> do
|
|
| 172 | - remoteTHCall (qsPipe s) StartRecover
|
|
| 173 | - e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) s
|
|
| 174 | - remoteTHCall (qsPipe s) (EndRecover (isLeft e))
|
|
| 175 | - case e of
|
|
| 176 | - Left GHCiQException{} -> h s
|
|
| 177 | - Right r -> return r
|
|
| 178 | - qLookupName isType occ = ghcCmd (LookupName isType occ)
|
|
| 179 | - qReify name = ghcCmd (Reify name)
|
|
| 180 | - qReifyFixity name = ghcCmd (ReifyFixity name)
|
|
| 181 | - qReifyType name = ghcCmd (ReifyType name)
|
|
| 182 | - qReifyInstances name tys = ghcCmd (ReifyInstances name tys)
|
|
| 183 | - qReifyRoles name = ghcCmd (ReifyRoles name)
|
|
| 184 | - |
|
| 185 | 166 | -- To reify annotations, we send GHC the AnnLookup and also the
|
| 186 | 167 | -- TypeRep of the thing we're looking for, to avoid needing to
|
| 187 | 168 | -- serialize irrelevant annotations.
|
| 188 | - qReifyAnnotations :: forall a . Data a => TH.AnnLookup -> GHCiQ [a]
|
|
| 189 | - qReifyAnnotations lookup =
|
|
| 169 | +reifyAnnotations :: forall a . Data a => TH.AnnLookup -> GHCiQ [a]
|
|
| 170 | +reifyAnnotations lookup =
|
|
| 190 | 171 | map (deserializeWithData . B.unpack) <$>
|
| 191 | 172 | ghcCmd (ReifyAnnotations lookup typerep)
|
| 192 | 173 | where typerep = typeOf (undefined :: a)
|
| 193 | 174 | |
| 194 | - qReifyModule m = ghcCmd (ReifyModule m)
|
|
| 195 | - qReifyConStrictness name = ghcCmd (ReifyConStrictness name)
|
|
| 196 | - qLocation = fromMaybe noLoc . qsLocation <$> getState
|
|
| 197 | - qGetPackageRoot = ghcCmd GetPackageRoot
|
|
| 198 | - qAddDependentFile file = ghcCmd (AddDependentFile file)
|
|
| 199 | - qAddDependentDirectory dir = ghcCmd (AddDependentDirectory dir)
|
|
| 200 | - qAddTempFile suffix = ghcCmd (AddTempFile suffix)
|
|
| 201 | - qAddTopDecls decls = ghcCmd (AddTopDecls decls)
|
|
| 202 | - qAddForeignFilePath lang fp = ghcCmd (AddForeignFilePath lang fp)
|
|
| 203 | - qAddModFinalizer fin = GHCiQ (\s -> mkRemoteRef fin >>= return . (, s)) >>=
|
|
| 175 | +-- TODO: !!!
|
|
| 176 | +-- This is wrong because it will discard any updates to the state.
|
|
| 177 | +-- What I should do instead is refactor GHCiQ first (!) to use an IORef for state, and then save/restore that.
|
|
| 178 | +runQinGHCiQ :: TH.Q a -> GHCiQ a
|
|
| 179 | +runQinGHCiQ (TH.Q m) = GHCiQ $ \s -> (,s) <$> m (runInIO s) metaHandlersGHCiQ
|
|
| 180 | + where
|
|
| 181 | + runInIO :: QState -> GHCiQ a -> IO a
|
|
| 182 | + runInIO s (GHCiQ m) = fst <$> m s
|
|
| 183 | + |
|
| 184 | +metaHandlersGHCiQ = TH.MetaHandlers {
|
|
| 185 | + mNewName = \str -> ghcCmd (NewName str)
|
|
| 186 | + , mReport = \isError msg -> ghcCmd (Report isError msg)
|
|
| 187 | + |
|
| 188 | + -- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice
|
|
| 189 | + , mRecover = \h a -> GHCiQ $ \s -> mask $ \unmask -> do
|
|
| 190 | + remoteTHCall (qsPipe s) StartRecover
|
|
| 191 | + e <- try $ unmask $ runGHCiQ (runQinGHCiQ a <* ghcCmd FailIfErrs) s -- TODO: simplify
|
|
| 192 | + remoteTHCall (qsPipe s) (EndRecover (isLeft e))
|
|
| 193 | + case e of
|
|
| 194 | + Left GHCiQException{} -> runGHCiQ (runQinGHCiQ h) s -- TODO: simplify
|
|
| 195 | + Right r -> return r
|
|
| 196 | + , mLookupName = \isType occ -> ghcCmd (LookupName isType occ)
|
|
| 197 | + , mReify = \name -> ghcCmd (Reify name)
|
|
| 198 | + , mReifyFixity = \name -> ghcCmd (ReifyFixity name)
|
|
| 199 | + , mReifyType = \name -> ghcCmd (ReifyType name)
|
|
| 200 | + , mReifyInstances = \name tys -> ghcCmd (ReifyInstances name tys)
|
|
| 201 | + , mReifyRoles = \name -> ghcCmd (ReifyRoles name)
|
|
| 202 | + |
|
| 203 | + , mReifyAnnotations = reifyAnnotations
|
|
| 204 | + , mReifyModule = \m -> ghcCmd (ReifyModule m)
|
|
| 205 | + , mReifyConStrictness = \name -> ghcCmd (ReifyConStrictness name)
|
|
| 206 | + , mLocation = fromMaybe noLoc . qsLocation <$> getState
|
|
| 207 | + , mGetPackageRoot = ghcCmd GetPackageRoot
|
|
| 208 | + , mAddDependentFile = \file -> ghcCmd (AddDependentFile file)
|
|
| 209 | + , mAddDependentDirectory = \dir -> ghcCmd (AddDependentDirectory dir)
|
|
| 210 | + , mAddTempFile = \suffix -> ghcCmd (AddTempFile suffix)
|
|
| 211 | + , mAddTopDecls = \decls -> ghcCmd (AddTopDecls decls)
|
|
| 212 | + , mAddForeignFilePath = \lang fp -> ghcCmd (AddForeignFilePath lang fp)
|
|
| 213 | + , mAddModFinalizer = \fin -> GHCiQ (\s -> mkRemoteRef fin >>= return . (, s)) >>=
|
|
| 204 | 214 | ghcCmd . AddModFinalizer
|
| 205 | - qAddCorePlugin str = ghcCmd (AddCorePlugin str)
|
|
| 206 | - qGetQ = GHCiQ $ \s ->
|
|
| 215 | + , mAddCorePlugin = \str -> ghcCmd (AddCorePlugin str)
|
|
| 216 | + , mGetQ = GHCiQ $ \s ->
|
|
| 207 | 217 | let lookup :: forall a. Typeable a => Map TypeRep Dynamic -> Maybe a
|
| 208 | 218 | lookup m = fromDynamic =<< M.lookup (typeOf (undefined::a)) m
|
| 209 | 219 | in return (lookup (qsMap s), s)
|
| 210 | - qPutQ k = GHCiQ $ \s ->
|
|
| 220 | + , mPutQ = \k -> GHCiQ $ \s ->
|
|
| 211 | 221 | return ((), s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })
|
| 212 | - qIsExtEnabled x = ghcCmd (IsExtEnabled x)
|
|
| 213 | - qExtsEnabled = ghcCmd ExtsEnabled
|
|
| 214 | - qPutDoc l s = ghcCmd (PutDoc l s)
|
|
| 215 | - qGetDoc l = ghcCmd (GetDoc l)
|
|
| 222 | + , mIsExtEnabled = \x -> ghcCmd (IsExtEnabled x)
|
|
| 223 | + , mExtsEnabled = ghcCmd ExtsEnabled
|
|
| 224 | + , mPutDoc = \l s -> ghcCmd (PutDoc l s)
|
|
| 225 | + , mGetDoc = \l -> ghcCmd (GetDoc l)
|
|
| 226 | +}
|
|
| 216 | 227 | |
| 217 | 228 | -- | The implementation of the 'StartTH' message: create
|
| 218 | 229 | -- a new IORef QState, and return a RemoteRef to it.
|
| ... | ... | @@ -231,7 +242,7 @@ runModFinalizerRefs pipe rstate qrefs = do |
| 231 | 242 | qs <- mapM localRef qrefs
|
| 232 | 243 | qstateref <- localRef rstate
|
| 233 | 244 | qstate <- readIORef qstateref
|
| 234 | - _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate { qsPipe = pipe }
|
|
| 245 | + _ <- runGHCiQ (runQinGHCiQ $ sequence_ qs) qstate { qsPipe = pipe }
|
|
| 235 | 246 | return ()
|
| 236 | 247 | |
| 237 | 248 | -- | The implementation of the 'RunTH' message
|
| ... | ... | @@ -269,6 +280,6 @@ runTHQ pipe rstate mb_loc ghciq = do |
| 269 | 280 | qstateref <- localRef rstate
|
| 270 | 281 | qstate <- readIORef qstateref
|
| 271 | 282 | let st = qstate { qsLocation = mb_loc, qsPipe = pipe }
|
| 272 | - (r,new_state) <- runGHCiQ (TH.runQ ghciq) st
|
|
| 283 | + (r,new_state) <- runGHCiQ (runQinGHCiQ ghciq) st
|
|
| 273 | 284 | writeIORef qstateref new_state
|
| 274 | 285 | return $! LB.toStrict (runPut (put r)) |