Specialise: InScopeSet of se_subst does not account for floated dictionary bindings
I'm in the process of debugging a scoping error in the Specialiser as part of my patch in !7788 (closed).
I'm getting a lookupIdSubst
panic in GHC.Hs.Expr with my changes. Upon some digging, I found out that the following assertion in spec_call
does not hold:
--- a/compiler/GHC/Core/Opt/Specialise.hs
+++ b/compiler/GHC/Core/Opt/Specialise.hs
@@ -615,6 +615,10 @@ specProgram guts@(ModGuts { mg_module = this_mod
go [] = return ([], emptyUDs)
go (bind:binds) = do (binds', uds) <- go binds
+ let db_bndrs = unionVarSets $ map (mkVarSet . filter isLocalId . bindersOf . db_bind) (bagToList (ud_binds uds))
+ let not_in_scope = db_bndrs `minusVarSet` getInScopeVars (Core.substInScope (se_subst top_env))
+ massertPpr (isEmptyVarSet not_in_scope)
+ (text "not in scope above" $$ ppr (bindersOf bind) $$ ppr (ud_binds uds) $$ ppr not_in_scope)
(bind', uds') <- specBind top_env bind uds
return (bind' ++ binds', uds')
When I compile a simpler form of T17966
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module Lib where
class C a b where
m :: a -> b -> String
instance Show b => C Bool b where
m a b = show a ++ show b
{-# INLINABLE [0] m #-}
f :: (C a b) => a -> b -> String
f a b = m a b ++ "!"
{-# INLINABLE [0] f #-}
x :: String
x = f True ()
I get an assertion error:
ASSERT failed!
not in scope above
[$fCBoolb]
{DB {bind: $dC_sQO :: C Bool ()
[LclId]
$dC_sQO = $fCBoolb @() $fShow()
fvs: {$fShow(), $fCBoolb}}}
{$dC_sQO}
We try to float out the $dC_sQO
dictionary from $sf
, but that results in an incomplete (IMO!) InScopeSet in top_env
, because $dC
will not be in scope when we analyse $fCBoolb
and above.
I think we should add any new dict binds and possibly their db_fvs
from the RHSs to the top_env
's InScopeSet
if we float them out. (Same for the other call site of specBind
in specExpr
). Perhaps specBind
should simply return a new SpecEnv
.