Nullary Data Con Wrappers are not tagged
This ticket is concerned with the tagging logic of nullary data con *wrappers*. Concretely, it seems we are not tagging nullary data con wrappers at their usage site. The immediate consequences of not tagging these wrappers must be considered in at least two distinct scenarios:
The data type is lifted:
* If we pass the datacon wrapper as an argument to a function untagged (tag=0), despite knowing exactly what the constructor is, in the function body we will treat the argument as a thunk (which means calling at least an extra function -- the data con _con_entry() -- which will in turn return a tagged pointer)
The data type is unlifted:
* When we pass the unlifted datacon wrapper untagged (tag=0) to a function (that must be expecting an unlifted argument), we will get a segmentation fault as in #23146. The reason is functions expect their unlifted arguments to be evaluated and tagged, and therefore do not handle the case in which the unlifted argument has tag=0.
This ticket is closely related to #23146 and I intend to fix both simultaneously.
This one discusses the tagging issue in its full generality as opposed to the segfault particular to the unlifted case.
----
# Examples
I've got a good example of this for each case.
There are two subtleties about them. Nonetheless, they showcase the bug well and we must handle this correctly regardless:
(1) Why do we even have a wrapper for a nullary data con?
Answer: because of the unlifted equality coercion the worker takes as an argument (despite this argument not being runtime relevant)
(2) The wrapper *doesn't* inline. (Why not?).
### Unlifted case
This example will segfault when run, and it's the reproducer from #23146.
```haskell
-- A.hs
{-# LANGUAGE GADTs, UnliftedDatatypes #-}
-- Unlifted, Nil worker has 1 Zero-Width argument
import GHC.Exts
import Data.Kind
import R2
fieldsSam :: NP xs -> NP xs -> Bool
fieldsSam (x' ::* xs) (y' ::* ys) = fieldsSam xs ys
fieldsSam UNil UNil = True
main :: IO ()
main = print (fieldsSam UNil UNil)
-- B.hs
{-# LANGUAGE UnliftedDatatypes #-}
module R2 where
import GHC.Exts
type NP :: [UnliftedType] -> UnliftedType
data NP xs where
UNil :: NP '[]
(::*) :: x -> NP xs -> NP (x ': xs)
```
### Lifted case
This example will not segfault, but inspecting the cmm will show the wrappers being passed untagged and the logic to handle untagged arguments (tag == 0)
```haskell
-- A.hs
{-# LANGUAGE GADTs, UnliftedDatatypes #-}
-- Lifted, 1 Zero-Width argument
import GHC.Exts
import Data.Kind
import S2
fieldsSam :: NP xs -> NP xs -> Bool
fieldsSam (x' ::* xs) (y' ::* ys) = fieldsSam xs ys
fieldsSam UNil UNil = True
main :: IO ()
main = print (fieldsSam UNil UNil)
-- B.hs
{-# LANGUAGE UnliftedDatatypes #-}
module S2 where
import Data.Kind
import GHC.Exts
type NP :: [Type] -> Type
data NP xs where
UNil :: NP '[]
(::*) :: x -> NP xs -> NP (x ': xs)
```
CC @simonpj @bgamari @AndreasK
issue