summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2021-10-20 17:52:04 +0100
committerSimon Peyton Jones <simonpj@microsoft.com>2021-10-20 17:56:23 +0100
commit617f7798009c261045a73a15e8f4f35ddbe9b627 (patch)
tree9fb5b293cf44d54c077bcc8093c37d42241657f8
parentd73131b9315abf39fc2d307b1c59bf8edd1e43ac (diff)
downloadhaskell-wip/T20200AgdaDatatypes.tar.gz
Look up knot-tied GlobalIds in the InScopeSetwip/T20200AgdaDatatypes
As shown in #20200, and described in Note [Simplifying recursive modules], it is possible to encounter an occurrence of a GlobalId that is bound in the module being compiled. Lint complains, quite properly. This patch updates GHC.Core.Opt.Simplify.Env.refineFromScope so that it looks up a GlobalId in the InScopeSet if the module is the one being compiled. That in turn means we need to know what is the module being compiled, hence the new st_module field in SimplTopEnv. Test case is simplCore/should_compile/AgdaDatatypes
-rw-r--r--compiler/GHC/Core/Opt/Pipeline.hs5
-rw-r--r--compiler/GHC/Core/Opt/Simplify.hs77
-rw-r--r--compiler/GHC/Core/Opt/Simplify/Env.hs99
-rw-r--r--compiler/GHC/Core/Opt/Simplify/Monad.hs24
-rw-r--r--compiler/GHC/Core/Opt/Simplify/Utils.hs2
-rw-r--r--compiler/GHC/Types/Id/Info.hs2
-rw-r--r--compiler/GHC/Types/Var.hs2
-rw-r--r--testsuite/tests/simplCore/should_compile/T20200AgdaBase.hs12
-rw-r--r--testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs15
-rw-r--r--testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs-boot5
-rw-r--r--testsuite/tests/simplCore/should_compile/T20200AgdaInternalToAbstract.hs13
-rw-r--r--testsuite/tests/simplCore/should_compile/T20200AgdaPretty.hs20
-rw-r--r--testsuite/tests/simplCore/should_compile/all.T1
13 files changed, 210 insertions, 67 deletions
diff --git a/compiler/GHC/Core/Opt/Pipeline.hs b/compiler/GHC/Core/Opt/Pipeline.hs
index 18ac910d15..aa8f1c14c5 100644
--- a/compiler/GHC/Core/Opt/Pipeline.hs
+++ b/compiler/GHC/Core/Opt/Pipeline.hs
@@ -593,7 +593,7 @@ simplifyExpr hsc_env expr
; let sz = exprSize expr
- ; (expr', counts) <- initSmpl logger dflags rule_env fi_env sz $
+ ; (expr', counts) <- initSmpl logger dflags Nothing rule_env fi_env sz $
simplExprGently simpl_env expr
; Logger.putDumpFileMaybe logger Opt_D_dump_simpl_stats
@@ -733,7 +733,8 @@ simplifyPgmIO pass@(CoreDoSimplify max_iterations mode)
-- Simplify the program
((binds1, rules1), counts1) <-
- initSmpl logger dflags (mkRuleEnv rule_base2 vis_orphs) fam_envs sz $
+ initSmpl logger dflags (Just this_mod)
+ (mkRuleEnv rule_base2 vis_orphs) fam_envs sz $
do { (floats, env1) <- {-# SCC "SimplTopBinds" #-}
simplTopBinds simpl_env tagged_binds
diff --git a/compiler/GHC/Core/Opt/Simplify.hs b/compiler/GHC/Core/Opt/Simplify.hs
index 407f84a6c5..1318aa5ac7 100644
--- a/compiler/GHC/Core/Opt/Simplify.hs
+++ b/compiler/GHC/Core/Opt/Simplify.hs
@@ -218,7 +218,8 @@ simplTopBinds env0 binds0
-- It's rather as if the top-level binders were imported.
-- See note [Glomming] in "GHC.Core.Opt.OccurAnal".
-- See Note [Bangs in the Simplifier]
- ; !env1 <- {-#SCC "simplTopBinds-simplRecBndrs" #-} simplRecBndrs env0 (bindersOfBinds binds0)
+ ; !env1 <- {-#SCC "simplTopBinds-simplRecBndrs" #-}
+ simplTopRecBndrs env0 (bindersOfBinds binds0)
; (floats, env2) <- {-#SCC "simplTopBinds-simpl_binds" #-} simpl_binds env1 binds0
; freeTick SimplifierDone
; return (floats, env2) }
@@ -1352,8 +1353,9 @@ simplTick env tickish expr cont
no_floating_past_tick =
do { let (inc,outc) = splitCont cont
; (floats, expr1) <- simplExprF env expr inc
+ ; mb_mod <- getSimplModule
; let expr2 = wrapFloats floats expr1
- tickish' = simplTickish env tickish
+ tickish' = simplTickish mb_mod tickish
; rebuild env (mkTick tickish' expr2) outc
}
@@ -1377,9 +1379,9 @@ simplTick env tickish expr cont
-- }
- simplTickish env tickish
+ simplTickish mb_mod tickish
| Breakpoint ext n ids <- tickish
- = Breakpoint ext n (map (getDoneId . substId env) ids)
+ = Breakpoint ext n (map (getDoneId . substId mb_mod env) ids)
| otherwise = tickish
-- Push type application and coercion inside a tick
@@ -1960,45 +1962,48 @@ outside. Surprisingly tricky!
************************************************************************
-}
-simplVar :: SimplEnv -> InVar -> SimplM OutExpr
+simplLocalVar :: SimplEnv -> InVar -> SimplM OutExpr
-- Look up an InVar in the environment
-simplVar env var
+-- Used only in a case alternative
+simplLocalVar env var
-- Why $! ? See Note [Bangs in the Simplifier]
| isTyVar var = return $! Type $! (substTyVar env var)
| isCoVar var = return $! Coercion $! (substCoVar env var)
| otherwise
- = case substId env var of
- ContEx tvs cvs ids e -> let env' = setSubstEnv env tvs cvs ids
- in simplExpr env' e
- DoneId var1 -> return (Var var1)
- DoneEx e _ -> return e
+ = case substId Nothing env var of
+ ContEx tvs cvs ids e -> let env' = setSubstEnv env tvs cvs ids
+ in simplExpr env' e
+ DoneId var1 -> return (Var var1)
+ DoneEx e _ -> return e
simplIdF :: SimplEnv -> InId -> SimplCont -> SimplM (SimplFloats, OutExpr)
simplIdF env var cont
- = case substId env var of
- ContEx tvs cvs ids e ->
- let env' = setSubstEnv env tvs cvs ids
- in simplExprF env' e cont
- -- Don't trim; haven't already simplified e,
- -- so the cont is not embodied in e
-
- DoneId var1 ->
- let cont' = trimJoinCont var (isJoinId_maybe var1) cont
- in completeCall env var1 cont'
-
- DoneEx e mb_join ->
- let env' = zapSubstEnv env
- cont' = trimJoinCont var mb_join cont
- in simplExprF env' e cont'
- -- Note [zapSubstEnv]
- -- The template is already simplified, so don't re-substitute.
- -- This is VITAL. Consider
- -- let x = e in
- -- let y = \z -> ...x... in
- -- \ x -> ...y...
- -- We'll clone the inner \x, adding x->x' in the id_subst
- -- Then when we inline y, we must *not* replace x by x' in
- -- the inlined copy!!
+ = do { mb_mod <- getSimplModule
+ ; case substId mb_mod env var of
+ ContEx tvs cvs ids e ->
+ let env' = setSubstEnv env tvs cvs ids
+ in simplExprF env' e cont
+ -- Don't trim; haven't already simplified e,
+ -- so the cont is not embodied in e
+
+ DoneId var1 ->
+ let cont' = trimJoinCont var (isJoinId_maybe var1) cont
+ in completeCall env var1 cont'
+
+ DoneEx e mb_join ->
+ let env' = zapSubstEnv env
+ cont' = trimJoinCont var mb_join cont
+ in simplExprF env' e cont'
+ -- Note [zapSubstEnv]
+ -- The template is already simplified, so don't re-substitute.
+ -- This is VITAL. Consider
+ -- let x = e in
+ -- let y = \z -> ...x... in
+ -- \ x -> ...y...
+ -- We'll clone the inner \x, adding x->x' in the id_subst
+ -- Then when we inline y, we must *not* replace x by x' in
+ -- the inlined copy!!
+ }
---------------------------------------------------------
-- Dealing with a call site
@@ -3316,7 +3321,7 @@ knownCon env scrut dc_floats dc dc_ty_args dc_args bndr bs rhs cont
| exprIsTrivial scrut = return (emptyFloats env
, extendIdSubst env bndr (DoneEx scrut Nothing))
-- See Note [Do not duplicate constructor applications]
- | otherwise = do { dc_args <- mapM (simplVar env) bs
+ | otherwise = do { dc_args <- mapM (simplLocalVar env) bs
-- dc_ty_args are already OutTypes,
-- but bs are InBndrs
; let con_app = Var (dataConWorkId dc)
diff --git a/compiler/GHC/Core/Opt/Simplify/Env.hs b/compiler/GHC/Core/Opt/Simplify/Env.hs
index 54a5f171ec..ec0af6cc59 100644
--- a/compiler/GHC/Core/Opt/Simplify/Env.hs
+++ b/compiler/GHC/Core/Opt/Simplify/Env.hs
@@ -20,10 +20,10 @@ module GHC.Core.Opt.Simplify.Env (
getSimplRules,
-- * Substitution results
- SimplSR(..), mkContEx, substId, lookupRecBndr, refineFromInScope,
+ SimplSR(..), mkContEx, substId, lookupRecBndr,
-- * Simplifying 'Id' binders
- simplNonRecBndr, simplNonRecJoinBndr, simplRecBndrs, simplRecJoinBndrs,
+ simplNonRecBndr, simplNonRecJoinBndr, simplTopRecBndrs, simplRecBndrs, simplRecJoinBndrs,
simplBinder, simplBinders,
substTy, substTyVar, getTCvSubst,
substCo, substCoVar,
@@ -64,6 +64,8 @@ import qualified GHC.Core.Type as Type
import GHC.Core.Type hiding ( substTy, substTyVar, substTyVarBndr, extendTvSubst, extendCvSubst )
import qualified GHC.Core.Coercion as Coercion
import GHC.Core.Coercion hiding ( substCo, substCoVar, substCoVarBndr )
+import GHC.Unit.Module ( Module )
+import GHC.Types.Name ( nameModule_maybe )
import GHC.Types.Basic
import GHC.Utils.Monad
import GHC.Utils.Outputable
@@ -687,27 +689,77 @@ So we want to look up the inner X.g_34 in the substitution, where we'll
find that it has been substituted by b. (Or conceivably cloned.)
-}
-substId :: SimplEnv -> InId -> SimplSR
+substId :: Maybe Module -> SimplEnv -> InId -> SimplSR
-- Returns DoneEx only on a non-Var expression
-substId (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v
+substId mb_mod (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v
= case lookupVarEnv ids v of -- Note [Global Ids in the substitution]
- Nothing -> DoneId (refineFromInScope in_scope v)
- Just (DoneId v) -> DoneId (refineFromInScope in_scope v)
+ Nothing -> DoneId (refineFromInScope mb_mod in_scope v)
+ Just (DoneId v1) -> DoneId (refineFromInScope mb_mod in_scope v1)
Just res -> res -- DoneEx non-var, or ContEx
- -- Get the most up-to-date thing from the in-scope set
- -- Even though it isn't in the substitution, it may be in
- -- the in-scope set with better IdInfo.
- --
- -- See also Note [In-scope set as a substitution] in GHC.Core.Opt.Simplify.
-
-refineFromInScope :: InScopeSet -> Var -> Var
-refineFromInScope in_scope v
- | isLocalId v = case lookupInScope in_scope v of
- Just v' -> v'
- Nothing -> pprPanic "refineFromInScope" (ppr in_scope $$ ppr v)
- -- c.f #19074 for a subtle place where this went wrong
+refineFromInScope :: Maybe Module -> InScopeSet -> Id -> Id
+-- Get the most up-to-date thing from the in-scope set
+-- Even though it isn't in the substitution (the Nothing case in
+-- substId), it may be in the in-scope set with better IdInfo.
+--
+-- See also Note [In-scope set as a substitution] in GHC.Core.Opt.Simplify.
+refineFromInScope mb_mod in_scope v
+ | lookup_in_scope
+ = case lookupInScope in_scope v of
+ Just v' -> v'
+ Nothing -> pprPanic "refineFromInScope" (ppr in_scope $$ ppr v)
+ -- c.f #19074 for a subtle place where this went wrong
| otherwise = v
+ where
+ v_mod = nameModule_maybe (varName v)
+ lookup_in_scope
+ | isLocalId v = True
+ ---- Below here v is a Globald -----
+
+ -- Implicit Ids don't have bindings until they are added by
+ -- Prep or Tidy, so the won't be in the in-scope set
+ | isImplicitId v = False
+
+ -- If we are compiling module M and come across a GlobalId M.foo
+ -- then we want to look it up in the in-scope set
+ -- See Note [Simplifying recursive modules]
+ | Just name_mod <- v_mod
+ , Just this_mod <- mb_mod
+ = name_mod == this_mod
+
+ -- All other (imported) GlobalIds won't be in the in-scope set
+ | otherwise = False
+
+{- Note [Simplifying recursive modules]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose we have
+ M.hs-boot module M where { foo :: Int -> Int }
+ A.hs module A where { import {-# SOURCE #-} M
+ ; bar = foo 3 }
+ M.hs module M where { import A; foo = id; ...bar... }
+
+In --make mode we'll make an unfolding for `bar` that refers to a GlobalId `M.foo`,
+but one with no useful info beyond the type of `foo`.
+
+Then when compiling M, we inline `bar` and lo! we have an occurrence of a
+GlobalId `M.foo` when the binding site `foo = id` is for a LocalId.
+Lint rightly complains (seee #20200).
+
+Solution:
+
+* In the (unchanging) SimplTopEnv we keep st_module,
+ which tracks the module being compiled.
+
+* The field can be Nothing, which is useful for GHCi, and in other
+ siuations where we don't need to worry about the boot-file problem.
+
+* When looking up a GlobalId, in refineFromInScope, if the Module part
+ of the Name is the same as the module being compiled (kept in
+ st_module), then it look up in the InScopeSet, just like a LocalId.
+
+* Wrinkle: data constructor workers aren't injected until the end, so
+ we won't find them in the in-scope set.
+-}
lookupRecBndr :: SimplEnv -> InId -> OutId
-- Look up an Id which has been put into the envt by simplRecBndrs,
@@ -716,7 +768,7 @@ lookupRecBndr (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v
= case lookupVarEnv ids v of
Just (DoneId v) -> v
Just _ -> pprPanic "lookupRecBndr" (ppr v)
- Nothing -> refineFromInScope in_scope v
+ Nothing -> refineFromInScope Nothing in_scope v
{-
************************************************************************
@@ -784,8 +836,15 @@ simplNonRecBndr !env id
; seqId id1 `seq` return (env1, id1) }
---------------
+simplTopRecBndrs :: SimplEnv -> [InBndr] -> SimplM SimplEnv
+-- Top-level recursive let binders
+-- No need to clone, and the current substitution is empty
+simplTopRecBndrs env@(SimplEnv { seInScope = in_scope }) ids
+ = assert (all (not . isJoinId) ids) $
+ return (env { seInScope = extendInScopeSetList in_scope ids })
+
simplRecBndrs :: SimplEnv -> [InBndr] -> SimplM SimplEnv
--- Recursive let binders
+-- Recursive let binders; used for nested (non-top-level) letrecs
simplRecBndrs env@(SimplEnv {}) ids
-- See Note [Bangs in the Simplifier]
= assert (all (not . isJoinId) ids) $
diff --git a/compiler/GHC/Core/Opt/Simplify/Monad.hs b/compiler/GHC/Core/Opt/Simplify/Monad.hs
index c730a3e981..03dba32c08 100644
--- a/compiler/GHC/Core/Opt/Simplify/Monad.hs
+++ b/compiler/GHC/Core/Opt/Simplify/Monad.hs
@@ -9,7 +9,7 @@ module GHC.Core.Opt.Simplify.Monad (
-- The monad
SimplM,
initSmpl, traceSmpl,
- getSimplRules, getFamEnvs, getOptCoercionOpts,
+ getSimplRules, getFamEnvs, getOptCoercionOpts, getSimplModule,
-- Unique supply
MonadUnique(..), newId, newJoinId,
@@ -37,6 +37,7 @@ import GHC.Driver.Config
import GHC.Core.Opt.Monad
import GHC.Utils.Outputable
import GHC.Data.FastString
+import GHC.Unit.Module ( Module )
import GHC.Utils.Monad
import GHC.Utils.Logger as Logger
import GHC.Utils.Misc ( count )
@@ -81,28 +82,32 @@ data SimplTopEnv
, st_max_ticks :: IntWithInf -- ^ Max #ticks in this simplifier run
, st_rules :: RuleEnv
, st_fams :: (FamInstEnv, FamInstEnv)
+ , st_module :: Maybe Module -- See Note [Simplifying recursive modules]
+ -- in GHC.Core.Opt.Simplify.Env
, st_co_opt_opts :: !OptCoercionOpts
-- ^ Coercion optimiser options
}
-initSmpl :: Logger -> DynFlags -> RuleEnv -> (FamInstEnv, FamInstEnv)
+initSmpl :: Logger -> DynFlags
+ -> Maybe Module -> RuleEnv -> (FamInstEnv, FamInstEnv)
-> Int -- Size of the bindings, used to limit
-- the number of ticks we allow
-> SimplM a
-> IO (a, SimplCount)
-initSmpl logger dflags rules fam_envs size m
+initSmpl logger dflags mb_mod rules fam_envs size m
= do -- No init count; set to 0
let simplCount = zeroSimplCount dflags
(result, count) <- unSM m env simplCount
return (result, count)
where
- env = STE { st_flags = dflags
- , st_logger = logger
- , st_rules = rules
- , st_max_ticks = computeMaxTicks dflags size
- , st_fams = fam_envs
+ env = STE { st_flags = dflags
+ , st_logger = logger
+ , st_rules = rules
+ , st_max_ticks = computeMaxTicks dflags size
+ , st_fams = fam_envs
+ , st_module = mb_mod
, st_co_opt_opts = initOptCoercionOpts dflags
}
@@ -202,6 +207,9 @@ instance MonadIO SimplM where
x <- m
return (x, sc)
+getSimplModule :: SimplM (Maybe Module)
+getSimplModule = SM (\st_env sc -> return (st_module st_env, sc))
+
getSimplRules :: SimplM RuleEnv
getSimplRules = SM (\st_env sc -> return (st_rules st_env, sc))
diff --git a/compiler/GHC/Core/Opt/Simplify/Utils.hs b/compiler/GHC/Core/Opt/Simplify/Utils.hs
index 5c3114e76b..790364b164 100644
--- a/compiler/GHC/Core/Opt/Simplify/Utils.hs
+++ b/compiler/GHC/Core/Opt/Simplify/Utils.hs
@@ -816,7 +816,7 @@ interestingArg env e = go env 0 e
where
-- n is # value args to which the expression is applied
go env n (Var v)
- = case substId env v of
+ = case substId Nothing env v of
DoneId v' -> go_var n v'
DoneEx e _ -> go (zapSubstEnv env) n e
ContEx tvs cvs ids e -> go (setSubstEnv env tvs cvs ids) n e
diff --git a/compiler/GHC/Types/Id/Info.hs b/compiler/GHC/Types/Id/Info.hs
index ec5607d40f..ae45101939 100644
--- a/compiler/GHC/Types/Id/Info.hs
+++ b/compiler/GHC/Types/Id/Info.hs
@@ -160,7 +160,9 @@ data IdDetails
-- or class operation of a class
| PrimOpId PrimOp -- ^ The 'Id' is for a primitive operator
+
| FCallId ForeignCall -- ^ The 'Id' is for a foreign call.
+ -- It is a GlobalId with (unusually) an Internal Name
-- Type will be simple: no type families, newtypes, etc
| TickBoxOpId TickBoxOp -- ^ The 'Id' is for a HPC tick box (both traditional and binary)
diff --git a/compiler/GHC/Types/Var.hs b/compiler/GHC/Types/Var.hs
index be2c677799..492732646e 100644
--- a/compiler/GHC/Types/Var.hs
+++ b/compiler/GHC/Types/Var.hs
@@ -296,12 +296,14 @@ A GlobalId is
* imported, or data constructor, or primop, or record selector
* has a Unique that is globally unique across the whole
GHC invocation (a single invocation may compile multiple modules)
+ * usually has an External Name -- the exception is FCallIds
* never treated as a candidate by the free-variable finder;
it's a constant!
A LocalId is
* bound within an expression (lambda, case, local let(rec))
* or defined at top level in the module being compiled
+ * may have an Internal or External Name
* always treated as a candidate by the free-variable finder
After CoreTidy, top-level LocalIds are turned into GlobalIds
diff --git a/testsuite/tests/simplCore/should_compile/T20200AgdaBase.hs b/testsuite/tests/simplCore/should_compile/T20200AgdaBase.hs
new file mode 100644
index 0000000000..6979362333
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T20200AgdaBase.hs
@@ -0,0 +1,12 @@
+module T20200AgdaBase where
+
+data QName = QName
+data Definition = D
+
+class Monad m => HasConstInfo m where
+ getConstInfo :: QName -> m Definition
+
+{-# SPECIALIZE getConstInfo :: QName -> IO Definition #-}
+
+instance HasConstInfo IO where
+ getConstInfo = undefined
diff --git a/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs b/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs
new file mode 100644
index 0000000000..31fa6da52d
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs
@@ -0,0 +1,15 @@
+module T20200AgdaDatatypes where
+
+import T20200AgdaBase
+import T20200AgdaPretty
+
+reportSDoc :: IO Doc -> IO ()
+reportSDoc d = render <$> d
+
+getConstructorData :: HasConstInfo m => QName -> m Definition
+getConstructorData = getConstInfo
+
+getConType :: QName -> IO a
+getConType t = do
+ _ <- reportSDoc $ prettyTCM t
+ return undefined
diff --git a/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs-boot b/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs-boot
new file mode 100644
index 0000000000..e3f16e9227
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T20200AgdaDatatypes.hs-boot
@@ -0,0 +1,5 @@
+module T20200AgdaDatatypes where
+
+import T20200AgdaBase
+
+getConstructorData :: HasConstInfo m => QName -> m Definition
diff --git a/testsuite/tests/simplCore/should_compile/T20200AgdaInternalToAbstract.hs b/testsuite/tests/simplCore/should_compile/T20200AgdaInternalToAbstract.hs
new file mode 100644
index 0000000000..925adcc5a0
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T20200AgdaInternalToAbstract.hs
@@ -0,0 +1,13 @@
+module T20200AgdaInternalToAbstract where
+
+import T20200AgdaBase
+import {-# SOURCE #-} T20200AgdaDatatypes (getConstructorData)
+
+class Reify i where
+ reify :: HasConstInfo m => i -> m Definition
+ reifyWhen :: HasConstInfo m => i -> m Definition
+
+instance Reify QName where
+ reifyWhen = undefined
+ reify c = do _ <- getConstructorData c
+ return undefined
diff --git a/testsuite/tests/simplCore/should_compile/T20200AgdaPretty.hs b/testsuite/tests/simplCore/should_compile/T20200AgdaPretty.hs
new file mode 100644
index 0000000000..248df375f2
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T20200AgdaPretty.hs
@@ -0,0 +1,20 @@
+module T20200AgdaPretty where
+
+import Control.Monad
+
+import T20200AgdaInternalToAbstract
+import T20200AgdaBase
+
+data Doc
+
+render :: a
+render = undefined
+
+prettyA :: a -> m Doc
+prettyA x = undefined
+
+class PrettyTCM a where
+ prettyTCM :: HasConstInfo m => a -> m Doc
+
+instance PrettyTCM QName where
+ prettyTCM = prettyA <=< reify
diff --git a/testsuite/tests/simplCore/should_compile/all.T b/testsuite/tests/simplCore/should_compile/all.T
index 3b75f2b7a5..6802a4b182 100644
--- a/testsuite/tests/simplCore/should_compile/all.T
+++ b/testsuite/tests/simplCore/should_compile/all.T
@@ -376,3 +376,4 @@ test('T20200', normal, compile, [''])
test('T20200a', normal, compile, ['-O2'])
test('T20200b', normal, compile, ['-O2'])
test('T20200KG', [extra_files(['T20200KGa.hs', 'T20200KG.hs-boot'])], multimod_compile, ['T20200KG', '-v0 -O2 -fspecialise-aggressively'])
+test('T20200AgdaDatatypes', [extra_files(['T20200AgdaBase.hs','T20200AgdaInternalToAbstract.hs','T20200AgdaPretty.hs','T20200AgdaDatatypes.hs-boot'])], multimod_compile, ['T20200AgdaDatatypes', '-v0 -O'])