04 Sep '26
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC
Commits:
252c2664 by Rodrigo Mesquita at 2026-09-04T19:25:54+01:00
fixes
- - - - -
1 changed file:
- compiler/GHC/Runtime/Eval.hs
Changes:
=====================================
compiler/GHC/Runtime/Eval.hs
=====================================
@@ -136,7 +136,6 @@ import Data.List.NonEmpty (NonEmpty)
import Unsafe.Coerce ( unsafeCoerce )
import qualified GHC.Unit.Home.Graph as HUG
import GHCi.BreakArray (BreakArray)
-import Data.Either (fromRight)
-- -----------------------------------------------------------------------------
-- running a statement interactively
@@ -315,8 +314,10 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
final_names = map getName final_ids
liftIO $ Loader.extendLoadedEnv interp modifyHomePackageBytecodeState (zip final_names hvals)
hsc_env' <- liftIO $ rttiEnvironment hsc_env{hsc_IC=final_ic}
+ let improved_final_ids = -- the final_ids are at the head of hsc_IC
+ take (length final_ids) [ i | AnId i <- ic_tythings (hsc_IC hsc_env') ]
setSession hsc_env'
- return (ExecComplete (Right final_ids {- TODO: THESE MUST BE IMPROVED BEFORE RETURNED -}) allocs)
+ return (ExecComplete (Right improved_final_ids) allocs)
-- Completed with an exception
EvalComplete alloc (EvalException e) ->
@@ -327,7 +328,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
let span = mkGeneralSrcSpan (fsLit "<unknown>")
- (hsc_env1, names) <- liftIO $
+ (hsc_env1, break_ids) <- liftIO $
bindLocalsAtBreakpoint hsc_env apStack_fhv span Nothing
let
resume = Resume
@@ -346,7 +347,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
hsc_env2 = pushResume hsc_env1 resume
setSession hsc_env2
- return (ExecBreak final_ids Nothing resume)
+ return (ExecBreak break_ids Nothing resume)
-- EvalBreak (Just ...) case: the interpreter stopped at a breakpoint
--
@@ -374,7 +375,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
-- This function only returns control to ghci with 'ExecBreak' when it is really meant to break.
-- Specifically, for :steplocal or :stepmodule, don't return control
-- and simply resume execution from here until we hit a breakpoint we do want to stop at.
- (hsc_env1, final_ids) <- liftIO $
+ (hsc_env1, break_ids) <- liftIO $
bindLocalsAtBreakpoint hsc_env apStack_fhv span (Just ibi)
let
resume = Resume
@@ -392,7 +393,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
}
hsc_env2 = pushResume hsc_env1 resume
setSession hsc_env2
- return (ExecBreak final_ids (Just ibi) resume)
+ return (ExecBreak break_ids (Just ibi) resume)
else do
-- resume with the same step type
let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
@@ -645,7 +646,9 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv span (Just ibi) = do
Loader.extendLoadedEnv interp modifyHomePackageBytecodeState (zip names fhvs)
when result_ok $ Loader.extendLoadedEnv interp modifyHomePackageBytecodeState [(result_name, apStack_fhv)]
hsc_env1 <- rttiEnvironment hsc_env{ hsc_IC = ictxt1 }
- return (hsc_env1, final_ids {-TODO: IMPROVE RTTI -})
+ let improved_final_ids = -- the final_ids are at the head of hsc_IC
+ take (length final_ids) [ i | AnId i <- ic_tythings (hsc_IC hsc_env1) ]
+ return (hsc_env1, improved_final_ids)
where
-- We need a fresh Unique for each Id we bind, because the linker
-- state is single-threaded and otherwise we'd spam old bindings
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/252c2664913c7b53b83b3cc70249ec0…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/252c2664913c7b53b83b3cc70249ec0…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/multi-threaded-debugger] 4 commits: move to GHCi.Run.Breakpoints
by Rodrigo Mesquita (@alt-romes) 04 Sep '26
by Rodrigo Mesquita (@alt-romes) 04 Sep '26
04 Sep '26
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC
Commits:
b7aee273 by Rodrigo Mesquita at 2026-09-04T10:39:49+01:00
move to GHCi.Run.Breakpoints
- - - - -
8dafed3e by Rodrigo Mesquita at 2026-09-04T17:10:33+01:00
Refactor: we have the interactive Ids directly... clean up significantly
Removes things like
names <- map idName ids
map (\i -> let id = find (\x -> i==idName x) names) idso
and is more straightforward because there's less state going around (no
more updating HscEnv as an output)
It also kills `printTypeOfNames` completely, since now we just have the
Ids. No longer `lookupName` names to get back the ids...
```
printTypeOfName :: GHC.GhcMonad m => Name -> m ()
printTypeOfName n
= do maybe_tything <- GHC.lookupName n
case maybe_tything of
Nothing -> return ()
Just thing -> printTyThing thing
```
now is just `printTyThing . AnId`
Also:
```diff
- tythings <- catMaybes `liftM` mapM GHC.lookupName namesSorted
- docs <- mapM pprTypeAndContents [i | AnId i <- tythings]
+ docs <- mapM pprTypeAndContents idsSorted
```
(and yes, in ExecBreaks there are only Ids, no TyThings, bc they are
only constructed by the interpreter execution which can only bind ids.
for TyThings use runDecls which doesn't return execBreaks.)
I have a plan for the `resumes`, I'm getting there.
Put the resume in ExecBreak
fixes
grrr
- - - - -
c43cb731 by Rodrigo Mesquita at 2026-09-04T17:29:40+01:00
UNDO the removing-hsc-env-globality part of the clean up and keep just the good parts
- - - - -
f7a22268 by Rodrigo Mesquita at 2026-09-04T17:38:20+01:00
just to test rtti of final_ids if that would work
- - - - -
9 changed files:
- compiler/GHC/Runtime/Debugger.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Monad.hs
- libraries/ghci/GHCi/Run.hs
- + libraries/ghci/GHCi/Run/Breakpoints.hs
- libraries/ghci/ghci.cabal.in
Changes:
=====================================
compiler/GHC/Runtime/Debugger.hs
=====================================
@@ -121,8 +121,7 @@ pprintClosureCommand bindThings force str = do
-- Then, we extract a substitution,
-- mapping the old tyvars to the reconstructed types.
let reconstructed_type = termType term
- hsc_env <- getSession
- case (improveRTTIType hsc_env id_ty' reconstructed_type) of
+ case improveRTTIType id_ty' reconstructed_type of
Nothing -> return (subst, term')
Just subst' -> do { logger <- getLogger
; liftIO $
=====================================
compiler/GHC/Runtime/Eval.hs
=====================================
@@ -131,7 +131,7 @@ import GHC.ByteCode.Breakpoints
import Control.Monad
import Data.Dynamic
import Data.IntMap (IntMap)
-import Data.List (find,intercalate)
+import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty)
import Unsafe.Coerce ( unsafeCoerce )
import qualified GHC.Unit.Home.Graph as HUG
@@ -244,12 +244,12 @@ execStmt' stmt stmt_text ExecOptions{..} = do
handleRunStatus execSingleStep execIsolateMode stmt_text bindings ids
status (emptyHistory size)
-runDecls :: GhcMonad m => String -> m [Name]
+runDecls :: GhcMonad m => String -> m [TyThing]
runDecls = runDeclsWithLocation "<interactive>" 1
-- | Run some declarations and return any user-visible names that were brought
-- into scope.
-runDeclsWithLocation :: GhcMonad m => String -> Int -> String -> m [Name]
+runDeclsWithLocation :: GhcMonad m => String -> Int -> String -> m [TyThing]
runDeclsWithLocation source line_num input = do
hsc_env <- getSession
decls <- liftIO (hscParseDeclsWithLocation hsc_env source line_num input)
@@ -258,18 +258,17 @@ runDeclsWithLocation source line_num input = do
-- | Like `runDeclsWithLocation`, but takes parsed declarations as argument.
-- Useful when doing preprocessing on the AST before execution, e.g. in GHCi
-- (see GHCi.UI.runStmt).
-runParsedDecls :: GhcMonad m => [LHsDecl GhcPs] -> m [Name]
+runParsedDecls :: GhcMonad m => [LHsDecl GhcPs] -> m [TyThing]
runParsedDecls decls = do
- hsc_env <- getSession
- (tyThings, ic) <- liftIO (hscParsedDecls hsc_env decls)
-
+ hsc_env <- getSession
+ (tyThings, ic) <- liftIO $ hscParsedDecls hsc_env decls
setSession $ hsc_env { hsc_IC = ic }
hsc_env <- getSession
hsc_env' <- liftIO $ rttiEnvironment hsc_env
setSession hsc_env'
- return $ filter (not . isDerivedOccName . nameOccName)
+ return $ filter (not . isDerivedOccName . nameOccName . getName)
-- For this filter, see Note [What to show to users]
- $ map getName tyThings
+ $ tyThings
{- Note [What to show to users]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -316,7 +315,8 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
liftIO $ Loader.extendLoadedEnv interp modifyHomePackageBytecodeState (zip final_names hvals)
hsc_env' <- liftIO $ rttiEnvironment hsc_env{hsc_IC=final_ic}
setSession hsc_env'
- return (ExecComplete (Right final_names) allocs)
+ let improved_final_ids = filter (`elem` final_ids) [ i | AnId i <- ic_tythings $ hsc_IC hsc_env' ] -- just to test
+ return (ExecComplete (Right improved_final_ids {- TODO: THESE MUST BE IMPROVED BEFORE RETURNED -}) allocs)
-- Completed with an exception
EvalComplete alloc (EvalException e) ->
@@ -327,7 +327,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
let span = mkGeneralSrcSpan (fsLit "<unknown>")
- (hsc_env1, names) <- liftIO $
+ (hsc_env1, break_ids) <- liftIO $
bindLocalsAtBreakpoint hsc_env apStack_fhv span Nothing
let
resume = Resume
@@ -346,7 +346,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
hsc_env2 = pushResume hsc_env1 resume
setSession hsc_env2
- return (ExecBreak names Nothing)
+ return (ExecBreak break_ids Nothing resume)
-- EvalBreak (Just ...) case: the interpreter stopped at a breakpoint
--
@@ -374,7 +374,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
-- This function only returns control to ghci with 'ExecBreak' when it is really meant to break.
-- Specifically, for :steplocal or :stepmodule, don't return control
-- and simply resume execution from here until we hit a breakpoint we do want to stop at.
- (hsc_env1, names) <- liftIO $
+ (hsc_env1, final_ids) <- liftIO $
bindLocalsAtBreakpoint hsc_env apStack_fhv span (Just ibi)
let
resume = Resume
@@ -392,7 +392,7 @@ handleRunStatus step isolateMode expr bindings final_ids status history0 = do
}
hsc_env2 = pushResume hsc_env1 resume
setSession hsc_env2
- return (ExecBreak names (Just ibi))
+ return (ExecBreak final_ids (Just ibi) resume)
else do
-- resume with the same step type
let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
@@ -500,13 +500,13 @@ getBreakArray interp InternalBreakpointId{ibi_info_mod} imbs = do
( ld_st'
, ba
)
-back :: GhcMonad m => Int -> m ([Name], Int, SrcSpan)
+back :: GhcMonad m => Int -> m ([Id], Int, SrcSpan)
back n = moveHist (+n)
-forward :: GhcMonad m => Int -> m ([Name], Int, SrcSpan)
+forward :: GhcMonad m => Int -> m ([Id], Int, SrcSpan)
forward n = moveHist (subtract n)
-moveHist :: GhcMonad m => (Int -> Int) -> m ([Name], Int, SrcSpan)
+moveHist :: GhcMonad m => (Int -> Int) -> m ([Id], Int, SrcSpan)
moveHist fn = do
hsc_env <- getSession
case ic_resume (hsc_IC hsc_env) of
@@ -530,7 +530,7 @@ moveHist fn = do
let hug = hsc_HUG hsc_env
brks <- readIModBreaks hug ibi
getBreakLoc (readIModModBreaks hug) ibi brks
- (hsc_env1, names) <-
+ (hsc_env1, final_ids) <-
liftIO $ bindLocalsAtBreakpoint hsc_env apStack span mb_info
let ic = hsc_IC hsc_env1
r' = r { resumeHistoryIx = new_ix }
@@ -538,7 +538,7 @@ moveHist fn = do
setSession hsc_env1{ hsc_IC = ic' }
- return (names, new_ix, span)
+ return (final_ids, new_ix, span)
-- careful: we want apStack to be the AP_STACK itself, not a thunk
-- around it, hence the cases are carefully constructed below to
@@ -564,7 +564,7 @@ bindLocalsAtBreakpoint
-> ForeignHValue
-> SrcSpan
-> Maybe InternalBreakpointId
- -> IO (HscEnv, [Name])
+ -> IO (HscEnv, [Id])
-- Nothing case: we stopped when an exception was raised, not at a
-- breakpoint. We have no location information or local variables to
@@ -584,7 +584,7 @@ bindLocalsAtBreakpoint hsc_env apStack span Nothing = do
interp = hscInterp hsc_env
--
Loader.extendLoadedEnv interp modifyHomePackageBytecodeState [(exn_name, apStack)]
- return (hsc_env{ hsc_IC = ictxt1 }, [exn_name])
+ return (hsc_env{ hsc_IC = ictxt1 }, [exn_id])
-- Just case: we stopped at a breakpoint, we have information about the location
-- of the breakpoint and the free variables of the expression.
@@ -645,7 +645,8 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv span (Just ibi) = do
Loader.extendLoadedEnv interp modifyHomePackageBytecodeState (zip names fhvs)
when result_ok $ Loader.extendLoadedEnv interp modifyHomePackageBytecodeState [(result_name, apStack_fhv)]
hsc_env1 <- rttiEnvironment hsc_env{ hsc_IC = ictxt1 }
- return (hsc_env1, if result_ok then result_name:names else names)
+ let improved_final_ids = filter (`elem` final_ids) [ i | AnId i <- ic_tythings $ hsc_IC hsc_env1 ] -- just to test
+ return (hsc_env1, improved_final_ids {-TODO: IMPROVE RTTI -})
where
-- We need a fresh Unique for each Id we bind, because the linker
-- state is single-threaded and otherwise we'd spam old bindings
@@ -692,12 +693,10 @@ rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do
[id | id <- tmp_ids
, not $ noSkolems id
, (occNameFS.nameOccName.idName) id /= result_fs]
- foldM improveTypes hsc_env (map idName incompletelyTypedIds)
+ foldM improveTypes hsc_env incompletelyTypedIds
where
noSkolems = noFreeVarsOfType . idType
- improveTypes hsc_env@HscEnv{hsc_IC=ic} name = do
- let tmp_ids = [id | AnId id <- ic_tythings ic]
- id = expectJust $ find (\i -> idName i == name) tmp_ids
+ improveTypes hsc_env@HscEnv{hsc_IC=ic} id = do
if noSkolems id
then return hsc_env
else do
@@ -706,7 +705,7 @@ rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do
case mb_new_ty of
Nothing -> return hsc_env
Just new_ty -> do
- case improveRTTIType hsc_env old_ty new_ty of
+ case improveRTTIType old_ty new_ty of
Nothing -> warnPprTrace True (":print failed to calculate the "
++ "improvement for a type")
(vcat [ text "id" <+> ppr id
=====================================
compiler/GHC/Runtime/Eval/Types.hs
=====================================
@@ -21,7 +21,6 @@ import GHCi.Message (EvalExpr)
import GHC.ByteCode.Types (InternalBreakpointId(..))
import GHC.Driver.Config (EvalStep(..))
import GHC.Types.Id
-import GHC.Types.Name
import GHC.Types.TyThing
import GHC.Types.Name.Reader
import GHC.Types.SrcLoc
@@ -160,7 +159,7 @@ data ExecResult
-- | Execution is complete with either an exception or the list of
-- user-visible names that were brought into scope.
= ExecComplete
- { execResult :: Either SomeException [Name]
+ { execResult :: Either SomeException [Id]
, execAllocation :: Word64
}
@@ -171,8 +170,10 @@ data ExecResult
-- subsequently deciding whether to really stop here.
-- `ExecBreak` always means GHCi breaks.
| ExecBreak
- { breakNames :: [Name]
+ { breakNames :: [Id]
, breakPointId :: Maybe InternalBreakpointId
+ , breakResume :: Resume
+ -- ^ to resume this thread blocked on this break
}
-- | Essentially a GlobalRdrEnv, but with additional cached values to allow
=====================================
compiler/GHC/Runtime/Heap/Inspect.hs
=====================================
@@ -1160,8 +1160,8 @@ findPtrTyss i tys = foldM step (i, []) tys
-- improveType <base_type> <rtti_type>
-- The types can contain skolem type variables, which need to be treated as normal vars.
-- In particular, we want them to unify with things.
-improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe Subst
-improveRTTIType _ base_ty new_ty = U.tcUnifyDebugger base_ty new_ty
+improveRTTIType :: RttiType -> RttiType -> Maybe Subst
+improveRTTIType base_ty new_ty = U.tcUnifyDebugger base_ty new_ty
getDataConArgTys :: DataCon -> Type -> TR [Type]
-- Given the result type ty of a constructor application (D a b c :: ty)
=====================================
ghc/GHCi/UI.hs
=====================================
@@ -1473,7 +1473,7 @@ runOneCommand eh gCmd = do
-- runStmt wrapper for temporarily overridden line-number
runStmtWithLineNum :: Int -> String -> SingleStep
- -> GHCi (Maybe GHC.ExecResult)
+ -> GHCi (Maybe RunResult)
runStmtWithLineNum lnum stmt step = do
st0 <- getGHCiState
setGHCiState st0 { line_number = lnum }
@@ -1538,7 +1538,7 @@ enqueueCommands cmds = do
-- | Entry point to execute some haskell code from user.
-- The return value True indicates success, as in `runOneCommand`.
-runStmt :: GhciMonad m => String -> SingleStep -> m (Maybe GHC.ExecResult)
+runStmt :: GhciMonad m => String -> SingleStep -> m (Maybe RunResult)
runStmt input step = do
dflags <- GHC.getInteractiveDynFlags
let pflags = initParserOpts dflags
@@ -1560,7 +1560,7 @@ runStmt input step = do
case mb_stmt of
Nothing ->
-- empty statement / comment
- return (Just exec_complete)
+ return (Just (RunDeclsResult []))
Just stmt ->
run_stmt stmt
@@ -1583,8 +1583,6 @@ runStmt input step = do
run_imports imports
run_decls decls
where
- exec_complete = GHC.ExecComplete (Right []) 0
-
run_imports imports = mapM_ (addImportToContext . unLoc) imports
set_pragmas pflags sec supported =
@@ -1593,12 +1591,12 @@ runStmt input step = do
opts = unLoc <$> loc_opts
in setOptions opts
- run_stmt :: GhciMonad m => GhciLStmt GhcPs -> m (Maybe GHC.ExecResult)
+ run_stmt :: GhciMonad m => GhciLStmt GhcPs -> m (Maybe RunResult)
run_stmt stmt = do
m_result <- GhciMonad.runStmt stmt input step
case m_result of
Nothing -> return Nothing
- Just result -> Just <$> afterRunStmt step result
+ Just result -> Just <$> afterRunStmt (RunStmtResult result)
-- `x = y` (a declaration) should be treated as `let x = y` (a statement).
-- The reason is because GHCi wasn't designed to support `x = y`, but then
@@ -1614,7 +1612,7 @@ runStmt input step = do
--
-- Instead of dealing with all these problems individually here we fix this
-- mess by just treating `x = y` as `let x = y`.
- run_decls :: GhciMonad m => [LHsDecl GhcPs] -> m (Maybe GHC.ExecResult)
+ run_decls :: GhciMonad m => [LHsDecl GhcPs] -> m (Maybe RunResult)
-- Only turn `FunBind` and `VarBind` into statements, other bindings
-- (e.g. `PatBind`) need to stay as decls.
run_decls [L l (ValD _ bind@FunBind{})] = run_stmt (mk_stmt (locA l) bind)
@@ -1629,8 +1627,8 @@ runStmt input step = do
-- a file, otherwise the read buffer can't be flushed).
_ <- liftIO $ tryIO $ hFlushAll stdin
m_result <- GhciMonad.runDecls' decls
- forM m_result $ \result ->
- afterRunStmt step (GHC.ExecComplete (Right result) 0)
+ forM m_result $ \result -> do
+ afterRunStmt (RunDeclsResult result)
mk_stmt :: SrcSpan -> HsBind GhcPs -> GhciLStmt GhcPs
mk_stmt loc bind =
@@ -1646,32 +1644,33 @@ runStmt input step = do
where
modStr = moduleNameString $ moduleName $ icInteractiveModule $ ic
+data RunResult
+ = RunStmtResult GHC.ExecResult
+ | RunDeclsResult [TyThing]
+
-- | Clean up the GHCi environment after a statement has run
-afterRunStmt :: GhciMonad m => SingleStep {-^ Type of step we took just before -}
- -> GHC.ExecResult -> m GHC.ExecResult
-afterRunStmt step run_result = do
- resumes <- GHC.getResumeContext
+afterRunStmt :: GhciMonad m
+ => RunResult -> m RunResult
+afterRunStmt run_result = do
case run_result of
- GHC.ExecComplete{..} ->
- case execResult of
- Left ex -> liftIO $ Exception.throwIO ex
- Right names -> do
- show_types <- isOptionSet ShowType
- when show_types $ printTypeOfNames names
- GHC.ExecBreak names mb_info
- | first_resume : _ <- resumes
- -> do mb_id_loc <- toBreakIdAndLocation mb_info
- let bCmd = maybe "" ( \(_,l) -> onBreakCmd l ) mb_id_loc
- if (null bCmd)
- then printStoppedAtBreakInfo first_resume names
- else enqueueCommands [bCmd]
- -- run the command set with ":set stop <cmd>"
- st <- getGHCiState
- enqueueCommands [stop st]
- return ()
-
- | otherwise -> resume step Nothing >>=
- afterRunStmt step >> return ()
+ RunDeclsResult things -> do
+ show_types <- isOptionSet ShowType
+ when show_types $ printTypeOfThings things
+ RunStmtResult GHC.ExecComplete{..} ->
+ case execResult of
+ Left ex -> liftIO $ Exception.throwIO ex
+ Right ids -> do
+ show_types <- isOptionSet ShowType
+ when show_types $ printTypeOfIds ids
+ RunStmtResult (GHC.ExecBreak ids mb_info resume) -> do
+ mb_id_loc <- toBreakIdAndLocation mb_info
+ let bCmd = maybe "" ( \(_,l) -> onBreakCmd l ) mb_id_loc
+ if (null bCmd)
+ then printStoppedAtBreakInfo resume ids
+ else enqueueCommands [bCmd]
+ -- run the command set with ":set stop <cmd>"
+ st <- getGHCiState
+ enqueueCommands [stop st]
flushInterpBuffers
withSignalHandlers $ do
@@ -1680,16 +1679,18 @@ afterRunStmt step run_result = do
return run_result
-runSuccess :: Maybe GHC.ExecResult -> Bool
+runSuccess :: Maybe RunResult -> Bool
runSuccess run_result
- | Just (GHC.ExecComplete { execResult = Right _ }) <- run_result = True
+ | Just (RunDeclsResult _) <- run_result = True
+ | Just (RunStmtResult GHC.ExecComplete { execResult = Right _ })
+ <- run_result = True
| otherwise = False
-runAllocs :: Maybe GHC.ExecResult -> Maybe Integer
+runAllocs :: Maybe RunResult -> Maybe Integer
runAllocs m = do
res <- m
case res of
- GHC.ExecComplete{..} -> Just (fromIntegral execAllocation)
+ RunStmtResult GHC.ExecComplete{..} -> Just (fromIntegral execAllocation)
_ -> Nothing
toBreakIdAndLocation :: GhciMonad m
@@ -1703,30 +1704,23 @@ toBreakIdAndLocation (Just inf) = do
return $ listToMaybe [ id_loc | id_loc@(_,loc) <- IntMap.assocs (breaks st),
breakId loc == bi ]
-printStoppedAtBreakInfo :: GHC.GhcMonad m => Resume -> [Name] -> m ()
-printStoppedAtBreakInfo res names = do
+printStoppedAtBreakInfo :: GHC.GhcMonad m => Resume -> [GHC.Id] -> m ()
+printStoppedAtBreakInfo res ids = do
printForUser =<< pprStopped res
- -- printTypeOfNames session names
- let namesSorted = sortBy compareNames names
- tythings <- catMaybes `liftM` mapM GHC.lookupName namesSorted
- docs <- mapM pprTypeAndContents [i | AnId i <- tythings]
+ let idsSorted = sortBy (compareNames `on` getName) ids
+ docs <- mapM pprTypeAndContents idsSorted
printForUserPartWay $ vcat docs
-printTypeOfNames :: GHC.GhcMonad m => [Name] -> m ()
-printTypeOfNames names
- = mapM_ (printTypeOfName ) $ sortBy compareNames names
+printTypeOfIds :: GHC.GhcMonad m => [GHC.Id] -> m ()
+printTypeOfIds = printTypeOfThings . map AnId
+
+printTypeOfThings :: GHC.GhcMonad m => [GHC.TyThing] -> m ()
+printTypeOfThings things
+ = mapM_ printTyThing $ sortBy (compareNames `on` getName) things
compareNames :: Name -> Name -> Ordering
compareNames = on compare getOccString S.<> on SrcLoc.leftmost_smallest getSrcSpan
-printTypeOfName :: GHC.GhcMonad m => Name -> m ()
-printTypeOfName n
- = do maybe_tything <- GHC.lookupName n
- case maybe_tything of
- Nothing -> return ()
- Just thing -> printTyThing thing
-
-
data MaybeCommand = GotCommand Command | BadCommand | NoLastCommand
-- | Entry point for execution a ':<command>' input from user
@@ -4375,7 +4369,7 @@ doContinue step = doContinue' step Nothing
doContinue' :: GhciMonad m => SingleStep -> Maybe Int -> m ()
doContinue' step mbCnt= do
runResult <- resume step mbCnt
- _ <- afterRunStmt step runResult
+ _ <- afterRunStmt (RunStmtResult runResult)
return ()
abandonCmd :: GhciMonad m => String -> m ()
@@ -4526,9 +4520,9 @@ backCmd arg
| otherwise = liftIO $ putStrLn "Syntax: :back [num]"
where
back num = withSandboxOnly ":back" $ do
- (names, _, pan) <- GHC.back num
+ (ids, _, pan) <- GHC.back num
printForUser $ text "Logged breakpoint at" <+> ppr pan
- printTypeOfNames names
+ printTypeOfIds ids
-- run the command set with ":set stop <cmd>"
st <- getGHCiState
enqueueCommands [stop st]
@@ -4540,11 +4534,11 @@ forwardCmd arg
| otherwise = liftIO $ putStrLn "Syntax: :forward [num]"
where
forward num = withSandboxOnly ":forward" $ do
- (names, ix, pan) <- GHC.forward num
+ (ids, ix, pan) <- GHC.forward num
printForUser $ (if (ix == 0)
then text "Stopped at"
else text "Logged breakpoint at") <+> ppr pan
- printTypeOfNames names
+ printTypeOfIds ids
-- run the command set with ":set stop <cmd>"
st <- getGHCiState
enqueueCommands [stop st]
=====================================
ghc/GHCi/UI/Monad.hs
=====================================
@@ -381,7 +381,7 @@ runStmt stmt stmt_text step = do
(EvalThis fhv) }
Just <$> GHC.execStmt' stmt stmt_text opts
-runDecls :: GhciMonad m => String -> m (Maybe [GHC.Name])
+runDecls :: GhciMonad m => String -> m (Maybe [GHC.TyThing])
runDecls decls = do
st <- getGHCiState
reifyGHCi $ \x ->
@@ -393,7 +393,7 @@ runDecls decls = do
r <- GHC.runDeclsWithLocation (progname st) (line_number st) decls
return (Just r)
-runDecls' :: GhciMonad m => [LHsDecl GhcPs] -> m (Maybe [GHC.Name])
+runDecls' :: GhciMonad m => [LHsDecl GhcPs] -> m (Maybe [GHC.TyThing])
runDecls' decls = do
st <- getGHCiState
reifyGHCi $ \x ->
=====================================
libraries/ghci/GHCi/Run.hs
=====================================
@@ -1,7 +1,6 @@
{-# LANGUAGE GADTs, RecordWildCards, MagicHash, ScopedTypeVariables, CPP,
- UnboxedTuples, LambdaCase, UnliftedFFITypes, NamedFieldPuns #-}
+ UnboxedTuples, LambdaCase, UnliftedFFITypes, NamedFieldPuns, BangPatterns #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
-{-# LANGUAGE BangPatterns #-}
-- |
-- Execute GHCi messages.
@@ -20,6 +19,7 @@ import GHCi.CreateBCO
import GHCi.InfoTable
#endif
+import GHCi.Run.Breakpoints
import GHCi.Coverage
import qualified GHC.InfoProv as InfoProv
import GHCi.Debugger
@@ -31,8 +31,6 @@ import GHCi.TH
import GHCi.BreakArray
import GHCi.StaticPtrTable
-import qualified Data.Map as Map
-import qualified Data.Set as Set
import Control.Concurrent
import Control.DeepSeq
import Control.Exception
@@ -290,7 +288,7 @@ abandonStmt hvref = do
tid <- localRef hvref
ResumeContext{..} <- getThreadResumeContext tid
killThread tid
- withShadowThread tid $ do
+ withIsolatedThread tid $ do
putMVar resumeBreakMVar ()
_ <- atomically $
readThreadEvalStatus tid
@@ -424,13 +422,13 @@ withBreakAction opts tid@(ThreadId tid#) act
runIf# singleStep rts_enableStopNextBreakpoint
runIf# stepOut rts_enableStopAfterReturn
- runIf isolateThreadBreaks setShadowThread
+ runIf isolateThreadBreaks setIsolatedThread
resetBreakAction () = do
poke exceptionFlag 0
rts_disableStopAfterReturn tid#
rts_disableStopNextBreakpoint tid#
- runIf isolateThreadBreaks unsetShadowThread
+ runIf isolateThreadBreaks unsetIsolatedThread
-- freeStablePtr stablePtr TODO: we never free the global stablePtr for
-- global action. Maybe we should have a de-init for clean up.
@@ -484,186 +482,3 @@ clearEvalStatus = \case
EvalComplete w (EvalSuccess _) -> EvalComplete w (EvalSuccess ())
EvalBreak ap mb rt rccs -> EvalBreak ap mb rt rccs
---------------------------------------------------------------------------------
--- Global Debugger Per-Thread Context
--- TODO: when to clean MVar? how to figure out when Threads are dead and we'll never need their mvars again? perhaps a finalizer on the thread id?
--- TODO: Move to independent module, and don't expose the global variables at all. move also the global break action maybe.
--- Maybe there could even be something in that module for setting the global
--- break action on start. maybe just an init function
---
--- TODO: Note which explains when things are evacuated from the map, why
--- EvalSuccess's don't leak, what is the idea, why have MVar and TVar, why have
--- shadowed threads. Must also explain whatever we come up with for Finalizers
--- to make sure threads evacuate themselves out of the map. Explain in the note
--- as well the status of GHCi multi-threaded, explaining it currently uses
--- single-threaded
---
--- TODO: Write a blog post about multi-threaded debugging (mby with the contents of the note..)
-
-globalBreakStablePtr :: StablePtr BreakpointCallback
-globalBreakStablePtr = unsafePerformIO $ newStablePtr globalBreakAction
-
-globalBreakAction :: BreakpointCallback
-globalBreakAction info_mod# info_mod_uid# infox# is_exception apStack = do
- tid <- myThreadId
- ResumeContext{..} <- getThreadResumeContext tid
- resume_r <- mkRemoteRef tid
- apStack_r <- mkRemoteRef apStack
- ccs <- toRemotePtr <$> getCCSOf apStack
- breakpoint <-
- if is_exception
- then pure Nothing
- else do
- info_mod <- BS.packCString (Ptr info_mod#)
- info_mod_uid <- BS.packCString (Ptr info_mod_uid#)
- pure (Just (EvalBreakpoint info_mod info_mod_uid (I# infox#)))
- writeThreadEvalStatus tid $ EvalBreak apStack_r breakpoint resume_r ccs
-
- -- Block until this thread is resumed (by the thread which took the
- -- `ResumeContext` from the `statusMVar`).
- --
- -- The `onBreak` function must have been called from `rts/Interpreter.c`
- -- when interpreting a `BRK_FUN`. After taking from the MVar, the function
- -- returns to the continuation on the stack which is where the interpreter
- -- was stopped.
- takeMVar resumeBreakMVar
-
-getThreadResumeContext :: ThreadId -> IO ResumeContext
-getThreadResumeContext tid = modifyMVar globalBreakActionMap $ \gbm0 -> do
- case Map.lookup tid gbm0 of
- Nothing -> do
- r <- ResumeContext <$> newEmptyMVar
- let !gbm1 = Map.insert tid r gbm0
- pure (gbm1, r)
- Just ctx -> do
- pure (gbm0, ctx)
-
--- | A global mapping from thread ids to the MVars on which they block when stopped.
-globalBreakActionMap :: MVar (Map.Map ThreadId ResumeContext)
-globalBreakActionMap = unsafePerformIO $ newMVar Map.empty
-{-# NOINLINE globalBreakActionMap #-}
-
-newtype ResumeContext = ResumeContext
- { resumeBreakMVar :: MVar ()
- -- ^ A thread that hits a breakpoint blocks reading its corresponding MVar
- -- (gotten from the 'globalBreakActionMap').
- -- The debugger can unblock that thread by signaling its MVar.
- }
-
--- | To evaluate a statement in the interpreter, a thread is forked by
--- `sandboxIO` to run the statement. To synchronize, the statement result will
--- be written by the thread to the global thread dbg status
--- (`writeThreadEvalStatus`) and `sandboxIO` will block waiting for either any
--- breakpoint to be hit ('readAnyThreadEvalBreak') /or/ for the thread
--- specifically to write its final result. To wait for that thread
--- specifically, use this function.
---
--- Succeeds when the given thread writes to the global debugger map an eval result.
-readThreadEvalStatus :: ThreadId -> STM (EvalStatus [HValueRef])
-readThreadEvalStatus tid = do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- case Map.lookup tid threadEvalStatuses of
- Just r -> do
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses = Map.delete tid threadEvalStatuses -- clear it
- , shadowedThreads
- }
- pure r
- Nothing -> retry -- until someone writes the result for this thread
-
--- | All threads running in the interpreter will be debugger-enabled, meaning
--- if they hit an enabled breakpoint, the 'globalBreakAction' will be
--- executed and write a 'EvalBreak' value to the global thread dbg status map.
--- The debugger will be waiting for threads to break using `readAnyThreadEvalBreak`.
---
--- Succeeds when any thread hits a breakpoint.
-readAnyThreadEvalBreak :: STM EvalBreak
-readAnyThreadEvalBreak = do
- dbgs <- readTVar globalDbgStatusVar
- -- try reading any thread's 'EvalBreak' and retry if none have yielded one yet
- foldr (\x next -> readStatus dbgs x `orElse` next) retry (Map.toList (threadEvalStatuses dbgs))
- where
- readStatus GlobalDbgStatus{..} (tid, _) -- don't look at dbg-shadowed threads
- | Set.member tid shadowedThreads
- = retry
-
- readStatus GlobalDbgStatus{..} (tid, status)
- = do
- case status of
- EvalComplete{} -> retry -- successes can be read with `readThreadEvalStatus`
- EvalPaused b -> do
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses = Map.delete tid threadEvalStatuses
- , shadowedThreads
- }
- pure b
-
-writeThreadEvalStatus :: ThreadId -> EvalStatus [HValueRef] -> IO ()
-writeThreadEvalStatus tid new =
- atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar $
- GlobalDbgStatus
- { threadEvalStatuses =
- Map.insertWith (\_ _ -> error "writeThreadEvalStatus: should be impossible")
- tid new threadEvalStatuses
- , shadowedThreads }
-
--- | Brackets 'setShadowThread'
-withShadowThread :: ThreadId -> IO a -> IO a
-withShadowThread tid = bracket_ (setShadowThread tid) (unsetShadowThread tid)
-
--- | Make sure that anyone watching for threads hitting breakpoints with
--- 'readAnyThreadEvalBreak' ignore the given thread if it ever hits a breakpoint.
---
--- This forces this thread's result to be received solely through
--- 'readThreadEvalStatus' for that ThreadId, which is typically desired when an
--- expression to run should not be seen at all by the debugger (e.g. when
--- evaluating an expr to :force a variable)
-setShadowThread :: ThreadId -> IO ()
-setShadowThread tid = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , shadowedThreads = Set.insert tid shadowedThreads
- }
-
--- | Undoes 'setShadowThread'
-unsetShadowThread :: ThreadId -> IO ()
-unsetShadowThread tid = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , shadowedThreads = Set.delete tid shadowedThreads
- }
-
--- | When we launch a new thread using the debugger, we map ... TODO ... all
--- threads launched by that main stmt may further spawn threads, and any of
--- them might hit a breakpoint (break array toggled, or per-thread step in
--- might be used, or pause button, etc...).
---
--- When we launch that main thread we also write its result to this map. So
--- this map will only contain the results of threads we explicitly care to
--- observe the result of. Not all threads will write their results here,
--- certainly not ones spawned unknowningly by the main stmt.
--- ...
--- todo words
-globalDbgStatusVar :: TVar GlobalDbgStatus
-globalDbgStatusVar = unsafePerformIO $ newTVarIO $ GlobalDbgStatus Map.empty Set.empty
-{-# NOINLINE globalDbgStatusVar #-}
-
-data GlobalDbgStatus = GlobalDbgStatus
- { threadEvalStatuses :: !(Map.Map ThreadId (EvalStatus [HValueRef]))
- -- ^ Whenever a thread hits a breakpoint it inserts in this map its 'EvalBreak'.
- -- We can additionally observe a thread's result by calling
- -- `writeThreadEvalStatus tid (EvalComplete ...)` when the main expression
- -- finishes evaluating, see e.g. 'sandboxIO'.
- , shadowedThreads :: !(Set.Set ThreadId)
- -- ^ Threads in this map are meant to be ignored by 'readAnyThreadEvalBreak'.
- -- That is, even if one of the threads in this set hits a breakpoint, we
- -- can only read its 'EvalBreak' with @'readThreadEvalStatus' tid@ directly.
- }
=====================================
libraries/ghci/GHCi/Run/Breakpoints.hs
=====================================
@@ -0,0 +1,264 @@
+{-# LANGUAGE GADTs, RecordWildCards, MagicHash, ScopedTypeVariables, CPP,
+ UnboxedTuples, LambdaCase, UnliftedFFITypes, NamedFieldPuns, BangPatterns #-}
+module GHCi.Run.Breakpoints
+ (
+ -- * Global break action
+ globalBreakStablePtr, globalBreakAction
+
+ -- * Per-thread breakpoints
+ , readThreadEvalStatus, readAnyThreadEvalBreak
+ , writeThreadEvalStatus
+
+ -- ** Isolating thread breakpoints
+ , withIsolatedThread
+ , setIsolatedThread, unsetIsolatedThread
+
+ -- ** Querying the global thread breakpoint map
+ , listOtherPausedThreads
+
+ -- * Per-thread resume-ing
+ , getThreadResumeContext, ResumeContext(..)
+
+ )
+ where
+
+import Prelude -- See note [Why do we import Prelude here?]
+
+import GHCi.Debugger
+import GHCi.Message
+import GHCi.RemoteTypes
+
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import Control.Concurrent
+import Control.Exception
+import qualified Data.ByteString.Short.Internal as BS
+#if defined(PROFILING)
+import GHC.Data.ShortByteString
+#endif
+import GHC.Exts
+import GHC.Stack
+import Foreign hiding (void)
+import GHC.Conc.Sync
+import GHC.IO hiding ( bracket )
+
+--------------------------------------------------------------------------------
+-- * Global break action
+--------------------------------------------------------------------------------
+-- Global Debugger Per-Thread Context
+-- TODO: when to clean MVar? how to figure out when Threads are dead and we'll
+-- never need their mvars again? perhaps a finalizer on the thread id?
+--
+-- TODO: Move to independent module, and don't expose the global variables at all. move also the global break action maybe.
+-- Maybe there could even be something in that module for setting the global
+-- break action on start. maybe just an init function
+--
+-- TODO: Note which explains when things are evacuated from the map, why
+-- EvalSuccess's don't leak, what is the idea, why have MVar and TVar, why have
+-- isolated threads. Must also explain whatever we come up with for Finalizers
+-- to make sure threads evacuate themselves out of the map. Explain in the note
+-- as well the status of GHCi multi-threaded, explaining it currently uses
+-- single-threaded
+--
+-- TODO: Write a blog post about multi-threaded debugging (mby with the contents of the note..)
+
+-- TODO: Maybe this could be the return value of doing initialization for the
+-- debugger, which allocates this stable pointer and sets it in the global action.
+--
+-- And de-initialization deallocates this stable pointer and then some other things
+globalBreakStablePtr :: StablePtr BreakpointCallback
+globalBreakStablePtr = unsafePerformIO $ newStablePtr globalBreakAction
+
+globalBreakAction :: BreakpointCallback
+globalBreakAction info_mod# info_mod_uid# infox# is_exception apStack = do
+ tid <- myThreadId
+ ResumeContext{..} <- getThreadResumeContext tid
+ resume_r <- mkRemoteRef tid
+ apStack_r <- mkRemoteRef apStack
+ ccs <- toRemotePtr <$> getCCSOf apStack
+ mb_breakpoint <-
+ if is_exception
+ then pure Nothing
+ else do
+ info_mod <- BS.packCString (Ptr info_mod#)
+ info_mod_uid <- BS.packCString (Ptr info_mod_uid#)
+ pure (Just (EvalBreakpoint info_mod info_mod_uid (I# infox#)))
+ writeThreadEvalStatus tid $ EvalBreak apStack_r mb_breakpoint resume_r ccs
+
+ -- Block until this thread is resumed (by the thread which took the
+ -- `ResumeContext` from the `statusMVar`).
+ --
+ -- The `onBreak` function must have been called from `rts/Interpreter.c`
+ -- when interpreting a `BRK_FUN`. After taking from the MVar, the function
+ -- returns to the continuation on the stack which is where the interpreter
+ -- was stopped.
+ takeMVar resumeBreakMVar
+
+--------------------------------------------------------------------------------
+-- * Per-thread breakpoints
+--------------------------------------------------------------------------------
+
+-- | To evaluate a statement in the interpreter, a thread is forked by
+-- `sandboxIO` to run the statement. To synchronize, the statement result will
+-- be written by the thread to the global thread dbg status
+-- (`writeThreadEvalStatus`) and `sandboxIO` will block waiting for either any
+-- breakpoint to be hit ('readAnyThreadEvalBreak') /or/ for the thread
+-- specifically to write its final result. To wait for that thread
+-- specifically, use this function.
+--
+-- Succeeds when the given thread writes to the global debugger map an eval result.
+readThreadEvalStatus :: ThreadId -> STM (EvalStatus [HValueRef])
+readThreadEvalStatus tid = do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ case Map.lookup tid threadEvalStatuses of
+ Just r -> do
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses = Map.delete tid threadEvalStatuses -- clear it
+ , isolatedThreads
+ }
+ pure r
+ Nothing -> retry -- until someone writes the result for this thread
+
+-- | All threads running in the interpreter will be debugger-enabled, meaning
+-- if they hit an enabled breakpoint, the 'globalBreakAction' will be
+-- executed and write a 'EvalBreak' value to the global thread dbg status map.
+-- The debugger will be waiting for threads to break using `readAnyThreadEvalBreak`.
+--
+-- Succeeds when any thread hits a breakpoint.
+readAnyThreadEvalBreak :: STM EvalBreak
+readAnyThreadEvalBreak = do
+ dbgs <- readTVar globalDbgStatusVar
+ -- try reading any thread's 'EvalBreak' and retry if none have yielded one yet
+ foldr (\x next -> readStatus dbgs x `orElse` next) retry (Map.toList (threadEvalStatuses dbgs))
+ where
+ readStatus GlobalDbgStatus{..} (tid, _) -- don't look at isolated threads
+ | Set.member tid isolatedThreads
+ = retry
+
+ readStatus GlobalDbgStatus{..} (tid, status)
+ = do
+ case status of
+ EvalComplete{} -> retry -- successes can be read with `readThreadEvalStatus`
+ EvalPaused b -> do
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses = Map.delete tid threadEvalStatuses
+ , isolatedThreads
+ }
+ pure b
+
+writeThreadEvalStatus :: ThreadId -> EvalStatus [HValueRef] -> IO ()
+writeThreadEvalStatus tid status =
+ atomically $ do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ writeTVar globalDbgStatusVar $
+ GlobalDbgStatus
+ { threadEvalStatuses =
+ Map.insertWith (\_ _ -> error "writeThreadEvalStatus: should be impossible")
+ tid status threadEvalStatuses
+ , isolatedThreads }
+
+
+-- ** Isolating thread breakpoints ---------------------------------------------
+
+-- | Brackets 'setIsolatedThread'
+withIsolatedThread :: ThreadId -> IO a -> IO a
+withIsolatedThread tid = bracket_ (setIsolatedThread tid) (unsetIsolatedThread tid)
+
+-- | Make sure that anyone watching for threads hitting breakpoints with
+-- 'readAnyThreadEvalBreak' ignore the given thread if it ever hits a breakpoint.
+--
+-- This forces this thread's result to be received solely through
+-- 'readThreadEvalStatus' for that ThreadId, which is typically desired when an
+-- expression to run should not be seen at all by the debugger (e.g. when
+-- evaluating an expr to :force a variable)
+setIsolatedThread :: ThreadId -> IO ()
+setIsolatedThread tid = atomically $ do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses
+ , isolatedThreads = Set.insert tid isolatedThreads
+ }
+
+-- | Undoes 'setIsolatedThread'
+unsetIsolatedThread :: ThreadId -> IO ()
+unsetIsolatedThread tid = atomically $ do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses
+ , isolatedThreads = Set.delete tid isolatedThreads
+ }
+
+-- ** Querying the global thread breakpoint map --------------------------------
+
+-- | List all other threads paused on a breakpoint.
+--
+-- We say "other" because this function is meant to be used in the scenario
+-- where you are already paused at a breakpoint (which has been popped off of
+-- the global thread mapping) and are asking about which other threads are
+-- paused to list them.
+listOtherPausedThreads :: IO [ThreadId]
+listOtherPausedThreads = do
+ m <- atomically $ readTVar globalDbgStatusVar
+ pure $ Map.keys $ Map.filter isBreak (threadEvalStatuses m)
+ where
+ isBreak EvalBreak{} = True
+ isBreak EvalComplete{} = False
+
+-- ** Global thread exec status map --------------------------------------------
+
+-- | When we launch a new thread using the debugger, we map ... TODO ... all
+-- threads launched by that main stmt may further spawn threads, and any of
+-- them might hit a breakpoint (break array toggled, or per-thread step in
+-- might be used, or pause button, etc...).
+--
+-- When we launch that main thread we also write its result to this map. So
+-- this map will only contain the results of threads we explicitly care to
+-- observe the result of. Not all threads will write their results here,
+-- certainly not ones spawned unknowningly by the main stmt.
+-- ...
+-- todo words
+globalDbgStatusVar :: TVar GlobalDbgStatus
+globalDbgStatusVar = unsafePerformIO $ newTVarIO $ GlobalDbgStatus Map.empty Set.empty
+{-# NOINLINE globalDbgStatusVar #-}
+
+data GlobalDbgStatus = GlobalDbgStatus
+ { threadEvalStatuses :: !(Map.Map ThreadId (EvalStatus [HValueRef]))
+ -- ^ Whenever a thread hits a breakpoint it inserts in this map its 'EvalBreak'.
+ -- We can additionally observe a thread's result by calling
+ -- `writeThreadEvalStatus tid (EvalComplete ...)` when the main expression
+ -- finishes evaluating, see e.g. 'sandboxIO'.
+ , isolatedThreads :: !(Set.Set ThreadId)
+ -- ^ Threads in this map are meant to be ignored by 'readAnyThreadEvalBreak'.
+ -- That is, even if one of the threads in this set hits a breakpoint, we
+ -- can only read its 'EvalBreak' with @'readThreadEvalStatus' tid@ directly.
+ }
+
+--------------------------------------------------------------------------------
+-- * Per-thread resume-ing
+--------------------------------------------------------------------------------
+
+getThreadResumeContext :: ThreadId -> IO ResumeContext
+getThreadResumeContext tid = modifyMVar globalBreakActionMap $ \gbm0 -> do
+ case Map.lookup tid gbm0 of
+ Nothing -> do
+ r <- ResumeContext <$> newEmptyMVar
+ let !gbm1 = Map.insert tid r gbm0
+ pure (gbm1, r)
+ Just ctx -> do
+ pure (gbm0, ctx)
+
+-- | A global mapping from thread ids to the MVars on which they block when stopped.
+globalBreakActionMap :: MVar (Map.Map ThreadId ResumeContext)
+globalBreakActionMap = unsafePerformIO $ newMVar Map.empty
+{-# NOINLINE globalBreakActionMap #-}
+
+newtype ResumeContext = ResumeContext
+ { resumeBreakMVar :: MVar ()
+ -- ^ A thread that hits a breakpoint blocks reading its corresponding MVar
+ -- (gotten from the 'globalBreakActionMap').
+ -- The debugger can unblock that thread by signaling its MVar.
+ }
=====================================
libraries/ghci/ghci.cabal.in
=====================================
@@ -61,6 +61,7 @@ library
exposed-modules:
GHCi.Coverage
GHCi.Run
+ GHCi.Run.Breakpoints
GHCi.Debugger
GHCi.CreateBCO
GHCi.ObjLink
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9d82db8234bbbbe11096988b3210d8…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9d82db8234bbbbe11096988b3210d8…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/T27731] Simplify the error-suppression mechanism in GHC.Tc.Errors
by Simon Peyton Jones (@simonpj) 04 Sep '26
by Simon Peyton Jones (@simonpj) 04 Sep '26
04 Sep '26
Simon Peyton Jones pushed to branch wip/T27731 at Glasgow Haskell Compiler / GHC
Commits:
46482c15 by Simon Peyton Jones at 2026-09-04T17:03:07+01:00
Simplify the error-suppression mechanism in GHC.Tc.Errors
In GHC.Tc.Errors.reportWanteds we suppress some errors in favour of
others. But the mechanism had grown crufty, and #27331 exposed a bug.
This MR makes it simpler and more uniform, by doing everything via
the `ei_suppress` field of the ErrorItem.
Claude then found another closely-related bug (see the review linked
on !16564). That bug is immortalised in new test T18851d.
I'm still not happy with `ignoreConstraint` but we can worry about
that another day; I have not touched it.
- - - - -
10 changed files:
- + changelog.d/T27731
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Types/Constraint.hs
- testsuite/tests/typecheck/should_fail/FunDepOrigin1b.hs
- + testsuite/tests/typecheck/should_fail/T18851e.hs
- + testsuite/tests/typecheck/should_fail/T18851e.stderr
- + testsuite/tests/typecheck/should_fail/T27731.hs
- + testsuite/tests/typecheck/should_fail/T27731.stderr
- testsuite/tests/typecheck/should_fail/all.T
Changes:
=====================================
changelog.d/T27731
=====================================
@@ -0,0 +1,11 @@
+section: compiler
+synopsis: Fix a compiler crash after typechecking
+issues: #27731
+mrs: !16564
+description: {
+ The type checker was failing to report an error, even though it had found one,
+ due to over-zealous error suppression. That led to subsequent chaos. This
+ MR fixes it *and* simplifies the code.
+
+ Claude found a second, closely-related bug, now immortalised as test ``T18851d``.
+}
=====================================
compiler/GHC/Tc/Errors.hs
=====================================
@@ -263,11 +263,11 @@ report_unsolved type_errors expr_holes
, cec_type_holes = type_holes
, cec_out_of_scope_holes = out_of_scope_holes
, cec_suppress = insolubleWC wanted
- -- See Note [Suppressing error messages]
- -- Suppress low-priority errors if there
- -- are insoluble errors anywhere;
- -- See #15539 and c.f. setting ic_status
- -- in GHC.Tc.Solver.setImplicationStatus
+ -- See (SLIE1) in
+ -- Note [cec_suppress: suppressing less-important error messages]
+ -- Suppress low-priority errors if there are insoluble errors
+ -- anywhere in the treee. See #15539 and c.f. setting ic_status
+ -- in GHC.Tc.Solver.setImplicationStatus
, cec_warn_redundant = warn_redundant
, cec_expand_syns = exp_syns
, cec_binds = binds_var }
@@ -322,30 +322,121 @@ we just switch off deferred type errors altogether. See #14605.
This is done by maybeSwitchOffDefer. It's also useful in one other
place: see Note [Wrapping failing kind equalities] in GHC.Tc.Solver.
-Note [Suppressing error messages]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The cec_suppress flag says "don't report any errors". Instead, just create
-evidence bindings (as usual). It's used when more important errors have occurred.
-
-Specifically (see reportWanteds)
- * If there are insoluble Givens, then we are in unreachable code and all bets
- are off. So don't report any further errors.
- * If there are any insolubles (eg Int~Bool), here or in a nested implication,
- then suppress errors from the simple constraints here. Sometimes the
- simple-constraint errors are a knock-on effect of the insolubles.
-
-This suppression behaviour is controlled by the Bool flag in
-ReportErrorSpec, as used in reportWanteds.
-
-But we need to take care: flags can turn errors into warnings, and we
-don't want those warnings to suppress subsequent errors (including
-suppressing the essential addTcEvBind for them: #15152). So in
-tryReporter we use askNoErrs to see if any error messages were
-/actually/ produced; if not, we don't switch on suppression.
-
-A consequence is that warnings never suppress warnings, so turning an
-error into a warning may allow subsequent warnings to appear that were
-previously suppressed. (e.g. partial-sigs/should_fail/T14584)
+Note [cec_suppress: suppressing less-important error messages]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The cec_suppress flag says "don't report any less-important errors". Instead, just create
+evidence bindings (as usual). It's used when more important errors have occurred
+(or will occur) eleswhere.
+
+(SLIE0)
+ * Note that `cec_suppress` does not affect more-important errors, namely the `report1`
+ group in `reportWanteds`
+ * But `cec_suppress` /does/ affect the less-important errors, namely the `report2` group.
+ To see this, look at the plumbing of cec suppress in `reportWanteds`
+
+(SLIE1) When we begin `reportAllUnsolved` we set `cec_suppress` if there are insoluble
+ wanteds /anywhere/ in the tree, via `insolubleWC`. That means we'll suppress all
+ less-important errors in favour of the more important (insolbule) ones
+
+(SLIE2) But we need to take care: flags can turn errors into warnings, and we
+ don't want those warnings to suppress subsequent errors (including
+ suppressing the essential addTcEvBind for them: #15152). So in
+ tryReporter we use askNoErrs to see if any error messages were
+ /actually/ produced; if not, we don't switch on suppression.
+
+ A consequence is that warnings never suppress warnings, so turning an
+ error into a warning may allow subsequent warnings to appear that were
+ previously suppressed. (e.g. partial-sigs/should_fail/T14584)
+
+(SLIE3) There is a tricky interaction between
+ * `cec_suppress` (a global flag) and
+ * `ei_suppress` (a local, per-error-item flag),
+ see Note [ei_suppress: suppressing confusing errors]
+ Suppose cec_suppress is True because of an insoluble constraint arising from a
+ superclass constraint -- this constraint will have ei_suppress=True.
+
+Note [ei_suppress: suppressing confusing errors]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Certain errors we might encounter are potentially confusing to users.
+If there are any other errors to report, at all, we want to suppress these.
+We achieve this by setting the `ei_suppress` flag in the `ErrorItem`.
+
+Which errors should be suppressed?
+
+(SCE1) Non-empty rewriter sets. See Note [Wanteds rewrite Wanteds: rewriter-sets]
+ in GHC.Tc.Types.Constraint
+
+(SCE2) Superclasses of Wanteds. These are generated only in case they trigger functional
+ dependencies. If such a constraint is unsolved, then its "parent" constraint must
+ also be unsolved, and is much more informative to the user. Example (#26255):
+ class (MinVersion <= F era) => Era era where { ... }
+ f :: forall era. EraFamily era -> IO ()
+ f = ..blah... -- [W] Era era
+ Here we have simply omitted "Era era =>" from f's type. But we'll end up with
+ /two/ Wanted constraints:
+ [W] d1 : Era era
+ [W] d2 : MinVersion <= F era -- Superclass of d1
+ We definitely want to report d1 and not d2! Happily it's easy to filter out those
+ superclass-Wanteds, becuase their Origin betrays them.
+
+There are wrinkles
+
+(SCE3) In rare cases we may suppress /all/ errors. That is catastrophic: GHC proceeds
+ to desugar and optimise the program, even though it is full of type errors (#22702,
+ #22793), and/or we fail to bind evidence (#27731).
+
+ If this happens, unless we are sure that an error will be reported some other way
+ (details in the defn of `tidy_items` in `reportWanteds), we just un-suppress the lot,
+ which is brutal but safe. It's a rare case.
+
+ How can it happen that there are /all/ errors are suppressed?
+ * See test T18851 for an example of how it is (just, barely) possible for the
+ /only/ errors to be superclass-of-Wanted constraints.
+ * Similarly #27731, which also involves a superclass-of-Wanted:
+ class (a ~ F b) => Ren a b
+ If we have a [W] Ren a b, we'll emit the superclass [W] a ~ F b, which will
+ rewrite the original class constraint to [W] Ren (F b) b. Now we have two
+ constraints: one has a non-empty rewriter set (SEC1) and one is a superclass of
+ a Wanted (SEC2).
+ * Also see Wrinkle (PER2) in Note [Prioritise Wanteds with empty
+ CoHoleSet] in GHC.Tc.Types.Constraint.
+
+Historical note. We used to suppress errors arising from the interaction of two
+ fundep constraints. But nowadays fundep constraints never "escape" into the main
+ solver and so never show up in error messages. See (SOLVE-FD) in Note [Overview
+ of functional dependencies in type inference] in GHC.Tc.Solver.FunDeps. So this
+ wrinkle is now just a historical note.
+
+ Errors which arise from the interaction of two Wanted fun-dep constraints.
+ Example:
+
+ class C a b | a -> b where
+ op :: a -> b -> b
+
+ foo _ = op True Nothing
+
+ bar _ = op False []
+
+ Here, we could infer
+ foo :: C Bool (Maybe a) => p -> Maybe a
+ bar :: C Bool [a] => p -> [a]
+
+ (The unused arguments suppress the monomorphism restriction.) The problem
+ is that these types can't both be correct, as they violate the functional
+ dependency. Yet reporting an error here is awkward: we must
+ non-deterministically choose either foo or bar to reject. We thus want
+ to report this problem only when there is nothing else to report.
+ See typecheck/should_fail/T13506 for an example of when to suppress
+ the error. The case above is actually accepted, because foo and bar
+ are checked separately, and thus the two fundep constraints never
+ encounter each other. It is test case typecheck/should_compile/FunDepOrigin1.
+
+ This case applies only when both fundeps are *Wanted* fundeps; when
+ both are givens, the error represents unreachable code. For
+ a Given/Wanted case, see #9612.
+
+ End of historical note
+
-}
reportImplic :: SolverReportErrCtxt -> Implication -> TcM ()
@@ -452,16 +543,6 @@ reportBadTelescope ctxt env (ForAllSkol telescope) skols
reportBadTelescope _ _ skol_info skols
= pprPanic "reportBadTelescope" (ppr skol_info $$ ppr skols)
--- | Should we completely ignore this constraint in error reporting?
--- It *must* be the case that any constraint for which this returns True
--- somehow causes an error to be reported elsewhere.
--- See Note [Constraints to ignore].
-ignoreConstraint :: Ct -> Bool
-ignoreConstraint ct
- = case ctOrigin ct of
- AssocFamPatOrigin -> True -- See (CIG1)
- _ -> False
-
-- | Makes an error item from a constraint, calculating whether or not the item
-- should be suppressed. See Note [Wanteds rewrite Wanteds: rewriter-sets]
-- in GHC.Tc.Types.Constraint. Returns Nothing if we should just ignore
@@ -473,56 +554,105 @@ mkErrorItem ct
; return Nothing } -- See Note [Constraints to ignore]
| otherwise
- = do { let loc = ctLoc ct
- flav = ctFlavour ct
+ = do { let ev = ctEvidence ct
+
+ m_evdest = case ev of
+ CtGiven {} -> Nothing
+ CtWanted (WantedCt { ctev_dest = dest }) -> Just dest
- -- For this `suppress` stuff see
- -- Note [Wanteds rewrite Wanteds: rewriter-sets] in GHC.Tc.Types.Constraint
- (suppress, m_evdest) = case ctEvidence ct of
- CtGiven {} -> (False, Nothing)
- CtWanted (WantedCt { ctev_rewriters = rws, ctev_dest = dest })
- -> (not (isEmptyCoHoleSet rws), Just dest)
- ; let m_reason = case ct of
+ m_reason = case ct of
CIrredCan (IrredCt { ir_reason = reason }) -> Just reason
_ -> Nothing
- insoluble_ct = insolubleCt ct
-
; return $ Just $ EI { ei_pred = ctPred ct
, ei_evdest = m_evdest
- , ei_flavour = flav
- , ei_loc = loc
+ , ei_flavour = ctFlavour ct
+ , ei_loc = ctLoc ct
, ei_m_reason = m_reason
- , ei_insoluble = insoluble_ct
- , ei_suppress = suppress }}
+ , ei_insoluble = insolubleCt ct
+ , ei_suppress = suppressCtError ev }}
-- | Actually report this 'ErrorItem'.
unsuppressErrorItem :: ErrorItem -> ErrorItem
unsuppressErrorItem ei = ei { ei_suppress = False }
+-- | Should we completely ignore this constraint in error reporting?
+-- It *must* be the case that any constraint for which this returns True
+-- somehow causes an error to be reported elsewhere.
+-- See Note [Constraints to ignore].
+ignoreConstraint :: Ct -> Bool
+ignoreConstraint ct
+ = case ctOrigin ct of
+ AssocFamPatOrigin -> True -- See (CIG1)
+ _ -> False
+
+suppressCtError :: CtEvidence -> Bool
+-- See Note [ei_suppress: suppressing confusing errors]
+suppressCtError (CtGiven {})
+ = False
+suppressCtError (CtWanted (WantedCt { ctev_rewriters = rws, ctev_loc = loc }))
+ | not (isEmptyCoHoleSet rws)
+ = True -- See (SCE1)
+
+ | isWantedSuperclassOrigin (ctLocOrigin loc)
+ = True -- See (SCE2)
+
+ | otherwise
+ = False
+
+{- Note [Constraints to ignore]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Some constraints are meant only to aid the solver by unification; a failure
+to solve them is not necessarily an error to report to the user. It is critical
+that compilation is aborted elsewhere if there are any ignored constraints here;
+they will remain unfilled, and might have been used to rewrite another constraint.
+
+Currently, the constraints to ignore are:
+
+(CIG1) Constraints generated in order to unify associated type instance parameters
+ with class parameters. Here are two illustrative examples:
+
+ class C (a :: k) where
+ type F (b :: k)
+
+ instance C True where
+ type F a = Int
+
+ instance C Left where
+ type F (Left :: a -> Either a b) = Bool
+
+ In the first instance, we want to infer that `a` has type Bool. So we emit
+ a constraint unifying kappa (the guessed type of `a`) with Bool. All is well.
+
+ In the second instance, we process the associated type instance only
+ after fixing the quantified type variables of the class instance. We thus
+ have skolems a1 and b1 such that the class instance is for (Left :: a1 -> Either a1 b1).
+ Unifying a1 and b1 with a and b in the type instance will fail, but harmlessly so.
+ checkConsistentFamInst checks for this, and will fail if anything has gone
+ awry. Really the equality constraints emitted are just meant as an aid, not
+ a requirement. This is test case T13972.
+
+ We detect this case by looking for an origin of AssocFamPatOrigin; constraints
+ with this origin are dropped entirely during error message reporting.
+
+ If there is any trouble, checkValidFamInst bleats, aborting compilation.
+
+(Note: Aug 25: this seems a rather tricky corner;
+ c.f. Note [ei_suppress: suppressing confusing errors])
+-}
+
----------------------------------------------------------------
reportWanteds :: SolverReportErrCtxt -> TcLevel -> WantedConstraints -> TcM ()
reportWanteds ctxt tc_lvl wc@(WC { wc_simple = simples, wc_impl = implics
, wc_errors = errs })
- | isEmptyWC wc = traceTc "reportWanteds empty WC" empty
+ | isEmptyWC wc
+ = traceTc "reportWanteds empty WC" empty
| otherwise
= do { tidy_items1 <- mapMaybeM mkErrorItem tidy_cts
- ; traceTc "reportWanteds 1" (vcat [ text "Simples =" <+> ppr simples
- , text "Suppress =" <+> ppr (cec_suppress ctxt)
- , text "tidy_cts =" <+> ppr tidy_cts
- , text "tidy_items1 =" <+> ppr tidy_items1
- , text "tidy_errs =" <+> ppr tidy_errs ])
-- Catch an awkward (and probably rare) case in which /all/ errors are
- -- suppressed: see Wrinkle (PER2) in Note [Prioritise Wanteds with empty
- -- CoHoleSet] in GHC.Tc.Types.Constraint.
- --
- -- Unless we are sure that an error will be reported some other way
- -- (details in the defn of tidy_items) un-suppress the lot. This makes
- -- sure we don't forget to report an error at all, which is
- -- catastrophic: GHC proceeds to desguar and optimise the program, even
- -- though it is full of type errors (#22702, #22793)
+ -- suppressed: see (SCE3) in Note [ei_suppress: suppressing confusing errors]
; errs_already <- ifErrsM (return True) (return False)
; let tidy_items
| not errs_already -- Have not already reported an error (perhaps
@@ -530,43 +660,54 @@ reportWanteds ctxt tc_lvl wc@(WC { wc_simple = simples, wc_impl = implics
, not (any ignoreConstraint simples) -- No error is ignorable (is reported elsewhere)
, all ei_suppress tidy_items1 -- All errors are suppressed
= map unsuppressErrorItem tidy_items1
+
| otherwise
= tidy_items1
+ ; traceTc "reportWanteds 1" (vcat [ text "Simples =" <+> ppr simples
+ , text "Suppress =" <+> ppr (cec_suppress ctxt)
+ , text "tidy_cts =" <+> ppr tidy_cts
+ , text "tidy_items1 =" <+> ppr tidy_items1
+ , text "tidy_errs =" <+> ppr tidy_errs ])
+
-- First, deal with any out-of-scope errors:
; let (out_of_scope, other_holes, not_conc_errs, mult_co_errs) = partition_errors tidy_errs
- -- don't suppress out-of-scope errors
+ -- Don't suppress out-of-scope errors
+ -- See (SLIE0) in Note [cec_suppress: suppressing less-important error messages]
ctxt_for_scope_errs = ctxt { cec_suppress = False }
; (_, no_out_of_scope) <- askNoErrs $
reportHoles tidy_items ctxt_for_scope_errs out_of_scope
-- Next, deal with things that are utterly wrong
- -- Like Int ~ Bool (incl nullary TyCons)
- -- or Int ~ t a (AppTy on one side)
- -- These /ones/ are not suppressed by the incoming context
- -- (but will be by out-of-scope errors)
+ -- These should not be suppressed by the incoming context
+ -- (but should be suppressed by out-of-scope errors)
+ -- See (SLIE0) in Note [cec_suppress: suppressing less-important error messages]
; let ctxt_for_insols = ctxt { cec_suppress = not no_out_of_scope }
- ; reportHoles tidy_items ctxt_for_insols other_holes
- -- holes never suppress
+ -- Don't suppress holes or concreteness errors unless we have scope errors
+ ; reportHoles tidy_items ctxt_for_insols other_holes
; reportNotConcreteErrs ctxt_for_insols not_conc_errs
-- We only want to report multiplicity coercion errors for multiplicity
-- constraints which are /solved/ with a non-reflexivity coercion. We
-- over approximate here: we only report multiplicity coercion errors
- -- when /all/ constraints are solved.
+ -- when /all/ other constraints are solved.
-- See wrinkle (DME1) in Note [Coercion errors in tcSubMult] in GHC.Tc.Utils.Unify.
- ; when (null simples) $ reportMultiplicityCoercionErrs ctxt_for_insols mult_co_errs
+ ; when (null simples) $
+ reportMultiplicityCoercionErrs ctxt_for_insols mult_co_errs
- -- See Note [Suppressing confusing errors]
- ; let (suppressed_items, reportable_items) = partition suppressItem tidy_items
- ; traceTc "reportWanteds suppressed:" (ppr suppressed_items)
- ; (ctxt1, items1) <- tryReporters ctxt_for_insols report1 reportable_items
+ -- Now the main batch of utterly-wrong things
+ -- Like Int ~ Bool (incl nullary TyCons)
+ -- or Int ~ t a (AppTy on one side)
+ ; (ctxt1, items1) <- tryReporters ctxt_for_insols report1 tidy_items
-- Now all the other constraints. We suppress errors here if
- -- any of the first batch failed, or if the enclosing context
- -- says to suppress
- ; let ctxt2 = ctxt1 { cec_suppress = cec_suppress ctxt || cec_suppress ctxt1 }
+ -- any of the first batch failed (ctxt1), or if the enclosing context
+ -- says to suppress AND there are no local insolubles
+ -- Why the AND part? In case all those local insolubles are suppressed.
+ -- See (SLIE3) in Note [cec_suppress: suppressing less-important error messages]
+ ; let ctxt2 = ctxt1 { cec_suppress = cec_suppress ctxt1
+ || (cec_suppress ctxt && not (any ei_insoluble tidy_items)) }
; (_, leftovers) <- tryReporters ctxt2 report2 items1
; massertPpr (null leftovers)
(text "The following unsolved Wanted constraints \
@@ -577,21 +718,10 @@ reportWanteds ctxt tc_lvl wc@(WC { wc_simple = simples, wc_impl = implics
-- NB ctxt2: don't suppress inner insolubles if there's only a
-- wanted insoluble here; but do suppress inner insolubles
-- if there's a *given* insoluble here (= inaccessible code)
-
- -- If there are no other errors to report, report suppressed errors.
- -- See (SCE3) in Note [Suppressing confusing errors].
- -- NB: with -fdefer-type-errors we might have reported warnings only from
- -- reportable_items`, but we still want to suppress the `suppressed_items`.
- ; when (null reportable_items) $
- do { (_, more_leftovers) <- tryReporters ctxt_for_insols (report1++report2)
- suppressed_items
- -- ctxt_for_insols: the suppressed errors can be Int~Bool, which
- -- will have made the incoming `ctxt` be True; don't make that
- -- suppress the Int~Bool error!
- ; massertPpr (null more_leftovers) (ppr more_leftovers) } }
+ }
where
env = cec_tidy ctxt
- tidy_cts = bagToList (mapBag (tidyCt env) simples)
+ tidy_cts = bagToList (mapBag (tidyCt env) simples)
tidy_errs = bagToList (mapBag (tidyDelayedError env) errs)
partition_errors :: [DelayedError] -> ([Hole], [Hole], [NotConcreteError], [(TcCoercion, CtLoc)])
@@ -610,8 +740,8 @@ reportWanteds ctxt tc_lvl wc@(WC { wc_simple = simples, wc_impl = implics
DE_Multiplicity mult_co loc
-> (es1, es2, es3, (mult_co, loc):es4)
- -- report1: ones that should *not* be suppressed by
- -- an insoluble somewhere else in the tree
+ -- report1: ones that should *not* be suppressed by cec_suppress,
+ -- (i.e. by an insoluble somewhere else in the tree)
-- It's crucial that anything that is considered insoluble
-- (see GHC.Tc.Utils.insolublWantedCt) is caught here, otherwise
-- we might suppress its error message, and proceed on past
@@ -752,15 +882,6 @@ reportWanteds ctxt tc_lvl wc@(WC { wc_simple = simples, wc_impl = implics
= has_gadt_match implics
---------------
-suppressItem :: ErrorItem -> Bool
- -- See Note [Suppressing confusing errors]
-suppressItem item
- | Wanted <- ei_flavour item
- , let orig = errorItemOrigin item
- = isWantedSuperclassOrigin orig -- See (SCE1)
- | otherwise
- = False
-
isSkolemTy :: TcLevel -> Type -> Bool
-- The type is a skolem tyvar
isSkolemTy tc_lvl ty
@@ -778,113 +899,8 @@ isTyFun_maybe ty = case tcSplitTyConApp_maybe ty of
Just (tc,_) | isTypeFamilyTyCon tc -> Just tc
_ -> Nothing
-{- Note [Suppressing confusing errors]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Certain errors we might encounter are potentially confusing to users.
-If there are any other errors to report, at all, we want to suppress these.
-
-Which errors should be suppressed?
-
-(SCE1) Superclasses of Wanteds. These are generated only in case they trigger functional
- dependencies. If such a constraint is unsolved, then its "parent" constraint must
- also be unsolved, and is much more informative to the user. Example (#26255):
- class (MinVersion <= F era) => Era era where { ... }
- f :: forall era. EraFamily era -> IO ()
- f = ..blah... -- [W] Era era
- Here we have simply omitted "Era era =>" from f's type. But we'll end up with
- /two/ Wanted constraints:
- [W] d1 : Era era
- [W] d2 : MinVersion <= F era -- Superclass of d1
- We definitely want to report d1 and not d2! Happily it's easy to filter out those
- superclass-Wanteds, becuase their Origin betrays them.
-
-Historical (SCE2). Fundep constraints never "escape" into the
- main solver and so never show up in error messages.
- See (SOLVE-FD) in Note [Overview of functional dependencies in type inference]
- in GHC.Tc.Solver.FunDeps. So this wrinkle is now just a historical note.
-
- Errors which arise from the interaction of two Wanted fun-dep constraints.
- Example:
-
- class C a b | a -> b where
- op :: a -> b -> b
-
- foo _ = op True Nothing
-
- bar _ = op False []
-
- Here, we could infer
- foo :: C Bool (Maybe a) => p -> Maybe a
- bar :: C Bool [a] => p -> [a]
-
- (The unused arguments suppress the monomorphism restriction.) The problem
- is that these types can't both be correct, as they violate the functional
- dependency. Yet reporting an error here is awkward: we must
- non-deterministically choose either foo or bar to reject. We thus want
- to report this problem only when there is nothing else to report.
- See typecheck/should_fail/T13506 for an example of when to suppress
- the error. The case above is actually accepted, because foo and bar
- are checked separately, and thus the two fundep constraints never
- encounter each other. It is test case typecheck/should_compile/FunDepOrigin1.
-
- This case applies only when both fundeps are *Wanted* fundeps; when
- both are givens, the error represents unreachable code. For
- a Given/Wanted case, see #9612.
-
- End of historical (SCE2)
-
-(SCE3) How can it happen that there are /only/ suppressed errors? See test T18851
- for an example of how it is (just, barely) possible for the /only/ errors to
- be superclass-of-Wanted constraints.
-
-Mechanism:
-
-We use the `suppress` function within reportWanteds to filter out these
-"suppress" cases, then report all other errors. After doing so, we return to these
-suppressed ones and report them only if there have been no errors so far.
-
-Note [Constraints to ignore]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Some constraints are meant only to aid the solver by unification; a failure
-to solve them is not necessarily an error to report to the user. It is critical
-that compilation is aborted elsewhere if there are any ignored constraints here;
-they will remain unfilled, and might have been used to rewrite another constraint.
-
-Currently, the constraints to ignore are:
-
-(CIG1) Constraints generated in order to unify associated type instance parameters
- with class parameters. Here are two illustrative examples:
-
- class C (a :: k) where
- type F (b :: k)
-
- instance C True where
- type F a = Int
-
- instance C Left where
- type F (Left :: a -> Either a b) = Bool
-
- In the first instance, we want to infer that `a` has type Bool. So we emit
- a constraint unifying kappa (the guessed type of `a`) with Bool. All is well.
-
- In the second instance, we process the associated type instance only
- after fixing the quantified type variables of the class instance. We thus
- have skolems a1 and b1 such that the class instance is for (Left :: a1 -> Either a1 b1).
- Unifying a1 and b1 with a and b in the type instance will fail, but harmlessly so.
- checkConsistentFamInst checks for this, and will fail if anything has gone
- awry. Really the equality constraints emitted are just meant as an aid, not
- a requirement. This is test case T13972.
-
- We detect this case by looking for an origin of AssocFamPatOrigin; constraints
- with this origin are dropped entirely during error message reporting.
-
- If there is any trouble, checkValidFamInst bleats, aborting compilation.
-
-(Note: Aug 25: this seems a rather tricky corner;
- c.f. Note [Suppressing confusing errors])
-
-Note [Implementation of Unsatisfiable constraints]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+{- Note [Implementation of Unsatisfiable constraints]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Unsatisfiable constraint was introduced in GHC proposal #433 (https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0433-u…)
See Note [The Unsatisfiable constraint] in GHC.TypeError.
@@ -1317,9 +1333,15 @@ maybeReportError :: SolverReportErrCtxt
maybeReportError ctxt items@(item1:|_) (SolverReport { sr_important_msg = important
, sr_supplementary = supp
, sr_hints = hints })
- | suppress_group = return ()
- | otherwise = do { msg <- mkErrorReport loc_env diag (Just ctxt) supp hints
- ; reportDiagnostic msg }
+ | suppress_group
+ = -- Suppress the report entirely
+ -- But NB we still create the evidence binding; see `reportGroup`.
+ return ()
+
+ | otherwise
+ = -- Spit out an error or warning
+ do { msg <- mkErrorReport loc_env diag (Just ctxt) supp hints
+ ; reportDiagnostic msg }
where
reason | any (nonDeferrableOrigin . errorItemOrigin) items = ErrorWithoutFlag
| otherwise = cec_defer_type_errors ctxt
@@ -1328,11 +1350,6 @@ maybeReportError ctxt items@(item1:|_) (SolverReport { sr_important_msg = import
loc_env = ctLocEnv (errorItemCtLoc item1)
suppress_group
- | all ei_suppress items
- = True -- If they are all suppressed (notably, have been rewritten by another unsolved wanted)
- -- report nothing. (If at least one is not suppressed, do report: the function that
- -- generates the error message should look for an unsuppressed error item.)
-
-- It is tempting to say that we always want to see all insoluble errors
-- But then we get a bit more than we want. Examples:
-- a ~ t a occurs check errors (T2534, mc25)
@@ -1344,6 +1361,12 @@ maybeReportError ctxt items@(item1:|_) (SolverReport { sr_important_msg = import
| cec_suppress ctxt
= True -- Some earlier error has occurred, so suppress this diagnostic
+ | all ei_suppress items
+ = True -- If they are all suppressed (notably, have been rewritten by another unsolved
+ -- wanted) report nothing. (If at least one is not suppressed, do report:
+ -- the function that generates the error message should look for an
+ -- unsuppressed error item.)
+
| otherwise
= False
@@ -1402,11 +1425,11 @@ mkErrorTerm ct_loc ty ctxt msg supp hints
; return $ evDelayedError ty err_str }
-tryReporters :: SolverReportErrCtxt -> [ReporterSpec] -> [ErrorItem] -> TcM (SolverReportErrCtxt, [ErrorItem])
+tryReporters :: SolverReportErrCtxt -> [ReporterSpec] -> [ErrorItem]
+ -> TcM (SolverReportErrCtxt, [ErrorItem])
-- Use the first reporter in the list whose predicate says True
tryReporters ctxt reporters items
- = do { let (vis_items, invis_items)
- = partition (isVisibleOrigin . errorItemOrigin) items
+ = do { let (vis_items, invis_items) = partition (isVisibleOrigin . errorItemOrigin) items
; traceTc "tryReporters {" (ppr vis_items $$ ppr invis_items)
; (ctxt', items') <- go ctxt reporters vis_items invis_items
; traceTc "tryReporters }" (ppr items')
@@ -1416,9 +1439,9 @@ tryReporters ctxt reporters items
= return (ctxt, vis_items ++ invis_items)
go ctxt (r : rs) vis_items invis_items
- -- always look at *visible* Origins before invisible ones
+ -- Always look at *visible* Origins before invisible ones
-- this is the whole point of isVisibleOrigin
- = do { (ctxt', vis_items') <- tryReporter ctxt r vis_items
+ = do { (ctxt', vis_items') <- tryReporter ctxt r vis_items
; (ctxt'', invis_items') <- tryReporter ctxt' r invis_items
; go ctxt'' rs vis_items' invis_items' }
-- Carry on with the rest, because we must make
@@ -1432,7 +1455,7 @@ tryReporter ctxt (str, keep_me, suppress_after, reporter) items = case nonEmpty
{ traceTc "tryReporter{ " (text str <+> ppr yeses)
; (_, no_errs) <- askNoErrs (reporter ctxt yeses)
; let suppress_now = not no_errs && suppress_after
- -- See Note [Suppressing error messages]
+ -- See (SLIE2) in Note [cec_suppress: suppressing less-important error messages]
ctxt' = ctxt { cec_suppress = suppress_now || cec_suppress ctxt }
; traceTc "tryReporter end }" (text str <+> ppr (cec_suppress ctxt) <+> ppr suppress_after)
; return (ctxt', nos) }
=====================================
compiler/GHC/Tc/Errors/Types.hs
=====================================
@@ -5477,7 +5477,7 @@ data SolverReportErrCtxt
, cec_suppress :: Bool -- ^ True <=> More important errors have occurred,
-- so create bindings if need be, but
-- don't issue any more errors/warnings
- -- See Note [Suppressing error messages]
+ -- See Note [cec_suppress: suppressing less-important error messages]
}
----------------------------------------------------------------------------
=====================================
compiler/GHC/Tc/Types/Constraint.hs
=====================================
@@ -1214,15 +1214,16 @@ insolubleWantedCt :: Ct -> Bool
--
-- See Note [Insoluble Wanteds]
insolubleWantedCt ct
- | CtWanted (WantedCt { ctev_loc = loc, ctev_rewriters = rewriters })
- <- ctEvidence ct
+ | CtWanted (WantedCt {}) <- ctEvidence ct
-- It's a Wanted
, insolubleCt ct
-- It's insoluble
- , isEmptyCoHoleSet rewriters
+-- , isEmptyCoHoleSet rewriters
-- It has no rewriters – see (IW1) in Note [Insoluble Wanteds]
- , not (isGivenLoc loc)
- -- isGivenLoc: see (IW2) in Note [Insoluble Wanteds]
+
+-- I don't understand IW2 so I'm going to get rid of it
+-- , not (isGivenLoc loc)
+-- -- isGivenLoc: see (IW2) in Note [Insoluble Wanteds]
-- See also historical (IW3) in Note [Insoluble Wanteds]
= True
=====================================
testsuite/tests/typecheck/should_fail/FunDepOrigin1b.hs
=====================================
@@ -8,4 +8,4 @@ class C a b | a -> b where
-- foo :: (C Bool (Maybe a), C Bool [b]) => x -> (Maybe a, [b])
foo _ = (op True Nothing, op False [])
--- See Note [Suppressing confusing errors] in GHC.Tc.Errors
+-- See Note [ei_suppress: suppressing confusing errors] in GHC.Tc.Errors
=====================================
testsuite/tests/typecheck/should_fail/T18851e.hs
=====================================
@@ -0,0 +1,39 @@
+{-# LANGUAGE FunctionalDependencies, FlexibleInstances, UndecidableInstances,
+ ScopedTypeVariables, TypeFamilies, TypeApplications,
+ FlexibleContexts, AllowAmbiguousTypes, ExtendedDefaultRules #-}
+
+module T18851e where
+
+default (Int)
+
+type family C_FD a
+class C_FD a ~ b => C a b
+
+type instance C_FD Int = Bool -- just for Show (C_FD Int)
+instance C Int b => C Int b
+
+class IsInt int
+instance int ~ Int => IsInt int
+
+data A
+instance Show A where
+ show _ = "A"
+data B
+instance Show B where
+ show _ = "B"
+
+data D
+
+f :: forall a b c int
+ . ( Show c, Num int
+ , C int a, C int b, C int c
+ -- , c ~ C_FD int -- add this to get rid of ambiguity error
+ )
+ => String
+f = show (undefined :: c)
+
+g :: String
+g = f @A @B ++ show (undefined :: D)
+ -- This variant, suggested by Claude in a review of !16564,
+ -- has an unsolved (Show D) constraint. It must not be suppressed
+ -- by the insoluble-but-suppressed constraints arising from (C Int a)
=====================================
testsuite/tests/typecheck/should_fail/T18851e.stderr
=====================================
@@ -0,0 +1,20 @@
+T18851e.hs:36:5: error: [GHC-39999]
+ • Ambiguous type variable ‘int0’ arising from a use of ‘f’
+ prevents the constraint ‘(Num int0)’ from being solved.
+ Probable fix: use a type annotation to specify what ‘int0’ should be.
+ Potentially matching instances:
+ instance Num Integer -- Defined in ‘GHC.Internal.Num’
+ instance Num Double -- Defined in ‘GHC.Internal.Float’
+ ...plus three others
+ ...plus one instance involving out-of-scope types
+ (use -fprint-potential-instances to see them all)
+ • In the first argument of ‘(++)’, namely ‘f @A @B’
+ In the expression: f @A @B ++ show (undefined :: D)
+ In an equation for ‘g’: g = f @A @B ++ show (undefined :: D)
+
+T18851e.hs:36:17: error: [GHC-39999]
+ • No instance for ‘Show D’ arising from a use of ‘show’
+ • In the second argument of ‘(++)’, namely ‘show (undefined :: D)’
+ In the expression: f @A @B ++ show (undefined :: D)
+ In an equation for ‘g’: g = f @A @B ++ show (undefined :: D)
+
=====================================
testsuite/tests/typecheck/should_fail/T27731.hs
=====================================
@@ -0,0 +1,36 @@
+{-# LANGUAGE ImplicitParams, TypeFamilies #-}
+
+module Bug where
+
+type family St (a :: k) :: *
+type family Ev (a :: k) :: * -> *
+
+data T1 a = C1 a
+data T2 h g = C2 (h ())
+
+class (h ~ Ev g, s ~ St g) => Ren s h g
+
+f1 ::
+ (s ~ St h, Ren s h g, ?settings :: settings)
+ => (g v -> g ()) -> g v -> g ()
+f1 form = (\_ a -> a) (C1 (f2 {-@g-})) form
+
+f2 :: forall g h s. ( s ~ St h, Ren s h g) => T2 h g
+f2 = error "urk"
+
+{- Call of f2
+
+[W] s ~ St h --> St g ~ St h --> St g ~ St (Ev g)
+[W] Ren s h g
+[W] s ~ St g -- Superclass of wanted
+[W] h ~ Ev g -- Superclass of wanted
+-}
+
+{-
+f3 ::
+ T1 ()
+ -> (g v -> g ())
+ -> g v -> g ()
+f3 wd form = ((\_ a -> a) wd form)
+
+-}
=====================================
testsuite/tests/typecheck/should_fail/T27731.stderr
=====================================
@@ -0,0 +1,16 @@
+T27731.hs:16:28: [GHC-05617]
+ • Could not deduce ‘St (Ev g0) ~ St g0’
+ arising from a superclass required to satisfy ‘Ren
+ (St (Ev g0)) (Ev g0) g0’,
+ arising from a use of ‘f2’
+ from the context: (s ~ St h, Ren s h g, ?settings::settings)
+ bound by the type signature for:
+ f1 :: forall s (h :: * -> *) (g :: * -> *) settings v.
+ (s ~ St h, Ren s h g, ?settings::settings) =>
+ (g v -> g ()) -> g v -> g ()
+ at T27731.hs:(13,1)-(15,33)
+ Note: ‘St’ is a non-injective type family.
+ The type variable ‘g0’ is ambiguous
+ • In the first argument of ‘C1’, namely ‘(f2)’
+ In the first argument of ‘\ _ a -> a’, namely ‘(C1 (f2))’
+ In the expression: (\ _ a -> a) (C1 (f2)) form
=====================================
testsuite/tests/typecheck/should_fail/all.T
=====================================
@@ -565,6 +565,7 @@ test('T17563', normal, compile_fail, [''])
test('T18851', normal, compile_fail, [''])
test('T18851b', normal, compile, [''])
test('T18851c', normal, compile, [''])
+test('T18851e', normal, compile_fail, [''])
test('T16946', normal, compile_fail, [''])
test('T16502', expect_broken(12854), compile, [''])
test('T17566b', normal, compile_fail, [''])
@@ -763,3 +764,4 @@ test('T26861', normal, compile_fail, [''])
test('T26862', normal, compile_fail, [''])
test('T27210', normal, compile_fail, [''])
test('T26532', normal, compile_fail, [''])
+test('T27731', normal, compile_fail, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/46482c15c020310f6256925eba6341f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/46482c15c020310f6256925eba6341f…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/T27629] Re-introduce the "weird SpecDict" case
by Simon Peyton Jones (@simonpj) 04 Sep '26
by Simon Peyton Jones (@simonpj) 04 Sep '26
04 Sep '26
Simon Peyton Jones pushed to branch wip/T27629 at Glasgow Haskell Compiler / GHC
Commits:
25d95ac8 by Simon Peyton Jones at 2026-09-04T17:00:01+01:00
Re-introduce the "weird SpecDict" case
In this commit
commit f80375dd4945350a1d784e436975b48b9c736eaf
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sun Jun 29 15:26:58 2025 +0100
Refactor of Specialise.hs
I removed a test for a dictionary with unbound type variables. This
turned out to be wrong; see #27629.
So this MR re-introduces it.
- - - - -
7 changed files:
- + changelog.d/T27629
- compiler/GHC/Core/Opt/Specialise.hs
- testsuite/tests/simplCore/should_compile/Makefile
- + testsuite/tests/simplCore/should_compile/T27629.hs
- + testsuite/tests/simplCore/should_compile/T27629.stderr
- + testsuite/tests/simplCore/should_compile/T27629Plugin.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
=====================================
changelog.d/T27629
=====================================
@@ -0,0 +1,6 @@
+section: compiler
+synopsis: Fix a scoping bug in the type-class specialiser
+description: We don't know of any user programs that can trigger this bug
+ (the repro involves a plugin) so there no user-visible effect.
+mrs: !16603
+issues: #27629
=====================================
compiler/GHC/Core/Opt/Specialise.hs
=====================================
@@ -1713,9 +1713,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs
; let spec_rhs_bndrs = spec_bndrs ++ inner_rhs_bndrs'
(rhs_uds2, inner_dumped_dbs) = dumpUDs spec_rhs_bndrs $
dx_binds `consDictBinds` rhs_uds
- -- dx_binds comes from the arguments to the call,
- -- and so can mention poly_qvars but no other local binders
- spec_rhs = mkLams spec_rhs_bndrs $
+ spec_rhs = mkLams spec_rhs_bndrs $
wrapDictBindsE inner_dumped_dbs rhs_body'
rule_rhs_args = spec_bndrs
@@ -1784,8 +1782,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs
, text "rule_act" <+> ppr rule_act
]
--- ; pprTrace "spec_call: rule" (vcat [ -- text "poly_qvars" <+> ppr poly_qvars
--- text "rule_bndrs" <+> ppr rule_bndrs
+-- ; pprTrace "spec_call: rule" (vcat [ text "rule_bndrs" <+> ppr rule_bndrs
-- , text "rule_lhs_args" <+> ppr rule_lhs_args
-- , text "all_call_args" <+> ppr all_call_args
-- , ppr spec_rule ]) $
@@ -2611,6 +2608,14 @@ specHeader subst (bndr:bndrs) (UnspecType : args)
-- a wildcard binder to match the dictionary (See Note [Specialising Calls] for
-- the nitty-gritty), as a LHS rule and unfolding details.
specHeader subst (bndr:bndrs) (SpecDict dict_arg : args)
+ | let in_scope = getInScopeVars (substInScopeSet subst)
+ bad_dict_fv v = isLocalVar v && not (v `elemVarSet` in_scope)
+ , not $ isEmptyVarSet $ exprSomeFreeVars bad_dict_fv dict_arg
+ = -- Do not specialise if `dict_arg` has any any free vars that are /not/ in scope
+ -- See Note [Weird special case for SpecDict]
+ specHeader subst (bndr:bndrs) (UnspecArg : args)
+
+ | otherwise
= do { -- Make up a fresh binder to use in the RULE
-- It might turn into a dict binding (via bindAuxiliaryDict) which we
-- then float, so we use cloneIdBndr to get a completely fresh binder
@@ -2886,13 +2891,15 @@ We could zap `k` to (Any @Type) and `a` to (Any @(Any @Type)), but that
is a lot of hard work for a very strange case.
So we simply refrain from specialising in this case; hence the guard
- allVarSet (`elemInScopeSet` in_scope) (exprFreeVars d)
-in the SpecDict cased of specHeader.
-
-How did this strange polymorphic mkD arise in the first place?
-From GHC.Core.Opt.Utils.abstractFloats, which was abstracting
-over too many type variables. But that too is now fixed;
-see Note [Which type variables to abstract over] in that module.
+ not $ isEmptyVarSet $ exprSomeFreeVars bad_dict_fv dict_arg
+in the SpecDict case of `specHeader`.
+
+How did this strange polymorphic mkD arise in the first place? From
+GHC.Core.Opt.Utils.abstractFloats, which was abstracting over too many type
+variables. But that too is now fixed; see Note [Which type variables to abstract
+over] in that module. So we don't actually have a Haskell source program that
+triggers it (see #27629) -- only test T27629 that uses a plugin to inject a strange
+definition.
-}
instance Outputable DictBind where
=====================================
testsuite/tests/simplCore/should_compile/Makefile
=====================================
@@ -3,6 +3,12 @@ include $(TOP)/mk/boilerplate.mk
include $(TOP)/mk/test.mk
+# T27629 runs a plugin that injects a Core binding
+# that made the specialiser crash
+T27629:
+ '$(TEST_HC)' $(TEST_HC_OPTS) --make -package ghc -dynamic-too -c T27629Plugin.hs -v0
+ '$(TEST_HC)' $(TEST_HC_OPTS) -c -O -fpolymorphic-specialisation -dcore-lint T27629.hs
+
# T18815 should not have a non-recursive join-point for 'go'
# Previously we ended up with
# join {go_sPI w_sQ3 = case w_sQ3 of { GHC.Types.I# ww1_sQ6 ->
=====================================
testsuite/tests/simplCore/should_compile/T27629.hs
=====================================
@@ -0,0 +1,29 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# OPTIONS_GHC -fplugin=T27629Plugin #-}
+
+-- The plugin rewrites 'poly' to the example of
+-- Note [Weird special case for SpecDict]:
+--
+-- poly = /\x. \t. split @T (mkD @x MkT) [t]
+--
+-- where mkD :: forall a. T -> C T is a new top-level binding
+-- (mkD = /\a. \_. $fCT), so the tyvar x is free in the dictionary
+-- *expression* but not in its *type* (C T).
+module T27629 (poly, split, T (..), C (..)) where
+
+import Data.Kind (Type)
+
+data T = MkT
+
+class C b where
+ meth :: b -> b
+
+instance C T where
+ meth x = x
+
+split :: C b => [b] -> [b]
+split [] = []
+split (x : xs) = meth x : split xs
+
+poly :: forall (x :: Type). T -> [T]
+poly t = [t]
=====================================
testsuite/tests/simplCore/should_compile/T27629.stderr
=====================================
@@ -0,0 +1 @@
+ReproPlugin: injected weird SpecDict call into poly
=====================================
testsuite/tests/simplCore/should_compile/T27629Plugin.hs
=====================================
@@ -0,0 +1,94 @@
+-- Injects the example of Note [Weird special case for SpecDict] in
+-- GHC.Core.Opt.Specialise, which cannot be written in source Haskell:
+-- a call whose dictionary argument mentions a type variable that is
+-- free in the dictionary expression but not in its type.
+--
+-- Runs before the main Core pipeline (in particular before the
+-- Specialiser). It adds
+--
+-- mkD :: forall a. T -> C T {-# NOINLINE mkD #-}
+-- mkD = /\a. \_. $fCT
+--
+-- and rewrites poly = /\x. \t. [t]
+-- to poly = /\x. \t. split @T (mkD @x MkT) [t]
+{-# LANGUAGE CPP #-}
+module T27629Plugin (plugin) where
+
+import GHC.Core.Make (mkCoreApps, mkListExpr)
+import GHC.Core.Predicate (getClassPredTys_maybe)
+import GHC.Plugins
+#if __GLASGOW_HASKELL__ >= 1000
+import GHC.Types.InlinePragma (neverInlinePragma)
+#else
+import GHC.Types.Basic (neverInlinePragma)
+#endif
+
+plugin :: Plugin
+plugin =
+ defaultPlugin
+ { installCoreToDos = \_ todos ->
+ return (CoreDoPluginPass "inject-weird-specdict" pass : todos)
+ , pluginRecompile = purePlugin
+ }
+
+pass :: ModGuts -> CoreM ModGuts
+pass guts = do
+ let binds = mg_binds guts
+
+ dfun = case [b | b <- bindersOfBinds binds, isDFunId b] of
+ (b : _) -> b
+ [] -> panic "ReproPlugin: no dfun binding in module"
+
+ dictTy = idType dfun -- C T
+ tyT = case getClassPredTys_maybe dictTy of
+ Just (_cls, [t]) -> t
+ _ -> panic "ReproPlugin: dfun type is not C T"
+
+ mkTCon = case tyConDataCons (tyConAppTyCon tyT) of
+ (dc : _) -> dataConWorkId dc
+ [] -> panic "ReproPlugin: T has no data constructors"
+
+ findTopId nm =
+ case [b | b <- bindersOfBinds binds, occNameString (getOccName b) == nm] of
+ (b : _) -> b
+ [] -> panic ("ReproPlugin: no top-level binding " ++ nm)
+
+ splitId = findTopId "split"
+ polyId = findTopId "poly"
+
+ uMkD <- getUniqueM
+ uTv <- getUniqueM
+ uWild <- getUniqueM
+
+ let aTv = mkTyVar (mkSystemName uTv (mkTyVarOcc "a")) liftedTypeKind
+ mkDTy = mkSpecForAllTys [aTv] (mkFunctionType ManyTy tyT dictTy)
+ mkDId =
+ setInlinePragma
+ (mkExportedVanillaId (mkSystemName uMkD (mkVarOcc "mkD")) mkDTy)
+ neverInlinePragma
+ wild = mkSysLocal (fsLit "eta") uWild ManyTy tyT
+ mkDBind = NonRec mkDId (mkLams [aTv, wild] (Var dfun))
+
+ rewritePoly rhs
+ | (bndrs, _body) <- collectBinders rhs
+ , (x : _) <- reverse (filter isTyVar bndrs)
+ , (t : _) <- filter isId bndrs
+ = mkLams bndrs $
+ mkCoreApps
+ (Var splitId)
+ [ Type tyT
+ , mkCoreApps (Var mkDId) [Type (mkTyVarTy x), Var mkTCon]
+ , mkListExpr tyT [Var t]
+ ]
+ | otherwise =
+ pprPanic "ReproPlugin: unexpected shape of poly" (ppr rhs)
+
+ go (b, rhs)
+ | b == polyId = (b, rewritePoly rhs)
+ | otherwise = (b, rhs)
+
+ -- One top-level Rec, so bind order is irrelevant.
+ binds' = [Rec (map go (flattenBinds binds) ++ flattenBinds [mkDBind])]
+
+ putMsgS "ReproPlugin: injected weird SpecDict call into poly"
+ return guts {mg_binds = binds'}
=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -612,3 +612,4 @@ test('T27296b', [], makefile_test, ['T27296b'])
test('T27589', [grep_errmsg(r'wombat')], compile, ['-O -ddump-simpl -dno-typeable-binds -dsuppress-uniques'])
test('T27590', [grep_errmsg(r'wombat')], compile, ['-O -ddump-simpl -dno-typeable-binds -dsuppress-uniques'])
test('T27556', [only_ways('ghci'), extra_hc_opts('-O -fno-unoptimized-core-for-interpreter')], ghci_script, ['T27556.script'])
+test('T27629', only_ways(['optasm']), makefile_test, ['T27629'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/25d95ac89306305438f62f1a09e88e4…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/25d95ac89306305438f62f1a09e88e4…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/mangoiv/9.12.5-rc4] hadrian: Fix links to remaining doc sites to not use the package hash for haddock links
by Magnus (@MangoIV) 04 Sep '26
by Magnus (@MangoIV) 04 Sep '26
04 Sep '26
Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
3e7c23cc by Zubin Duggal at 2026-09-04T17:57:10+02:00
hadrian: Fix links to remaining doc sites to not use the package hash for haddock links
In 07267f79d91169f474cacc8bcd38d76a6e97887d we changed hadrian to not include the package hash in the haddock
directory. This patch takes care of a few remaining links that were missed in that patch
Fixes #27671
(cherry picked from commit 1446bb039a635f2b836be19f062cbffa568fe4c5)
- - - - -
3 changed files:
- docs/index.html.in
- docs/users_guide/ghc_config.py.in
- hadrian/src/Rules/Generate.hs
Changes:
=====================================
docs/index.html.in
=====================================
@@ -39,7 +39,7 @@
<LI>
<P>
- <B><A HREF="libraries/@LIBRARY_ghc_UNIT_ID@/index.html">GHC API</A></B>
+ <B><A HREF="libraries/@LIBRARY_ghc_ID@/index.html">GHC API</A></B>
</P>
<P>
Documentation for the GHC API.
=====================================
docs/users_guide/ghc_config.py.in
=====================================
@@ -15,16 +15,16 @@ else:
libs_base_uri = '../libraries'
-# N.B. If you add a package to this list be sure to also add a corresponding
-# LIBRARY_VERSION macro call to configure.ac.
+# N.B. If you add a package to this list be sure to also add it to
+# 'packageIds' in hadrian/src/Rules/Generate.hs.
lib_versions = {
- 'base': '@LIBRARY_base_UNIT_ID@',
- 'ghc-prim': '@LIBRARY_ghc_prim_UNIT_ID@',
- 'template-haskell': '@LIBRARY_template_haskell_UNIT_ID@',
- 'ghc-compact': '@LIBRARY_ghc_compact_UNIT_ID@',
- 'ghc': '@LIBRARY_ghc_UNIT_ID@',
- 'Cabal': '@LIBRARY_Cabal_UNIT_ID@',
- 'array': '@LIBRARY_array_UNIT_ID@',
+ 'base': '@LIBRARY_base_ID@',
+ 'ghc-prim': '@LIBRARY_ghc_prim_ID@',
+ 'template-haskell': '@LIBRARY_template_haskell_ID@',
+ 'ghc-compact': '@LIBRARY_ghc_compact_ID@',
+ 'ghc': '@LIBRARY_ghc_ID@',
+ 'Cabal': '@LIBRARY_Cabal_ID@',
+ 'array': '@LIBRARY_array_ID@',
}
version = '@ProjectVersion@'
=====================================
hadrian/src/Rules/Generate.hs
=====================================
@@ -320,13 +320,15 @@ packageVersions = foldMap f [ base, ghcPrim, compiler, ghc, cabal, templateHaske
f pkg = interpolateVar var $ version <$> readPackageData pkg
where var = "LIBRARY_" <> escapedPkgName pkg <> "_VERSION"
-packageUnitIds :: Stage -> Interpolations
-packageUnitIds stage =
+-- We don't want to use the hash in the html documentation because it
+-- makes it harder for non-boot packages to link to boot packages, see #26635
+packageIds :: Interpolations
+packageIds =
foldMap f [ base, ghcPrim, compiler, ghc, cabal, templateHaskell, ghcCompact, array ]
where
f :: Package -> Interpolations
- f pkg = interpolateVar var $ pkgUnitId stage pkg
- where var = "LIBRARY_" <> escapedPkgName pkg <> "_UNIT_ID"
+ f pkg = interpolateVar var $ pkgSimpleIdentifier pkg
+ where var = "LIBRARY_" <> escapedPkgName pkg <> "_ID"
escapedPkgName :: Package -> String
escapedPkgName = map f . pkgName
@@ -382,10 +384,10 @@ templateRules = do
, interpolateSetting "ProjectPatchLevel1" ProjectPatchLevel1
, interpolateSetting "ProjectPatchLevel2" ProjectPatchLevel2
]
- templateRule "docs/index.html" $ packageUnitIds Stage1
+ templateRule "docs/index.html" $ packageIds
templateRule "docs/users_guide/ghc_config.py" $ mconcat
[ projectVersion
- , packageUnitIds Stage1
+ , packageIds
, interpolateSetting "LlvmMinVersion" LlvmMinVersion
, interpolateSetting "LlvmMaxVersion" LlvmMaxVersion
]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3e7c23ccfa488483b405b7391a41e24…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3e7c23ccfa488483b405b7391a41e24…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/mangoiv/9.12.5-rc4] 7 commits: Never make an absent filler for a constraint type
by Magnus (@MangoIV) 04 Sep '26
by Magnus (@MangoIV) 04 Sep '26
04 Sep '26
Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
7d9fcc03 by Simon Peyton Jones at 2026-09-04T17:47:51+02:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
type family F a :: Constraint
type instance F W = TC W
a :: F W => Int -> Int -- (F W) argument is absent
Oops! Entered absent arg Arg: irred
Type: F W
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
(cherry picked from commit 7bf546fc423e23cb649580016ee494efbfe144ab)
- - - - -
7c5c173a by Zubin Duggal at 2026-09-04T17:47:51+02:00
Add tests for absent fillers at dictionary types
T27627 a unary class whose superclass is a non-unary class
T27627a ...whose superclass is a Constraint-kinded type family
T27627b ...whose superclass is a quantified constraint
T27627c a unary class applied to itself, (UC (UC (TC a)))
T27627e a (forall b. P b) dictionary that loops
(cherry picked from commit 5f474953d1880232b5e6c5741f08746e28cd25ab)
- - - - -
2dafea33 by Zubin Duggal at 2026-09-04T17:47:51+02:00
An abstract TyCon may hide a unary class
A class declared in an hs-boot file is an AbstractTyCon inside the
module loop, and compiling the real declaration may reveal it to be a
UnaryClassTyCon.
- isTerminatingType returned True for such AbstractTyCons
- IfaceToCore set the unary flag to False in the DFunId
So we could end up speculating bottom dictionaries because inside a module
loop we see an UnaryClassTyCon as an AbstractTyCon
Use isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
Note by the backporter (mangoiv): we're backporting by the fact that
a tycon is a unary type class con IFF it is a type class con and a
newtype prior to 9bd7fcc518111a1549c98720c222cdbabd32ed46.
(cherry picked from commit db1e0bda5c3209d75133fa9e2560d684f35f950b)
- - - - -
6db19f5f by Zubin Duggal at 2026-09-04T17:47:52+02:00
Specialise: don't replace dead args with absent fillers
specHeader decides an argument is dead by calling isDeadBinder on a binder of
the /optimised RHS/, then applies the filler to the /stable unfolding/
template instead. The two may differ, so the argument can be dead in
the RHS and not in the template.
The specialised function's unfolding then has an absent filler, and any call
site that inlines it evaluates the error thunk.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
(cherry picked from commit 2b3cdef64bbc535bd72e3dfb512b4cfd10a72ca2)
- - - - -
ca315182 by Zubin Duggal at 2026-09-04T17:47:52+02:00
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
(cherry picked from commit 664efc76286c475e0fcd1c1624e847831e7fc07a)
- - - - -
ef81b484 by Ben Gamari at 2026-09-04T17:47:52+02:00
base: Don't drop exception context in SomeException(toException)
For reasons that are lost to time, the implementation of [CLC #200]
that was merged inappropriately dropped `ExceptionContext` in the
`toException` implementation given to `SomeException`.
Fix this infelicity.
[CLC #200]: https://github.com/haskell/core-libraries-committee/issues/200
(cherry picked from commit 2ab02c579a9625438f5281a258051cb32ba40004)
- - - - -
0aa6f299 by Zubin Duggal at 2026-09-04T17:47:52+02:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
(cherry picked from commit e31885816b7c597ad89b05da64b21c5a241bf674)
- - - - -
82 changed files:
- + changelog.d/27626
- + changelog.d/27627
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- + changelog.d/T27455
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- libraries/base/changelog.md
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
- + testsuite/tests/core-to-stg/T27627/Callee.hs
- + testsuite/tests/core-to-stg/T27627/Caller.hs
- + testsuite/tests/core-to-stg/T27627/Main.hs
- + testsuite/tests/core-to-stg/T27627/T27627.stdout
- + testsuite/tests/core-to-stg/T27627/all.T
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
- + testsuite/tests/core-to-stg/T27627c/Callee.hs
- + testsuite/tests/core-to-stg/T27627c/Caller.hs
- + testsuite/tests/core-to-stg/T27627c/Main.hs
- + testsuite/tests/core-to-stg/T27627c/T27627c.stdout
- + testsuite/tests/core-to-stg/T27627c/all.T
- + testsuite/tests/core-to-stg/T27627e.hs
- + testsuite/tests/core-to-stg/T27627e.stdout
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- testsuite/tests/core-to-stg/all.T
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
- testsuite/tests/ghc-e/should_fail/T18441fail7.stderr
- testsuite/tests/ghc-e/should_run/ghc-e005.stderr
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d750a543f3f024b7815146e7e4d656…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d750a543f3f024b7815146e7e4d656…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/andrea/gbc-backport] 41 commits: rts/Interpreter: Factor out ctoi tuple info tables into data
by Andrea Vezzosi (@trac-Saizan) 04 Sep '26
by Andrea Vezzosi (@trac-Saizan) 04 Sep '26
04 Sep '26
Andrea Vezzosi pushed to branch wip/andrea/gbc-backport at Glasgow Haskell Compiler / GHC
Commits:
e6e58a01 by Ben Gamari at 2026-09-04T14:06:47+02:00
rts/Interpreter: Factor out ctoi tuple info tables into data
Instead of a massive case let's put this into data which we can reuse
elsewhere.
(cherry picked from commit d2b89603425b6d08df199deb7b84cfdbd711869a)
- - - - -
e76ed947 by Cheng Shao at 2026-09-04T14:06:47+02:00
compiler: only use `Name` instead of `Id` in `SptEntry`
As a part of #26298, this patch refactors `SptEntry` to only carry a
`Name` instead of `Id`: we do not care about extra information like
caffyness or type at all in any static pointer related codegen logic.
This is necessary to make `SptEntry` serializable, as a part of the
grand plan of serializable bytecode.
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit 39b2e3823da3ab168aa2b7365f0f27e01b1ecd0e)
- - - - -
dece03fa by Oleg Grenrus at 2026-09-04T14:06:47+02:00
Generalise thNameToGhcName by adding HasHscEnv
There were multiple single monad-specific `getHscEnv` across codebase.
HasHscEnv is modelled on HasDynFlags.
My first idea was to simply add thNameToGhcNameHsc and
thNameToGhcNameTc, but those would been exactly the same
as thNameToGhcName already.
Also add an usage example to thNameToGhcName and mention that it's
recommended way of looking up names in GHC plugins
(cherry picked from commit eb2ab1e2cadde93c330330ca7fdc64b31cd61473)
- - - - -
f1b29d68 by Simon Peyton Jones at 2026-09-04T14:06:47+02:00
Refactor ForAllCo
This is a pure refactor, addressing #26389.
It arranges that the kind coercion in a ForAllCo is a MCoercion, rather
than a plain Coercion, thus removing redundancy in the common case.
See (FC8) in Note [ForAllCo]
It's a nice cleanup.
(cherry picked from commit 94b62aa7e7b40b53442161a0c4f380955fda2f14)
- - - - -
8d8323a7 by Cheng Shao at 2026-09-04T14:06:47+02:00
testsuite: remove legacy :shell trick
This commit makes use of the built-in :shell functionality in ghci in
the test cases, and remove the legacy :shell trick.
(cherry picked from commit 186054f7e7a190ee175a6e93c6cfb63b8a67baca)
- - - - -
13832c23 by Cheng Shao at 2026-09-04T14:07:12+02:00
compiler/ghci: replace the LoadDLL message with LoadDLLs
As a part of #25407, this commit changes the LoadDLL message to
LoadDLLs, which takes a list of DLL paths to load and returns the list
of remote pointer handles. The wasm dyld is refactored to take
advantage of LoadDLLs and harvest background parallelism. On other
platforms, LoadDLLs is based on a fallback codepath that does
sequential loading.
The driver is not actually emitting singular LoadDLLs message with
multiple DLLs yet, this is left in subsequent commits.
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit a4d664c78642438f0f178ffa243e2ffdbdf20397)
- - - - -
abb6ab7f by Cheng Shao at 2026-09-04T14:07:37+02:00
driver: separate downsweep/upsweep phase in loadPackages'
This commit refactors GHC.Linker.Loader.loadPackages' to be separated
into downsweep/upsweep phases:
- The downsweep phase performs dependency analysis and generates a
list of topologically sorted packages to load
- The upsweep phase sequentially loads these packages by calling
loadPackage
This is a necessary refactoring to make it possible to make loading of
DLLs concurrent.
(cherry picked from commit c7fc4baeddcfad500126b2e91ce94d2b9391560d)
- - - - -
1e2f2665 by Cheng Shao at 2026-09-04T14:07:37+02:00
driver: emit single LoadDLLs message to load multiple DLLs
This commit refactors the driver so that it emits a single LoadDLLs
message to load multiple DLLs in GHC.Linker.Loader.loadPackages'.
Closes #25407.
-------------------------
Metric Increase:
MultiLayerModulesTH_OneShot
TcPlugin_RewritePerf
-------------------------
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit ab180104b61a930e8a1e906f013c151669ce6fff)
- - - - -
c9ed826f by Matthew Pickering at 2026-09-04T14:07:37+02:00
driver: Load bytecode static pointer entries during linking
Previously the entries were loaded too eagerly, during upsweep, but we
should delay loading them until we know that the relevant bytecode
object is demanded.
Towards #25230
(cherry picked from commit e9445c013fbccf9318739ca3d095a3e0a2e1be8a)
- - - - -
cbfd55f5 by Matthew Pickering at 2026-09-04T14:07:37+02:00
loader: Unify loadDecls and loadModuleLinkables functions
These two functions nearly did the same thing. I have refactored them so
that `loadDecls` now calls `loadModuleLinkables`.
Fixes #26459
(cherry picked from commit da9633a9009a08132b974b0407c4057cae3577f7)
- - - - -
29f939cc by Matthew Pickering at 2026-09-04T14:07:37+02:00
testsuite: Use ghci_ways to set ways in PackedDataCon/UnboxedTuples/UnliftedDataTypeInterp tests
These tests reimplemented the logic from `valid_way` in order to
determine what ways to run. It's easier to use this combination of
`only_ways` and `extra_ways` to only run in GHCi ways and always run in
GHCi ways.
(cherry picked from commit 1275d3607299734228adbbc47bfb69bf8800279f)
- - - - -
04f0cd41 by Matthew Pickering at 2026-09-04T14:08:05+02:00
Rename interpreterBackend to bytecodeBackend
This is preparation for creating bytecode files.
The "interpreter" is one way in which we can run bytecode objects. It is
more accurate to describe that the backend produces bytecode, rather
than the means by which the code will eventually run.
The "interpreterBackend" binding is left as a deprecated alias.
(cherry picked from commit c06b534bbd6dbb570b760f2e82b3e375d3956bcf)
- - - - -
e25906cb by sheaf at 2026-09-04T14:08:12+02:00
Improvements to 'mayLookIdentical'
This commit makes significant improvements to the machinery that decides
when we should pretty-print the "invisible bits" of a type, such as:
- kind applications, e.g. '@k' in 'Proxy @k ty'
- RuntimeReps, e.g. 'TYPE r'
- multiplicities and linear arrows 'a %1 -> b'
To do this, this commit refactors 'mayLookIdentical' to return **which**
of the invisible bits don't match up, e.g. in
(a %1 -> b) ~ (a %Many -> b)
we find that the invisible bit that doesn't match up is a multiplicity,
so we should set 'sdocLinearTypes = True' when pretty-printing, and with
e.g.
Proxy @k1 ~ Proxy @k2
we find that the invisible bit that doesn't match up is an invisible
TyCon argument, so we set 'sdocPrintExplicitKinds = True'.
We leverage these changes to remove the ad-hoc treatment of linearity
of data constructors with 'dataConDisplayType' and 'dataConNonLinearType'.
This is now handled by the machinery of 'pprWithInvisibleBits'.
Fixes #26335 #26340
(cherry picked from commit 2b8baada761b13199b99268f243bebd009e6e1b5)
- - - - -
8455e964 by sheaf at 2026-09-04T14:08:12+02:00
Store SDoc context in SourceError
This commits modifies the SourceError datatype which is used for
throwing and then reporting exceptions by storing all the info we need
to be able to print the SDoc, including whether we should print with
explicit kinds, explicit runtime-reps, etc.
This is done using the new datatype:
data SourceErrorContext
= SEC
!DiagOpts
!(DiagnosticOpts GhcMessage)
Now, when we come to report an error (by handling the exception), we
have access to the full context we need.
Fixes #26387
(cherry picked from commit 129ce32d3a4f2601f5c2d1bbaeeb2fbaf8dc9ef9)
- - - - -
0bc8aca7 by Matthew Pickering at 2026-09-04T14:08:12+02:00
Add support for generating bytecode objects
This commit adds the `-fwrite-byte-code` option which makes GHC emit a
`.gbc` file which contains a serialised representation of bytecode.
The bytecode can be loaded by the compiler to avoid having to
reinterpret a module when using the bytecode interpreter (for example,
in GHCi).
There are also the new options:
* -gbcdir=<DIR>: Specify the directory to place the gbc files
* -gbcsuf=<suffix>: Specify the suffix for gbc files
The option `-fbyte-code-and-object-code` now implies
`-fwrite-byte-code`.
These performance tests fail due to https://github.com/haskell/directory/issues/204
-------------------------
Metric Increase:
MultiComponentModules
MultiLayerModules
MultiComponentModulesRecomp
MultiLayerModulesRecomp
MultiLayerModulesTH_Make
MultiLayerModulesTH_OneShot
T13701
-------------------------
The bytecode serialisation part was implemented by Cheng Shao
Co-authored-by: Cheng Shao <terrorjack(a)type.dance>
(cherry picked from commit 6bd8155c991cfce7cca594bc63ba8f6ca6751667)
- - - - -
4115a0e1 by Ben Gamari at 2026-09-04T14:08:12+02:00
rts: Eliminate uses of implicit constant arrays
Folding of `const`-sized variable-length arrays to a constant-length
array is a gnu extension which clang complains about.
Closes #26502.
(cherry picked from commit 0c00c9c3b4e9b8515d4839f2c1d7d771781dc6f4)
- - - - -
ac07fe20 by Cheng Shao at 2026-09-04T14:08:12+02:00
rts: use computed goto for instruction dispatch in the bytecode interpreter
This patch uses computed goto for instruction dispatch in the bytecode
interpreter. Previously instruction dispatch is done by a classic
switch loop, so executing the next instruction requires two jumps: one
to the start of the switch loop and another to the case block based on
the instruction tag. By using computed goto, we can build a jump table
consisted of code addresses indexed by the instruction tags
themselves, so executing the next instruction requires only one jump,
to the destination directly fetched from the jump table.
Closes #12953.
(cherry picked from commit 39567e85fa514c0c4dfeaf8faa586a47dd3e11ca)
- - - - -
c8b15db3 by Georgios Karachalias at 2026-09-04T14:08:12+02:00
Remove the `CoreBindings` constructor from `LinkablePart`
Adjust HscRecompStatus to disallow unhydrated WholeCoreBindings
from being passed as input to getLinkDeps (which would previously
panic in this case).
Fixes #26497
(cherry picked from commit b253013ebeea5273a1cc7bb0082ed952456af906)
- - - - -
85d90643 by Matthew Pickering at 2026-09-04T14:08:20+02:00
driver: Properly handle errors during LinkNode steps
Previously we were not properly catching errors during the LinkNode step
(see T9930fail test).
This is fixed by wrapping the `LinkNode` action in `wrapAction`, the
same handler which is used for module compilation.
Fixes #26496
(cherry picked from commit 6c91582f915f80daff774db7738094bda6ab3b44)
- - - - -
38732812 by Sylvain Henry at 2026-09-04T14:08:30+02:00
Build external interpreter program on demand (#24731)
This patch teaches GHC how to build the external interpreter program
when it is missing. As long as we have the `ghci` library, doing this is
trivial so most of this patch is refactoring for doing it sanely.
(cherry picked from commit 55eab80d337e47decacbe979c29a1b7b47d0a872)
- - - - -
94cb7636 by Matthew Pickering at 2026-09-04T14:08:34+02:00
Use 'OsPath' in getModificationTimeIfExists
This part of the compiler is quite hot during recompilation checking in
particular since the filepaths will be translated to a string. It is
better to use the 'OsPath' native function, which turns out to be easy
to do.
(cherry picked from commit ef0dc33b2034fd91e11950751264e8b2e9f03fc4)
- - - - -
ca31b6da by Georgios Karachalias at 2026-09-04T14:08:34+02:00
Use OsPath in PkgDbRef and UnitDatabase, not FilePath
(cherry picked from commit fa3bd0a67eea13701d1b50053636f9645e999308)
- - - - -
c8061506 by Matthew Pickering at 2026-09-04T14:08:34+02:00
Add support for building bytecode libraries
A bytecode library is a collection of bytecode files (.gbc) and a
library which combines together additional object files.
A bytecode library is created by invoking GHC with the `-bytecodelib`
flag.
A library can be created from in-memory `ModuleByteCode` linkables or
by passing `.gbc` files as arguments on the command line.
Fixes #26298
(cherry picked from commit e36a5fcbeaa33abb17707173c9d1e81730f85008)
- - - - -
6a4cc6e1 by Matthew Pickering at 2026-09-04T14:08:34+02:00
Load bytecode libraries to satisfy package dependencies
This commit allows you to use a bytecode library to satisfy a package
dependency when using the interpreter.
If a user enables `-fprefer-byte-code`, then if a package provides a
bytecode library, that will be loaded and used to satisfy the
dependency.
The main change is to separate the relevant parts of the `LoaderState`
into external and home package byte code. Bytecode is loaded into either
the home package or external part (similar to HPT/EPS split), HPT
bytecode can be unloaded. External bytecode is never unloaded.
The unload function has also only been called with an empty list of
"stable linkables" for a long time. It has been modified to directly
implement a complete unloading of the home package bytecode linkables.
At the moment, the bytecode libraries are found in the "library-dirs"
field from the package description. In the future when `Cabal`
implements support for "bytecode-library-dirs" field, we can read the
bytecode libraries from there. No changes to the Cabal submodule are
necessary at the moment.
Four new tests are added in testsuite/tests/cabal, which generate fake
package descriptions and test loading the libraries into GHCi.
Fixes #26298
(cherry picked from commit 8f9ae3397dbcbb4b0bd7a574ffc6b1fbaa137b6c)
- - - - -
815183f4 by Hécate Kleidukos at 2026-09-04T14:08:56+02:00
driver: Hide source paths at verbosity level 1 by default
(cherry picked from commit aa9c5e2ca0d42f7361fc465d79f5beb056987a41)
- - - - -
4e2a7300 by Matthew Pickering at 2026-09-04T14:09:02+02:00
determinism: Use a deterministic renaming when writing bytecode files
Now when writing the bytecode file, a counter and substitution are used
to provide deterministic keys to local variables (rather than relying on
uniques). This change ensures that `.gbc` are produced
deterministically.
Fixes #26499
(cherry picked from commit 9b64ad3a458bf4e4165f125059e4bc3d518c5834)
- - - - -
5965dcdc by Cheng Shao at 2026-09-04T14:09:07+02:00
compiler: implement string interning logic for BCONPtrFS
This patch adds a `FastStringEnv`-based cache of `MallocStrings`
requests to `Interp`, so that when we load bytecode with many
breakpoints that share the same module names & unit ids, we reuse the
allocated remote pointers instead of issuing duplicte `MallocStrings`
requests and bloating the C heap. Closes #26995.
(cherry picked from commit d054b4676298ea2189cd48fcf175676b97f559e7)
- - - - -
127317a4 by Cheng Shao at 2026-09-04T14:09:11+02:00
compiler: use `Binary` instance of `BCOByteArray` for bytecode objects
This commit defines `Binary` (from `compiler`) instance of
`BCOByteArray` which serializes the underlying buffer directly, and
uses it directly in bytecode object serialization. Previously we reuse
the `Binary` (from `binary`) instance, and this change allows us to
avoid double-copying via an intermediate `ByteString` when using
`put`/`get` in `binnary`. Also see added comment for explanation.
(cherry picked from commit fdf828aef8c78ae23ad058ed4696910b6beba232)
- - - - -
05966b14 by Luite Stegeman at 2026-09-04T14:09:13+02:00
Support arbitrary size unboxed tuples in bytecode
This stores the size (number of words on the stack) of the next
expected tuple in the TSO, ctoi_spill_size field, eliminating
the need of stg_ctoi_tN frames for each size.
Note: On 32 bit platform there is still a bytecode tuple size
limit of 255 words on the stack.
Fixes #26946
(cherry picked from commit a85bd503a480cfa31f5afa09eb38f56c85c237de)
- - - - -
3c1922d1 by Luite Stegeman at 2026-09-04T14:09:17+02:00
Add specialized frames for small tuples
Small tuples are now returned more efficiently to the interpreter.
They use one less word of stack space and don't need manipulation
of the TSO anymore.
(cherry picked from commit e2209031734770a51f04883e5b538b562449cba1)
- - - - -
13f3d273 by fendor at 2026-09-04T14:09:17+02:00
Extract Binary instances to `GHC.ByteCode.Binary`
(cherry picked from commit acffb1b1da1a59dbded9ec2dcb65e495aee31f40)
- - - - -
55eb7968 by fendor at 2026-09-04T14:09:17+02:00
Add `seqNonEmpty` for evaluating `NonEmpty a`
(cherry picked from commit e2ea8e25c5128afa8191669c751942348ab3043b)
- - - - -
28e21382 by fendor at 2026-09-04T14:09:20+02:00
Record `LinkableUsage` instead of `Linkable` in `LoaderState`
Retaining a ByteCode `Linkable` after it has been loaded retains its
`UnlinkedBCO`, keeping it alive for the remainder of the program.
This starts accumulating a lot of `UnlinkedBCO` and memory over time.
However, the `Linkable` is merely used to later record its usage in
`mkObjectUsage`, which is used for recompilation checking.
However, this is incorrect, as the interface file and bytecode objects
could be in different states, e.g. the interface changes, but the
bytecode library hasn't changed so we don't need to recompile and vice
versa.
By computing a `Fingerprint` for the `ModuleByteCode`, and recording it
in the `LinkableUsage`, we know precisely whether the `ByteCode` object
on disk is outdated.
Thus, parts of this commit just makes sure that we efficiently compute a
`Fingerprint` for `ModuleByteCode` and store it in the on-disk
representation of `ModuleByteCode`.
We change the `LoaderState` to retain `LinkableUsage`, which is smaller
representation of a `Linkable`. This allows us to free the unneeded
fields of `Linkable` after linking them.
We declare the following memory invariants that this commit implements:
* No `LinkablePart` should be retained from `LoaderState`.
* `Linkable`s should be unloaded after they have been loaded.
These invariants are unfortunately tricky to automatically uphold, so we
are simply documenting our assumptions for now.
We introduce the `linkable-space` test which makes sure that after
loading, no `DotGBC` or `UnlinkedBCO` is retained.
-------------------------
Metric Increase:
MultiLayerModulesTH_OneShot
-------------------------
We allocate a bit more, but the peak number of bytes doesn't change.
While a bit unfortunate, accepting the metric increase.
We add multiple new performance measurements where we were able to
observe the desired memory invariants. Further, we add regression tests
to validate that the recompilation checker behaves more correct than
before.
(cherry picked from commit 048b00b7c231dec199a4441caac279f3bbcd75f4)
- - - - -
8adaa450 by Matthew Pickering at 2026-09-04T14:09:23+02:00
bytecode: Add magic header/version to bytecode files
In order to avoid confusing errors when using stale interface files (ie
from an older compiler version), we add a simple header/version check
like the one for interface files.
Fixes #27068
(cherry picked from commit 5cbc2c823fb0710c37f6e9144382dd59e0353782)
- - - - -
067b6047 by fendor at 2026-09-04T14:09:23+02:00
Add constants for bytecode in-memory buffer size
Introduce a common constant for the default size of the .gbc and
.bytecodelib binary buffer.
The buffer is by default set to 1 MB.
(cherry picked from commit d95a19366f64e6d866c8df0b1ab917884cc015dc)
- - - - -
bf758b55 by Cheng Shao at 2026-09-04T14:15:45+02:00
ghci: use ShortByteString for LookupSymbol/LookupSymbolInDLL/LookupClosure messages
This patch refactors ghci to use `ShortByteString` for
`LookupSymbol`/`LookupSymbolInDLL`/`LookupClosure` messages as the
first part of #27147.
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit 8dd6f453348e467686bd222551c89ecc466d36f0)
- - - - -
a59c4fd3 by Cheng Shao at 2026-09-04T14:15:53+02:00
ghci: use ShortByteString for MkCostCentres message
This patch refactors ghci to use `ShortByteString` for `MkCostCentres`
messages as a first part of #27147. This also considerably lowers the
memory overhead of breakpoints when cost center profiling is enabled.
-------------------------
Metric Decrease:
interpreter_steplocal
-------------------------
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit 371ef200bcb2664c4511daca75f72a52c1473950)
- - - - -
b3d0fef2 by fendor at 2026-09-04T14:16:00+02:00
Make HPC work with bytecode interpreter
Add support to generate .tix files from bytecode objects and the
bytecode interpreter.
Conceptually, we insert HPC ticks into the bytecode similar to how we insert
breakpoints.
HPC and breakpoints do not share the same tick array but we use a separate
tick-array for hpc/breakpoint ticks during bytecode generation.
We teach the bytecode interpreter to handle hpc ticks.
The implementation is quite trivial, simply increment the counter in the
global hpc_ticks array for the respective module.
This hpc_ticks array is generated as part of the `CStub`, so we can rely
on it existing.
A tricky bit is "registering" a bytecode object for HPC instrumentation.
In the compiled case, this is achieved via CStub and initializer/finalizers
`.init` sections which are called when the executable is run.
After the initializers have been invoked, which is before `hs_init_ghc`,
we then call `startup_hpc` in `hs_init_ghc` iff any modules were "registered"
for hpc instrumentation via `hs_hpc_module`.
Since bytecode objects are loaded after starting up GHCi, this workflow
doesn't work for supporting `hpc` and the `hpc` run-time is never
started, even if a module is added for instrumentation.
We fix this issue by employing the same technique as is for `SptEntry`s:
* We introduce a new field to `CompiledByteCode`, called `ByteCodeHpcInfo`
which contains enough information to call `hs_hpc_module`, allowing us to
register the module for `hpc` instrumentation`.
* After registering the module, we unconditionally call `startupHpc`, to make
sure the .tix file is written.
Calling `startupHpc` multiple times is safe.
Calling `hs_hpc_module` multiple times for the same module is also safe.
If we didn't register the hpc module in this way, evaluating a bytecode object
instrumented with `-fhpc` without registering it in the `hpc` run-time will
simply not generate any `.tix` files for this bytecode object.
However, this shouldn't happen if everything is set up correctly.
Closes #27036
(cherry picked from commit 28f07d702a1b47230122df22e84b60fda49897c5)
- - - - -
3d73bda0 by Andrea Vezzosi at 2026-09-04T16:50:50+02:00
fix validate build
- - - - -
2d28fe9b by Cheng Shao at 2026-09-04T16:52:37+02:00
ghci: add :shell command
This patch adds a new :shell command to ghci which works similarly to
:!, except it guarantees to run the command via sh -c. On POSIX hosts
the behavior is identical to :!, but on Windows it uses the msys2
shell instead of system cmd.exe shell. This is convenient when writing
simple ghci scripts that run simple POSIX commands, and the behavior
can be expected to be coherent on both Windows and POSIX.
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit 06d2562362768051e4901878248cbad103e66e92)
- - - - -
5eb91f03 by Cheng Shao at 2026-09-04T16:52:51+02:00
docs: document :shell in ghci
This commit documents the :shell command in ghci.
Co-authored-by: Codex <codex(a)openai.com>
(cherry picked from commit 0a3a4aa30894820731e646e2788e9819b10a7d07)
- - - - -
388 changed files:
- + changelog.d/bytecode-interpreter-hpc-support
- compiler/GHC.hs
- compiler/GHC/Builtin/PrimOps.hs
- compiler/GHC/ByteCode/Asm.hs
- + compiler/GHC/ByteCode/Binary.hs
- compiler/GHC/ByteCode/Breakpoints.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Linker.hs
- + compiler/GHC/ByteCode/Recomp/Binary.hs
- + compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Core/Coercion.hs-boot
- compiler/GHC/Core/Coercion/Opt.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Multiplicity.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/Opt/Monad.hs
- compiler/GHC/Core/Reduction.hs
- compiler/GHC/Core/TyCo/Compare.hs
- compiler/GHC/Core/TyCo/FVs.hs
- compiler/GHC/Core/TyCo/Ppr.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Core/TyCo/Subst.hs
- compiler/GHC/Core/TyCo/Tidy.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Unify.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/FlatBag.hs
- compiler/GHC/Data/OsPath.hs
- compiler/GHC/Data/SmallArray.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/Backend/Internal.hs
- compiler/GHC/Driver/Backpack.hs
- + compiler/GHC/Driver/ByteCode.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Config/Finder.hs
- + compiler/GHC/Driver/Config/Interpreter.hs
- compiler/GHC/Driver/Config/Linker.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Env/Types.hs
- compiler/GHC/Driver/Errors.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Hooks.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeFile.hs
- compiler/GHC/Driver/Messager.hs
- compiler/GHC/Driver/Monad.hs
- compiler/GHC/Driver/Phases.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Breakpoints.hs
- compiler/GHC/HsToCore/Coverage.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Make.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Recomp/Types.hs
- compiler/GHC/Iface/Rename.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Iface/Tidy/StaticPtrTable.hs
- compiler/GHC/Iface/Type.hs
- compiler/GHC/IfaceToCore.hs
- + compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Config.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Dynamic.hs
- + compiler/GHC/Linker/Executable.hs
- − compiler/GHC/Linker/ExtraObj.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/MacOS.hs
- compiler/GHC/Linker/Static.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Linker/Windows.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Plugins.hs
- compiler/GHC/Runtime/Debugger.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Interpreter.hs
- + compiler/GHC/Runtime/Interpreter/C.hs
- + compiler/GHC/Runtime/Interpreter/Init.hs
- compiler/GHC/Runtime/Interpreter/Types.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Settings.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/StgToJS/StaticPtr.hs
- compiler/GHC/SysTools/Tasks.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Utils.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/CtLoc.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Tc/Utils/TcMType.hs
- compiler/GHC/Tc/Utils/TcType.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Tc/Utils/Unify.hs-boot
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/HpcInfo.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/SourceError.hs
- compiler/GHC/Types/SptEntry.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Types/TyThing/Ppr.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/Finder/Types.hs
- compiler/GHC/Unit/Home/ModInfo.hs
- compiler/GHC/Unit/Home/PackageTable.hs
- compiler/GHC/Unit/Info.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/Graph.hs
- compiler/GHC/Unit/Module/Location.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- compiler/GHC/Unit/Module/ModSummary.hs
- compiler/GHC/Unit/Module/Status.hs
- compiler/GHC/Unit/Module/WholeCoreBindings.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Error.hs
- compiler/GHC/Utils/Misc.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/ghc.cabal.in
- docs/users_guide/extending_ghc.rst
- docs/users_guide/ghci.rst
- docs/users_guide/phases.rst
- docs/users_guide/separate_compilation.rst
- ghc/GHCi/Leak.hs
- ghc/GHCi/UI.hs
- ghc/Main.hs
- hadrian/src/Settings/Builders/RunTest.hs
- libraries/ghc-boot/GHC/Unit/Database.hs
- + libraries/ghci/GHCi/Coverage.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/ObjLink.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Apply.cmm
- rts/Continuation.c
- rts/ContinuationOps.cmm
- rts/Disassembler.c
- rts/Hpc.c
- rts/Interpreter.c
- rts/Printer.c
- rts/RaiseAsync.c
- rts/RtsSymbols.c
- rts/Schedule.c
- rts/StgMiscClosures.cmm
- rts/Threads.c
- rts/Threads.h
- rts/include/rts/Bytecodes.h
- rts/include/rts/storage/TSO.h
- rts/include/stg/MiscClosures.h
- rts/posix/OSMem.c
- testsuite/config/ghc
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- testsuite/mk/boilerplate.mk
- testsuite/mk/test.mk
- testsuite/tests/backpack/cabal/bkpcabal08/bkpcabal08.stdout
- testsuite/tests/bytecode/T24634/T24634a.stdout
- testsuite/tests/bytecode/T24634/T24634b.stdout
- + testsuite/tests/bytecode/TLinkable/BCOTemplate.hs
- + testsuite/tests/bytecode/TLinkable/LinkableUsage01.stderr
- + testsuite/tests/bytecode/TLinkable/LinkableUsage02.stderr
- + testsuite/tests/bytecode/TLinkable/Makefile
- + testsuite/tests/bytecode/TLinkable/all.T
- + testsuite/tests/bytecode/TLinkable/genLinkables.sh
- + testsuite/tests/bytecode/TLinkable/linkable-space.hs
- + testsuite/tests/bytecode/TLinkable/linkable-space.stdout
- + testsuite/tests/bytecode/tuplestress/ByteCode.hs
- + testsuite/tests/bytecode/tuplestress/Common.hs-incl
- + testsuite/tests/bytecode/tuplestress/Obj.hs
- + testsuite/tests/bytecode/tuplestress/TupleStress.hs
- + testsuite/tests/bytecode/tuplestress/TupleStress.stdout
- + testsuite/tests/bytecode/tuplestress/all.T
- + testsuite/tests/cabal/Bytecode.hs
- + testsuite/tests/cabal/BytecodeForeign.c
- + testsuite/tests/cabal/BytecodeForeign.hs
- testsuite/tests/cabal/Makefile
- testsuite/tests/cabal/all.T
- + testsuite/tests/cabal/bytecode.pkg
- + testsuite/tests/cabal/bytecode.script
- + testsuite/tests/cabal/bytecode_foreign.pkg
- + testsuite/tests/cabal/bytecode_foreign.script
- testsuite/tests/cabal/ghcpkg03.stderr
- testsuite/tests/cabal/ghcpkg03.stderr-mingw32
- testsuite/tests/cabal/ghcpkg05.stderr
- testsuite/tests/cabal/ghcpkg05.stderr-mingw32
- + testsuite/tests/cabal/pkg_bytecode.stderr
- + testsuite/tests/cabal/pkg_bytecode.stdout
- + testsuite/tests/cabal/pkg_bytecode_foreign.stderr
- + testsuite/tests/cabal/pkg_bytecode_foreign.stdout
- + testsuite/tests/cabal/pkg_bytecode_with_gbc.stderr
- + testsuite/tests/cabal/pkg_bytecode_with_gbc.stdout
- + testsuite/tests/cabal/pkg_bytecode_with_o.stderr
- + testsuite/tests/cabal/pkg_bytecode_with_o.stdout
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/cpranal/should_compile/T18174.stderr
- testsuite/tests/driver/T11429c.stderr
- testsuite/tests/driver/T20030/test1/all.T
- testsuite/tests/driver/T20030/test2/all.T
- testsuite/tests/driver/T20030/test3/all.T
- testsuite/tests/driver/T20030/test4/all.T
- testsuite/tests/driver/T20030/test5/all.T
- testsuite/tests/driver/T20030/test6/all.T
- testsuite/tests/driver/T21682.stderr
- + testsuite/tests/driver/T24731.hs
- testsuite/tests/driver/T5313.hs
- testsuite/tests/driver/T8526/T8526.script
- testsuite/tests/driver/all.T
- + testsuite/tests/driver/bytecode-object/A.hs
- + testsuite/tests/driver/bytecode-object/BytecodeForeign.c
- + testsuite/tests/driver/bytecode-object/BytecodeForeign.hs
- + testsuite/tests/driver/bytecode-object/BytecodeMain.hs
- + testsuite/tests/driver/bytecode-object/BytecodeTest.hs
- + testsuite/tests/driver/bytecode-object/Makefile
- + testsuite/tests/driver/bytecode-object/all.T
- + testsuite/tests/driver/bytecode-object/bytecode_object12.stderr
- + testsuite/tests/driver/bytecode-object/bytecode_object13.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object14.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object15.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object16.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object17.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object18.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object19.script
- + testsuite/tests/driver/bytecode-object/bytecode_object19.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object20.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object21.stderr
- + testsuite/tests/driver/bytecode-object/bytecode_object21.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object23.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object24.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object25.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object4.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object5.stdout
- + testsuite/tests/driver/bytecode-object/bytecode_object6.stdout
- testsuite/tests/driver/dynamicToo/dynamicToo001/Makefile
- testsuite/tests/driver/fat-iface/T22405/T22405.stdout
- testsuite/tests/driver/fat-iface/T22405/T22405b.stdout
- testsuite/tests/driver/fat-iface/fat011.stderr
- testsuite/tests/driver/fat-iface/fat014.script
- testsuite/tests/driver/implicit-dyn-too/Makefile
- testsuite/tests/driver/linkwhole/Main.hs
- testsuite/tests/driver/multipleHomeUnits/all.T
- testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_recomp_th.stdout
- + testsuite/tests/driver/recomp022/A1.hs
- + testsuite/tests/driver/recomp022/A2.hs
- + testsuite/tests/driver/recomp022/A3.hs
- + testsuite/tests/driver/recomp022/B.hs
- + testsuite/tests/driver/recomp022/C.hs
- + testsuite/tests/driver/recomp022/Makefile
- + testsuite/tests/driver/recomp022/all.T
- + testsuite/tests/driver/recomp022/recomp022a.stdout
- + testsuite/tests/driver/recomp022/recomp022b.stdout
- testsuite/tests/ghc-api/T10052/T10052.hs
- testsuite/tests/ghc-api/T10942.hs
- testsuite/tests/ghc-api/T8639_api.hs
- testsuite/tests/ghc-api/annotations-literals/literals.hs
- testsuite/tests/ghc-api/apirecomp001/myghc.hs
- testsuite/tests/ghc-e/should_fail/T9930fail.stderr
- testsuite/tests/ghc-e/should_fail/all.T
- testsuite/tests/ghci.debugger/scripts/break022/all.T
- testsuite/tests/ghci.debugger/scripts/break022/break022.script
- testsuite/tests/ghci.debugger/scripts/break023/all.T
- testsuite/tests/ghci.debugger/scripts/break023/break023.script
- − testsuite/tests/ghci/linking/T11531.stderr
- testsuite/tests/ghci/linking/dyn/T3372.hs
- testsuite/tests/ghci/prog001/prog001.T
- testsuite/tests/ghci/prog001/prog001.script
- testsuite/tests/ghci/prog002/prog002.T
- testsuite/tests/ghci/prog002/prog002.script
- testsuite/tests/ghci/prog003/prog003.T
- testsuite/tests/ghci/prog003/prog003.script
- testsuite/tests/ghci/prog005/prog005.T
- testsuite/tests/ghci/prog005/prog005.script
- testsuite/tests/ghci/prog010/all.T
- testsuite/tests/ghci/prog010/ghci.prog010.script
- testsuite/tests/ghci/prog012/all.T
- testsuite/tests/ghci/prog012/prog012.script
- testsuite/tests/ghci/prog018/prog018.script
- testsuite/tests/ghci/recompTHghci/all.T
- testsuite/tests/ghci/recompTHghci/recompTHghci.script
- testsuite/tests/ghci/scripts/T13869.script
- testsuite/tests/ghci/scripts/T13997.script
- testsuite/tests/ghci/scripts/T17669.script
- testsuite/tests/ghci/scripts/T18330.script
- testsuite/tests/ghci/scripts/T18330.stdout
- testsuite/tests/ghci/scripts/T1914.script
- testsuite/tests/ghci/scripts/T20217.script
- testsuite/tests/ghci/scripts/T20587.script
- testsuite/tests/ghci/scripts/T6105.script
- testsuite/tests/ghci/scripts/T6106.script
- testsuite/tests/ghci/scripts/T8042.script
- testsuite/tests/ghci/scripts/T8042recomp.script
- testsuite/tests/ghci/scripts/T8353.script
- testsuite/tests/ghci/scripts/all.T
- testsuite/tests/ghci/scripts/ghci038.script
- testsuite/tests/ghci/scripts/ghci058.script
- testsuite/tests/ghci/scripts/ghci063.script
- − testsuite/tests/ghci/shell.hs
- testsuite/tests/ghci/should_run/Makefile
- testsuite/tests/ghci/should_run/PackedDataCon/packeddatacon.T
- testsuite/tests/ghci/should_run/T18064.script
- testsuite/tests/ghci/should_run/UnboxedTuples/unboxedtuples.T
- testsuite/tests/ghci/should_run/UnliftedDataTypeInterp/unlifteddatatypeinterp.T
- testsuite/tests/hpc/Makefile
- testsuite/tests/hpc/T17073.stdout → testsuite/tests/hpc/T17073a.stdout
- + testsuite/tests/hpc/T17073b.stdout
- testsuite/tests/hpc/T20568.stdout → testsuite/tests/hpc/T20568a.stdout
- + testsuite/tests/hpc/T20568b.stdout
- testsuite/tests/hpc/all.T
- testsuite/tests/hpc/fork/Makefile
- testsuite/tests/hpc/function/Makefile
- testsuite/tests/hpc/function/test.T
- + testsuite/tests/hpc/function/tough1.stderr
- + testsuite/tests/hpc/function/tough1.stdout
- testsuite/tests/hpc/function2/test.T
- + testsuite/tests/hpc/function2/tough3.script
- + testsuite/tests/hpc/ghc_ghci/BytecodeMain.hs
- testsuite/tests/hpc/ghc_ghci/Makefile
- + testsuite/tests/hpc/ghc_ghci/hpc_ghc_ghci_bytecode.stdout
- + testsuite/tests/hpc/ghc_ghci/hpc_ghci01.stdout
- + testsuite/tests/hpc/ghc_ghci/hpc_ghci02.stdout
- testsuite/tests/hpc/ghc_ghci/test.T
- testsuite/tests/hpc/simple/Makefile
- + testsuite/tests/hpc/simple/hpc002.hs
- + testsuite/tests/hpc/simple/hpc002.stdout
- + testsuite/tests/hpc/simple/hpc003.hs
- + testsuite/tests/hpc/simple/hpc003.script
- + testsuite/tests/hpc/simple/hpc003.stdout
- testsuite/tests/hpc/simple/test.T
- testsuite/tests/indexed-types/should_fail/T14887.stderr
- testsuite/tests/linear/should_fail/T19361.stderr
- testsuite/tests/perf/compiler/Makefile
- testsuite/tests/perf/compiler/MultiLayerModulesDefsGhci.script
- + testsuite/tests/perf/compiler/MultiLayerModulesDefsGhciWithBytecodeFiles.script
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/roles/should_compile/Roles13.stderr
- testsuite/tests/rts/KeepCafsMain.hs
- testsuite/tests/rts/T13676.script
- testsuite/tests/rts/linker/T2615.hs
- testsuite/tests/simplCore/should_compile/OpaqueNoCastWW.stderr
- testsuite/tests/simplCore/should_compile/T17673.stderr
- testsuite/tests/simplCore/should_compile/T18078.stderr
- testsuite/tests/simplCore/should_compile/T18995.stderr
- testsuite/tests/simplCore/should_compile/T19890.stderr
- testsuite/tests/simplCore/should_compile/T21948.stderr
- testsuite/tests/simplCore/should_compile/T21960.stderr
- testsuite/tests/simplCore/should_compile/T24808.stderr
- − testsuite/tests/simplCore/should_compile/T25713.stderr
- testsuite/tests/simplCore/should_compile/T4201.stdout
- testsuite/tests/simplCore/should_compile/T8331.stderr
- testsuite/tests/simplStg/should_compile/T22840.stderr
- testsuite/tests/typecheck/no_skolem_info/T20232.stderr
- testsuite/tests/typecheck/should_fail/T11672.stderr
- testsuite/tests/typecheck/should_fail/T12373.stderr
- testsuite/tests/typecheck/should_fail/T15807.stderr
- testsuite/tests/typecheck/should_fail/T16074.stderr
- testsuite/tests/typecheck/should_fail/T18357a.stderr
- testsuite/tests/typecheck/should_fail/T19627.stderr
- testsuite/tests/typecheck/should_fail/T21530a.stderr
- testsuite/tests/typecheck/should_fail/VisFlag1.stderr
- utils/check-exact/Parsers.hs
- utils/check-exact/Preprocess.hs
- utils/deriveConstants/Main.hs
- utils/ghc-pkg/Main.hs
- utils/iserv/iserv.cabal.in
- utils/jsffi/dyld.mjs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1cc418c4ca6ff4749b61ddfca95602…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1cc418c4ca6ff4749b61ddfca95602…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/mangoiv/9.12.5-rc4] base: Don't drop exception context in SomeException(toException)
by Magnus (@MangoIV) 04 Sep '26
by Magnus (@MangoIV) 04 Sep '26
04 Sep '26
Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
d750a543 by Ben Gamari at 2026-09-04T16:43:06+02:00
base: Don't drop exception context in SomeException(toException)
For reasons that are lost to time, the implementation of [CLC #200]
that was merged inappropriately dropped `ExceptionContext` in the
`toException` implementation given to `SomeException`.
Fix this infelicity.
[CLC #200]: https://github.com/haskell/core-libraries-committee/issues/200
(cherry picked from commit 2ab02c579a9625438f5281a258051cb32ba40004)
- - - - -
5 changed files:
- + changelog.d/T27455
- libraries/base/changelog.md
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
- testsuite/tests/ghc-e/should_fail/T18441fail7.stderr
- testsuite/tests/ghc-e/should_run/ghc-e005.stderr
Changes:
=====================================
changelog.d/T27455
=====================================
@@ -0,0 +1,8 @@
+section: base
+issues: #27455
+mrs: !16274
+synopsis:
+ Don't drop `ExceptionContext` in `SomeException(toException)`
+description:
+ Previously the implementation of ``Exception(toException)`` given to `SomeException` would inappropriately drop the carried `ExceptionContext`. Now ``toException = id``, faithfully implementing the semantics proposed in :ref:`CLC Proposal #200 <https://github.com/haskell/core-libraries-committee/issues/200>`.
+
=====================================
libraries/base/changelog.md
=====================================
@@ -2,6 +2,8 @@
## 4.21.3.0 *June 2026*
* System.Info.fullCompilerVersion: add 'since' annotation
+ * The implementation of `toException` in `SomeException`'s `Exception` instance no longer drops exception context, in keeping with the behavior originally proposed in [CLC Proposal #200](https://github.com/haskell/core-libraries-committee/issues/200).
+
## 4.21.2.0 *March 2026*
* Expose `Backtraces` constructor and fields ([CLC proposal #199](https://github.com/haskell/core-libraries-committee/issues/199), [#26049](https://gitlab.haskell.org/ghc/ghc/-/issues/26049))
=====================================
libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
=====================================
@@ -206,7 +206,16 @@ Caught MismatchedParentheses
-}
class (Typeable e, Show e) => Exception e where
- -- | @toException@ should produce a 'SomeException' with no attached 'ExceptionContext'.
+ -- | 'toException' converts an exception into the existential 'SomeException'
+ -- wrapper type.
+ --
+ -- In doing so, 'toException' should not /add/ an 'ExceptionContext'.
+ --
+ -- - In most cases, the exception does not store its own 'ExceptionContext'.
+ -- The default implementation of 'toException' (which does not store any
+ -- 'ExceptionContext') is suitable for these cases.
+ -- - In the rare case that the exception itself stores an 'ExceptionContext',
+ -- this context should be preserved by 'toException'.
toException :: e -> SomeException
fromException :: SomeException -> Maybe e
@@ -229,13 +238,11 @@ class (Typeable e, Show e) => Exception e where
-- | @since base-4.8.0.0
instance Exception Void
--- | This drops any attached 'ExceptionContext'.
+-- | NB: this instance preserves the attached 'ExceptionContext'.
--
-- @since base-3.0
instance Exception SomeException where
- toException (SomeException e) =
- let ?exceptionContext = emptyExceptionContext
- in SomeException e
+ toException = id
fromException = Just
backtraceDesired (SomeException e) = backtraceDesired e
displayException (SomeException e) = displayException e
=====================================
testsuite/tests/ghc-e/should_fail/T18441fail7.stderr
=====================================
@@ -1,10 +1,10 @@
-<interactive>: Uncaught exception ghc-9.13-inplace:GHC.Utils.Panic.GhcException:
+<interactive>: Uncaught exception ghc-9.12.4.20260710-inplace:GHC.Utils.Panic.GhcException:
IO error: "Abcde" does not exist
While handling IO error: "Abcde" does not exist
HasCallStack backtrace:
- throwIO, called at compiler/GHC/Utils/Error.hs:512:19 in ghc-9.13-inplace:GHC.Utils.Error
+ throwIO, called at compiler/GHC/Utils/Error.hs:512:19 in ghc-9.12.4.20260710-inplace:GHC.Utils.Error
1
=====================================
testsuite/tests/ghc-e/should_run/ghc-e005.stderr
=====================================
@@ -4,3 +4,8 @@ foo
HasCallStack backtrace:
error, called at ghc-e005.hs:12:10 in main:Main
+
+
+HasCallStack backtrace:
+ throwIO, called at ghc\GHCi\UI.hs:1655:31 in ghc-bin-10.1.20260629-inplace:GHCi.UI
+
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d750a543f3f024b7815146e7e4d656f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d750a543f3f024b7815146e7e4d656f…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/mangoiv/9.12.5-rc4] CorePrep: don't speculate a call across an hs-boot edge
by Magnus (@MangoIV) 04 Sep '26
by Magnus (@MangoIV) 04 Sep '26
04 Sep '26
Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
785781c5 by Zubin Duggal at 2026-09-04T16:24:41+02:00
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
(cherry picked from commit 664efc76286c475e0fcd1c1624e847831e7fc07a)
- - - - -
9 changed files:
- + changelog.d/27717
- compiler/GHC/CoreToStg/Prep.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
Changes:
=====================================
changelog.d/27717
=====================================
@@ -0,0 +1,6 @@
+section: compiler
+synopsis: Fix a bug where -fspec-eval-dictfun could speculatively evaluate a
+ looping dictionary whose recursion went through an hs-boot import, hanging
+ the program.
+mrs: !16519
+issues: #27717
=====================================
compiler/GHC/CoreToStg/Prep.hs
=====================================
@@ -1892,87 +1892,131 @@ long as the callee might evaluate it. And if it is evaluated on
most code paths anyway, we get to turn the unknown eval in the
callee into a known call at the call site.
-Very Nasty Wrinkle
+(SEV1) There is a very nasty wrinkle: we must be very careful not to speculate
+ recursive calls! Doing so might well change termination behavior.
+
+ That comes up in practice for DFuns, which are considered ok-for-spec,
+ because they always immediately return a constructor.
+ See Note [NON-BOTTOM-DICTS invariant] in GHC.Core.
+
+ But not so if you speculate the recursive call, as #20836 shows:
+
+ class Foo m => Foo m where
+ runFoo :: m a -> m a
+ newtype Trans m a = Trans { runTrans :: m a }
+ instance Monad m => Foo (Trans m) where
+ runFoo = id
+
+ (NB: class Foo m => Foo m` looks weird and needs -XUndecidableSuperClasses. The
+ example in #20836 is more compelling, but boils down to the same thing.)
+ This program compiles to the following DFun for the `Trans` instance:
+
+ Rec {
+ $fFooTrans
+ = \ @m $dMonad -> C:Foo ($fFooTrans $dMonad) (\ @a -> id)
+ end Rec }
+
+ Note that the DFun immediately terminates and produces a dictionary, just
+ like DFuns ought to, but it calls itself recursively to produce the `Foo m`
+ dictionary. But alas, if we treat `$fFooTrans` as always-terminating, so
+ that we can speculate its calls, and hence use call-by-value, we get:
+
+ $fFooTrans
+ = \ @m $dMonad -> case ($fFooTrans $dMonad) of sc ->
+ C:Foo sc (\ @a -> id)
+
+ and that's an infinite loop!
+ Note that this bad-ness only happens in `$fFooTrans`'s own RHS. In the
+ *body* of the letrec, it's absolutely fine to use call-by-value on
+ `foo ($fFooTrans d)`.
+
+ Our solution is this: we track in cpe_rec_ids the set of enclosing
+ recursively-bound Ids, the RHSs of which we are currently transforming and then
+ in 'exprOkForSpecEval' (a special entry point to 'exprOkForSpeculation',
+ basically) we'll say that any binder in this set is not ok-for-spec.
+
+ Note if we have a letrec group `Rec { f1 = rhs1; ...; fn = rhsn }`, and we
+ prep up `rhs1`, we have to include not only `f1`, but all binders of the group
+ `f1..fn` in this set, otherwise our fix is not robust wrt. mutual recursive
+ DFuns.
+
+ NB: If at some point we decide to have a termination analysis for general
+ functions (#8655, !1866), we need to take similar precautions for (guarded)
+ recursive functions:
+
+ repeat x = x : repeat x
+
+ Same problem here: As written, repeat evaluates rapidly to WHNF. So `repeat x`
+ is a cheap call that we are willing to speculate, but *not* in repeat's RHS.
+ Fortunately, pce_rec_ids already has all the information we need in that case.
+
+ The problem is very similar to Note [Eta reduction in recursive RHSs].
+ Here as well as there it is *unsound* to change the termination properties
+ of the very function whose termination properties we are exploiting.
+
+ It is also similar to Note [Do not strictify a DFun's parameter dictionaries],
+ where marking recursive DFuns (of undecidable *instances*) strict in dictionary
+ *parameters* leads to quite the same change in termination as above.
+
+(SEV2) The situation in (SEV1) can cross modules. `cpe_rec_ids` is module-local,
+ so it does not catch a recursion that goes through an hs-boot import.
+ Suppose Mid and Callee form a module loop, and Mid sees Callee through its
+ hs-boot file:
+
+ -- Mid.hs
+ $fCAT = \ @a $dCB -> C:CA ($fxCBT $dCB) ...
+ -- Callee.hs
+ $fCBT = \ @a $dCB -> C:CB ($fCAT $dCB) ...
+
+ Neither module can see that these two call each other, so each looks
+ non-recursive, and we speculate the inner call in both:
+
+ $fCAT = \ @a $dCB -> case $fxCBT $dCB of s { __DEFAULT -> C:CA s ... }
+ $fCBT = \ @a $dCB -> case $fCAT $dCB of s { __DEFAULT -> C:CB s ... }
+
+ Now each forces the other and the program loops. Such a cycle must cross an
+ hs-boot edge, so we do not speculate a call whose callee has a BootUnfolding.
+ Note [Inlining and hs-boot files] in GHC.CoreToIface does the same for
+ infinite inlining.
+
+(SEV3) Belt and braces: do not speculate absent bindings.
+
+ In 'decideFloatInfo' we decline to speculate a binding whose demand is
+ absent. There is no point in speculating an absent binding, since its
+ value is (presumably) not needed.
+
+ This used to matter more. Worker/wrapper would bind an absent dictionary
+ to a rubbish literal filler, and speculation could force a superclass
+ selection out of that rubbish literal, causing a segfault (#25924).
+ Nowadays we never make a filler for a dictionary in the first place, so
+ this can no longer happen and the guard is merely belt and braces.
+ See Note [Don't make fillers for constraint types]
+ in GHC.Core.Opt.WorkWrap.Utils.
-We must be very careful not to speculate recursive calls! Doing so
-might well change termination behavior.
+Note [Controlling Speculative Evaluation]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-That comes up in practice for DFuns, which are considered ok-for-spec,
-because they always immediately return a constructor.
-See Note [NON-BOTTOM-DICTS invariant] in GHC.Core.
+Most of the time, speculative evaluation in the coreprep phase has a positive
+effect on performance, however we have found that some forms of speculative
+evaluation can lead to large performance regressions. See #25284.
-But not so if you speculate the recursive call, as #20836 shows:
+Therefore we have some flags to control which types of speculative evaluation
+are done:
- class Foo m => Foo m where
- runFoo :: m a -> m a
- newtype Trans m a = Trans { runTrans :: m a }
- instance Monad m => Foo (Trans m) where
- runFoo = id
+ -fspec-eval
+ Globally enable/disable speculative evaluation ( -fno-spec-eval also turns
+ off all other speculative evaluation). On by default for all
+ optimization levels. Turning on this flag by itself should never cause
+ a performance regression. Please open a ticket if you find any.
-(NB: class Foo m => Foo m` looks weird and needs -XUndecidableSuperClasses. The
-example in #20836 is more compelling, but boils down to the same thing.)
-This program compiles to the following DFun for the `Trans` instance:
+ -fspec-eval-dictfun
+ Enable speculative evaluation for dictionary functions. Off by default
+ since it can cause an increase in allocations (#24284). We have no
+ examples that show a large performance improvement when turning on this
+ flag. Please open a ticket if you find any.
- Rec {
- $fFooTrans
- = \ @m $dMonad -> C:Foo ($fFooTrans $dMonad) (\ @a -> id)
- end Rec }
-
-Note that the DFun immediately terminates and produces a dictionary, just
-like DFuns ought to, but it calls itself recursively to produce the `Foo m`
-dictionary. But alas, if we treat `$fFooTrans` as always-terminating, so
-that we can speculate its calls, and hence use call-by-value, we get:
-
- $fFooTrans
- = \ @m $dMonad -> case ($fFooTrans $dMonad) of sc ->
- C:Foo sc (\ @a -> id)
-
-and that's an infinite loop!
-Note that this bad-ness only happens in `$fFooTrans`'s own RHS. In the
-*body* of the letrec, it's absolutely fine to use call-by-value on
-`foo ($fFooTrans d)`.
-
-Our solution is this: we track in cpe_rec_ids the set of enclosing
-recursively-bound Ids, the RHSs of which we are currently transforming and then
-in 'exprOkForSpecEval' (a special entry point to 'exprOkForSpeculation',
-basically) we'll say that any binder in this set is not ok-for-spec.
-
-Note if we have a letrec group `Rec { f1 = rhs1; ...; fn = rhsn }`, and we
-prep up `rhs1`, we have to include not only `f1`, but all binders of the group
-`f1..fn` in this set, otherwise our fix is not robust wrt. mutual recursive
-DFuns.
-
-NB: If at some point we decide to have a termination analysis for general
-functions (#8655, !1866), we need to take similar precautions for (guarded)
-recursive functions:
-
- repeat x = x : repeat x
-
-Same problem here: As written, repeat evaluates rapidly to WHNF. So `repeat x`
-is a cheap call that we are willing to speculate, but *not* in repeat's RHS.
-Fortunately, pce_rec_ids already has all the information we need in that case.
-
-The problem is very similar to Note [Eta reduction in recursive RHSs].
-Here as well as there it is *unsound* to change the termination properties
-of the very function whose termination properties we are exploiting.
-
-It is also similar to Note [Do not strictify a DFun's parameter dictionaries],
-where marking recursive DFuns (of undecidable *instances*) strict in dictionary
-*parameters* leads to quite the same change in termination as above.
-
-Belt and braces: do not speculate absent bindings
-
-In 'decideFloatInfo' we decline to speculate a binding whose demand is absent.
-There is no point in speculating an absent binding, since its value is
-(presumably) not needed.
-
-This used to matter more. Worker/wrapper would bind an absent dictionary to a
-rubbish literal filler, and speculation could force a superclass selection out
-of that rubbish literal, causing a segfault (#25924). Nowadays we never make a
-filler for a dictionary in the first place, so this can no longer happen and
-the guard is merely belt and braces.
-See Note [Don't make fillers for constraint types]
-in GHC.Core.Opt.WorkWrap.Utils.
+Also see the optimization section in the User's Guide for the description of
+these flags and when to use them.
Note [BindInfo and FloatInfo]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2030,16 +2074,6 @@ conceptually.
See also Note [Floats and FloatDecision] for how we maintain whole groups of
floats and how far they go.
-Note [Controlling Speculative Evaluation]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Most of the time, speculative evaluation has a positive effect on performance,
-but we have found a case where speculative evaluation of dictionary functions
-leads to a performance regression #25284.
-
-Therefore we have some flags to control it. See the optimization section in
-the User's Guide for the description of these flags and when to use them.
-
Note [Floats and FloatDecision]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We have a special datatype `Floats` for modelling a telescope of `FloatingBind`
@@ -2243,11 +2277,18 @@ mkNonRecFloat env is_unlifted bndr rhs
ok_for_spec = exprOkForSpecEval call_ok_for_spec rhs
-- See Note [Controlling Speculative Evaluation]
call_ok_for_spec x
- | is_rec_call x = False
- | not (cp_specEval cfg) = False
- | not (cp_specEvalDFun cfg) && isDFunId x = False
+ -- See Note [Speculative evaluation]
+ | is_rec_call x = False -- See (SEV1)
+ | is_boot_call x = False -- See (SEV2)
+
+ -- See [Controlling speculative evaluation]
+ | not (cp_specEval cfg) = False -- Flag -fspec-eval
+ | not (cp_specEvalDFun cfg) && isDFunId x = False -- Flag -fspec-eval-dictfun
+
| otherwise = True
- is_rec_call = (`elemUnVarSet` cpe_rec_ids env)
+ is_rec_call = (`elemUnVarSet` cpe_rec_ids env)
+ is_boot_call = isBootUnfolding . realIdUnfolding
+ -- See Note [Speculative evaluation], Very Nasty Wrinkle
is_data_con = isJust . isDataConId_maybe
-- | Wrap floats around an expression
=====================================
testsuite/tests/core-to-stg/T27717/Callee.hs
=====================================
@@ -0,0 +1,11 @@
+{-# LANGUAGE UndecidableInstances, FlexibleInstances, FlexibleContexts #-}
+module Callee where
+
+import Ty
+import Mid
+
+instance CB a => CB (T a) where
+ opB _ = 2
+
+instance CB Int where
+ opB _ = 8
=====================================
testsuite/tests/core-to-stg/T27717/Callee.hs-boot
=====================================
@@ -0,0 +1,7 @@
+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
+module Callee where
+
+import Ty
+
+instance CB a => CB (T a)
+instance CB Int
=====================================
testsuite/tests/core-to-stg/T27717/Main.hs
=====================================
@@ -0,0 +1,6 @@
+module Main where
+import Mid
+import Callee ()
+
+main :: IO ()
+main = print (g (1 :: Int))
=====================================
testsuite/tests/core-to-stg/T27717/Mid.hs
=====================================
@@ -0,0 +1,26 @@
+{-# LANGUAGE UndecidableInstances, FlexibleInstances, FlexibleContexts #-}
+{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables #-}
+module Mid where
+
+import Ty
+import {-# SOURCE #-} Callee
+
+-- $fCATa calls Callee's $fxCBT, and that calls this one back. The cycle runs
+-- through the hs-boot import, so both dfuns are NonRec in their own module and
+-- cpe_rec_ids cannot see it. Speculating either one loops.
+instance CB a => CA (T a) where
+ opA _ = 1
+
+instance CA Int where
+ opA _ = 9
+
+data Dict c where
+ Dict :: c => Dict c
+
+{-# NOINLINE keep #-}
+keep :: [Dict c] -> Int
+keep xs = length xs + 41
+
+{-# NOINLINE g #-}
+g :: forall a. CB a => a -> Int
+g _ = keep [Dict :: Dict (CA (T a))]
=====================================
testsuite/tests/core-to-stg/T27717/T27717.stdout
=====================================
@@ -0,0 +1 @@
+42
=====================================
testsuite/tests/core-to-stg/T27717/Ty.hs
=====================================
@@ -0,0 +1,10 @@
+{-# LANGUAGE UndecidableSuperClasses #-}
+module Ty where
+
+data T a = MkT a
+
+class CB a => CA a where
+ opA :: a -> Int
+
+class CA a => CB a where
+ opB :: a -> Int
=====================================
testsuite/tests/core-to-stg/T27717/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27717',
+ [extra_files(['Main.hs', 'Mid.hs', 'Ty.hs', 'Callee.hs', 'Callee.hs-boot'])],
+ multimod_compile_and_run,
+ ['Main', '-O -fspec-eval-dictfun'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/785781c580f67e65790e40c5c0fb5c7…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/785781c580f67e65790e40c5c0fb5c7…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/T26543b] 5 commits: Add -Wimplicit-field-strictness (#16836)
by Simon Peyton Jones (@simonpj) 04 Sep '26
by Simon Peyton Jones (@simonpj) 04 Sep '26
04 Sep '26
Simon Peyton Jones pushed to branch wip/T26543b at Glasgow Haskell Compiler / GHC
Commits:
9e7739ab by Simon Jakobi at 2026-09-03T13:45:43-04:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (`!` or `~`). It complements the
LazyFieldAnnotations extension (4762a8bf30f) from GHC proposal 752,
which makes `~` annotations available for this purpose.
Deciding which fields to report requires their levity, so the check
runs after typechecking. To keep the noise down, the diagnostic is
emitted once per data declaration, grouped by constructor.
Closes #16836.
Assisted-by: Claude Fable 5
- - - - -
6a44d591 by mangoiv at 2026-09-03T13:46:22-04:00
simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
Prior to this commit
commit 08bc245be70d95801bc1138804ed1de9474fbdc0
Author: sheaf <sam.derbyshire(a)gmail.com>
Date: Sat Feb 28 16:30:43 2026 +0100
Clean up join points, casts & ticks
This commit shores up the logic dealing with casts and ticks occurring
in between a join point binding and a jump
any `PlaceRuntime` ticks we observed were profiling ticks, even though that
isn't necessary.
The more liberal rules in the above commit allow e.g. breakpoint ticks
(which are valid PlaceRuntime ticks) to legitimately appear in an argument
position.
Fixes #27556
- - - - -
eaa95a6f by Andrei Borzenkov at 2026-09-04T08:08:40-04:00
Parentheses in prefix GADT constructors (#27423)
Updated `splitLHsGadtTy` to allow looking
through the parentheses for inner binders. General example
of a code pattern that's allowed now:
data S a where
MkS :: (forall a. S a)
That should work now with any combination of nested
foralls and parentheses.
We don't perform parenthesis unwrapping for record
GADT constructors in accordance with GHC Proposal #402.
To this end `con_inner_bndrs` no longer stores plain forall
telescopes: `[HsForAllTelescope pass]` is replaced with
`[LHsGadtTelescope pass]`, a new `HsArg`-style type whose
`HsGadtForAll` holds an inner telescope and whose `HsGadtPar`
holds a pair of parentheses. The parentheses carry no meaning
for renaming or type checking; the only reason to record them
is exact-printing.
Updated `pprConDecl` to improve the `parse == parse . ppr . parse`
property of GADT pretty-printing.
The pretty printer can now output code that's similar to this:
data T a where
MkT1 :: (forall a. T a)
MkT2 :: forall . forall a. T a
These are special cases of inner forall binders
for prefix GADT constructors, when we have either
implicit or zero explicit outer binders.
- - - - -
639456a8 by Evgeny Malyshev at 2026-09-04T08:09:25-04:00
HsPat: Add spacing to unary tuple patterns
Use hsep when printing unary boxed TuplePat applications so that the
MkSolo constructor is separated from its argument. The old hcat rendered
wildcard and parenthesized arguments as MkSolo_ and MkSolo().
Add T27034 to exercise the HsPat splice-dump path with wildcard and
variable arguments, and update existing affected golden output.
Fixes #27034
Assisted-By: OpenAI Codex
- - - - -
a30f172e by Simon Peyton Jones at 2026-09-04T15:00:13+01:00
Line up simpleUnifyCheck and check_ty_eq_rhs
Even after #26543 was allegedly fixed, the original repo case in the
Description continued to fail. The reason was that the QuickLook
unifier, `qlUnify`, used `simpleUnifyCheck` for checking unification
invariants; and `simpleUnifyCheck` conservatively rejected any RHS
with a coercion hole in it. The upshot was that QuickLook was not
as clever as it should be, wrongly failing to do an impredicative
instantiation.
Interestingly `check_ty_eq_rhs`, which does the same job, only during
constraint solving, was more liberal: it just looked at the free
vars of the coercion, and allowed coercion holes.
This MR lines them up, adding some careful notes. See
Note [simpleUnifyCheck] esp (SUC1)
Note [Unification preconditions] esp (COERCIONS)
Things are better than before, but I am still uncomfortable about the
possibilty that a hole might be filled with a coercion that mentions
the LHS type variable; for now I have left this discomfort documented
in (SUC1).
- - - - -
67 changed files:
- + changelog.d/27556
- + changelog.d/T26543
- + changelog.d/allow-gadt-prefix-con-parens
- + changelog.d/implicit-field-strictness-warning
- changelog.d/lazy-field-annotations
- compiler/GHC/Core/TyCo/FVs.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Types/Tickish.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Type.hs
- docs/users_guide/exts/gadt_syntax.rst
- docs/users_guide/exts/strict.rst
- docs/users_guide/using-warnings.rst
- − testsuite/tests/gadt/T14320.stderr
- testsuite/tests/gadt/T18191.hs
- testsuite/tests/gadt/T18191.stderr
- + testsuite/tests/gadt/T27423a.hs
- + testsuite/tests/gadt/T27423b.hs
- + testsuite/tests/gadt/T27423b.stderr
- testsuite/tests/gadt/all.T
- testsuite/tests/printer/Makefile
- + testsuite/tests/printer/T27423c.hs
- testsuite/tests/printer/all.T
- + testsuite/tests/simplCore/should_compile/T27556.hs
- + testsuite/tests/simplCore/should_compile/T27556.script
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/th/T17380.stderr
- testsuite/tests/th/T18612.stderr
- + testsuite/tests/th/T27034.hs
- + testsuite/tests/th/T27034.stderr
- testsuite/tests/th/T8761.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/typecheck/should_compile/T26543_orig.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/warnings/should_compile/T16836a.hs
- + testsuite/tests/warnings/should_compile/T16836a.stderr
- + testsuite/tests/warnings/should_compile/T16836b.hs
- + testsuite/tests/warnings/should_compile/T16836c.hs
- + testsuite/tests/warnings/should_compile/T16836c.stderr
- + testsuite/tests/warnings/should_compile/T16836d.hs
- + testsuite/tests/warnings/should_compile/T16836d.stderr
- testsuite/tests/warnings/should_compile/all.T
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f7571be8581071281f848de09966ea…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f7571be8581071281f848de09966ea…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0