--- type ParSeg id = [([LStmt id], [id])]
--- go :: NameSet -> [ParSeg RdrName]
--- -> RnM (([ParSeg Name], thing), FreeVars)
-
- go orig_lcl_env bndrs []
- = do { let { (bndrs', dups) = removeDups cmpByOcc bndrs
- ; inner_env = extendLocalRdrEnv orig_lcl_env bndrs' }
- ; mappM dupErr dups
- ; (thing, fvs) <- setLocalRdrEnv inner_env thing_inside
- ; return (([], thing), fvs) }
-
- go orig_lcl_env bndrs_so_far ((stmts, _) : segs)
- = do { ((stmts', (bndrs, segs', thing)), fvs)
- <- rnNormalStmts par_ctxt stmts $ do
- { -- Find the Names that are bound by stmts
- lcl_env <- getLocalRdrEnv
- ; let { rdr_bndrs = collectLStmtsBinders stmts
- ; bndrs = map ( expectJust "rnStmt"
- . lookupLocalRdrEnv lcl_env
- . unLoc) rdr_bndrs
- ; new_bndrs = nub bndrs ++ bndrs_so_far
- -- The nub is because there might be shadowing
- -- x <- e1; x <- e2
- -- So we'll look up (Unqual x) twice, getting
- -- the second binding both times, which is the
- } -- one we want
-
- -- Typecheck the thing inside, passing on all
- -- the Names bound, but separately; revert the envt
- ; ((segs', thing), fvs) <- setLocalRdrEnv orig_lcl_env $
- go orig_lcl_env new_bndrs segs
-
- -- Figure out which of the bound names are used
- ; let used_bndrs = filter (`elemNameSet` fvs) bndrs
- ; return ((used_bndrs, segs', thing), fvs) }
-
- ; let seg' = (stmts', bndrs)
- ; return (((seg':segs'), thing),
- delListFromNameSet fvs bndrs) }
-
- par_ctxt = ParStmtCtxt ctxt
-
- cmpByOcc n1 n2 = nameOccName n1 `compare` nameOccName n2
- dupErr vs = addErr (ptext SLIT("Duplicate binding in parallel list comprehension for:")
- <+> quotes (ppr (head vs)))
+ rnMaybeLExpr Nothing = return (Nothing, emptyFVs)
+ rnMaybeLExpr (Just expr) = do
+ (expr', fv_expr) <- rnLExpr expr
+ return (Just expr', fv_expr)
+
+rnStmt ctxt (GroupStmt (stmts, _) groupByClause) thing_inside = do
+ checkTransformStmt ctxt
+
+ -- We must rename the using expression in the context before the transform is begun
+ groupByClauseAction <-
+ case groupByClause of
+ GroupByNothing usingExpr -> do
+ (usingExpr', fv_usingExpr) <- rnLExpr usingExpr
+ (return . return) (GroupByNothing usingExpr', fv_usingExpr)
+ GroupBySomething eitherUsingExpr byExpr -> do
+ (eitherUsingExpr', fv_eitherUsingExpr) <-
+ case eitherUsingExpr of
+ Right _ -> return (Right $ HsVar groupWithName, unitNameSet groupWithName)
+ Left usingExpr -> do
+ (usingExpr', fv_usingExpr) <- rnLExpr usingExpr
+ return (Left usingExpr', fv_usingExpr)
+
+ return $ do
+ (byExpr', fv_byExpr) <- rnLExpr byExpr
+ return (GroupBySomething eitherUsingExpr' byExpr', fv_eitherUsingExpr `plusFV` fv_byExpr)
+
+ -- We only use rnNormalStmtsAndFindUsedBinders to get unshadowed_bndrs, so
+ -- perhaps we could refactor this to use rnNormalStmts directly?
+ ((stmts', _, (groupByClause', usedBinderMap, thing)), fvs) <-
+ rnNormalStmtsAndFindUsedBinders (TransformStmtCtxt ctxt) stmts $ \unshadowed_bndrs -> do
+ (groupByClause', fv_groupByClause) <- groupByClauseAction
+
+ unshadowed_bndrs' <- mapM newLocalName unshadowed_bndrs
+ let binderMap = zip unshadowed_bndrs unshadowed_bndrs'
+
+ -- Bind the "thing" inside a context where we have REBOUND everything
+ -- bound by the statements before the group. This is necessary since after
+ -- the grouping the same identifiers actually have different meanings
+ -- i.e. they refer to lists not singletons!
+ (thing, fv_thing) <- bindLocalNames unshadowed_bndrs' thing_inside
+
+ -- We remove entries from the binder map that are not used in the thing_inside.
+ -- We can then use that usage information to ensure that the free variables do
+ -- not contain the things we just bound, but do contain the things we need to
+ -- make those bindings (i.e. the corresponding non-listy variables)
+
+ -- Note that we also retain those entries which have an old binder in our
+ -- own free variables (the using or by expression). This is because this map
+ -- is reused in the desugarer to create the type to bind from the statements
+ -- that occur before this one. If the binders we need are not in the map, they
+ -- will never get bound into our desugared expression and hence the simplifier
+ -- crashes as we refer to variables that don't exist!
+ let usedBinderMap = filter
+ (\(old_binder, new_binder) ->
+ (new_binder `elemNameSet` fv_thing) ||
+ (old_binder `elemNameSet` fv_groupByClause)) binderMap
+ (usedOldBinders, usedNewBinders) = unzip usedBinderMap
+ real_fv_thing = (delListFromNameSet fv_thing usedNewBinders) `plusFV` (mkNameSet usedOldBinders)
+
+ return ((groupByClause', usedBinderMap, thing), fv_groupByClause `plusFV` real_fv_thing)
+
+ traceRn (text "rnStmt: implicitly rebound these used binders:" <+> ppr usedBinderMap)
+ return ((GroupStmt (stmts', usedBinderMap) groupByClause', thing), fvs)
+
+rnNormalStmtsAndFindUsedBinders :: HsStmtContext Name
+ -> [LStmt RdrName]
+ -> ([Name] -> RnM (thing, FreeVars))
+ -> RnM (([LStmt Name], [Name], thing), FreeVars)
+rnNormalStmtsAndFindUsedBinders ctxt stmts thing_inside = do
+ ((stmts', (used_bndrs, inner_thing)), fvs) <- rnNormalStmts ctxt stmts $ do
+ -- Find the Names that are bound by stmts that
+ -- by assumption we have just renamed
+ local_env <- getLocalRdrEnv
+ let
+ stmts_binders = collectLStmtsBinders stmts
+ bndrs = map (expectJust "rnStmt"
+ . lookupLocalRdrEnv local_env
+ . unLoc) stmts_binders
+
+ -- If shadow, we'll look up (Unqual x) twice, getting
+ -- the second binding both times, which is the
+ -- one we want
+ unshadowed_bndrs = nub bndrs
+
+ -- Typecheck the thing inside, passing on all
+ -- the Names bound before it for its information
+ (thing, fvs) <- thing_inside unshadowed_bndrs
+
+ -- Figure out which of the bound names are used
+ -- after the statements we renamed
+ let used_bndrs = filter (`elemNameSet` fvs) bndrs
+ return ((used_bndrs, thing), fvs)
+
+ -- Flatten the tuple returned by the above call a bit!
+ return ((stmts', used_bndrs, inner_thing), fvs)
+
+rnParallelStmts :: HsStmtContext Name -> [([LStmt RdrName], [RdrName])]
+ -> RnM (thing, FreeVars)
+ -> RnM (([([LStmt Name], [Name])], thing), FreeVars)
+rnParallelStmts ctxt segs thing_inside = do
+ orig_lcl_env <- getLocalRdrEnv
+ go orig_lcl_env [] segs
+ where
+ go orig_lcl_env bndrs [] = do
+ let (bndrs', dups) = removeDups cmpByOcc bndrs
+ inner_env = extendLocalRdrEnv orig_lcl_env bndrs'
+
+ mappM dupErr dups
+ (thing, fvs) <- setLocalRdrEnv inner_env thing_inside
+ return (([], thing), fvs)
+
+ go orig_lcl_env bndrs_so_far ((stmts, _) : segs) = do
+ ((stmts', bndrs, (segs', thing)), fvs) <- rnNormalStmtsAndFindUsedBinders ctxt stmts $ \new_bndrs -> do
+ -- Typecheck the thing inside, passing on all
+ -- the Names bound, but separately; revert the envt
+ setLocalRdrEnv orig_lcl_env $ do
+ go orig_lcl_env (new_bndrs ++ bndrs_so_far) segs
+
+ let seg' = (stmts', bndrs)
+ return (((seg':segs'), thing), delListFromNameSet fvs bndrs)
+
+ cmpByOcc n1 n2 = nameOccName n1 `compare` nameOccName n2
+ dupErr vs = addErr (ptext (sLit "Duplicate binding in parallel list comprehension for:")
+ <+> quotes (ppr (head vs)))