[Git][ghc/ghc][master] We can't UNPACK multi-constructor GADTs

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Wed Mar 12 02:39:48 UTC 2025



Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC


Commits:
b6d5b091 by Simon Peyton Jones at 2025-03-11T22:39:23-04:00
We can't UNPACK multi-constructor GADTs

This MR fixes #25672

See Note [Unpacking GADTs and existentials] in GHC.Types.Id.Make

- - - - -


6 changed files:

- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Types/Id/Make.hs
- + testsuite/tests/simplCore/should_fail/T25672.hs
- + testsuite/tests/simplCore/should_fail/T25672.stderr
- testsuite/tests/simplCore/should_fail/all.T


Changes:

=====================================
compiler/GHC/Core/DataCon.hs
=====================================
@@ -1018,6 +1018,9 @@ instance Data.Data DataCon where
     gunfold _ _  = error "gunfold"
     dataTypeOf _ = mkNoRepType "DataCon"
 
+instance Outputable HsSrcBang where
+    ppr (HsSrcBang _source_text bang) = ppr bang
+
 instance Outputable HsBang where
     ppr (HsBang prag mark) = ppr prag <+> ppr mark
 


=====================================
compiler/GHC/Tc/TyCl.hs
=====================================
@@ -4817,6 +4817,8 @@ checkValidDataCon dflags existential_ok tc con
         ; traceTc "Done validity of data con" $
           vcat [ ppr con
                , text "Datacon wrapper type:" <+> ppr (dataConWrapperType con)
+               , text "Datacon src bangs:" <+> ppr (dataConSrcBangs con)
+               , text "Datacon impl bangs:" <+> ppr (dataConImplBangs con)
                , text "Datacon rep type:" <+> ppr (dataConRepType con)
                , text "Datacon display type:" <+> ppr data_con_display_type
                , text "Rep typcon binders:" <+> ppr (tyConBinders (dataConTyCon con))


=====================================
compiler/GHC/Types/Id/Make.hs
=====================================
@@ -1540,39 +1540,87 @@ shouldUnpackArgTy bang_opts prag fam_envs arg_ty
 
 
 
--- Given a type already assumed to have been normalized by topNormaliseType,
--- unpackable_type_datacons ty = Just datacons
--- iff ty is of the form
---     T ty1 .. tyn
--- and T is an algebraic data type (not newtype), in which no data
--- constructors have existentials, and datacons is the list of data
--- constructors of T.
 unpackable_type_datacons :: Type -> Maybe [DataCon]
+-- Given a type already assumed to have been normalized by topNormaliseType,
+--    unpackable_type_datacons (T ty1 .. tyn) = Just datacons
+-- iff the type can be unpacked (see Note [Unpacking GADTs and existentials])
+-- and `datacons` are the data constructors of T
 unpackable_type_datacons ty
   | Just (tc, _) <- splitTyConApp_maybe ty
-  , not (isNewTyCon tc)  -- Even though `ty` has been normalised, it could still
-                         -- be a /recursive/ newtype, so we must check for that
+  , not (isNewTyCon tc)
+      -- isNewTyCon: even though `ty` has been normalised, whic includes looking
+      -- through newtypes, it could still be a /recursive/ newtype, so we must
+      -- check for that case
   , Just cons <- tyConDataCons_maybe tc
-  , not (null cons)      -- Don't upack nullary sums; no need.
-                         -- They already take zero bits
-  , all (null . dataConExTyCoVars) cons
-  = Just cons -- See Note [Unpacking GADTs and existentials]
+  , unpackable_cons cons
+  = Just cons
   | otherwise
   = Nothing
+  where
+    unpackable_cons :: [DataCon] -> Bool
+    -- True if we can unpack a value of type (T t1 .. tn),
+    -- where T is an algebraic data type with these constructors
+    -- See Note [Unpacking GADTs and existentials]
+    unpackable_cons []   -- Don't unpack nullary sums; no need.
+      = False            -- They already take zero bits; see (UC0)
+
+    unpackable_cons [con]   -- Exactly one data constructor; see (UC1)
+      = null (dataConExTyCoVars con)
+
+    unpackable_cons cons  -- More than one data constructor; see (UC2)
+      = all isVanillaDataCon cons
 
 {-
 Note [Unpacking GADTs and existentials]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-There is nothing stopping us unpacking a data type with equality
-components, like
-  data Equal a b where
-    Equal :: Equal a a
-
-And it'd be fine to unpack a product type with existential components
-too, but that would require a bit more plumbing, so currently we don't.
+Can we unpack a value of an algebraic data type T? For example
+   data D a = MkD {-# UNPACK #-} (T a)
+Can we unpack that (T a) field?
+
+Three cases to consider in `unpackable_cons`
+
+(UC0) No data constructors; a nullary sum type.  This already takes zero
+      bits so there is no point in unpacking it.
+
+(UC1) Single-constructor types (products).  We can just represent it by
+   its fields. For example, if `T` is defined as:
+      data T a = MkT a a Int
+   then we can unpack it as follows.  The worker for MkD takes three unpacked fields:
+       data D a = MkD a a Int
+       $MkD :: T a -> D a
+       $MkD (MkT a1 a2 i) = MkD a1 a2 i
+
+   We currently /can't/ do this if T has existentially-bound type variables,
+   hence:   null (dataConExTyCoVars con)   in `unpackable_cons`.
+   But see also (UC3) below.
+
+   But we /can/ do it for (some) GADTs, such as:
+      data Equal a b where { Equal :: Equal a a }
+      data Wom a where { Wom1 :: Int -> Wom Bool }
+   We will get a MkD constructor that includes some coercion arguments,
+   but that is fine.   See #14978.  We still can't accommodate existentials,
+   but these particular examples don't use existentials.
+
+(UC2) Multi-constructor types, e.g.
+        data T a = T1 a | T2 Int a
+  Here we unpack the field to an unboxed sum type, thus:
+    data D a = MkD (# a | (# Int, a #) #)
+
+  However, now we can't deal with GADTs at all, because we'd need an
+  unboxed sum whose component was a unboxed tuple, whose component(s)
+  have kind (CONSTRAINT r); and that's not well-kinded.  Hence the
+    all isVanillaDataCon
+  condition in `unpackable_cons`. See #25672.
+
+(UC3)  For single-constructor types, with some more plumbing we could
+   allow existentials. e.g.
+       data T a = forall b. MkT a (b->Int) b
+   could unpack to
+       data D a = forall b. MkD a (b->Int) b
+       $MkD :: T a -> D a
+       $MkD (MkT @b x f y) = MkD @b x f y
+   Eminently possible, but more plumbing needed.
 
-So for now we require: null (dataConExTyCoVars data_con)
-See #14978
 
 Note [Unpack one-wide fields]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


=====================================
testsuite/tests/simplCore/should_fail/T25672.hs
=====================================
@@ -0,0 +1,15 @@
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+
+module T25672 where
+
+data IntOrWord (isInt :: Bool) where
+    Int :: !Int -> IntOrWord True
+    Word :: !Word -> IntOrWord False
+
+data WrapIntOrWord (isInt :: Bool)
+    = WrapIntOrWord {lit :: {-# UNPACK #-} !(IntOrWord isInt)}
+
+boom :: WrapIntOrWord True
+boom = WrapIntOrWord (Int 1)


=====================================
testsuite/tests/simplCore/should_fail/T25672.stderr
=====================================
@@ -0,0 +1,6 @@
+T25672.hs:12:7: warning: [GHC-40091]
+    • Ignoring unusable UNPACK pragma
+        on the first argument of ‘WrapIntOrWord’
+    • In the definition of data constructor ‘WrapIntOrWord’
+      In the data type declaration for ‘WrapIntOrWord’
+


=====================================
testsuite/tests/simplCore/should_fail/all.T
=====================================
@@ -1,3 +1,6 @@
 test('T7411', [expect_broken_for(7411, ['optasm', 'optllvm',
                                         'threaded2', 'dyn']),
                exit_code(1)], compile_and_run, [''])
+
+# This one produces a warning
+test('T25672', normal, compile, ['-O'])



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b6d5b09103dea97351774c5ab34082165504b997

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b6d5b09103dea97351774c5ab34082165504b997
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20250311/775de916/attachment-0001.html>


More information about the ghc-commits mailing list