summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simon.peytonjones@gmail.com>2023-02-28 11:11:41 +0000
committerBen Gamari <ben@smart-cactus.org>2023-03-02 10:16:05 -0500
commit3b9bf327243f0167b2c486b036812273e24dc394 (patch)
tree7432b8d5c3cbd7cc6d2a201a81dbea8b746a30d3
parentc00a8a78e9e6d02fca5e5c5360d1387b17e67b89 (diff)
downloadhaskell-3b9bf327243f0167b2c486b036812273e24dc394.tar.gz
Account for TYPE vs CONSTRAINT in mkSelCo
As #23018 showed, in mkRuntimeRepCo we need to account for coercions between TYPE and COERCION. See Note [mkRuntimeRepCo] in GHC.Core.Coercion. (cherry picked from commit bb500e2a2d039dc75c8bb80d47ea2349b97fbf1b)
-rw-r--r--compiler/GHC/Core/Coercion.hs37
-rw-r--r--compiler/GHC/Core/Coercion/Opt.hs7
-rw-r--r--compiler/GHC/Core/Type.hs9
-rw-r--r--testsuite/tests/typecheck/should_compile/T23018.hs9
-rw-r--r--testsuite/tests/typecheck/should_compile/all.T1
5 files changed, 56 insertions, 7 deletions
diff --git a/compiler/GHC/Core/Coercion.hs b/compiler/GHC/Core/Coercion.hs
index bab53e323b..6459136973 100644
--- a/compiler/GHC/Core/Coercion.hs
+++ b/compiler/GHC/Core/Coercion.hs
@@ -612,14 +612,40 @@ eqTyConRole tc
| otherwise
= pprPanic "eqTyConRole: unknown tycon" (ppr tc)
--- | Given a coercion @co1 :: (a :: TYPE r1) ~ (b :: TYPE r2)@,
--- (or CONSTRAINT instead of TYPE)
--- produce a coercion @rep_co :: r1 ~ r2@.
+-- | Given a coercion `co :: (t1 :: TYPE r1) ~ (t2 :: TYPE r2)`
+-- produce a coercion `rep_co :: r1 ~ r2`
+-- But actually it is possible that
+-- co :: (t1 :: CONSTRAINT r1) ~ (t2 :: CONSTRAINT r2)
+-- or co :: (t1 :: TYPE r1) ~ (t2 :: CONSTRAINT r2)
+-- or co :: (t1 :: CONSTRAINT r1) ~ (t2 :: TYPE r2)
+-- See Note [mkRuntimeRepCo]
mkRuntimeRepCo :: HasDebugCallStack => Coercion -> Coercion
mkRuntimeRepCo co
- = mkSelCo (SelTyCon 0 Nominal) kind_co
+ = assert (isTYPEorCONSTRAINT k1 && isTYPEorCONSTRAINT k2) $
+ mkSelCo (SelTyCon 0 Nominal) kind_co
where
kind_co = mkKindCo co -- kind_co :: TYPE r1 ~ TYPE r2
+ Pair k1 k2 = coercionKind kind_co
+
+{- Note [mkRuntimeRepCo]
+~~~~~~~~~~~~~~~~~~~~~~~~
+Given
+ class C a where { op :: Maybe a }
+we will get an axiom
+ axC a :: (C a :: CONSTRAINT r1) ~ (Maybe a :: TYPE r2)
+(See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim.)
+
+Then we may call mkRuntimeRepCo on (axC ty), and that will return
+ mkSelCo (SelTyCon 0 Nominal) (Kind (axC ty)) :: r1 ~ r2
+
+So mkSelCo needs to be happy with decomposing a coercion of kind
+ CONSTRAINT r1 ~ TYPE r2
+
+Hence the use of `tyConIsTYPEorCONSTRAINT` in the assertion `good_call`
+in `mkSelCo`. See #23018 for a concrete example. (In this context it's
+important that TYPE and CONSTRAINT have the same arity and kind, not
+merely that they are not-apart; otherwise SelCo would not make sense.)
+-}
isReflCoVar_maybe :: Var -> Maybe Coercion
-- If cv :: t~t then isReflCoVar_maybe cv = Just (Refl t)
@@ -1173,7 +1199,8 @@ mkSelCo_maybe cs co
, Just (tc2, tys2) <- splitTyConApp_maybe ty2
, let { len1 = length tys1
; len2 = length tys2 }
- = tc1 == tc2
+ = (tc1 == tc2 || (tyConIsTYPEorCONSTRAINT tc1 && tyConIsTYPEorCONSTRAINT tc2))
+ -- tyConIsTYPEorCONSTRAINT: see Note [mkRuntimeRepCo]
&& len1 == len2
&& n < len1
&& r == tyConRole (coercionRole co) tc1 n
diff --git a/compiler/GHC/Core/Coercion/Opt.hs b/compiler/GHC/Core/Coercion/Opt.hs
index 6e10eae9e2..60718db082 100644
--- a/compiler/GHC/Core/Coercion/Opt.hs
+++ b/compiler/GHC/Core/Coercion/Opt.hs
@@ -44,6 +44,13 @@ import Control.Monad ( zipWithM )
%* *
%************************************************************************
+This module does coercion optimisation. See the paper
+
+ Evidence normalization in Systtem FV (RTA'13)
+ https://simon.peytonjones.org/evidence-normalization/
+
+The paper is also in the GHC repo, in docs/opt-coercion.
+
Note [Optimising coercion optimisation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Looking up a coercion's role or kind is linear in the size of the
diff --git a/compiler/GHC/Core/Type.hs b/compiler/GHC/Core/Type.hs
index b0702a1602..f4cbf84063 100644
--- a/compiler/GHC/Core/Type.hs
+++ b/compiler/GHC/Core/Type.hs
@@ -124,7 +124,7 @@ module GHC.Core.Type (
-- *** Levity and boxity
sORTKind_maybe, typeTypeOrConstraint,
- typeLevity_maybe,
+ typeLevity_maybe, tyConIsTYPEorCONSTRAINT,
isLiftedTypeKind, isUnliftedTypeKind, pickyIsLiftedTypeKind,
isLiftedRuntimeRep, isUnliftedRuntimeRep, runtimeRepLevity_maybe,
isBoxedRuntimeRep,
@@ -2638,13 +2638,18 @@ isPredTy ty = case typeTypeOrConstraint ty of
TypeLike -> False
ConstraintLike -> True
------------------------------------------
-- | Does this classify a type allowed to have values? Responds True to things
-- like *, TYPE Lifted, TYPE IntRep, TYPE v, Constraint.
isTYPEorCONSTRAINT :: Kind -> Bool
-- ^ True of a kind `TYPE _` or `CONSTRAINT _`
isTYPEorCONSTRAINT k = isJust (sORTKind_maybe k)
+tyConIsTYPEorCONSTRAINT :: TyCon -> Bool
+tyConIsTYPEorCONSTRAINT tc
+ = tc_uniq == tYPETyConKey || tc_uniq == cONSTRAINTTyConKey
+ where
+ !tc_uniq = tyConUnique tc
+
isConstraintLikeKind :: Kind -> Bool
-- True of (CONSTRAINT _)
isConstraintLikeKind kind
diff --git a/testsuite/tests/typecheck/should_compile/T23018.hs b/testsuite/tests/typecheck/should_compile/T23018.hs
new file mode 100644
index 0000000000..980fa5254c
--- /dev/null
+++ b/testsuite/tests/typecheck/should_compile/T23018.hs
@@ -0,0 +1,9 @@
+module T23018 where
+
+import qualified Control.DeepSeq as DeepSeq
+
+class XX f where
+ rnf :: DeepSeq.NFData a => f a -> ()
+
+instance XX Maybe where
+ rnf = DeepSeq.rnf
diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T
index 20725c3d50..caebd46dfd 100644
--- a/testsuite/tests/typecheck/should_compile/all.T
+++ b/testsuite/tests/typecheck/should_compile/all.T
@@ -864,3 +864,4 @@ test('T22912', normal, compile, [''])
test('T22924', normal, compile, [''])
test('T22985a', normal, compile, ['-O'])
test('T22985b', normal, compile, [''])
+test('T23018', normal, compile, [''])