summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Pickering <matthewtpickering@gmail.com>2021-08-14 20:20:01 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2021-08-17 08:05:34 -0400
commitacb188e0c02a114927d340dac78a68626c659cd3 (patch)
tree403ad95f07dca55a6086daa5d2791beb336a5f76
parent31dc013fdec0cf2ddac1ba093c8a6933a0b62df2 (diff)
downloadhaskell-acb188e0c02a114927d340dac78a68626c659cd3.tar.gz
ghci: Fix rec statements in interactive prompt
We desugar a recursive Stmt to somethign like (a,_,c) <- mfix (\(a,b,_) -> do { ... ; return (a,b,c) }) ...stuff after the rec... The knot-tied tuple must contain * All the variables that are used before they are bound in the `rec` block * All the variables that are used after the entire `rec` block In the case of GHCi, however, we don't know what variables will be used after the `rec` (#20206). For example, we might have ghci> rec { x <- e1; y <- e2 } ghci> print x ghci> print y So we have to assume that *all* the variables bound in the `rec` are used afterwards. We use `Nothing` in the argument to segmentRecStmts to signal that all the variables are used. Fixes #20206
-rw-r--r--compiler/GHC/Rename/Expr.hs51
-rw-r--r--testsuite/tests/ghci/scripts/T20206.script3
-rw-r--r--testsuite/tests/ghci/scripts/T20206.stdout1
-rwxr-xr-xtestsuite/tests/ghci/scripts/all.T1
4 files changed, 47 insertions, 9 deletions
diff --git a/compiler/GHC/Rename/Expr.hs b/compiler/GHC/Rename/Expr.hs
index 1e1f7bdce1..fe8056f6c6 100644
--- a/compiler/GHC/Rename/Expr.hs
+++ b/compiler/GHC/Rename/Expr.hs
@@ -35,7 +35,7 @@ import GHC.Hs
import GHC.Tc.Errors.Types
import GHC.Tc.Utils.Env ( isBrackStage )
import GHC.Tc.Utils.Monad
-import GHC.Unit.Module ( getModule )
+import GHC.Unit.Module ( getModule, isInteractiveModule )
import GHC.Rename.Env
import GHC.Rename.Fixity
import GHC.Rename.Utils ( HsDocContext(..), bindLocalNamesFV, checkDupNames
@@ -1170,7 +1170,11 @@ rnStmt ctxt rnBody (L loc (RecStmt { recS_stmts = L _ rec_stmts })) thing_inside
segs
-- See Note [Deterministic ApplicativeDo and RecursiveDo desugaring]
; (thing, fvs_later) <- thing_inside bndrs
- ; let (rec_stmts', fvs) = segmentRecStmts (locA loc) ctxt empty_rec_stmt segs fvs_later
+ -- In interactive mode, assume that all variables are used later
+ ; is_interactive <- isInteractiveModule . tcg_mod <$> getGblEnv
+ ; let
+ final_fvs_later = if is_interactive then Nothing else Just fvs_later
+ (rec_stmts', fvs) = segmentRecStmts (locA loc) ctxt empty_rec_stmt segs final_fvs_later
-- We aren't going to try to group RecStmts with
-- ApplicativeDo, so attaching empty FVs is fine.
; return ( ((zip rec_stmts' (repeat emptyNameSet)), thing)
@@ -1522,15 +1526,17 @@ rn_rec_stmts ctxt rnBody bndrs stmts
segmentRecStmts :: AnnoBody body
=> SrcSpan -> HsStmtContext GhcRn
-> Stmt GhcRn (LocatedA (body GhcRn))
- -> [Segment (LStmt GhcRn (LocatedA (body GhcRn)))] -> FreeVars
+ -> [Segment (LStmt GhcRn (LocatedA (body GhcRn)))]
+ -> Maybe FreeVars -- Nothing when in interactive mode, everything can be used later
+ -- Note [What is "used later" in a rec stmt]
-> ([LStmt GhcRn (LocatedA (body GhcRn))], FreeVars)
-segmentRecStmts loc ctxt empty_rec_stmt segs fvs_later
+segmentRecStmts loc ctxt empty_rec_stmt segs mfvs_later
| null segs
- = ([], fvs_later)
+ = ([], final_fv_uses)
| HsDoStmt (MDoExpr _) <- ctxt
- = segsToStmts empty_rec_stmt grouped_segs fvs_later
+ = segsToStmts empty_rec_stmt grouped_segs later_ids
-- Step 4: Turn the segments into Stmts
-- Use RecStmt when and only when there are fwd refs
-- Also gather up the uses from the end towards the
@@ -1540,14 +1546,20 @@ segmentRecStmts loc ctxt empty_rec_stmt segs fvs_later
| otherwise
= ([ L (noAnnSrcSpan loc) $
empty_rec_stmt { recS_stmts = noLocA ss
- , recS_later_ids = nameSetElemsStable
- (defs `intersectNameSet` fvs_later)
+ , recS_later_ids = nameSetElemsStable later_ids
, recS_rec_ids = nameSetElemsStable
(defs `intersectNameSet` uses) }]
-- See Note [Deterministic ApplicativeDo and RecursiveDo desugaring]
- , uses `plusFV` fvs_later)
+ , uses `plusFV` final_fv_uses)
where
+ final_fv_uses = case mfvs_later of
+ Nothing -> defs
+ Just later -> uses `plusFV` later
+ later_ids = case mfvs_later of
+ Nothing -> defs
+ Just fvs_later -> defs `intersectNameSet` fvs_later
+
(defs_s, uses_s, _, ss) = unzip4 segs
defs = plusFVs defs_s
uses = plusFVs uses_s
@@ -1622,6 +1634,27 @@ glom it together with the first two groups
{ rec { x <- ...y...; p <- z ; y <- ...x... ;
q <- x ; z <- y } ;
r <- x }
+
+Note [What is "used later" in a rec stmt]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We desugar a recursive Stmt to somethign like
+
+ (a,_,c) <- mfix (\(a,b,_) -> do { ... ; return (a,b,c) })
+ ...stuff after the rec...
+
+The knot-tied tuple must contain
+* All the variables that are used before they are bound in the `rec` block
+* All the variables that are used after the entire `rec` block
+
+In the case of GHCi, however, we don't know what variables will be used
+after the `rec` (#20206). For example, we might have
+ ghci> rec { x <- e1; y <- e2 }
+ ghci> print x
+ ghci> print y
+
+So we have to assume that *all* the variables bound in the `rec` are used
+afterwards. We use `Nothing` in the argument to segmentRecStmts to signal
+that all the variables are used.
-}
glomSegments :: HsStmtContext GhcRn
diff --git a/testsuite/tests/ghci/scripts/T20206.script b/testsuite/tests/ghci/scripts/T20206.script
new file mode 100644
index 0000000000..530a30bc67
--- /dev/null
+++ b/testsuite/tests/ghci/scripts/T20206.script
@@ -0,0 +1,3 @@
+:set -XRecursiveDo
+rec x <- pure ()
+print x
diff --git a/testsuite/tests/ghci/scripts/T20206.stdout b/testsuite/tests/ghci/scripts/T20206.stdout
new file mode 100644
index 0000000000..6a452c185a
--- /dev/null
+++ b/testsuite/tests/ghci/scripts/T20206.stdout
@@ -0,0 +1 @@
+()
diff --git a/testsuite/tests/ghci/scripts/all.T b/testsuite/tests/ghci/scripts/all.T
index b75ac178a9..93199562f3 100755
--- a/testsuite/tests/ghci/scripts/all.T
+++ b/testsuite/tests/ghci/scripts/all.T
@@ -344,3 +344,4 @@ test('T19650',
['T19650.script'])
test('T20019', normal, ghci_script, ['T20019.script'])
test('T20101', normal, ghci_script, ['T20101.script'])
+test('T20206', normal, ghci_script, ['T20206.script'])