Skip to content

Worker/wrapper still makes error thunks for strict constructor fields.

While working on tag inference I found that ghc still seems to put error thunks into strict fields at times.

In particular we have this source code:

    fail_tycon global_env ty_con =
      let pprov = case lookupGRE_Name global_env (tyConName ty_con) of
            Just gre -> nest 2 (pprNameProvenance gre)
            Nothing  -> empty
      in failWithTc (term_level_tycons ty_con $$ pprov)

Which after WW comes out to this relevant core:

join {
  -- Essentially takes a unboxed name as argument
  $w$j_soEE [InlPrag=[2], Dmd=MCM(CM(CM(CM(CM(CM(CM(CM(L))))))))]
    :: GHC.Types.Name.NameSort
       -> NameSpace
       -> ghc-prim:GHC.Prim.Int#
       -> ghc-prim:GHC.Prim.Int#
       -> ghc-prim:GHC.Prim.ByteArray#
       -> GHC.Data.FastString.FastZString
       -> ghc-prim:GHC.Prim.Int#
       -> Name
       -> GHC.Utils.Ppr.Doc
  [LclId[JoinId(8)],
   Arity=8,
   Str=<L><SL><L><L><L><LP(L,L,L)><L><LP(L,LP(L,LP(L,L,L,LP(L,L,L))),L,A)>]
  $w$j_soEE (w_soEo :: GHC.Types.Name.NameSort)
            (ww_soEu [Dmd=SL]
               :: NameSpace
               Unf=OtherCon [])
            (ww_soEx :: ghc-prim:GHC.Prim.Int#)
            (ww_soEy :: ghc-prim:GHC.Prim.Int#)
            (ww_soEz :: ghc-prim:GHC.Prim.ByteArray#)
            (ww_soEA [Dmd=LP(L,L,L)] :: GHC.Data.FastString.FastZString)
            (w_soEq :: ghc-prim:GHC.Prim.Int#)
            (w_soEs [Dmd=LP(L,LP(L,LP(L,L,L,LP(L,L,L))),L,A)] :: Name)
    = ... -- Some let's removed for clarity
      let {
        w_soEr [Dmd=A] :: SrcSpan
        [LclId]
        w_soEr
          = ghc-prim:GHC.Prim.Panic.absentError
              @SrcSpan
              "Arg: w_soEr\n\
              \Type: SrcSpan\n\
              \Id: w_soEr\n\
              \In output file  `_build/stage1/compiler/build/GHC/Tc/Gen/Head.o'"# } in
      let {
        ds1_ajuu [Dmd=SP(SL,SP(L,L,L,LP(L,L,L))), OS=OneShot] :: OccName
        [LclId]
        ds1_ajuu = w_soEp } in
      let {
        ds2_ajuw [Dmd=A, OS=OneShot] :: SrcSpan
        [LclId, Unf=OtherCon []]
        ds2_ajuw = w_soEr } in
      let {
        wild_ajus [Dmd=LP(L,LP(L,LP(L,L,L,LP(L,L,L))),L,A), OS=OneShot]
          :: Name
        [LclId,
         Unf=Unf{Src=InlineStable, TopLvl=False, Value=True, ConLike=True,
                 WorkFree=True, Expandable=True,
                 Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=False)
                 Tmpl= GHC.Types.Name.Name ds_ajut ds1_ajuu dt_ajuv ds2_ajuw}]
        wild_ajus = w_soEs } in
      case ds1_ajuu of
      { GHC.Types.Name.Occurrence.OccName ww_amZ3 [Dmd=SL]
                                          ww1_amZ4 [Dmd=SP(L,L,L,LP(L,L,L))] ->
      case ww1_amZ4 of
      { GHC.Data.FastString.FastString ww2_amZ7 ww3_amZ8 ww4_amZ9
                                       ww5_amZa [Dmd=LP(L,L,L)] ->
      let {
        d1_snVy [Dmd=SC1(L)] :: SDocContext -> GHC.Utils.Ppr.Doc
        d1_snVy
          = ... -- not imporant
      case GHC.Types.Name.Reader.$wlookupGRE_Name_OccName
             global_env_adne
             wild_ajus
             ww_amZ3
             ww2_amZ7
             ww3_amZ8
             ww4_amZ9
             ww5_amZa
      of {
        -- Not important
      }
      }
      } } in
      ...

The absentError we enter is the RHS of w_soEr, w_soEr also being bound to ds2_ajuw. They both have a demand of plain 'A' (Absent).

This is a problem for WW, which relies on them being absent and strict if they might end up in a strict field. How I think they get their demand is that w_soEr get's it's demand from ds2_ajuw. ds2_ajuw appears as second argument to $wlookupGRE_Name_OccName which in turn has this demand: Str=<1L><LP(L,LP(L,LP(L,L,L,LP(L,L,L))),L,A)><SL><L><L><L><LP(L,L,L)>

Putting the demand <LP(L,LP(L,LP(L,L,L,LP(L,L,L))),L,A)> onto wild_ajus which then results in ds2_ajuw getting just A.

Now this poses a problem. What happens down the line is we rebox the Name argument by inlining wild_ajus like this:

$wlookupGRE_Name_OccName global_env_adne
    (GHC.Types.Name.Name ds_ajut ds1_ajuu dt_ajuv ds2_ajuw)

With ds2_ajuw being our error thunk being stored in a strict field now, but not having a strict demand.

I wonder if this could simply be solved if we give every argument T a demand of at least C_10 for it's strict components.

In a simple example like this:

data T = MkT Char !(Bool,Bool)

foo :: Int -> T -> _
foo n x = n `seq`
    case x of
        MkT c _ -> (c, undefined)

Instead of giving the current demand Str=<S,1*H><S,1*U(U,A)>, we would then give the demand Str=<S,1*H><S,1*U(U,B)>.

Not sure if there would be problems with this approach. Currently I think we already do so during WW only in an adhoc-fashion using addDataConStrictness.

As a result I think the current way of marking these bindings as strict falls short for this purpose.

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