Skip to content

Commit 913b90a

Browse files
committed
Qualify modules with their pkg, but unambiguous
1 parent b32ada0 commit 913b90a

6 files changed

Lines changed: 137 additions & 57 deletions

File tree

haskell-debugger/GHC/Debugger/Monad.hs

Lines changed: 42 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import System.Process.Internals (mkProcessHandle)
4141
import Text.Read (readMaybe)
4242

4343
import GHC
44-
import GHC.Data.FastString
4544
import GHC.Data.StringBuffer
4645
import GHC.Driver.Config.Diagnostic
4746
import GHC.Driver.Config.Logger
@@ -61,9 +60,7 @@ import GHC.Runtime.Interpreter as GHCi
6160
import GHC.Runtime.Loader as GHC
6261
import GHC.Runtime.Context as GHCi
6362
import GHC.Types.Error
64-
import GHC.Types.PkgQual
6563
import GHC.Types.SourceError
66-
import GHC.Types.SourceText
6764
import GHC.Types.Unique.Supply as GHC
6865
import GHC.Unit.Module.Graph
6966
import GHC.Unit.State
@@ -270,7 +267,7 @@ runDebuggerAction :: forall a. LogAction IO DebuggerLog
270267
-> Debugger a
271268
-> Ghc a
272269
runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = flip MC.finally cleanupInterp $ -- See Note [Shutting down the external interpreter]
273-
do
270+
annotateCallStackGhc $ do
274271
#ifdef MIN_VERSION_unix
275272
-- Workaround #4162
276273
-- FIXME: setup reasonable handlers to run cleanupSession for every debugger thread, because runGhc's `withSignalHandlers` is not it.
@@ -319,7 +316,7 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
319316
-- Override the logger to output to the given handle
320317
GHC.pushLogHook $ const $ ghcLogAction l
321318

