summaryrefslogtreecommitdiff
path: root/compiler/GHC/Tc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Tc')
-rw-r--r--compiler/GHC/Tc/Solver/Canonical.hs226
-rw-r--r--compiler/GHC/Tc/Solver/InertSet.hs21
-rw-r--r--compiler/GHC/Tc/Solver/Rewrite.hs70
3 files changed, 152 insertions, 165 deletions
diff --git a/compiler/GHC/Tc/Solver/Canonical.hs b/compiler/GHC/Tc/Solver/Canonical.hs
index ea2d95e80d..8375498a93 100644
--- a/compiler/GHC/Tc/Solver/Canonical.hs
+++ b/compiler/GHC/Tc/Solver/Canonical.hs
@@ -1084,7 +1084,7 @@ can_eq_nc' _rewritten _rdr_env _envs ev eq_rel
-- Decompose type constructor applications
-- NB: we have expanded type synonyms already
-can_eq_nc' rewritten _rdr_env _envs ev eq_rel ty1 _ ty2 _
+can_eq_nc' _rewritten _rdr_env _envs ev eq_rel ty1 _ ty2 _
| Just (tc1, tys1) <- tcSplitTyConApp_maybe ty1
, Just (tc2, tys2) <- tcSplitTyConApp_maybe ty2
-- we want to catch e.g. Maybe Int ~ (Int -> Int) here for better
@@ -1092,7 +1092,7 @@ can_eq_nc' rewritten _rdr_env _envs ev eq_rel ty1 _ ty2 _
-- hence no direct match on TyConApp
, not (isTypeFamilyTyCon tc1)
, not (isTypeFamilyTyCon tc2)
- = canTyConApp rewritten ev eq_rel tc1 tys1 tc2 tys2
+ = canTyConApp ev eq_rel tc1 tys1 tc2 tys2
can_eq_nc' _rewritten _rdr_env _envs ev eq_rel
s1@(ForAllTy (Bndr _ vis1) _) _
@@ -1114,8 +1114,12 @@ can_eq_nc' True _rdr_env _envs ev NomEq ty1 _ ty2 _
-------------------
-- No similarity in type structure detected. Rewrite and try again.
-can_eq_nc' False _rdr_env _envs ev eq_rel _ ps_ty1 _ ps_ty2
- = rewrite_and_try_again ev eq_rel ps_ty1 ps_ty2
+can_eq_nc' False rdr_env envs ev eq_rel _ ps_ty1 _ ps_ty2
+ = -- Rewrite the two types and try again
+ do { (redn1@(Reduction _ xi1), rewriters1) <- rewrite ev ps_ty1
+ ; (redn2@(Reduction _ xi2), rewriters2) <- rewrite ev ps_ty2
+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2
+ ; can_eq_nc' True rdr_env envs new_ev eq_rel xi1 xi1 xi2 xi2 }
----------------------------
-- Look for a canonical LHS. See Note [Canonical LHS].
@@ -1153,15 +1157,6 @@ can_eq_nc' True _rdr_env _envs ev eq_rel _ ps_ty1 _ ps_ty2
-- No need to call canEqFailure/canEqHardFailure because they
-- rewrite, and the types involved here are already rewritten
--- Rewrite the two types and try again
-rewrite_and_try_again :: CtEvidence -> EqRel -> TcType -> TcType -> TcS (StopOrContinue Ct)
-rewrite_and_try_again ev eq_rel ty1 ty2
- = do { (redn1@(Reduction _ xi1), rewriters1) <- rewrite ev ty1
- ; (redn2@(Reduction _ xi2), rewriters2) <- rewrite ev ty2
- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2
- ; rdr_env <- getGlobalRdrEnvTcS
- ; envs <- getFamInstEnvs
- ; can_eq_nc' True rdr_env envs new_ev eq_rel xi1 xi1 xi2 xi2 }
{- Note [Unsolved equalities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1407,62 +1402,41 @@ which is easier to satisfy.
Conclusion: we must unwrap newtypes before decomposing them. This happens
in `can_eq_newtype_nc`
-But even this is challenging. Here are two cases to consider:
-
-Case 1:
-
- newtype Age = MkAge Int
- [G] c
- [W] w1 :: IO Age ~R# IO Int
-
-Case 2:
-
- newtype A = MkA [A]
- [W] A ~R# [A]
-
-For Case 1, recall that IO is an abstract newtype. Then read Note
-[Decomposing newtype equalities]. According to that Note, we should not
-decompose w1, because we have an Irred Given. Yet we still want to solve
-the wanted! We can do so by unwrapping the (non-abstract) Age newtype
-underneath the IO, giving
- [W] w2 :: IO Int ~R# IO Int
- w1 = (IO unwrap-Age ; w2)
-where unwrap-Age :: Age ~R# Int. Now we case solve w2 by reflexivity;
-see Note [Eager reflexivity check].
-
-Conclusion: unwrap newtypes (deeply, inside types) in the rewriter:
-specifically in GHC.Tc.Solver.Rewrite.rewrite_newtype_app.
-
-Yet for Case 2, deep rewriting would be a disaster: we would loop.
- [W] A ~R# [A] ---> {unwrap}
- [W] [A] ~R# [[A]]
- ---> {decompose}
- [W] A ~R# [A]
-
-In this case, we just want to unwrap newtypes /at the top level/, allowing us
-to succeed via Note [Eager reflexivity check]:
- [W] A ~R# [A] ---> {unwrap at top level only}
- [W] [A] ~R# [A]
- ---> {reflexivity} success
-
-Conclusion: to satisfy Case 1 and Case 2, we unwrap
-* /both/ at top level, in can_eq_nc'
-* /and/ deeply, in the rewriter, rewrite_newtype_app
-
-The former unwraps outer newtypes (when the data constructor is in scope).
-The latter unwraps deeply -- but it won't be invoked in Case 2, when we can
-recognize an equality between the types [A] and [A] before rewriting
-deeply.
-
-This "before" business is delicate -- there is still a real risk of a loop
-in the type checker with recursive newtypes -- but I think we're doomed to do
-*something* delicate, as we're really trying to solve for equirecursive
-type equality. Bottom line for users: recursive newtypes are dangerous.
-See also Section 5.3.1 and 5.3.4 of
+We did flirt with making the /rewriter/ expand newtypes, rather than
+doing it in `can_eq_newtype_nc`. But with recursive newtypes we want
+to be super-careful about expanding!
+
+ newtype A = MkA [A] -- Recursive!
+
+ f :: A -> [A]
+ f = coerce
+
+We have [W] A ~R# [A]. If we rewrite [A], it'll expand to
+ [[[[[...]]]]]
+and blow the reduction stack. See Note [Newtypes can blow the stack]
+in GHC.Tc.Solver.Rewrite. But if we expand only the /top level/ of
+both sides, we get
+ [W] [A] ~R# [A]
+which we can, just, solve by reflexivity.
+
+So we simply unwrap, on-demand, at top level, in `can_eq_newtype_nc`.
+
+This is all very delicate. There is a real risk of a loop in the type checker
+with recursive newtypes -- but I think we're doomed to do *something*
+delicate, as we're really trying to solve for equirecursive type
+equality. Bottom line for users: recursive newtypes do not play well with type
+inference for representational equality. See also Section 5.3.1 and 5.3.4 of
"Safe Zero-cost Coercions for Haskell" (JFP 2016).
-Another approach -- which we ultimately decided against -- is described in
-Note [Decomposing newtypes a bit more aggressively].
+See also Note [Decomposing newtype equalities].
+
+--- Historical side note ---
+
+We flirted with doing /both/ unwrap-at-top-level /and/ rewrite-deeply;
+see #22519. But that didn't work: see discussion in #22924. Specifically
+we got a loop with a minor variation:
+ f2 :: a -> [A]
+ f2 = coerce
Note [Eager reflexivity check]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1492,6 +1466,24 @@ we do a reflexivity check.
(This would be sound in the nominal case, but unnecessary, and I [Richard
E.] am worried that it would slow down the common case.)
+
+ Note [Newtypes can blow the stack]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose we have
+
+ newtype X = MkX (Int -> X)
+ newtype Y = MkY (Int -> Y)
+
+and now wish to prove
+
+ [W] X ~R Y
+
+This Wanted will loop, expanding out the newtypes ever deeper looking
+for a solid match or a solid discrepancy. Indeed, there is something
+appropriate to this looping, because X and Y *do* have the same representation,
+in the limit -- they're both (Fix ((->) Int)). However, no finitely-sized
+coercion will ever witness it. This loop won't actually cause GHC to hang,
+though, because we check our depth in `can_eq_newtype_nc`.
-}
------------------------
@@ -1598,8 +1590,7 @@ canEqCast rewritten ev eq_rel swapped ty1 co1 ty2 ps_ty2
role = eqRelRole eq_rel
------------------------
-canTyConApp :: Bool -- True <=> the types have been rewritten
- -> CtEvidence -> EqRel
+canTyConApp :: CtEvidence -> EqRel
-> TyCon -> [TcType]
-> TyCon -> [TcType]
-> TcS (StopOrContinue Ct)
@@ -1607,17 +1598,13 @@ canTyConApp :: Bool -- True <=> the types have been rewritten
-- See Note [Decomposing Dependent TyCons and Processing Wanted Equalities]
-- Neither tc1 nor tc2 is a saturated funTyCon, nor a type family
-- But they can be data families.
-canTyConApp rewritten ev eq_rel tc1 tys1 tc2 tys2
+canTyConApp ev eq_rel tc1 tys1 tc2 tys2
| tc1 == tc2
, tys1 `equalLength` tys2
= do { inerts <- getTcSInerts
; if can_decompose inerts
then canDecomposableTyConAppOK ev eq_rel tc1 tys1 tys2
- else if rewritten
- then canEqFailure ev eq_rel ty1 ty2
- else rewrite_and_try_again ev eq_rel ty1 ty2 }
- -- Why rewrite and try again? See Case 1
- -- of Note [Unwrap newtypes first]
+ else canEqFailure ev eq_rel ty1 ty2 }
-- See Note [Skolem abstract data] in GHC.Core.Tycon
| tyConSkolem tc1 || tyConSkolem tc2
@@ -1641,7 +1628,7 @@ canTyConApp rewritten ev eq_rel tc1 tys1 tc2 tys2
ty2 = mkTyConApp tc2 tys2
-- See Note [Decomposing TyConApp equalities]
- -- Note [Decomposing newtypes a bit more aggressively]
+ -- and Note [Decomposing newtype equalities]
can_decompose inerts
= isInjectiveTyCon tc1 (eqRelRole eq_rel)
|| (assert (eq_rel == ReprEq) $
@@ -1650,7 +1637,8 @@ canTyConApp rewritten ev eq_rel tc1 tys1 tc2 tys2
-- Moreover isInjectiveTyCon is True for Representational
-- for algebraic data types. So we are down to newtypes
-- and data families.
- ctEvFlavour ev == Wanted && noGivenIrreds inerts)
+ ctEvFlavour ev == Wanted && noGivenNewtypeReprEqs tc1 inerts)
+ -- See Note [Decomposing newtype equalities] (EX2)
{-
Note [Use canEqFailure in canDecomposableTyConApp]
@@ -1838,13 +1826,13 @@ Example is wrinkle {1} in Note [Decomposing TyConApp equalities].
For a Wanted with r=R, since newtypes are not injective at representational
role, decomposition is sound, but we may lose completeness. Nevertheless,
-if the newtype is abstraction (so can't be unwrapped) we can only solve
+if the newtype is abstract (so can't be unwrapped) we can only solve
the equality by (a) using a Given or (b) decomposition. If (a) is impossible
-(e.g. no Givens) then (b) is safe.
+(e.g. no Givens) then (b) is safe albeit potentially incomplete.
-Conclusion: decompose newtypes (at role R) only if there are no usable Givens.
+There are two ways in which decomposing (N ty1) ~r (N ty2) could be incomplete:
-* Incompleteness example (EX1)
+* Incompleteness example (EX1): unwrap first
newtype Nt a = MkNt (Id a)
type family Id a where Id a = a
@@ -1856,39 +1844,68 @@ Conclusion: decompose newtypes (at role R) only if there are no usable Givens.
Conclusion: always unwrap newtypes before attempting to decompose
them. This is done in can_eq_nc'. Of course, we can't unwrap if the data
- constructor isn't in scope. See See Note [Unwrap newtypes first].
+ constructor isn't in scope. See Note [Unwrap newtypes first].
-* Incompleteness example (EX2)
+* Incompleteness example (EX2): available Givens
newtype Nt a = Mk Bool -- NB: a is not used in the RHS,
type role Nt representational -- but the user gives it an R role anyway
- If we have [W] Nt alpha ~R Nt beta, we *don't* want to decompose to
- [W] alpha ~R beta, because it's possible that alpha and beta aren't
- representationally equal.
+ [G] Nt t1 ~R Nt t2
+ [W] Nt alpha ~R Nt beta
- and maybe there is a Given (Nt t1 ~R Nt t2), just waiting to be used, if we
- figure out (elsewhere) that alpha:=t1 and beta:=t2. This is somewhat
- similar to the question of overlapping Givens for class constraints: see
- Note [Instance and Given overlap] in GHC.Tc.Solver.Interact.
+ We *don't* want to decompose to [W] alpha ~R beta, because it's possible
+ that alpha and beta aren't representationally equal. And if we figure
+ out (elsewhere) that alpha:=t1 and beta:=t2, we can solve the Wanted
+ from the Given. This is somewhat similar to the question of overlapping
+ Givens for class constraints: see Note [Instance and Given overlap] in
+ GHC.Tc.Solver.Interact.
Conclusion: don't decompose [W] N s ~R N t, if there are any Given
equalities that could later solve it.
- But what does "any Given equalities that could later solve it" mean, precisely?
- It must be a Given constraint that could turn into N s ~ N t. But that
- could include [G] (a b) ~ (c d), or even just [G] c. But it'll definitely
- be an CIrredCan. So we settle for having no CIrredCans at all, which is
- conservative but safe. See noGivenIrreds and #22331.
+ But what precisely does it mean to say "any Given equalities that could
+ later solve it"?
+
+ In #22924 we had
+ [G] f a ~R# a [W] Const (f a) a ~R# Const a a
+ where Const is an abstract newtype. If we decomposed the newtype, we
+ could solve. Not-decomposing on the grounds that (f a ~R# a) might turn
+ into (Const (f a) a ~R# Const a a) seems a bit silly.
+
+ In #22331 we had
+ [G] N a ~R# N b [W] N b ~R# N a
+ (where N is abstract so we can't unwrap). Here we really /don't/ want to
+ decompose, because the /only/ way to solve the Wanted is from that Given
+ (with a Sym).
+
+ In #22519 we had
+ [G] a <= b [W] IO Age ~R# IO Int
+
+ (where IO is abstract so we can't unwrap, and newtype Age = Int; and (<=)
+ is a type-level comparison on Nats). Here we /must/ decompose, despite the
+ existence of an Irred Given, or we will simply be stuck. (Side note: We
+ flirted with deep-rewriting of newtypes (see discussion on #22519 and
+ !9623) but that turned out not to solve #22924, and also makes type
+ inference loop more often on recursive newtypes.)
+
+ The currently-implemented compromise is this:
+
+ we decompose [W] N s ~R# N t unless there is a [G] N s' ~ N t'
+
+ that is, a Given Irred equality with both sides headed with N.
+ See the call to noGivenNewtypeReprEqs in canTyConApp.
+
+ This is not perfect. In principle a Given like [G] (a b) ~ (c d), or
+ even just [G] c, could later turn into N s ~ N t. But since the free
+ vars of a Given are skolems, or at least untouchable unification
+ variables, this is extremely unlikely to happen.
- Well not 100.0% safe. There could be a CDictCan with some un-expanded
- superclasses; but only in some very obscure recursive-superclass
- situations.
+ Another worry: there could, just, be a CDictCan with some
+ un-expanded equality superclasses; but only in some very obscure
+ recursive-superclass situations.
-If there are no Irred Givens (which is quite common) then we will
-successfuly decompose [W] (IO Age) ~R (IO Int), and solve it. But
-that won't happen and [W] (IO Age) ~R (IO Int) will be stuck.
-We /could/, however, be a bit more aggressive about decomposition;
-see Note [Decomposing newtypes a bit more aggressively].
+ Yet another approach (!) is desribed in
+ Note [Decomposing newtypes a bit more aggressively].
Remember: decomposing Wanteds is always /sound/. This Note is
only about /completeness/.
@@ -1896,7 +1913,8 @@ only about /completeness/.
Note [Decomposing newtypes a bit more aggressively]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IMPORTANT: the ideas in this Note are *not* implemented. Instead, the
-current approach is detailed in Note [Unwrap newtypes first].
+current approach is detailed in Note [Decomposing newtype equalities]
+and Note [Unwrap newtypes first].
For more details about the ideas in this Note see
* GHC propoosal: https://github.com/ghc-proposals/ghc-proposals/pull/549
* issue #22441
diff --git a/compiler/GHC/Tc/Solver/InertSet.hs b/compiler/GHC/Tc/Solver/InertSet.hs
index 60b422e1fc..3b565f378c 100644
--- a/compiler/GHC/Tc/Solver/InertSet.hs
+++ b/compiler/GHC/Tc/Solver/InertSet.hs
@@ -21,7 +21,7 @@ module GHC.Tc.Solver.InertSet (
addInertItem,
noMatchableGivenDicts,
- noGivenIrreds,
+ noGivenNewtypeReprEqs,
mightEqualLater,
prohibitedSuperClassSolve,
@@ -1537,9 +1537,22 @@ isOuterTyVar tclvl tv
-- becomes "outer" even though its level numbers says it isn't.
| otherwise = False -- Coercion variables; doesn't much matter
-noGivenIrreds :: InertSet -> Bool
-noGivenIrreds (IS { inert_cans = inert_cans })
- = isEmptyBag (inert_irreds inert_cans)
+noGivenNewtypeReprEqs :: TyCon -> InertSet -> Bool
+-- True <=> there is no Irred looking like (N tys1 ~ N tys2)
+-- See Note [Decomposing newtype equalities] (EX2) in GHC.Tc.Solver.Canonical
+-- This is the only call site.
+noGivenNewtypeReprEqs tc inerts
+ = not (anyBag might_help (inert_irreds (inert_cans inerts)))
+ where
+ might_help ct
+ = case classifyPredType (ctPred ct) of
+ EqPred ReprEq t1 t2
+ | Just (tc1,_) <- tcSplitTyConApp_maybe t1
+ , tc == tc1
+ , Just (tc2,_) <- tcSplitTyConApp_maybe t2
+ , tc == tc2
+ -> True
+ _ -> False
-- | Returns True iff there are no Given constraints that might,
-- potentially, match the given class consraint. This is used when checking to see if a
diff --git a/compiler/GHC/Tc/Solver/Rewrite.hs b/compiler/GHC/Tc/Solver/Rewrite.hs
index b4e4b25a29..16ab2471b3 100644
--- a/compiler/GHC/Tc/Solver/Rewrite.hs
+++ b/compiler/GHC/Tc/Solver/Rewrite.hs
@@ -42,7 +42,6 @@ import GHC.Builtin.Types (tYPETyCon)
import Data.List ( find )
import GHC.Data.List.Infinite (Infinite)
import qualified GHC.Data.List.Infinite as Inf
-import GHC.Tc.Instance.Family (tcTopNormaliseNewTypeTF_maybe)
{-
************************************************************************
@@ -225,10 +224,10 @@ rewrite ev ty
; return result }
-- | See Note [Rewriting]
--- This variant of 'rewrite' rewrites w.r.t. nominal equality only,
--- as this is better than full rewriting for error messages. Specifically,
--- we want to avoid unwrapping newtypes, as doing so can end up causing
--- an otherwise-unnecessary stack overflow.
+-- `rewriteForErrors` is a variant of 'rewrite' that rewrites
+-- w.r.t. nominal equality only, as this is better than full rewriting
+-- for error messages. (This was important when we flirted with rewriting
+-- newtypes but perhaps less so now.)
rewriteForErrors :: CtEvidence -> TcType
-> TcS (Reduction, RewriterSet)
rewriteForErrors ev ty
@@ -499,27 +498,14 @@ rewrite_one (TyVarTy tv)
rewrite_one (AppTy ty1 ty2)
= rewrite_app_tys ty1 [ty2]
-rewrite_one ty@(TyConApp tc tys)
+rewrite_one (TyConApp tc tys)
-- If it's a type family application, try to reduce it
| isTypeFamilyTyCon tc
= rewrite_fam_app tc tys
- | otherwise
- = do { eq_rel <- getEqRel
- ; if eq_rel == ReprEq
-
- then -- Rewriting w.r.t. representational equality requires
- -- unwrapping newtypes; see GHC.Tc.Solver.Canonical.
- -- Note [Unwrap newtypes first]
- -- NB: try rewrite_newtype_app even when tc isn't a newtype;
- -- the allows the possibility of having a newtype buried under
- -- a synonym. Needed for e.g. T12067.
- rewrite_newtype_app ty
-
- else -- For * a normal data type application
- -- * data family application
- -- we just recursively rewrite the arguments.
- rewrite_ty_con_app tc tys }
+ | otherwise -- We just recursively rewrite the arguments.
+ -- See Note [Do not rewrite newtypes]
+ = rewrite_ty_con_app tc tys
rewrite_one (FunTy { ft_af = vis, ft_mult = mult, ft_arg = ty1, ft_res = ty2 })
= do { arg_redn <- rewrite_one ty1
@@ -678,42 +664,12 @@ rewrite_vector ki roles tys
fvs = tyCoVarsOfType ki
{-# INLINE rewrite_vector #-}
--- Rewrite a (potential) newtype application
--- Precondition: the ambient EqRel is ReprEq
--- Precondition: the type is a TyConApp
--- See Note [Newtypes can blow the stack]
-rewrite_newtype_app :: TcType -> RewriteM Reduction
-rewrite_newtype_app ty@(TyConApp tc tys)
- = do { rdr_env <- liftTcS getGlobalRdrEnvTcS
- ; tf_envs <- liftTcS getFamInstEnvs
- ; case (tcTopNormaliseNewTypeTF_maybe tf_envs rdr_env ty) of
- Nothing -> -- Non-newtype or abstract newtype
- rewrite_ty_con_app tc tys
-
- Just ((used_ctors, co), ty') -- co :: ty ~ ty'
- -> do { liftTcS $ recordUsedGREs used_ctors
- ; checkStackDepth ty
- ; rewrite_reduction (Reduction co ty') } }
-
-rewrite_newtype_app other_ty = pprPanic "rewrite_newtype_app" (ppr other_ty)
-
-{- Note [Newtypes can blow the stack]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Suppose we have
-
- newtype X = MkX (Int -> X)
- newtype Y = MkY (Int -> Y)
-
-and now wish to prove
-
- [W] X ~R Y
-This Wanted will loop, expanding out the newtypes ever deeper looking
-for a solid match or a solid discrepancy. Indeed, there is something
-appropriate to this looping, because X and Y *do* have the same representation,
-in the limit -- they're both (Fix ((->) Int)). However, no finitely-sized
-coercion will ever witness it. This loop won't actually cause GHC to hang,
-though, because we check our depth when unwrapping newtypes.
+{- Note [Do not rewrite newtypes]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We flirted with unwrapping newtypes in the rewriter -- see GHC.Tc.Solver.Canonical
+Note [Unwrap newtypes first]. But that turned out to be a bad idea because
+of recursive newtypes, as that Note says. So be careful if you re-add it!
Note [Rewriting synonyms]
~~~~~~~~~~~~~~~~~~~~~~~~~~