summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/typecheck/TcSplice.hs77
-rw-r--r--testsuite/tests/th/T13885.hs23
-rw-r--r--testsuite/tests/th/T13885.stdout1
-rw-r--r--testsuite/tests/th/all.T1
4 files changed, 83 insertions, 19 deletions
diff --git a/compiler/typecheck/TcSplice.hs b/compiler/typecheck/TcSplice.hs
index 8b5ed7d5f0..029ae28b7a 100644
--- a/compiler/typecheck/TcSplice.hs
+++ b/compiler/typecheck/TcSplice.hs
@@ -1449,7 +1449,7 @@ reifyDataCon isGadtDataCon tys dc
(ex_tvs, theta, arg_tys)
= dataConInstSig dc tys
-- used for GADTs data constructors
- (g_univ_tvs, g_ex_tvs, g_eq_spec, g_theta, g_arg_tys, g_res_ty)
+ (g_univ_tvs, g_ex_tvs, g_eq_spec, g_theta', g_arg_tys', g_res_ty')
= dataConFullSig dc
(srcUnpks, srcStricts)
= mapAndUnzip reifySourceBang (dataConSrcBangs dc)
@@ -1459,7 +1459,14 @@ reifyDataCon isGadtDataCon tys dc
-- Universal tvs present in eq_spec need to be filtered out, as
-- they will not appear anywhere in the type.
eq_spec_tvs = mkVarSet (map eqSpecTyVar g_eq_spec)
- g_unsbst_univ_tvs = filterOut (`elemVarSet` eq_spec_tvs) g_univ_tvs
+
+ ; (univ_subst, g_unsbst_univ_tvs)
+ -- See Note [Freshen reified GADT constructors' universal tyvars]
+ <- freshenTyVarBndrs $
+ filterOut (`elemVarSet` eq_spec_tvs) g_univ_tvs
+ ; let g_theta = substTys univ_subst g_theta'
+ g_arg_tys = substTys univ_subst g_arg_tys'
+ g_res_ty = substTy univ_subst g_res_ty'
; r_arg_tys <- reifyTypes (if isGadtDataCon then g_arg_tys else arg_tys)
@@ -1497,23 +1504,55 @@ reifyDataCon isGadtDataCon tys dc
; ASSERT( arg_tys `equalLength` dcdBangs )
ret_con }
--- Note [Reifying GADT data constructors]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- At this point in the compilation pipeline we have no way of telling whether a
--- data type was declared as a H98 data type or as a GADT. We have to rely on
--- heuristics here. We look at dcEqSpec field of all data constructors in a
--- data type declaration. If at least one data constructor has non-empty
--- dcEqSpec this means that the data type must have been declared as a GADT.
--- Consider these declarations:
---
--- data T a where
--- MkT :: forall a. (a ~ Int) => T a
---
--- data T a where
--- MkT :: T Int
---
--- First declaration will be reified as a GADT. Second declaration will be
--- reified as a normal H98 data type declaration.
+{-
+Note [Reifying GADT data constructors]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+At this point in the compilation pipeline we have no way of telling whether a
+data type was declared as a H98 data type or as a GADT. We have to rely on
+heuristics here. We look at dcEqSpec field of all data constructors in a
+data type declaration. If at least one data constructor has non-empty
+dcEqSpec this means that the data type must have been declared as a GADT.
+Consider these declarations:
+
+ data T1 a where
+ MkT1 :: T1 Int
+
+ data T2 a where
+ MkT2 :: forall a. (a ~ Int) => T2 a
+
+T1 will be reified as a GADT, as it has a non-empty EqSpec [(a, Int)] due to
+MkT1's return type. T2 will be reified as a normal H98 data type declaration
+since MkT2 uses an explicit type equality in its context instead of an implicit
+equality in its return type, and therefore has an empty EqSpec.
+
+Note [Freshen reified GADT constructors' universal tyvars]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose one were to reify this data type:
+
+ data a :~: b = (a ~ b) => Refl
+
+This will be reified as if it were a GADT definiton, so the reified definition
+will be closer to:
+
+ data a :~: b where
+ Refl :: forall a b. (a ~ b) => a :~: b
+
+We ought to be careful here about the uniques we give to the occurrences of `a`
+and `b` in this definition. That is because in the original DataCon, all uses
+of `a` and `b` have the same unique, since `a` and `b` are both universally
+quantified type variables--that is, they are used in both the (:~:) tycon as
+well as in the constructor type signature. But when we turn the DataCon
+definition into the reified one, the `a` and `b` in the constructor type
+signature becomes differently scoped than the `a` and `b` in `data a :~: b`.
+
+While it wouldn't technically be *wrong* per se to re-use the same uniques for
+`a` and `b` across these two different scopes, it's somewhat annoying for end
+users of Template Haskell, since they wouldn't be able to rely on the
+assumption that all TH names have globally distinct uniques (#13885). For this
+reason, we freshen the universally quantified tyvars that go into the reified
+GADT constructor type signature to give them distinct uniques from their
+counterparts in the tycon.
+-}
------------------------------
reifyClass :: Class -> TcM TH.Info
diff --git a/testsuite/tests/th/T13885.hs b/testsuite/tests/th/T13885.hs
new file mode 100644
index 0000000000..0e29c88610
--- /dev/null
+++ b/testsuite/tests/th/T13885.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+module Main where
+
+import Data.Function (on)
+import Language.Haskell.TH.Syntax
+
+data a :~: b = a ~ b => Refl
+
+$(return [])
+
+main :: IO ()
+main = print
+ $(do TyConI (DataD _ _ tycon_tyvars _
+ [ForallC con_tyvars _ _] _) <- reify ''(:~:)
+
+ let tvbName :: TyVarBndr -> Name
+ tvbName (PlainTV n) = n
+ tvbName (KindedTV n _) = n
+
+ lift $ and $ zipWith ((/=) `on` tvbName) tycon_tyvars con_tyvars)
diff --git a/testsuite/tests/th/T13885.stdout b/testsuite/tests/th/T13885.stdout
new file mode 100644
index 0000000000..0ca95142bb
--- /dev/null
+++ b/testsuite/tests/th/T13885.stdout
@@ -0,0 +1 @@
+True
diff --git a/testsuite/tests/th/all.T b/testsuite/tests/th/all.T
index 5d61fa4880..1e737ace8f 100644
--- a/testsuite/tests/th/all.T
+++ b/testsuite/tests/th/all.T
@@ -391,6 +391,7 @@ test('T13781', normal, compile, ['-v0'])
test('T13782', normal, compile, [''])
test('T13837', normal, compile_fail, ['-v0 -dsuppress-uniques'])
test('T13856', normal, compile, ['-v0 -ddump-splices -dsuppress-uniques'])
+test('T13885', normal, compile_and_run, ['-v0'])
test('T13887', normal, compile_and_run, ['-v0'])
test('T13968', normal, compile_fail, ['-v0'])
test('T14060', normal, compile_and_run, ['-v0'])