322-
dflags2 <- getLogger >>= \logger -> do
319+
dflags2 <- getLogger >>= \logger -> annotateCallStackGhc $ do
323320
-- Set the extra GHC arguments for ALL units by setting them early in
324321
-- dynflags. This is important to make sure unfoldings for interfaces
325322
-- loaded because of the built-in loaded classes (like
@@ -338,7 +335,7 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
338335
-- Make sure to override the function which creates the external
339336
-- interpreter, because we need to keep track of the standard handles
340337
iserv_handles <- liftIO newEmptyMVar
341-
case conf.externalInterpreterCustomProc of
338+
annotateCallStackGhc $ case conf.externalInterpreterCustomProc of
342339
-- Left: GHC will launch the external interpreter itself on demand if
343340
-- using external interpreter, and we just provide the stdin stream
344341
Left givenStdStream ->
@@ -387,7 +384,7 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
387384
(forwardHandleToLogger serv_out (contramap LogDebuggeeOut l))
388385

389386
mainGhcThread :: Ghc a
390-
mainGhcThread = do
387+
mainGhcThread = annotateCallStackGhc $ do
391388
-- Initializes interpreter!
392389
_ <- GHC.setSessionDynFlags dflags2
393390

@@ -398,9 +395,7 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
398395
GHC.getSessionDynFlags >>= \df -> liftIO $
399396
GHC.initUniqSupply (GHC.initialUnique df) (GHC.uniqueIncrement df)
400397

401-
loadHomeUnit
402-
-- See Note [Must explicitly expose module graph units]
403-
setExposedInUnit interactiveGhcDebuggerUnitId . graphUnits . hsc_mod_graph =<< getSession
398+
annotateCallStackGhc $ loadHomeUnit
404399

405400
-- Ensure all the home units are built with same Ways and return them.
406401
buildWays <- do
@@ -411,37 +406,41 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
411406

412407
-- Find haskell-debugger-view in (deps of) home units, or load one from
413408
-- in-memory sources.
414-
(hdv_uid, loadedBuiltinModNames) <- findOrLoadHaskellDebuggerView l buildWays
409+
(hdv_uid, loadedBuiltinModNames) <- annotateCallStackGhc $ findOrLoadHaskellDebuggerView l buildWays
415410

411+
-- See Note [Must explicitly expose module graph units]
412+
setExposedInUnit interactiveGhcDebuggerUnitId . graphUnits . hsc_mod_graph =<< getSession
416413

417414
-- Set interactive context to import all loaded modules
418415
let preludeImp = GHC.IIDecl . GHC.simpleImportDecl $ GHC.mkModuleName "Prelude"
416+
417+
hsc_env_new <- getSession
418+
419419
-- dbgView should always be available, either because we manually loaded it
420420
-- or because it's in the transitive closure.
421-
hug <- hsc_HUG <$> getSession
422421
let dbgViewImps
423-
-- If hs-dbg-view is a home-unit, refer to it directly
424-
-- See Note [Do not package-qualify imports for home units]
425-
| memberHugUnitId hdv_uid hug
426-
= map (GHC.IIModule . mkModule (RealUnit (Definite hdv_uid))) loadedBuiltinModNames
427-
-- It's available in an exposed unit in the transitive closure. Resolve it
428-
| otherwise
429-
= map (\mn ->
430-
GHC.IIDecl (GHC.simpleImportDecl mn)
431-
{ ideclPkgQual = RawPkgQual
432-
StringLiteral
433-
{ sl_st = NoSourceText
434-
, sl_fs = mkFastString (unitIdString hdv_uid)
435-
, sl_tc = Nothing
436-
}
437-
}) loadedBuiltinModNames
422+
= map (packageImportDecl hvd_pkgName) loadedBuiltinModNames
423+
where
424+
hvd_pkgName = fromMaybe (error $ "No package name for: " ++ unitIdString hdv_uid) $
425+
lookupUnitPackageName hsc_env_new hdv_uid
426+
427+
-- See Note [Must explicitly expose module graph units]
428+
setExposedInUnit interactiveGhcDebuggerUnitId . graphUnits . hsc_mod_graph =<< getSession
438429

439430
mss <- getAllLoadedModules
440431

441-
GHC.setContext
442-
(preludeImp :
443-
dbgViewImps ++
444-
map (GHC.IIModule . GHC.ms_mod) mss)
432+
let
433+
imports
434+
= preludeImp :
435+
dbgViewImps ++
436+
[ packageImportDecl pkgName (moduleName modl)
437+
| modl <- map GHC.ms_mod mss
438+
, let uid = moduleUnitId modl
439+
, let pkgName = fromMaybe (error $ "No package name for: " ++ unitIdString uid) $ lookupUnitPackageName hsc_env_new uid
440+
]
441+
442+
443+
GHC.setContext imports
445444

446445
-- See Note [External interpreter buffering]
447446
setBufferings <- compileExprRemote """
@@ -462,16 +461,16 @@ runDebuggerAction l rootDir extraGhcArgs conf loadHomeUnit (Debugger action) = f
462461
then Nothing
463462
else Just hdv_uid)
464463

465-
case conf.externalInterpreterCustomProc of
464+
annotateCallStackGhc $ case conf.externalInterpreterCustomProc of
466465
Left _ -> do
467466
-- We launched the external interpreter ourselves, so forward its output to the logger.
468-
withUnliftGhc $ \ unlift -> do
469-
withAsync (void externalInterpFwdThread) $ \ fwd_thr -> do
470-
liftIO $ link fwd_thr
471-
unlift mainGhcThread
467+
withUnliftGhc $ \ unlift -> annotateCallStackIO $ do
468+
withAsync (annotateCallStackIO $ void externalInterpFwdThread) $ \ fwd_thr -> do
469+
liftIO $ annotateCallStackIO $ link fwd_thr
470+
annotateCallStackIO $ unlift mainGhcThread
472471
Right _ ->
473472
-- Ext interp is running in user terminal, no need to forward output to logger
474-
mainGhcThread
473+
annotateCallStackGhc $ mainGhcThread
475474

476475
findOrLoadHaskellDebuggerView :: LogAction IO DebuggerLog
477476
-> Ways
@@ -581,6 +580,7 @@ Note [Do not package-qualify imports for home units]
581580
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
582581
A package-qualified module import will be looked up directly in the exposed
583582
packages, IGNORING the home units modules.
583+
-- FIXME: PackageImport expects a PackageName, it only works with unit-ids with external packages by a quirk of the implementation, for home units though you can set your own package names and get around the problem.
584584
585585
This can lead to two scenarios:
586586
@@ -623,10 +623,6 @@ cleanupInterp = do
623623
sendMessage i Shutdown
624624
pure InterpPending
625625

626-
-- | WARNING: callback is not to be used from other threads.
627-
withUnliftGhc :: ((Ghc b -> IO b) -> IO a) -> Ghc a
628-
withUnliftGhc k = reifyGhc $ \ s -> k (flip reflectGhc s)
629-
630626
annotateDebuggerStackString :: String -> Debugger a -> Debugger a
631627
annotateDebuggerStackString s (Debugger m) = Debugger $ do
632628
r <- ReaderT $ \val -> do
@@ -857,20 +853,20 @@ doLoad if_cache how_much mg = do
857853

858854

859855
loadInMemoryModules ::
860-
GhcMonad m
861-
=> LogAction IO DebuggerLog
856+
LogAction IO DebuggerLog
862857
-> UnitId
863-
-> [(ModuleName,StringBuffer)] -> m [SuccessFlag]
864-
loadInMemoryModules l uid ts = do
858+
-> [(ModuleName,StringBuffer)] -> Ghc [SuccessFlag]
859+
loadInMemoryModules l uid ts = annotateCallStackGhc $ do
865860
old_targets <- GHC.getTargets
866861
tgts <- forM ts $ \(modName,modContents) ->
867862
liftIO $ makeInMemoryTarget uid modName modContents
863+
-- liftIO $ putStrLn $ "SETTING TARGETS"
868864
GHC.setTargets (tgts ++ old_targets)
869865
mod_graph <- hsc_mod_graph <$> GHC.getSession
870866
-- TODO: use [incremental API](https://gitlab.haskell.org/ghc/ghc/-/issues/27054) when ready.
871867
dvc_mod_graph <- doDownsweep (Just mod_graph)
872868
modifySession $ GHC.setModuleGraph dvc_mod_graph
873-
869+
-- liftIO $ putStrLn $ (++) "MODULEGRAPH" $ showPprUnsafe $ withPprStyle (PprDump alwaysQualify) $ ppr $ mg_mss dvc_mod_graph
874870
restore_logger <- GHC.getLogger
875871
dflags <- getSessionDynFlags
876872
GHC.modifyLogger $

haskell-debugger/GHC/Debugger/Run.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,11 @@ withCurrentBreakExtensions m = do
262262
Just breakModule -> do
263263
ic_dyn_flags <- getInteractiveDebuggerDynFlags
264264
break_dyn_flags <- ms_hspp_opts <$> GHC.getModSummary breakModule
265+
old_context <- GHC.getContext
265266
setInteractiveDebuggerDynFlags $ adjustFlags ic_dyn_flags break_dyn_flags
267+
GHC.setContext (IIModule breakModule : old_context)
266268
x <- m
269+
GHC.setContext old_context
267270
setInteractiveDebuggerDynFlags ic_dyn_flags
268271
return x
269272
where

haskell-debugger/GHC/Debugger/Session.hs

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,19 @@ module GHC.Debugger.Session (
3333
setExposedInUnit,
3434
graphUnits,
3535
compileModuleWithDepsInHpt,
36+
home_unit_dflags,
37+
packageImportDecl,
38+
withUnliftGhc,
39+
annotateCallStackGhc,
40+
lookupUnitPackageName,
3641
)
3742
where
3843

3944
#if MIN_VERSION_ghc(9,14,2)
4045
import Data.Function ((&))
4146
#endif
47+
import Control.Applicative ((<|>))
48+
import Control.Exception (assert)
4249
import Control.Monad
4350
import Control.Monad.IO.Class
4451
import qualified Crypto.Hash.SHA1 as H
@@ -75,7 +82,7 @@ import qualified GHC.Unit.Home.Graph as HUG
7582
import qualified Data.Set as Set
7683
import Data.Maybe
7784
import GHC.Types.Target (InputFileBuffer)
78-
import GHC (SingleStep, ExecResult, ModSummary (ms_hspp_opts))
85+
import GHC (SingleStep, ExecResult, ModSummary (ms_hspp_opts), ideclPkgQual)
7986
import Data.Set (Set)
8087
import qualified GHC.Unit as GHC
8188
import GHC.Unit.Module.Graph (mg_mss, ModuleGraphNode (..), mnKey)
@@ -89,7 +96,10 @@ import GHC.Driver.Pipeline (compileOne)
8996
import qualified GHC.Unit.Home.ModInfo as GHC
9097
import GHC.Utils.TmpFs
9198
import Data.Foldable (for_)
92-
import GHC.Plugins (SourceError, try)
99+
import GHC.Plugins (SourceError, try, RawPkgQual (..), HasCallStack, PackageName (..), mkFastString, lookupUnitId, GenericUnitInfo (unitPackageName))
100+
import GHC.Types.SourceText (StringLiteral(..), SourceText (..))
101+
import GHC.Stack.Annotation
102+
import GHC.Stack (callStack)
93103

94104
-- | Throws if package flags are unsatisfiable
95105
parseHomeUnitArguments :: GhcMonad m
@@ -161,13 +171,24 @@ setupHomeUnitGraph flagsAndTargets = do
161171
-- | Set up the 'HomeUnitGraph' with empty 'HomeUnitEnv's.
162172
-- The first 'DynFlags' are the 'DynFlags' for the interactive session.
163173
createHomeUnitGraph :: GHC.Logger -> [DynFlags] -> IO HomeUnitGraph
164-
createHomeUnitGraph logger unitDflags = do
174+
createHomeUnitGraph logger unitDflags0 = do
175+
-- See Note [ Ambiguous Package Qualified Imports Workaround ]
176+
let unitDflags = fixFlagsForIIDecl unitDflags0
165177
let home_units = Set.fromList $ map homeUnitId_ unitDflags
178+
166179
unitEnvList <- flip traverse unitDflags $ \ dflags -> do
180+
let uid = homeUnitId_ dflags
167181
hue <- setupNewHomeUnitEnv logger dflags Nothing home_units
168-
pure (homeUnitId_ dflags, hue)
182+
assert (homeUnitId_ (homeUnitEnv_dflags hue) == uid) $
183+
pure (uid, hue)
169184

170185
pure $ unitEnv_new (Map.fromList unitEnvList)
186+
where
187+
-- | Makes package names of home units unique and removes hidden modules.
188+
fixFlagsForIIDecl [df] | Just{} <- thisPackageName df = [df {hiddenModules = Set.empty}]
189+
-- TODO #288: pick more user-friendly names.
190+
fixFlagsForIIDecl dfss = map (\ dflags -> dflags { thisPackageName = Just (unitIdString (homeUnitId_ dflags))
191+
, hiddenModules = Set.empty}) dfss
171192

172193
setupNewHomeUnitEnv :: GHC.Logger -> DynFlags -> Maybe [GHC.UnitDatabase UnitId] -> Set UnitId -> IO HomeUnitEnv
173194
setupNewHomeUnitEnv logger dflags cached_dbs other_home_units = do
@@ -225,11 +246,18 @@ graphUnits mod_graph = L.nubOrd .
225246
InstantiationNode uid _ -> Just uid
226247
LinkNode _ _ -> Nothing
227248

249+
-- | WARNING: callback is not to be used from other threads.
250+
withUnliftGhc :: ((Ghc b -> IO b) -> IO a) -> Ghc a
251+
withUnliftGhc k = reifyGhc $ \ s -> k (flip reflectGhc s)
252+
253+
annotateCallStackGhc :: HasCallStack => Ghc a -> Ghc a
254+
annotateCallStackGhc m = let x = callStack in withUnliftGhc $ \k -> annotateStackShowIO x $ k m
255+
228256
-- | Rebuilds the UnitState of the unit, exposing the given packages.
229257
--
230258
-- Takes care of updating hsc_dflags, ue_platform, and ue_namever if this is the ue_currentUnit.
231259
setExposedInUnit :: UnitId -> [UnitId] -> Ghc ()
232-
setExposedInUnit unitId exposed = do
260+
setExposedInUnit unitId exposed = annotateCallStackGhc $ do
233261
env <- GHC.getSession
234262
let old_ie = case lookupHugUnitId unitId (hsc_HUG env) of
235263
Just hue -> hue
@@ -280,7 +308,7 @@ setupMultiHomeUnitGhcSession
280308
-> HscEnv -- ^ An empty HscEnv that we can use the setup the session.
281309
-> [(DynFlags, [GHC.Target])] -- ^ New components to be loaded. Expected to be non-empty.
282310
-> IO (HscEnv, [TargetDetails])
283-
setupMultiHomeUnitGhcSession exts hsc_env cis = do
311+
setupMultiHomeUnitGhcSession exts hsc_env cis = annotateCallStackIO $ do
284312
let dfs = map fst cis
285313

286314
hscEnv' <- initHomeUnitEnv dfs hsc_env
@@ -353,6 +381,31 @@ fromTargetId _ _ unitId (GHC.TargetFile f _) ctts = do
353381
| otherwise = (f ++ "-boot")
354382
return [TargetDetails (TargetFile f) [f, other] unitId ctts]
355383

384+
{-
385+
Note [ Ambiguous Package Qualified Imports Workaround ]
386+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
387+
388+
Source level package qualified imports `import "foo" A` interpret "foo" as a package name.
389+
390+
When one manually builds a `RawPkgQual` for an `ImportDecl` one can get away with using a unit-id, but only for external (i.e. not home) units.
391+
That it works does not seem entirely intended (see quoted snippet below), the code is in `renamePkgQual`: If a package qualifier is not found among packages it's looked up as an external unit. This is already in the code path for `OtherPkg` though, which is why Home Units are excluded.
392+
```
393+
| otherwise
394+
-> OtherPkg (UnitId pkg_fs)
395+
-- not really correct as pkg_fs is unlikely to be a valid unit-id but
396+
-- we will report the failure later...
397+
```
398+
399+
Home units will only be found if the qualifier matches their dflags' `thisPackageName`. However that's bugged because the lookup doesn't bother considering there can be multiple units in the same package (library, sublibraries and exe units), and just picks the first found, leading to an import error if e.g. the library unit is picked but the module was in the exe one.
400+
Related GHC issue: https://gitlab.haskell.org/ghc/ghc/-/issues/24227
401+
402+
Turns out that the package name of a home unit is pretty meaningless though, so we can update the dflags to replace it with anything that's actually unique so we can dodge the bug.
403+
404+
Another stumbling block is that the `IIDecl` mode of an `InteractiveImport` does not allow importing hidden modules, but again for home units we can alter the DynFlags so all modules are exposed.
405+
406+
See issue #288 for what can we do for users at the repl.
407+
-}
408+
356409
-- ----------------------------------------------------------------------------
357410
-- GHC Utils that should likely be exposed by GHC
358411
-- ----------------------------------------------------------------------------
@@ -363,6 +416,30 @@ mkSimpleTarget df fp = GHC.Target (GHC.TargetFile fp Nothing) True (homeUnitId_
363416
hscSetUnitEnv :: UnitEnv -> HscEnv -> HscEnv
364417
hscSetUnitEnv ue env = env { hsc_unit_env = ue }
365418

419+
home_unit_dflags :: HscEnv -> UnitId -> Maybe DynFlags
420+
home_unit_dflags hsc_env uid
421+
= fmap homeUnitEnv_dflags
422+
. HUG.lookupHugUnitId uid . ue_home_unit_graph
423+
. hsc_unit_env
424+
$ hsc_env
425+
426+
427+
lookupUnitPackageName :: HscEnv -> UnitId -> Maybe PackageName
428+
lookupUnitPackageName env uid = home_unit_name <|> ext_unit_name
429+
where
430+
home_unit_name = PackageName . mkFastString <$> (thisPackageName =<< home_unit_dflags env uid)
431+
ext_unit_name = unitPackageName <$> lookupUnitId (hsc_units env) uid
432+
433+
packageImportDecl :: PackageName -> ModuleName -> GHC.InteractiveImport
434+
packageImportDecl (PackageName pkgName) mn =
435+
GHC.IIDecl (GHC.simpleImportDecl $ mn)
436+
{ ideclPkgQual = RawPkgQual
437+
StringLiteral
438+
{ sl_st = NoSourceText
439+
, sl_fs = pkgName
440+
, sl_tc = Nothing
441+
}
442+
}
366443
-- ----------------------------------------------------------------------------
367444
-- Session cache directory
368445
-- ----------------------------------------------------------------------------
@@ -417,10 +494,10 @@ getTargetFileSummary hsc_env target
417494
home_unit = ue_unitHomeUnit uid (hsc_unit_env hsc_env)
418495
dflags = homeUnitEnv_dflags (ue_findHomeUnitEnv uid (hsc_unit_env hsc_env))
419496

420-
compileModuleWithDepsInHpt :: GhcMonad m =>
497+
compileModuleWithDepsInHpt ::
421498
GHC.Target ->
422-
m (Maybe SourceError)
423-
compileModuleWithDepsInHpt target@GHC.Target{targetUnitId = uid} = do
499+
Ghc (Maybe SourceError)
500+
compileModuleWithDepsInHpt target@GHC.Target{targetUnitId = uid} = annotateCallStackGhc $ do
424501
hsc_env0 <- getSession
425502
let !old_active = hscActiveUnitId hsc_env0
426503
let !hsc_env = hscSetActiveUnitId uid hsc_env0

haskell-debugger/GHC/Debugger/Session/Builtin.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ addInMemoryHsDebuggerViewUnit base_uids initialDynFlags = do
107107
, unitId /= rtsUnitId
108108
, unitId /= ghcInternalUnitId
109109
]
110+
, thisPackageName = Just "haskell-debugger-view"
110111
}
111112
& setGeneralFlag' Opt_HideAllPackages
112113
hsc_env <- getSession

0 commit comments

Comments
 (0)