summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/GHC/Rename/Expr.hs2
-rw-r--r--compiler/GHC/Tc/Errors.hs23
-rw-r--r--compiler/GHC/Tc/Errors/Ppr.hs41
-rw-r--r--compiler/GHC/Tc/Errors/Types.hs18
-rw-r--r--compiler/GHC/Tc/Gen/Arrow.hs2
-rw-r--r--compiler/GHC/Tc/Gen/Match.hs89
-rw-r--r--compiler/GHC/Tc/Utils/Concrete.hs112
-rw-r--r--compiler/GHC/Types/Basic.hs13
-rw-r--r--testsuite/tests/typecheck/should_compile/T20855b.hs40
-rw-r--r--testsuite/tests/typecheck/should_compile/T20864.hs26
-rw-r--r--testsuite/tests/typecheck/should_compile/all.T2
-rw-r--r--testsuite/tests/typecheck/should_fail/T20855.hs14
-rw-r--r--testsuite/tests/typecheck/should_fail/T20855.stderr10
-rw-r--r--testsuite/tests/typecheck/should_fail/T20855c.hs15
-rw-r--r--testsuite/tests/typecheck/should_fail/T20855c.stderr10
-rw-r--r--testsuite/tests/typecheck/should_fail/T20864_fail.hs16
-rw-r--r--testsuite/tests/typecheck/should_fail/T20864_fail.stderr20
-rw-r--r--testsuite/tests/typecheck/should_fail/all.T3
18 files changed, 422 insertions, 34 deletions
diff --git a/compiler/GHC/Rename/Expr.hs b/compiler/GHC/Rename/Expr.hs
index 6316ecea63..7300908ea7 100644
--- a/compiler/GHC/Rename/Expr.hs
+++ b/compiler/GHC/Rename/Expr.hs
@@ -1256,7 +1256,7 @@ rnStmt ctxt _ (L loc (TransStmt { trS_stmts = stmts, trS_by = by, trS_form = for
; let all_fvs = fvs1 `plusFV` fvs2 `plusFV` fvs3
`plusFV` fvs4 `plusFV` fvs5
bndr_map = used_bndrs `zip` used_bndrs
- -- See Note [TransStmt binder map] in GHC.Hs.Expr
+ -- See Note [TransStmt binder map] in Language.Haskell.Syntax.Expr
; traceRn "rnStmt: implicitly rebound these used binders:" (ppr bndr_map)
; return (([(L loc (TransStmt { trS_ext = noExtField
diff --git a/compiler/GHC/Tc/Errors.hs b/compiler/GHC/Tc/Errors.hs
index 237c6fa4a3..a9dabdc783 100644
--- a/compiler/GHC/Tc/Errors.hs
+++ b/compiler/GHC/Tc/Errors.hs
@@ -1676,10 +1676,14 @@ mkTyVarEqErr' :: SolverReportErrCtxt -> ErrorItem
-> (TcTyVar, TcCoercionN) -> TcType -> TcM (AccReportMsgs, [GhcHint])
mkTyVarEqErr' ctxt item (tv1, co1) ty2
- -- Is this a representation-polymorphism error, e.g.
- -- alpha[conc] ~# rr[sk] ? If so, handle that first.
- | Just frr_info <- mb_concrete_reason
- = do
+ -- Is this an error involving a concrete metavariable being equal
+ -- to a non-concrete type, e.g. alpha[conc] ~# rr[sk]?
+ -- If so, handle that first.
+ | Just (conc_reason, conc_tv, not_conc) <- mb_concrete_reason
+ = case conc_reason of
+ ConcreteFRR frr_orig -> do
+ let frr_info = FRR_Info { frr_info_origin = frr_orig
+ , frr_info_not_concrete = Just (conc_tv, not_conc) }
(_, infos) <- zonkTidyFRRInfos (cec_tidy ctxt) [frr_info]
return (FixedRuntimeRepError infos :| [], [])
@@ -1776,20 +1780,17 @@ mkTyVarEqErr' ctxt item (tv1, co1) ty2
-- we need to retrieve the ConcreteTvOrigin. Just knowing whether
-- there is an error is not sufficient. See #21430.
mb_concrete_reason
- | Just frr_orig <- isConcreteTyVar_maybe tv1
+ | Just conc_orig <- isConcreteTyVar_maybe tv1
, not (isConcrete ty2)
- = Just $ frr_reason frr_orig tv1 ty2
- | Just (tv2, frr_orig) <- isConcreteTyVarTy_maybe ty2
+ = Just (conc_orig, tv1, ty2)
+ | Just (tv2, conc_orig) <- isConcreteTyVarTy_maybe ty2
, not (isConcreteTyVar tv1)
- = Just $ frr_reason frr_orig tv2 ty1
+ = Just (conc_orig, tv2, ty1)
-- NB: if it's an unsolved equality in which both sides are concrete
-- (e.g. a concrete type variable on both sides), then it's not a
-- representation-polymorphism problem.
| otherwise
= Nothing
- frr_reason (ConcreteFRR frr_orig) conc_tv not_conc
- = FRR_Info { frr_info_origin = frr_orig
- , frr_info_not_concrete = Just (conc_tv, not_conc) }
ty1 = mkTyVarTy tv1
diff --git a/compiler/GHC/Tc/Errors/Ppr.hs b/compiler/GHC/Tc/Errors/Ppr.hs
index 349f587ddc..b89556a42a 100644
--- a/compiler/GHC/Tc/Errors/Ppr.hs
+++ b/compiler/GHC/Tc/Errors/Ppr.hs
@@ -3,6 +3,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance Diagnostic TcRnMessage
@@ -723,7 +724,8 @@ instance Diagnostic TcRnMessage where
, text "in the following constraint" <> plural tidy_wanteds ])
2
(pprWithArising tidy_wanteds)
-
+ TcRnMustBeLifted ctxt bad_ids
+ -> mkSimpleDecorated $ pprMustBeLiftedError ctxt bad_ids
TcRnForeignImportPrimExtNotSet _decl
-> mkSimpleDecorated $
@@ -1271,6 +1273,8 @@ instance Diagnostic TcRnMessage where
-> ErrorWithoutFlag
TcRnPragmaWarning{}
-> WarningWithFlag Opt_WarnWarningsDeprecations
+ TcRnMustBeLifted {}
+ -> ErrorWithoutFlag
diagnosticHints = \case
TcRnUnknownMessage m
@@ -1581,10 +1585,14 @@ instance Diagnostic TcRnMessage where
-> noHints
TcRnSpecialiseNotVisible name
-> [SuggestSpecialiseVisibilityHints name]
- TcRnNameByTemplateHaskellQuote{} -> noHints
- TcRnIllegalBindingOfBuiltIn{} -> noHints
- TcRnPragmaWarning{} -> noHints
-
+ TcRnNameByTemplateHaskellQuote{}
+ -> noHints
+ TcRnIllegalBindingOfBuiltIn{}
+ -> noHints
+ TcRnPragmaWarning{}
+ -> noHints
+ TcRnMustBeLifted {}
+ -> noHints
-- | Change [x] to "x", [x, y] to "x and y", [x, y, z] to "x, y, and z",
-- and so on. The `and` stands for any `conjunction`, which is passed in.
@@ -1597,6 +1605,7 @@ commafyWith conjunction xs = addConjunction $ punctuate comma xs
addConjunction (x : xs) = x : addConjunction xs
addConjunction _ = panic "commafyWith expected 2 or more elements"
+
deriveInstanceErrReasonHints :: Class
-> UsingGeneralizedNewtypeDeriving
-> DeriveInstanceErrReason
@@ -2228,7 +2237,8 @@ pprTcSolverReportMsg _ (FixedRuntimeRepError frr_origs) =
= quotes (text "Levity")
| otherwise
= text "type"
-
+pprTcSolverReportMsg _ (NotLiftedError reason id) =
+ pprMustBeLiftedError reason (id :| [])
pprTcSolverReportMsg _ (SkolemEscape item implic esc_skols) =
let
esc_doc = sep [ text "because" <+> what <+> text "variable" <> plural esc_skols
@@ -2497,6 +2507,25 @@ pprTcSolverReportMsg _ (UnsafeOverlap item matches unsafe_overlapped) =
pred = errorItemPred item
(clas, tys) = getClassPredTys pred
+-- | Pretty-print an error when an 'Id' that is required to be lifted
+-- does not have a lifted type.
+pprMustBeLiftedError :: MustBeLiftedReason -> NonEmpty Id -> SDoc
+pprMustBeLiftedError ctxt (NE.toList -> bad_ids) =
+ vcat
+ ( hsep [ text "Expected" <+> what <> comma
+ , text "but the following variable" <> plural bad_ids <+> isOrAre bad_ids <+> text "unlifted:" ]
+ : map ppr_id bad_ids ++ [info] )
+ where
+ what :: SDoc
+ what = case bad_ids of { [_] -> text "a lifted variable"; _ -> text "lifted variables"}
+ ppr_id :: Id -> SDoc
+ ppr_id v = bullet <+> ppr v <+> dcolon <+> ppr ty <+> dcolon <+> ppr ki
+ where
+ ty = idType v
+ ki = typeKind ty
+ info :: SDoc
+ info = text "NB: variables used in" <+> ppr ctxt <> text "s" <+> text "must be lifted."
+
{- *********************************************************************
* *
Displaying potential instances
diff --git a/compiler/GHC/Tc/Errors/Types.hs b/compiler/GHC/Tc/Errors/Types.hs
index e8c3c6e411..4b8056b2a8 100644
--- a/compiler/GHC/Tc/Errors/Types.hs
+++ b/compiler/GHC/Tc/Errors/Types.hs
@@ -41,6 +41,7 @@ module GHC.Tc.Errors.Types (
, Exported(..)
, HsDocContext(..)
, FixedRuntimeRepErrorInfo(..)
+ , MustBeLiftedReason(..)
, ErrorItem(..), errorItemOrigin, errorItemEqRel, errorItemPred, errorItemCtLoc
@@ -2175,6 +2176,15 @@ data TcRnMessage where
pragma_warning_defined_mod :: ModuleName
} -> TcRnMessage
+ {-| TcRnMustBeLifted is an error that occurs when an 'Id' has an unlifted type,
+ even though it appears in a context which requires it to be lifted, for example
+ because we want to put a variable with that 'Id' into a boxed tuple.
+
+ Test cases: T20864, T20855.
+ -}
+ TcRnMustBeLifted :: MustBeLiftedReason -> NE.NonEmpty Id -> TcRnMessage
+
+
-- | Specifies which back ends can handle a requested foreign import or export
type ExpectedBackends = [Backend]
@@ -2807,6 +2817,14 @@ data TcSolverReportMsg
-- See 'FixedRuntimeRepErrorInfo' and 'FixedRuntimeRepContext' for more information.
| FixedRuntimeRepError [FixedRuntimeRepErrorInfo]
+ -- | The type of an 'Id' should have been lifted, but wasn't.
+ --
+ -- Used when we want to put 'Id's into a big tuple for desugaring parallel and transform
+ -- list comprehensions.
+ --
+ -- Test cases: T20864, T20855.
+ | NotLiftedError MustBeLiftedReason Id
+
-- | A skolem type variable escapes its scope.
--
-- Example:
diff --git a/compiler/GHC/Tc/Gen/Arrow.hs b/compiler/GHC/Tc/Gen/Arrow.hs
index 6e4166d36d..83c1f3b211 100644
--- a/compiler/GHC/Tc/Gen/Arrow.hs
+++ b/compiler/GHC/Tc/Gen/Arrow.hs
@@ -438,7 +438,7 @@ tcArrDoStmt env ctxt (RecStmt { recS_stmts = L l stmts, recS_later_ids = later_n
-- (see Note [How RecStmt works] in Language.Haskell.Syntax.Expr)
; let rec_ids = takeList rec_names tup_ids
- ; later_ids <- tcLookupLocalIds later_names
+ ; later_ids <- lookupBigTupIds ArrowRecStmtMustBeLifted later_names
; let rec_rets = takeList rec_names tup_rets
; let ret_table = zip tup_ids tup_rets
diff --git a/compiler/GHC/Tc/Gen/Match.hs b/compiler/GHC/Tc/Gen/Match.hs
index e1a0c2401b..776bda1f00 100644
--- a/compiler/GHC/Tc/Gen/Match.hs
+++ b/compiler/GHC/Tc/Gen/Match.hs
@@ -32,6 +32,8 @@ module GHC.Tc.Gen.Match
, tcDoStmt
, tcGuardStmt
, checkPatCounts
+
+ , lookupBigTupIds
)
where
@@ -50,7 +52,7 @@ import GHC.Tc.Gen.Head( tcCheckId )
import GHC.Tc.Utils.TcMType
import GHC.Tc.Utils.TcType
import GHC.Tc.Gen.Bind
-import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic )
+import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic, unifyTypeRuntimeRep_syntactic )
import GHC.Tc.Utils.Unify
import GHC.Tc.Types.Origin
import GHC.Tc.Types.Evidence
@@ -494,7 +496,7 @@ tcLcStmt m_tc ctxt (ParStmt _ bndr_stmts_s _ _) elt_ty thing_inside
loop (ParStmtBlock x stmts names _ : pairs)
= do { (stmts', (ids, pairs', thing))
<- tcStmtsAndThen ctxt (tcLcStmt m_tc) stmts elt_ty $ \ _elt_ty' ->
- do { ids <- tcLookupLocalIds names
+ do { ids <- lookupBigTupIds ParStmtMustBeLifted names
; (pairs', thing) <- loop pairs
; return (ids, pairs', thing) }
; return ( ParStmtBlock x stmts' ids noSyntaxExpr : pairs', thing ) }
@@ -509,7 +511,7 @@ tcLcStmt m_tc ctxt (TransStmt { trS_form = form, trS_stmts = stmts
; (stmts', (bndr_ids, by'))
<- tcStmtsAndThen (TransStmtCtxt ctxt) (tcLcStmt m_tc) stmts unused_ty $ \_ -> do
{ by' <- traverse tcInferRho by
- ; bndr_ids <- tcLookupLocalIds bndr_names
+ ; bndr_ids <- lookupBigTupIds TransStmtMustBeLifted bndr_names
; return (bndr_ids, by') }
; let m_app ty = mkTyConApp m_tc [ty]
@@ -546,7 +548,7 @@ tcLcStmt m_tc ctxt (TransStmt { trS_form = form, trS_stmts = stmts
-- Ensure that every old binder of type `b` is linked up with its
-- new binder which should have type `n b`
- -- See Note [TransStmt binder map] in GHC.Hs.Expr
+ -- See Note [TransStmt binder map] in Language.Haskell.Syntax.Expr
n_bndr_ids = zipWith mk_n_bndr n_bndr_names bndr_ids
bindersMap' = bndr_ids `zip` n_bndr_ids
@@ -699,7 +701,7 @@ tcMcStmt ctxt (TransStmt { trS_stmts = stmts, trS_bndrs = bindersMap
; return (Just e') }
-- Find the Ids (and hence types) of all old binders
- ; bndr_ids <- tcLookupLocalIds bndr_names
+ ; bndr_ids <- lookupBigTupIds TransStmtMustBeLifted bndr_names
-- 'return' is only used for the binders, so we know its type.
-- return :: (a,b,c,..) -> m (a,b,c,..)
@@ -739,7 +741,7 @@ tcMcStmt ctxt (TransStmt { trS_stmts = stmts, trS_bndrs = bindersMap
-- Ensure that every old binder of type `b` is linked up with its
-- new binder which should have type `n b`
- -- See Note [TransStmt binder map] in GHC.Hs.Expr
+ -- See Note [TransStmt binder map] in Language.Haskell.Syntax.Expr
n_bndr_ids = zipWithEqual "tcMcStmt" mk_n_bndr n_bndr_names bndr_ids
bindersMap' = bndr_ids `zip` n_bndr_ids
@@ -831,7 +833,7 @@ tcMcStmt ctxt (ParStmt _ bndr_stmts_s mzip_op bind_op) res_ty thing_inside
; (stmts', (ids, return_op', pairs', thing))
<- tcStmtsAndThen ctxt tcMcStmt stmts (mkCheckExpType m_tup_ty) $
\m_tup_ty' ->
- do { ids <- tcLookupLocalIds names
+ do { ids <- lookupBigTupIds ParStmtMustBeLifted names
; let tup_ty = mkBigCoreVarTupTy ids
; (_, return_op') <-
tcSyntaxOp MCompOrigin return_op
@@ -949,7 +951,7 @@ tcDoStmt ctxt (RecStmt { recS_stmts = L l stmts, recS_later_ids = later_names
; return (thing, new_res_ty) }
; let rec_ids = takeList rec_names tup_ids
- ; later_ids <- tcLookupLocalIds later_names
+ ; later_ids <- lookupBigTupIds RecStmtMustBeLifted later_names
; traceTc "tcdo" $ vcat [ppr rec_ids <+> ppr (map idType rec_ids),
ppr later_ids <+> ppr (map idType later_ids)]
; return (RecStmt { recS_stmts = L l stmts', recS_later_ids = later_ids
@@ -1168,3 +1170,74 @@ check_match_pats matchContext (MG { mg_alts = L _ (match1:matches) })
args_in_match :: (LocatedA (Match GhcRn body1) -> Int)
args_in_match (L _ (Match { m_pats = pats })) = length pats
+
+{-**********************************************************************
+* *
+\subsection{Ensuring liftedness}
+* *
+************************************************************************
+
+Note [Ensuring liftedness]
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Parallel list comprehensions and transform list comprehensions are
+desugared by using tuples, for example:
+
+ [ x + y1 + y2 | x <- xs, y1 <- ys | y2 <- ys ]
+
+ ==>
+
+ [ x + y1 + y2 | ((x,y1),y2) <- zip [ (x,y1) | x <- xs, y1 <- ys ] ys ].
+
+Such a desugaring only works if we can indeed put x, y1, y2 into tuples;
+in particular, they must be lifted.
+
+This means that, when typechecking this parallel list comprehension, we need
+to ensure that the Ids 'x', 'y1' and 'y2' are lifted. (Failing to do so
+caused #20864 and #20855.)
+
+ (1)
+ The Ids are already known to have a syntactically fixed RuntimeRep,
+ in the sense of Note [Fixed RuntimeRep].
+ That is, we have already performed a representation-polymorphism
+ check elsewhere in the program. This check might well have been
+ a PHASE 2 check that produced a coercion. See
+ Note [The Concrete mechanism].
+
+ (2)
+ We thus need to check that the RuntimeRep of each Id is LiftedRep.
+ A syntactic check suffices, as we have already handled all rewriting
+ in (1). In particular, if we decided to perform unification
+
+ co <- unifyKind rep liftedRepTy
+
+ the resulting coercion, co, would be rather useless: it would always be
+ either reflexive or a deferred type error. We would then have to find
+ a good place to store this coercion in the AST, which would involve
+ changing a fair bit of the parallel/transform list comprehension code,
+ for no real benefit.
+
+As a result, it is best to do a simple syntactic check, which is achieved
+by the function unifyTypeRuntimeRep_syntactic in GHC.Tc.Utils.Concrete.
+-}
+
+-- | Given a list of binder names, this function:
+--
+-- - looks up the associated local 'Id's using 'tcLookupLocalIds'
+-- - ensures their types are all lifted, throwing an error in the 'TcM' monad
+-- if this is not the case.
+lookupBigTupIds :: MustBeLiftedReason -> [Name] -> TcM [TcId]
+lookupBigTupIds lifted_reason names
+ = do { ids <- tcLookupLocalIds names
+ ; bad_ids <- filterM not_lifted ids
+ ; case bad_ids of
+ [] -> return ids
+ bad:bads -> failWithTc (TcRnMustBeLifted lifted_reason (bad NE.:| bads)) }
+
+ where
+ not_lifted :: TcId -> TcM Bool
+ not_lifted v
+ = do { is_lifted <- unifyTypeRuntimeRep_syntactic ty liftedRepTy
+ -- See Note [Ensuring liftedness].
+ ; return $ not is_lifted }
+ where
+ ty = idType v
diff --git a/compiler/GHC/Tc/Utils/Concrete.hs b/compiler/GHC/Tc/Utils/Concrete.hs
index da9efb7ef3..01e95c18c7 100644
--- a/compiler/GHC/Tc/Utils/Concrete.hs
+++ b/compiler/GHC/Tc/Utils/Concrete.hs
@@ -11,6 +11,9 @@ module GHC.Tc.Utils.Concrete
-- * Making a type concrete
, makeTypeConcrete
+
+ -- * Syntactic unification of concrete types
+ , unifyTypeRuntimeRep_syntactic, unifyConcrete_syntactic,
)
where
@@ -18,27 +21,33 @@ import GHC.Prelude
import GHC.Builtin.Types ( liftedTypeKindTyCon, unliftedTypeKindTyCon )
-import GHC.Core.Coercion ( coToMCo, mkCastTyMCo )
+import GHC.Core.Coercion ( coToMCo, mkCastTyMCo, isReflexiveCo )
import GHC.Core.TyCo.Rep ( Type(..), MCoercion(..) )
import GHC.Core.TyCon ( isConcreteTyCon )
import GHC.Core.Type ( isConcrete, typeKind, tyVarKind, tcView
- , mkTyVarTy, mkTyConApp, mkFunTy, mkAppTy )
-
+ , mkTyVarTy, mkTyConApp, mkFunTy, mkAppTy
+ , getRuntimeRep )
import GHC.Tc.Types ( TcM, ThStage(..), PendingStuff(..) )
-import GHC.Tc.Types.Constraint ( NotConcreteError(..), NotConcreteReason(..) )
+import GHC.Tc.Types.Constraint ( NotConcreteError(..), NotConcreteReason(..)
+ , isEmptyWC )
import GHC.Tc.Types.Evidence ( Role(..), TcCoercionN, TcMCoercionN
, mkTcGReflRightMCo, mkTcNomReflCo )
-import GHC.Tc.Types.Origin ( CtOrigin(..), FixedRuntimeRepContext, FixedRuntimeRepOrigin(..) )
-import GHC.Tc.Utils.Monad ( emitNotConcreteError, setTcLevel, getCtLocM, getStage, traceTc )
+import GHC.Tc.Types.Origin ( CtOrigin(..), FixedRuntimeRepContext, FixedRuntimeRepOrigin(..)
+ , TypedThing(..) )
+import GHC.Tc.Utils.Monad ( emitNotConcreteError, setTcLevel, getCtLocM, getStage, traceTc
+ , captureConstraints )
import GHC.Tc.Utils.TcType ( TcType, TcKind, TcTypeFRR
, MetaInfo(..), ConcreteTvOrigin(..)
- , isMetaTyVar, metaTyVarInfo, tcTyVarLevel )
+ , isMetaTyVar, metaTyVarInfo, tcTyVarLevel
+ , tcEqType )
import GHC.Tc.Utils.TcMType ( newConcreteTyVar, isFilledMetaTyVar_maybe, writeMetaTyVar
, emitWantedEq )
+import {-# SOURCE #-} GHC.Tc.Utils.Unify ( unifyKind )
import GHC.Types.Basic ( TypeOrKind(..) )
import GHC.Utils.Misc ( HasDebugCallStack )
import GHC.Utils.Outputable
+import GHC.Utils.Panic
import Control.Monad ( void )
import Data.Functor ( ($>) )
@@ -682,3 +691,92 @@ makeTypeConcrete conc_orig ty =
bale_out :: TcType -> NotConcreteReason -> WriterT [NotConcreteReason] TcM TcType
bale_out ty reason = do { tell [reason]; return ty }
+
+
+{-
+%************************************************************************
+%* *
+ Syntactic unification
+%* *
+%**********************************************************************-}
+
+
+-- | Given a type @ty :: TYPE rr@ and a desired 'RuntimeRep' @desired_rr@,
+-- this function attempts to syntactically unify @rr@ with @desired_rr@,
+-- returning whether it succeeded.
+--
+-- Preconditions:
+-- - The kind of @ty@ must be of the form @TYPE rr@.
+-- - Both @rr@ and @desired_rr@ are concrete 'RuntimeRep's,
+-- in the sense of Note [Concrete types].
+unifyTypeRuntimeRep_syntactic :: HasDebugCallStack
+ => TcType -- ^ type to constrain (e.g. @ty :: TYPE rr@)
+ -> TcType -- ^ desired 'RuntimeRep' (e.g. 'liftedRepTy')
+ -> TcM Bool
+unifyTypeRuntimeRep_syntactic ty desired_rr
+ = do { res <- unifyConcrete_syntactic rr desired_rr
+ -- NB: unifyConcrete_syntactic asserts that both arguments are concrete,
+ -- so no need to assert that here.
+ ; traceTc "unifyTypeRuntimeRep_syntatic" $
+ vcat [ text "ty:" <+> ppr ty
+ , text "rr:" <+> ppr rr
+ , text "desired_rr:" <+> ppr desired_rr
+ , text "OK:" <+> ppr res ]
+ ; return res }
+ where
+ rr = getRuntimeRep ty
+
+-- | Syntactically unify two concrete types. Looks through filled metavariables,
+-- but does not do any rewriting, nor does it ever defer work to the
+-- constraint solver (this function never emits any constraints).
+--
+-- Preconditions:
+-- - The LHS and RHS types are both concrete, in the sense of
+-- Note [Concrete types] in GHC.Tc.Utils.Concrete.
+-- - The kinds of LHS and RHS are equal (according to 'tcEqType').
+unifyConcrete_syntactic :: HasDebugCallStack => TcType -> TcType -> TcM Bool
+unifyConcrete_syntactic ty desired_ty
+ = do { massertPpr (conc1 && conc2) $
+ vcat [ text "unifyConcrete_syntactic: non-concrete" <+> what_not_conc
+ , text "LHS:" <+> ppr ty
+ , text "RHS:" <+> ppr desired_ty ]
+ ; massertPpr (ki1 `tcEqType` ki2) $
+ vcat [ text "unifyConcrete_syntactic: kind mismatch"
+ , text "LHS:" <+> ppr ty <+> dcolon <+> ppr ki1
+ , text "RHS:" <+> ppr desired_ty <+> dcolon <+> ppr ki2 ]
+ ; (co, wc) <- captureConstraints $ unifyKind (Just $ TypeThing desired_ty) ty desired_ty
+ ; traceTc "unifyConcrete_syntactic" $
+ vcat [ text "ty:" <+> ppr ty
+ , text "desired_ty:" <+> ppr desired_ty
+ , text "co:" <+> ppr co
+ , text "wc:" <+> ppr wc ]
+
+ ; if not $ isEmptyWC wc
+
+ -- There were unsolved constraints, perhaps something like `IntRep ~# LiftedRep`.
+ then return False
+
+ -- No unsolved constraints: we expect evidence for the equality to be Refl,
+ -- as both sides are concrete.
+ else
+ do { massertPpr (isReflexiveCo co) $
+ vcat [ text "unifyConcrete_syntactic: unification succeeded with non-reflexive coercion"
+ , text "ty:" <+> ppr ty
+ , text "desired_ty:" <+> ppr desired_ty
+ , text "co:" <+> ppr co ]
+ ; return True } }
+ where
+ ki1, ki2 :: TcKind
+ ki1 = typeKind ty
+ ki2 = typeKind desired_ty
+ conc1, conc2 :: Bool
+ conc1 = isConcrete ty
+ conc2 = isConcrete desired_ty
+ what_not_conc :: SDoc
+ what_not_conc
+ | not conc1 && not conc2
+ = text "LHS and RHS"
+ | not conc1
+ = text "LHS"
+ | otherwise
+ = text "RHS"
diff --git a/compiler/GHC/Types/Basic.hs b/compiler/GHC/Types/Basic.hs
index 027fe63bad..c5f2f786ed 100644
--- a/compiler/GHC/Types/Basic.hs
+++ b/compiler/GHC/Types/Basic.hs
@@ -105,6 +105,7 @@ module GHC.Types.Basic (
TypeOrKind(..), isTypeLevel, isKindLevel,
Levity(..), mightBeLifted, mightBeUnlifted,
+ MustBeLiftedReason(..),
NonStandardDefaultingStrategy(..),
DefaultingStrategy(..), defaultNonStandardTyVars,
@@ -1898,6 +1899,18 @@ mightBeUnlifted :: Maybe Levity -> Bool
mightBeUnlifted (Just Lifted) = False
mightBeUnlifted _ = True
+data MustBeLiftedReason
+ = ParStmtMustBeLifted
+ | TransStmtMustBeLifted
+ | RecStmtMustBeLifted
+ | ArrowRecStmtMustBeLifted
+
+instance Outputable MustBeLiftedReason where
+ ppr ParStmtMustBeLifted = text "parallel list comprehension"
+ ppr TransStmtMustBeLifted = text "transform list comprehension"
+ ppr RecStmtMustBeLifted = text "recursive statement"
+ ppr ArrowRecStmtMustBeLifted = text "arrow recursive statement"
+
{- *********************************************************************
* *
Defaulting options
diff --git a/testsuite/tests/typecheck/should_compile/T20855b.hs b/testsuite/tests/typecheck/should_compile/T20855b.hs
new file mode 100644
index 0000000000..3b33971736
--- /dev/null
+++ b/testsuite/tests/typecheck/should_compile/T20855b.hs
@@ -0,0 +1,40 @@
+{-# language Haskell2010
+ , AllowAmbiguousTypes
+ , DataKinds
+ , ParallelListComp
+ , PolyKinds
+ , ScopedTypeVariables
+ , StandaloneKindSignatures
+ , TypeApplications
+ , TypeFamilies
+ , UnliftedNewtypes #-}
+
+module T20855b where
+
+import Data.Kind ( Constraint, Type )
+import GHC.Exts ( TYPE, RuntimeRep(..), LiftedRep )
+
+type R :: Type -> RuntimeRep
+type family R a where
+ R Bool = IntRep
+ R () = LiftedRep
+
+type C :: Type -> Constraint
+class C a where
+ type T a :: TYPE (R a)
+ foo :: [D a] -> T a
+
+instance C () where
+ type T () = Int
+ foo ds = sum . take 5 . drop (10^7)
+ $ [ (a + b)
+ | a <- [ bar () .. ]
+ | MkD b <- ds ]
+
+data family D a
+data instance D () = MkD (T ())
+
+bar :: () -> T ()
+bar _ = 10
+
+main = print $ foo @() (map MkD [ bar () .. ])
diff --git a/testsuite/tests/typecheck/should_compile/T20864.hs b/testsuite/tests/typecheck/should_compile/T20864.hs
new file mode 100644
index 0000000000..2324091b63
--- /dev/null
+++ b/testsuite/tests/typecheck/should_compile/T20864.hs
@@ -0,0 +1,26 @@
+{-# language MagicHash, TransformListComp, GADTs, TypeFamilies, PolyKinds, DataKinds #-}
+{-# language PartialTypeSignatures, NamedWildCards, UnliftedNewtypes #-}
+
+module T20864 where
+
+import Data.Kind
+import GHC.Exts
+
+loom :: [Int] -> [Int]
+loom xs = [z | I# x <- xs, let p = x +# 3#, z <- [1 .. I# p], then take 3]
+
+type R :: Type -> RuntimeRep
+type family R a where
+ R Int = LiftedRep
+
+class C a where
+ type T a :: TYPE (R a)
+ data D a :: Type
+ cool :: [D a] -> [D a]
+
+instance C Int where
+ type T Int = Int
+ data D Int = MkD (T Int)
+ -- this one requires type-family reduction in the kind to observe liftedness
+ cool :: [D Int] -> [D Int]
+ cool xs = [MkD (x + 1612) | MkD x <- xs, then take 3]
diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T
index 62edaf99e6..afdeda7cb5 100644
--- a/testsuite/tests/typecheck/should_compile/all.T
+++ b/testsuite/tests/typecheck/should_compile/all.T
@@ -843,3 +843,5 @@ test('T18802', normal, compile, [''])
test('T18802b', normal, compile, [''])
test('T21289', normal, compile, [''])
test('HardRecordUpdate', normal, compile, [''])
+test('T20864', normal, compile, [''])
+test('T20855b', normal, compile, [''])
diff --git a/testsuite/tests/typecheck/should_fail/T20855.hs b/testsuite/tests/typecheck/should_fail/T20855.hs
new file mode 100644
index 0000000000..1af4a8590b
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20855.hs
@@ -0,0 +1,14 @@
+{-# language Haskell2010, ParallelListComp, MagicHash #-}
+
+module T20855 where
+
+import GHC.Exts
+
+gore :: [Maybe Int] -> [Int] -> [Int]
+gore xs ys =
+ [ I# (x +# y)
+ | Just (I# x) <- xs
+ | I# y <- ys
+ ]
+
+main = print $ take 5 . drop (10^7) $ gore [Just i | i <- [1..]] [10..]
diff --git a/testsuite/tests/typecheck/should_fail/T20855.stderr b/testsuite/tests/typecheck/should_fail/T20855.stderr
new file mode 100644
index 0000000000..e6d519eea2
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20855.stderr
@@ -0,0 +1,10 @@
+
+T20855.hs:10:14: error:
+ • Expected a lifted variable, but the following variable is unlifted:
+ • x :: Int# :: TYPE 'IntRep
+ NB: variables used in parallel list comprehensions must be lifted.
+ • In a stmt of a list comprehension:
+ Just (I# x) <- xs | I# y <- ys
+ In the expression: [I# (x +# y) | Just (I# x) <- xs | I# y <- ys]
+ In an equation for ‘gore’:
+ gore xs ys = [I# (x +# y) | Just (I# x) <- xs | I# y <- ys]
diff --git a/testsuite/tests/typecheck/should_fail/T20855c.hs b/testsuite/tests/typecheck/should_fail/T20855c.hs
new file mode 100644
index 0000000000..b6be82b687
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20855c.hs
@@ -0,0 +1,15 @@
+{-# language Haskell2010, ParallelListComp, MagicHash, DataKinds #-}
+
+module T20855c where
+
+import GHC.Exts
+
+gore :: (IntRep ~ LiftedRep) => [Maybe Int] -> [Int] -> [Int]
+gore xs ys =
+ [ I# (x +# y)
+ | Just (I# x) <- xs
+ | I# y <- ys
+ ]
+
+main :: (IntRep ~ LiftedRep) => IO ()
+main = print $ take 5 . drop (10^7) $ gore [Just i | i <- [1..]] [10..]
diff --git a/testsuite/tests/typecheck/should_fail/T20855c.stderr b/testsuite/tests/typecheck/should_fail/T20855c.stderr
new file mode 100644
index 0000000000..e27e93071e
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20855c.stderr
@@ -0,0 +1,10 @@
+
+T20855c.hs:10:14: error:
+ • Expected a lifted variable, but the following variable is unlifted:
+ • x :: Int# :: TYPE 'IntRep
+ NB: variables used in parallel list comprehensions must be lifted.
+ • In a stmt of a list comprehension:
+ Just (I# x) <- xs | I# y <- ys
+ In the expression: [I# (x +# y) | Just (I# x) <- xs | I# y <- ys]
+ In an equation for ‘gore’:
+ gore xs ys = [I# (x +# y) | Just (I# x) <- xs | I# y <- ys]
diff --git a/testsuite/tests/typecheck/should_fail/T20864_fail.hs b/testsuite/tests/typecheck/should_fail/T20864_fail.hs
new file mode 100644
index 0000000000..3ed49b82f8
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20864_fail.hs
@@ -0,0 +1,16 @@
+{-# language MagicHash, TransformListComp, GADTs, TypeFamilies #-}
+module T20864_fail where
+import GHC.Exts
+
+type family F a where
+ F a = Int#
+
+data Foo a where
+ Foo :: F a -> F a -> Foo a
+
+-- These fail
+glum2 :: [Foo Int] -> [Int]
+glum2 xs = [I# (x +# y) | Foo x y <- xs, then take 3]
+
+glum :: [Int] -> [Int]
+glum xs = [I# p | I# x <- xs, let p = x +# 3#, then take 3]
diff --git a/testsuite/tests/typecheck/should_fail/T20864_fail.stderr b/testsuite/tests/typecheck/should_fail/T20864_fail.stderr
new file mode 100644
index 0000000000..29c6224038
--- /dev/null
+++ b/testsuite/tests/typecheck/should_fail/T20864_fail.stderr
@@ -0,0 +1,20 @@
+
+T20864_fail.hs:13:33: error:
+ • Expected lifted variables, but the following variables are unlifted:
+ • x :: F Int :: TYPE 'IntRep
+ • y :: F Int :: TYPE 'IntRep
+ NB: variables used in transform list comprehensions must be lifted.
+ • In a stmt of a list comprehension: then take 3
+ In the expression: [I# (x +# y) | Foo x y <- xs, then take 3]
+ In an equation for ‘glum2’:
+ glum2 xs = [I# (x +# y) | Foo x y <- xs, then take 3]
+
+T20864_fail.hs:16:22: error:
+ • Expected a lifted variable, but the following variable is unlifted:
+ • p :: Int# :: TYPE 'IntRep
+ NB: variables used in transform list comprehensions must be lifted.
+ • In a stmt of a list comprehension: then take 3
+ In the expression:
+ [I# p | I# x <- xs, let p = x +# 3#, then take 3]
+ In an equation for ‘glum’:
+ glum xs = [I# p | I# x <- xs, let p = x +# 3#, then take 3]
diff --git a/testsuite/tests/typecheck/should_fail/all.T b/testsuite/tests/typecheck/should_fail/all.T
index 457e0c5bc1..6892300780 100644
--- a/testsuite/tests/typecheck/should_fail/all.T
+++ b/testsuite/tests/typecheck/should_fail/all.T
@@ -657,3 +657,6 @@ test('T20768_fail', normal, compile_fail, [''])
test('T21327', normal, compile_fail, [''])
test('T21338', normal, compile_fail, [''])
test('T21158', normal, compile_fail, [''])
+test('T20855', normal, compile_fail, ['-fdefer-type-errors'])
+test('T20855c', normal, compile_fail, ['-fdefer-type-errors'])
+test('T20864_fail', normal, compile_fail, ['-fdefer-type-errors'])