diff --git a/compiler/ghci/ByteCodeGen.hs b/compiler/ghci/ByteCodeGen.hs index 8b23e08003de6c13c61302de1205b71e7c99460a..12331e2d52b2544e8f17ea2593292b36f19741d6 100644 --- a/compiler/ghci/ByteCodeGen.hs +++ b/compiler/ghci/ByteCodeGen.hs @@ -432,8 +432,8 @@ schemeER_wrk d p rhs return $ breakInstr `consOL` code | otherwise = schemeE d 0 p rhs -getVarOffSets :: DynFlags -> StackDepth -> BCEnv -> [Id] -> [(Id, Word16)] -getVarOffSets dflags depth env = catMaybes . map getOffSet +getVarOffSets :: DynFlags -> StackDepth -> BCEnv -> [Id] -> [Maybe (Id, Word16)] +getVarOffSets dflags depth env = map getOffSet where getOffSet id = case lookupBCEnv_maybe id env of Nothing -> Nothing diff --git a/compiler/ghci/ByteCodeTypes.hs b/compiler/ghci/ByteCodeTypes.hs index 628b576ca0fc26a6bfd6f813e1b535255ea43124..0c0c34ad64cb7b8a02e1bb5b30fc86a9f4a13303 100644 --- a/compiler/ghci/ByteCodeTypes.hs +++ b/compiler/ghci/ByteCodeTypes.hs @@ -35,6 +35,7 @@ import Data.Array.Base ( UArray(..) ) import Data.ByteString (ByteString) import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap +import Data.Maybe (catMaybes) import GHC.Exts.Heap import GHC.Stack.CCS @@ -110,14 +111,15 @@ instance NFData BCONPtr where -- | Information about a breakpoint that we know at code-generation time data CgBreakInfo = CgBreakInfo - { cgb_vars :: [(Id,Word16)] + { cgb_vars :: [Maybe (Id,Word16)] , cgb_resty :: Type } +-- See Note [Syncing breakpoint info] in compiler/main/InteractiveEval.hs -- Not a real NFData instance because we can't rnf Id or Type seqCgBreakInfo :: CgBreakInfo -> () seqCgBreakInfo CgBreakInfo{..} = - rnf (map snd cgb_vars) `seq` + rnf (map snd (catMaybes (cgb_vars))) `seq` seqType cgb_resty instance Outputable UnlinkedBCO where diff --git a/compiler/main/InteractiveEval.hs b/compiler/main/InteractiveEval.hs index b2c644e65c0632931a147a214f704472d3d10b20..ee43aaf6751cb92341836e2962a9606746860ab9 100644 --- a/compiler/main/InteractiveEval.hs +++ b/compiler/main/InteractiveEval.hs @@ -521,20 +521,17 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv (Just BreakInfo{..}) = do breaks = getModBreaks hmi info = expectJust "bindLocalsAtBreakpoint2" $ IntMap.lookup breakInfo_number (modBreaks_breakInfo breaks) - vars = cgb_vars info + mbVars = cgb_vars info result_ty = cgb_resty info occs = modBreaks_vars breaks ! breakInfo_number span = modBreaks_locs breaks ! breakInfo_number decl = intercalate "." $ modBreaks_decls breaks ! breakInfo_number - -- Filter out any unboxed ids; + -- Filter out any unboxed ids by changing them to Nothings; -- we can't bind these at the prompt - pointers = filter (\(id,_) -> isPointer id) vars - isPointer id | [rep] <- typePrimRep (idType id) - , isGcPtrRep rep = True - | otherwise = False + mbPointers = nullUnboxed <$> mbVars - (ids, offsets) = unzip pointers + (ids, offsets, occs') = syncOccs mbPointers occs free_tvs = tyCoVarsOfTypesList (result_ty:map idType ids) @@ -550,11 +547,12 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv (Just BreakInfo{..}) = do us <- mkSplitUniqSupply 'I' -- Dodgy; will give the same uniques every time let tv_subst = newTyVars us free_tvs - filtered_ids = [ id | (id, Just _hv) <- zip ids mb_hValues ] + (filtered_ids, occs'') = unzip -- again, sync the occ-names + [ (id, occ) | (id, Just _hv, occ) <- zip3 ids mb_hValues occs' ] (_,tidy_tys) = tidyOpenTypes emptyTidyEnv $ map (substTy tv_subst . idType) filtered_ids - new_ids <- zipWith3M mkNewId occs tidy_tys filtered_ids + new_ids <- zipWith3M mkNewId occs'' tidy_tys filtered_ids result_name <- newInteractiveBinder hsc_env (mkVarOccFS result_fs) span let result_id = Id.mkVanillaGlobal result_name @@ -591,6 +589,24 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv (Just BreakInfo{..}) = do | (tv, uniq) <- tvs `zip` uniqsFromSupply us , let name = setNameUnique (tyVarName tv) uniq ] + isPointer id | [rep] <- typePrimRep (idType id) + , isGcPtrRep rep = True + | otherwise = False + + -- Convert unboxed Id's to Nothings + nullUnboxed (Just (fv@(id, _))) + | isPointer id = Just fv + | otherwise = Nothing + nullUnboxed Nothing = Nothing + + -- See Note [Syncing breakpoint info] + syncOccs :: [Maybe (a,b)] -> [c] -> ([a], [b], [c]) + syncOccs mbVs ocs = unzip3 $ catMaybes $ joinOccs mbVs ocs + where + joinOccs :: [Maybe (a,b)] -> [c] -> [Maybe (a,b,c)] + joinOccs = zipWith joinOcc + joinOcc mbV oc = (\(a,b) c -> (a,b,c)) <$> mbV <*> pure oc + rttiEnvironment :: HscEnv -> IO HscEnv rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do let tmp_ids = [id | AnId id <- ic_tythings ic] @@ -632,6 +648,35 @@ pushResume hsc_env resume = hsc_env { hsc_IC = ictxt1 } ictxt0 = hsc_IC hsc_env ictxt1 = ictxt0 { ic_resume = resume : ic_resume ictxt0 } + + {- + Note [Syncing breakpoint info] + + To display the values of the free variables for a single breakpoint, the + function `compiler/main/InteractiveEval.hs:bindLocalsAtBreakpoint` pulls + out the information from the fields `modBreaks_breakInfo` and + `modBreaks_vars` of the `ModBreaks` data structure. + For a specific breakpoint this gives 2 lists of type `Id` (or `Var`) + and `OccName`. + They are used to create the Id's for the free variables and must be kept + in sync! + + There are 3 situations where items are removed from the Id list + (or replaced with `Nothing`): + 1.) If function `compiler/ghci/ByteCodeGen.hs:schemeER_wrk` (which creates + the Id list) doesn't find an Id in the ByteCode environement. + 2.) If function `compiler/main/InteractiveEval.hs:bindLocalsAtBreakpoint` + filters out unboxed elements from the Id list, because GHCi cannot + yet handle them. + 3.) If the GHCi interpreter doesn't find the reference to a free variable + of our breakpoint. This also happens in the function + bindLocalsAtBreakpoint. + + If an element is removed from the Id list, then the corresponding element + must also be removed from the Occ list. Otherwise GHCi will confuse + variable names as in #8487. + -} + -- ----------------------------------------------------------------------------- -- Abandoning a resume context diff --git a/testsuite/tests/ghci.debugger/scripts/T8487.hs b/testsuite/tests/ghci.debugger/scripts/T8487.hs new file mode 100644 index 0000000000000000000000000000000000000000..d77738e3c97a352381850173d20edd7d0bd7589b --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T8487.hs @@ -0,0 +1,11 @@ +import Control.Exception + +f = do + ma <- try $ evaluate a + x <- case ma of + Right str -> return a + Left err -> return $ show (err :: SomeException) + putStrLn x + where + a :: String + a = error "hi" diff --git a/testsuite/tests/ghci.debugger/scripts/T8487.script b/testsuite/tests/ghci.debugger/scripts/T8487.script new file mode 100644 index 0000000000000000000000000000000000000000..628088e954c77886ebfaa55a0f035f239e7d13b0 --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T8487.script @@ -0,0 +1,3 @@ +:l T8487.hs +:b 5 +f diff --git a/testsuite/tests/ghci.debugger/scripts/T8487.stdout b/testsuite/tests/ghci.debugger/scripts/T8487.stdout new file mode 100644 index 0000000000000000000000000000000000000000..ab7151a5636ac380885fbdebb7faf89aa810e512 --- /dev/null +++ b/testsuite/tests/ghci.debugger/scripts/T8487.stdout @@ -0,0 +1,4 @@ +Breakpoint 0 activated at T8487.hs:(5,8)-(7,53) +Stopped in Main.f, T8487.hs:(5,8)-(7,53) +_result :: IO String = _ +ma :: Either SomeException String = Left _ diff --git a/testsuite/tests/ghci.debugger/scripts/all.T b/testsuite/tests/ghci.debugger/scripts/all.T index 8460fbb8093f2b5efeb03959dd7c949a6da53b0f..214222c8c3006b63d91a56a58f653a1157880b18 100644 --- a/testsuite/tests/ghci.debugger/scripts/all.T +++ b/testsuite/tests/ghci.debugger/scripts/all.T @@ -106,6 +106,7 @@ test('T2740', normal, ghci_script, ['T2740.script']) test('getargs', extra_files(['../getargs.hs']), ghci_script, ['getargs.script']) test('T7386', normal, ghci_script, ['T7386.script']) +test('T8487', normal, ghci_script, ['T8487.script']) test('T8557', normal, ghci_script, ['T8557.script']) test('T12458', normal, ghci_script, ['T12458.script']) test('T13825-debugger', when(arch('powerpc64'), expect_broken(14455)),