Skip to content

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.

Edited by Sebastian Graf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information