summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2016-10-25 17:41:45 +0100
committerSimon Peyton Jones <simonpj@microsoft.com>2016-11-25 11:18:52 +0000
commit1eec1f21268af907f59b5d5c071a9a25de7369c7 (patch)
tree818ea9214d94e0a3896ba675b52b737018a74a98
parent0123efde8090fc60a6bfef5943ba35440cec0c69 (diff)
downloadhaskell-1eec1f21268af907f59b5d5c071a9a25de7369c7.tar.gz
Another major constraint-solver refactoring
This patch takes further my refactoring of the constraint solver, which I've been doing over the last couple of months in consultation with Richard. It fixes a number of tricky bugs that made the constraint solver actually go into a loop, including Trac #12526 Trac #12444 Trac #12538 The main changes are these * Flatten unification variables (fmvs/fuvs) appear on the LHS of a tvar/tyvar equality; thus fmv ~ alpha and not alpha ~ fmv See Note [Put flatten unification variables on the left] in TcUnify. This is implemented by TcUnify.swapOverTyVars. * Don't reduce a "loopy" CFunEqCan where the fsk appears on the LHS: F t1 .. tn ~ fsk where 'fsk' is free in t1..tn. See Note [FunEq occurs-check principle] in TcInteract This neatly stops some infinite loops that people reported; and it allows us to delete some crufty code in reduce_top_fun_eq. And it appears to be no loss whatsoever. As well as fixing loops, ContextStack2 and T5837 both terminate when they didn't before. * Previously we generated "derived shadow" constraints from Wanteds, but we could (and sometimes did; Trac #xxxx) repeatedly generate a derived shadow from the same Wanted. A big change in this patch is to have two kinds of Wanteds: [WD] behaves like a pair of a Wanted and a Derived [W] behaves like a Wanted only See CtFlavour and ShadowInfo in TcRnTypes, and the ctev_nosh field of a Wanted. This turned out to be a lot simpler. A [WD] gets split into a [W] and a [D] in TcSMonad.maybeEmitShaodow. See TcSMonad Note [The improvement story and derived shadows] * Rather than have a separate inert_model in the InertCans, I've put the derived equalities back into inert_eqs. We weren't gaining anything from a separate field. * Previously we had a mode for the constraint solver in which it would more aggressively solve Derived constraints; it was used for simplifying the context of a 'deriving' clause, or a 'default' delcaration, for example. But the complexity wasn't worth it; now I just make proper Wanted constraints. See TcMType.cloneWC * Don't generate injectivity improvement for Givens; see Note [No FunEq improvement for Givens] in TcInteract * solveSimpleWanteds leaves the insolubles in-place rather than returning them. Simpler. I also did lots of work on comments, including fixing Trac #12821.
-rw-r--r--compiler/iface/ToIface.hs2
-rw-r--r--compiler/typecheck/TcCanonical.hs2
-rw-r--r--compiler/typecheck/TcErrors.hs21
-rw-r--r--compiler/typecheck/TcExpr.hs1
-rw-r--r--compiler/typecheck/TcFlatten.hs307
-rw-r--r--compiler/typecheck/TcInteract.hs777
-rw-r--r--compiler/typecheck/TcMType.hs25
-rw-r--r--compiler/typecheck/TcRnTypes.hs264
-rw-r--r--compiler/typecheck/TcRules.hs21
-rw-r--r--compiler/typecheck/TcSMonad.hs1134
-rw-r--r--compiler/typecheck/TcSimplify.hs146
-rw-r--r--compiler/typecheck/TcType.hs18
-rw-r--r--compiler/typecheck/TcUnify.hs45
-rw-r--r--testsuite/tests/indexed-types/should_compile/T10226.hs57
-rw-r--r--testsuite/tests/indexed-types/should_compile/T10634.hs15
-rw-r--r--testsuite/tests/indexed-types/should_compile/T12526.hs69
-rw-r--r--testsuite/tests/indexed-types/should_compile/T12538.hs40
-rw-r--r--testsuite/tests/indexed-types/should_compile/T12538.stderr13
-rw-r--r--testsuite/tests/indexed-types/should_compile/T3017.stderr2
-rw-r--r--testsuite/tests/indexed-types/should_compile/T4338.hs35
-rw-r--r--testsuite/tests/indexed-types/should_compile/all.T2
-rw-r--r--testsuite/tests/indexed-types/should_fail/T2544.stderr24
-rw-r--r--testsuite/tests/indexed-types/should_fail/T2627b.stderr4
-rw-r--r--testsuite/tests/indexed-types/should_fail/T3330c.stderr6
-rw-r--r--testsuite/tests/indexed-types/should_fail/T4179.stderr6
-rw-r--r--testsuite/tests/indexed-types/should_fail/T6123.stderr6
-rw-r--r--testsuite/tests/indexed-types/should_fail/T7786.hs28
-rw-r--r--testsuite/tests/indexed-types/should_fail/T7786.stderr43
-rw-r--r--testsuite/tests/indexed-types/should_fail/T8227.stderr13
-rw-r--r--testsuite/tests/partial-sigs/should_compile/T10403.stderr15
-rw-r--r--testsuite/tests/perf/compiler/T5837.hs31
-rw-r--r--testsuite/tests/perf/compiler/T5837.stderr91
-rw-r--r--testsuite/tests/perf/compiler/all.T7
-rw-r--r--testsuite/tests/polykinds/T12444.hs65
-rw-r--r--testsuite/tests/polykinds/T12444.stderr16
-rw-r--r--testsuite/tests/polykinds/T9222.stderr6
-rw-r--r--testsuite/tests/polykinds/all.T1
-rw-r--r--testsuite/tests/typecheck/should_compile/Improvement.hs12
-rw-r--r--testsuite/tests/typecheck/should_compile/T6018.hs32
-rw-r--r--testsuite/tests/typecheck/should_compile/T6018.stderr8
-rw-r--r--testsuite/tests/typecheck/should_fail/ContextStack2.hs2
-rw-r--r--testsuite/tests/typecheck/should_fail/ContextStack2.stderr13
-rw-r--r--testsuite/tests/typecheck/should_fail/Makefile5
-rw-r--r--testsuite/tests/typecheck/should_fail/T5691.stderr10
-rw-r--r--testsuite/tests/typecheck/should_fail/T5853.stderr28
-rw-r--r--testsuite/tests/typecheck/should_fail/T8450.stderr10
-rw-r--r--testsuite/tests/typecheck/should_fail/T9260.stderr11
-rw-r--r--testsuite/tests/typecheck/should_fail/all.T2
48 files changed, 1882 insertions, 1609 deletions
diff --git a/compiler/iface/ToIface.hs b/compiler/iface/ToIface.hs
index 48a95a97b2..7e892b68c7 100644
--- a/compiler/iface/ToIface.hs
+++ b/compiler/iface/ToIface.hs
@@ -107,6 +107,8 @@ toIfaceKind = toIfaceType
toIfaceType :: Type -> IfaceType
-- Synonyms are retained in the interface type
toIfaceType (TyVarTy tv) = IfaceTyVar (toIfaceTyVar tv)
+-- | isTcTyVar tv = IfaceTyVar (toIfaceTyVar tv `appendFS` consFS '_' (mkFastString (showSDocUnsafe (ppr (getUnique tv)))))
+-- | otherwise
toIfaceType (AppTy t1 t2) = IfaceAppTy (toIfaceType t1) (toIfaceType t2)
toIfaceType (LitTy n) = IfaceLitTy (toIfaceTyLit n)
toIfaceType (ForAllTy b t) = IfaceForAllTy (toIfaceForAllBndr b) (toIfaceType t)
diff --git a/compiler/typecheck/TcCanonical.hs b/compiler/typecheck/TcCanonical.hs
index 209eec978f..5aeeeb8e18 100644
--- a/compiler/typecheck/TcCanonical.hs
+++ b/compiler/typecheck/TcCanonical.hs
@@ -3,7 +3,7 @@
module TcCanonical(
canonicalize,
unifyDerived,
- makeSuperClasses,
+ makeSuperClasses, maybeSym,
StopOrContinue(..), stopWith, continueWith
) where
diff --git a/compiler/typecheck/TcErrors.hs b/compiler/typecheck/TcErrors.hs
index d73c94f046..276a6b8201 100644
--- a/compiler/typecheck/TcErrors.hs
+++ b/compiler/typecheck/TcErrors.hs
@@ -2682,13 +2682,14 @@ relevantBindings want_filtering ctxt ct
-- et really should be filled in by now. But there's a chance
-- it hasn't, if, say, we're reporting a kind error en route to
-- checking a term. See test indexed-types/should_fail/T8129
- ; ty <- case mb_ty of
- Just ty -> return ty
- Nothing -> do { traceTc "Defaulting an ExpType in relevantBindings"
- (ppr et)
- ; expTypeToType et }
- ; go2 name ty top_lvl }
+ -- Or we are reporting errors from the ambiguity check on
+ -- a local type signature
+ ; case mb_ty of
+ Just ty -> go2 name ty top_lvl
+ Nothing -> discard_it -- No info; discard
+ }
where
+ discard_it = go tidy_env ct_tvs n_left tvs_seen docs discards tc_bndrs
go2 id_name id_type top_lvl
= do { (tidy_env', tidy_ty) <- zonkTidyTcType tidy_env id_type
; traceTc "relevantBindings 1" (ppr id_name <+> dcolon <+> ppr tidy_ty)
@@ -2702,17 +2703,19 @@ relevantBindings want_filtering ctxt ct
&& id_tvs `disjointVarSet` ct_tvs)
-- We want to filter out this binding anyway
-- so discard it silently
- then go tidy_env ct_tvs n_left tvs_seen docs discards tc_bndrs
+ then discard_it
else if isTopLevel top_lvl && not (isNothing n_left)
-- It's a top-level binding and we have not specified
-- -fno-max-relevant-bindings, so discard it silently
- then go tidy_env ct_tvs n_left tvs_seen docs discards tc_bndrs
+ then discard_it
else if run_out n_left && id_tvs `subVarSet` tvs_seen
-- We've run out of n_left fuel and this binding only
-- mentions aleady-seen type variables, so discard it
- then go tidy_env ct_tvs n_left tvs_seen docs True tc_bndrs
+ then go tidy_env ct_tvs n_left tvs_seen docs
+ True -- Record that we have now discarded something
+ tc_bndrs
-- Keep this binding, decrement fuel
else go tidy_env' ct_tvs (dec_max n_left) new_seen (doc:docs) discards tc_bndrs }
diff --git a/compiler/typecheck/TcExpr.hs b/compiler/typecheck/TcExpr.hs
index 4c7417fa21..39a88844b6 100644
--- a/compiler/typecheck/TcExpr.hs
+++ b/compiler/typecheck/TcExpr.hs
@@ -1669,6 +1669,7 @@ tcUnboundId unbound res_ty
; loc <- getCtLocM HoleOrigin Nothing
; let can = CHoleCan { cc_ev = CtWanted { ctev_pred = ty
, ctev_dest = EvVarDest ev
+ , ctev_nosh = WDeriv
, ctev_loc = loc}
, cc_hole = ExprHole unbound }
; emitInsoluble can
diff --git a/compiler/typecheck/TcFlatten.hs b/compiler/typecheck/TcFlatten.hs
index 6987191443..3adbee1808 100644
--- a/compiler/typecheck/TcFlatten.hs
+++ b/compiler/typecheck/TcFlatten.hs
@@ -22,6 +22,7 @@ import VarEnv
import NameEnv
import Outputable
import TcSMonad as TcS
+import BasicTypes( SwapFlag(..) )
import Util
import Bag
@@ -62,11 +63,16 @@ Note [The flattening story]
then xis1 /= xis2
i.e. at most one CFunEqCan with a particular LHS
-* Each canonical CFunEqCan x : F xis ~ fsk/fmv has its own
- distinct evidence variable x and flatten-skolem fsk/fmv.
+* Each canonical [G], [W], or [WD] CFunEqCan x : F xis ~ fsk/fmv
+ has its own distinct evidence variable x and flatten-skolem fsk/fmv.
Why? We make a fresh fsk/fmv when the constraint is born;
and we never rewrite the RHS of a CFunEqCan.
+ In contrast a [D] CFunEqCan shares its fmv with its partner [W],
+ but does not "own" it. If we reduce a [D] F Int ~ fmv, where
+ say type instance F Int = ty, then we don't discharge fmv := ty.
+ Rather we simply generate [D] fmv ~ ty
+
* Function applications can occur in the RHS of a CTyEqCan. No reason
not allow this, and it reduces the amount of flattening that must occur.
@@ -144,7 +150,7 @@ But since fsk = F alpha Int, this is really an occurs check error. If
that is all we know about alpha, we will succeed in constraint
solving, producing a program with an infinite type.
-Even if we did finally get (g : fsk ~ Boo)l by solving (F alpha Int ~ fsk)
+Even if we did finally get (g : fsk ~ Bool) by solving (F alpha Int ~ fsk)
using axiom, zonking would not see it, so (x::alpha) sitting in the
tree will get zonked to an infinite type. (Zonking always only does
refl stuff.)
@@ -161,8 +167,9 @@ Look at Simple13, with unification-fmvs only
[W] x : F a ~ fmv
--> subst a in x
- x = F g' ; x2
- [W] x2 : F [fmv] ~ fmv
+ g' = g;[x]
+ x = F g' ; x2
+ [W] x2 : F [fmv] ~ fmv
And now we have an evidence cycle between g' and x!
@@ -203,67 +210,32 @@ Moreover these two errors could arise in entirely unrelated parts of
the code. (In the alpha case, there must be *some* connection (eg
v:alpha in common envt).)
-Note [Orientation of equalities with fmvs] and
Note [Unflattening can force the solver to iterate]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Here is a bad dilemma concerning flatten meta-vars (fmvs).
-
-This example comes from IndTypesPerfMerge, T10226, T10009.
-From the ambiguity check for
- f :: (F a ~ a) => a
-we get:
- [G] F a ~ a
- [W] F alpha ~ alpha, alpha ~ a
-
-From Givens we get
- [G] F a ~ fsk, fsk ~ a
-
-Now if we flatten we get
- [W] alpha ~ fmv, F alpha ~ fmv, alpha ~ a
-
-Now, processing the first one first, choosing alpha := fmv
- [W] F fmv ~ fmv, fmv ~ a
-
-And now we are stuck. We must either *unify* fmv := a, or
-use the fmv ~ a to rewrite F fmv ~ fmv, so we can make it
-meet up with the given F a ~ blah.
+Look at Trac #10340:
+ type family Any :: * -- No instances
+ get :: MonadState s m => m s
+ instance MonadState s (State s) where ...
-Old solution: always put fmvs on the left, so we get
- [W] fmv ~ alpha, F alpha ~ fmv, alpha ~ a
-
-BUT this works badly for Trac #10340:
- get :: MonadState s m => m s
- instance MonadState s (State s) where ...
-
- foo :: State Any Any
- foo = get
+ foo :: State Any Any
+ foo = get
For 'foo' we instantiate 'get' at types mm ss
- [W] MonadState ss mm, [W] mm ss ~ State Any Any
+ [WD] MonadState ss mm, [WD] mm ss ~ State Any Any
Flatten, and decompose
- [W] MonadState ss mm, [W] Any ~ fmv, [W] mm ~ State fmv, [W] fmv ~ ss
+ [WD] MonadState ss mm, [WD] Any ~ fmv
+ [WD] mm ~ State fmv, [WD] fmv ~ ss
Unify mm := State fmv:
- [W] MonadState ss (State fmv), [W] Any ~ fmv, [W] fmv ~ ss
-If we orient with (untouchable) fmv on the left we are now stuck:
-alas, the instance does not match!! But if instead we orient with
-(touchable) ss on the left, we unify ss:=fmv, to get
- [W] MonadState fmv (State fmv), [W] Any ~ fmv
-Now we can solve.
-
-This is a real dilemma. CURRENT SOLUTION:
- * Orient with touchable variables on the left. This is the
- simple, uniform thing to do. So we would orient ss ~ fmv,
- not the other way round.
-
- * In the 'f' example, we get stuck with
- F fmv ~ fmv, fmv ~ a
- But during unflattening we will fail to dischargeFmv for the
- CFunEqCan F fmv ~ fmv, because fmv := F fmv would make an ininite
- type. Instead we unify fmv:=a, AND record that we have done so.
-
- If any such "non-CFunEqCan unifications" take place (in
- unflatten_eq in TcFlatten.unflatten) iterate the entire process.
- This is done by the 'go' loop in solveSimpleWanteds.
+ [WD] MonadState ss (State fmv)
+ [WD] Any ~ fmv, [WD] fmv ~ ss
+Now we are stuck; the instance does not match!! So unflatten:
+ fmv := Any
+ ss := Any (*)
+ [WD] MonadState Any (State Any)
+
+The unification (*) represents progress, so we must do a second
+round of solving; this time it succeeds. This is done by the 'go'
+loop in solveSimpleWanteds.
This story does not feel right but it's the best I can do; and the
iteration only happens in pretty obscure circumstances.
@@ -271,26 +243,6 @@ iteration only happens in pretty obscure circumstances.
************************************************************************
* *
-* Other notes (Oct 14)
- I have not revisted these, but I didn't want to discard them
-* *
-************************************************************************
-
-
-Try: rewrite wanted with wanted only for fmvs (not all meta-tyvars)
-
-But: fmv ~ alpha[0]
- alpha[0] ~ fmv’
-Now we don’t see that fmv ~ fmv’, which is a problem for injectivity detection.
-
-Conclusion: rewrite wanteds with wanted for all untouchables.
-
-skol ~ untch, must re-orieint to untch ~ skol, so that we can use it to rewrite.
-
-
-
-************************************************************************
-* *
* Examples
Here is a long series of examples I had to work through
* *
@@ -313,9 +265,6 @@ axiom F [a] = [F a]
[G] a ~ [fsk2]
[G] fsk ~ a
-
------------------------------------
-
----------------------------------------
indexed-types/should_compile/T44984
@@ -510,6 +459,16 @@ data FlattenMode -- Postcondition for all three: inert wrt the type substitutio
-- -- * If flat_top is True, top level is not a function application
-- -- (but under type constructors is ok e.g. [F a])
+instance Outputable FlattenMode where
+ ppr FM_FlattenAll = text "FM_FlattenAll"
+ ppr FM_SubstOnly = text "FM_SubstOnly"
+
+eqFlattenMode :: FlattenMode -> FlattenMode -> Bool
+eqFlattenMode FM_FlattenAll FM_FlattenAll = True
+eqFlattenMode FM_SubstOnly FM_SubstOnly = True
+-- FM_Avoid tv1 b1 `eq` FM_Avoid tv2 b2 = tv1 == tv2 && b1 == b2
+eqFlattenMode _ _ = False
+
mkFlattenEnv :: FlattenMode -> CtEvidence -> FlatWorkListRef -> FlattenEnv
mkFlattenEnv fm ctev ref = FE { fe_mode = fm
, fe_loc = ctEvLoc ctev
@@ -612,14 +571,9 @@ setEqRel new_eq_rel thing_inside
setMode :: FlattenMode -> FlatM a -> FlatM a
setMode new_mode thing_inside
= FlatM $ \env ->
- if new_mode `eq` fe_mode env
+ if new_mode `eqFlattenMode` fe_mode env
then runFlatM thing_inside env
else runFlatM thing_inside (env { fe_mode = new_mode })
- where
- FM_FlattenAll `eq` FM_FlattenAll = True
- FM_SubstOnly `eq` FM_SubstOnly = True
--- FM_Avoid tv1 b1 `eq` FM_Avoid tv2 b2 = tv1 == tv2 && b1 == b2
- _ `eq` _ = False
-- | Use when flattening kinds/kind coercions. See
-- Note [No derived kind equalities] in TcCanonical
@@ -628,7 +582,7 @@ flattenKinds thing_inside
= FlatM $ \env ->
let kind_flav = case fe_flavour env of
Given -> Given
- _ -> Wanted
+ _ -> Wanted WDeriv
in
runFlatM thing_inside (env { fe_eq_rel = NomEq, fe_flavour = kind_flav })
@@ -637,15 +591,6 @@ bumpDepth (FlatM thing_inside)
= FlatM $ \env -> do { let env' = env { fe_loc = bumpCtLocDepth (fe_loc env) }
; thing_inside env' }
--- Flatten skolems
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-newFlattenSkolemFlatM :: TcType -- F xis
- -> FlatM (CtEvidence, Coercion, TcTyVar) -- [W] x:: F xis ~ fsk
-newFlattenSkolemFlatM ty
- = do { flavour <- getFlavour
- ; loc <- getLoc
- ; liftTcS $ newFlattenSkolem flavour loc ty }
-
{-
Note [The flattening work list]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -795,6 +740,7 @@ flattenManyNom ev tys
; traceTcS "flatten }" (vcat (map ppr tys'))
; return (tys', cos) }
+
{- *********************************************************************
* *
* The main flattening functions
@@ -1113,15 +1059,14 @@ flatten_exact_fam_app tc tys
= do { mode <- getMode
; role <- getRole
; case mode of
- FM_FlattenAll -> flatten_exact_fam_app_fully tc tys
-
- FM_SubstOnly -> do { (xis, cos) <- flatten_many roles tys
+ -- These roles are always going to be Nominal for now,
+ -- but not if #8177 is implemented
+ FM_SubstOnly -> do { let roles = tyConRolesX role tc
+ ; (xis, cos) <- flatten_many roles tys
; return ( mkTyConApp tc xis
, mkTyConAppCo role tc cos ) }
- where
- -- These are always going to be Nominal for now,
- -- but not if #8177 is implemented
- roles = tyConRolesX role tc }
+
+ FM_FlattenAll -> flatten_exact_fam_app_fully tc tys }
-- FM_Avoid tv flat_top ->
-- do { (xis, cos) <- flatten_many fmode roles tys
@@ -1134,20 +1079,22 @@ flatten_exact_fam_app_fully tc tys
-- See Note [Reduce type family applications eagerly]
= try_to_reduce tc tys False id $
do { -- First, flatten the arguments
- ; (xis, cos) <- setEqRel NomEq $ flatten_many_nom tys
- ; eq_rel <- getEqRel
+ ; (xis, cos) <- setEqRel NomEq $
+ flatten_many_nom tys
+ ; eq_rel <- getEqRel
+ ; cur_flav <- getFlavour
; let role = eqRelRole eq_rel
ret_co = mkTyConAppCo role tc cos
-- ret_co :: F xis ~ F tys
-- Now, look in the cache
; mb_ct <- liftTcS $ lookupFlatCache tc xis
- ; fr <- getFlavourRole
; case mb_ct of
Just (co, rhs_ty, flav) -- co :: F xis ~ fsk
- | (flav, NomEq) `funEqCanDischargeFR` fr
+ -- flav is [G] or [WD]
+ -- See Note [Type family equations] in TcSMonad
+ | (NotSwapped, _) <- flav `funEqCanDischargeF` cur_flav
-> -- Usable hit in the flat-cache
- -- We certainly *can* use a Wanted for a Wanted
do { traceFlat "flatten/flat-cache hit" $ (ppr tc <+> ppr xis $$ ppr rhs_ty)
; (fsk_xi, fsk_co) <- flatten_one rhs_ty
-- The fsk may already have been unified, so flatten it
@@ -1161,11 +1108,8 @@ flatten_exact_fam_app_fully tc tys
-- Try to reduce the family application right now
-- See Note [Reduce type family applications eagerly]
_ -> try_to_reduce tc xis True (`mkTransCo` ret_co) $
- do { let fam_ty = mkTyConApp tc xis
- ; (ev, co, fsk) <- newFlattenSkolemFlatM fam_ty
- ; let fsk_ty = mkTyVarTy fsk
- ; liftTcS $ extendFlatCache tc xis ( co
- , fsk_ty, ctEvFlavour ev)
+ do { loc <- getLoc
+ ; (ev, co, fsk) <- liftTcS $ newFlattenSkolem cur_flav loc tc xis
-- The new constraint (F xis ~ fsk) is not necessarily inert
-- (e.g. the LHS may be a redex) so we must put it in the work list
@@ -1175,12 +1119,13 @@ flatten_exact_fam_app_fully tc tys
, cc_fsk = fsk }
; emitFlatWork ct
- ; traceFlat "flatten/flat-cache miss" $ (ppr fam_ty $$ ppr fsk $$ ppr ev)
- ; (fsk_xi, fsk_co) <- flatten_one fsk_ty
- ; return (fsk_xi, fsk_co
- `mkTransCo`
- maybeSubCo eq_rel (mkSymCo co)
- `mkTransCo` ret_co ) }
+ ; traceFlat "flatten/flat-cache miss" $
+ (ppr tc <+> ppr xis $$ ppr fsk $$ ppr ev)
+
+ -- NB: fsk's kind is already flattend because
+ -- the xis are flattened
+ ; return (mkTyVarTy fsk, maybeSubCo eq_rel (mkSymCo co)
+ `mkTransCo` ret_co ) }
}
where
@@ -1322,31 +1267,25 @@ flatten_tyvar1 tv
; return (FTRFollowed ty (mkReflCo role ty)) } ;
Nothing -> do { traceFlat "Unfilled tyvar" (ppr tv)
; fr <- getFlavourRole
- ; flatten_tyvar2 tv fr } }
+ ; flatten_tyvar2 tv fr } }
flatten_tyvar2 :: TcTyVar -> CtFlavourRole -> FlatM FlattenTvResult
+-- The tyvar is not a filled-in meta-tyvar
-- Try in the inert equalities
-- See Definition [Applying a generalised substitution] in TcSMonad
-- See Note [Stability of flattening] in TcSMonad
-flatten_tyvar2 tv fr@(flavour, eq_rel)
- | Derived <- flavour -- For derived equalities, consult the inert_model (only)
- = do { model <- liftTcS $ getInertModel
- ; case lookupDVarEnv model tv of
- Just (CTyEqCan { cc_rhs = rhs })
- -> return (FTRFollowed rhs (pprPanic "flatten_tyvar2" (ppr tv $$ ppr rhs)))
- -- Evidence is irrelevant for Derived contexts
- _ -> return FTRNotFollowed }
-
- | otherwise -- For non-derived equalities, consult the inert_eqs (only)
+flatten_tyvar2 tv fr@(_, eq_rel)
= do { ieqs <- liftTcS $ getInertEqs
+ ; mode <- getMode
; case lookupDVarEnv ieqs tv of
Just (ct:_) -- If the first doesn't work,
-- the subsequent ones won't either
| CTyEqCan { cc_ev = ctev, cc_tyvar = tv, cc_rhs = rhs_ty } <- ct
- , ctEvFlavourRole ctev `eqCanRewriteFR` fr
- -> do { traceFlat "Following inert tyvar" (ppr tv <+> equals <+> ppr rhs_ty $$ ppr ctev)
- ; let rewrite_co1 = mkSymCo $ ctEvCoercion ctev
+ , let ct_fr = ctEvFlavourRole ctev
+ , ct_fr `eqCanRewriteFR` fr -- This is THE key call of eqCanRewriteFR
+ -> do { traceFlat "Following inert tyvar" (ppr mode <+> ppr tv <+> equals <+> ppr rhs_ty $$ ppr ctev)
+ ; let rewrite_co1 = mkSymCo (ctEvCoercion ctev)
rewrite_co = case (ctEvEqRel ctev, eq_rel) of
(ReprEq, _rel) -> ASSERT( _rel == ReprEq )
-- if this ASSERT fails, then
@@ -1366,7 +1305,7 @@ flatten_tyvar2 tv fr@(flavour, eq_rel)
Note [An alternative story for the inert substitution]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(This entire note is just background, left here in case we ever want
- to return the the previousl state of affairs)
+ to return the the previous state of affairs)
We used (GHC 7.8) to have this story for the inert substitution inert_eqs
@@ -1484,7 +1423,7 @@ unflatten tv_eqs funeqs
----------------
unflatten_funeq :: Ct -> Cts -> TcS Cts
unflatten_funeq ct@(CFunEqCan { cc_fun = tc, cc_tyargs = xis
- , cc_fsk = fmv, cc_ev = ev }) rest
+ , cc_fsk = fmv, cc_ev = ev }) rest
= do { -- fmv should be an un-filled flatten meta-tv;
-- we now fix its final value by filling it, being careful
-- to observe the occurs check. Zonking will eliminate it
@@ -1492,8 +1431,7 @@ unflatten tv_eqs funeqs
rhs' <- zonkTcType (mkTyConApp tc xis)
; case occCheckExpand fmv rhs' of
Just rhs'' -- Normal case: fill the tyvar
- -> do { setEvBindIfWanted ev
- (EvCoercion (mkTcReflCo (ctEvRole ev) rhs''))
+ -> do { setReflEvidence ev NomEq rhs''
; unflattenFmv fmv rhs''
; return rest }
@@ -1512,17 +1450,22 @@ unflatten tv_eqs funeqs
----------------
unflatten_eq :: TcLevel -> Ct -> Cts -> TcS Cts
- unflatten_eq tclvl ct@(CTyEqCan { cc_ev = ev, cc_tyvar = tv, cc_rhs = rhs }) rest
+ unflatten_eq tclvl ct@(CTyEqCan { cc_ev = ev, cc_tyvar = tv
+ , cc_rhs = rhs, cc_eq_rel = eq_rel }) rest
| isFmvTyVar tv -- Previously these fmvs were untouchable,
-- but now they are touchable
- -- NB: unlike unflattenFmv, filling a fmv here does
+ -- NB: unlike unflattenFmv, filling a fmv here /does/
-- bump the unification count; it is "improvement"
-- Note [Unflattening can force the solver to iterate]
- = do { lhs_elim <- tryFill tv rhs ev
- ; if lhs_elim then return rest else
- do { rhs_elim <- try_fill tclvl ev rhs (mkTyVarTy tv)
- ; if rhs_elim then return rest else
- return (ct `consCts` rest) } }
+ , tyVarKind tv `eqType` typeKind rhs
+ = do { is_filled <- isFilledMetaTyVar tv
+ ; elim <- case is_filled of
+ False -> do { traceTcS "unflatten_eq 2" (ppr ct)
+ ; tryFill ev eq_rel tv rhs }
+ True -> do { traceTcS "unflatten_eq 2" (ppr ct)
+ ; try_fill_rhs ev eq_rel tclvl tv rhs }
+ ; if elim then return rest
+ else return (ct `consCts` rest) }
| otherwise
= return (ct `consCts` rest)
@@ -1530,51 +1473,67 @@ unflatten tv_eqs funeqs
unflatten_eq _ ct _ = pprPanic "unflatten_irred" (ppr ct)
----------------
+ try_fill_rhs ev eq_rel tclvl lhs_tv rhs
+ -- Constraint is lhs_tv ~ rhs_tv,
+ -- and lhs_tv is filled, so try RHS
+ | Just (rhs_tv, co) <- getCastedTyVar_maybe rhs
+ -- co :: kind(rhs_tv) ~ kind(lhs_tv)
+ , isFmvTyVar rhs_tv || (isTouchableMetaTyVar tclvl rhs_tv
+ && not (isSigTyVar rhs_tv))
+ -- LHS is a filled fmv, and so is a type
+ -- family application, which a SigTv should
+ -- not unify with
+ = do { is_filled <- isFilledMetaTyVar rhs_tv
+ ; if is_filled then return False
+ else tryFill ev eq_rel rhs_tv
+ (mkTyVarTy lhs_tv `mkCastTy` mkSymCo co) }
+
+ | otherwise
+ = return False
+
+ ----------------
finalise_eq :: Ct -> Cts -> TcS Cts
finalise_eq (CTyEqCan { cc_ev = ev, cc_tyvar = tv
, cc_rhs = rhs, cc_eq_rel = eq_rel }) rest
| isFmvTyVar tv
= do { ty1 <- zonkTcTyVar tv
- ; ty2 <- zonkTcType rhs
- ; let is_refl = ty1 `tcEqType` ty2
- ; if is_refl then do { setEvBindIfWanted ev
- (EvCoercion $
- mkTcReflCo (eqRelRole eq_rel) rhs)
- ; return rest }
- else return (mkNonCanonical ev `consCts` rest) }
+ ; rhs' <- zonkTcType rhs
+ ; if ty1 `tcEqType` rhs'
+ then do { setReflEvidence ev eq_rel rhs'
+ ; return rest }
+ else return (mkNonCanonical ev `consCts` rest) }
+
| otherwise
= return (mkNonCanonical ev `consCts` rest)
finalise_eq ct _ = pprPanic "finalise_irred" (ppr ct)
- ----------------
- try_fill tclvl ev ty1 ty2
- | Just tv1 <- tcGetTyVar_maybe ty1
- , isTouchableOrFmv tclvl tv1
- , typeKind ty1 `eqType` tyVarKind tv1
- = tryFill tv1 ty2 ev
- | otherwise
- = return False
-
-tryFill :: TcTyVar -> TcType -> CtEvidence -> TcS Bool
--- (tryFill tv rhs ev) sees if 'tv' is an un-filled MetaTv
--- If so, and if tv does not appear in 'rhs', set tv := rhs
--- bind the evidence (which should be a CtWanted) to Refl<rhs>
--- and return True. Otherwise return False
-tryFill tv rhs ev
+tryFill :: CtEvidence -> EqRel -> TcTyVar -> TcType -> TcS Bool
+-- (tryFill tv rhs ev) assumes 'tv' is an /un-filled/ MetaTv
+-- If tv does not appear in 'rhs', it set tv := rhs,
+-- binds the evidence (which should be a CtWanted) to Refl<rhs>
+-- and return True. Otherwise returns False
+tryFill ev eq_rel tv rhs
= ASSERT2( not (isGiven ev), ppr ev )
- do { is_filled <- isFilledMetaTyVar tv
- ; if is_filled then return False else
do { rhs' <- zonkTcType rhs
- ; case occCheckExpand tv rhs' of
+ ; case tcGetTyVar_maybe rhs' of {
+ Just tv' | tv == tv' -> do { setReflEvidence ev eq_rel rhs
+ ; return True } ;
+ _other ->
+ do { case occCheckExpand tv rhs' of
Just rhs'' -- Normal case: fill the tyvar
- -> do { setEvBindIfWanted ev
- (EvCoercion (mkTcReflCo (ctEvRole ev) rhs''))
+ -> do { setReflEvidence ev eq_rel rhs''
; unifyTyVar tv rhs''
; return True }
Nothing -> -- Occurs check
- return False } }
+ return False } } }
+
+setReflEvidence :: CtEvidence -> EqRel -> TcType -> TcS ()
+setReflEvidence ev eq_rel rhs
+ = setEvBindIfWanted ev (EvCoercion refl_co)
+ where
+ refl_co = mkTcReflCo (eqRelRole eq_rel) rhs
{-
Note [Unflatten using funeqs first]
diff --git a/compiler/typecheck/TcInteract.hs b/compiler/typecheck/TcInteract.hs
index 005be19f6e..cb0b44ff2b 100644
--- a/compiler/typecheck/TcInteract.hs
+++ b/compiler/typecheck/TcInteract.hs
@@ -9,7 +9,8 @@ module TcInteract (
#include "HsVersions.h"
-import BasicTypes ( infinity, IntWithInf, intGtLimit )
+import BasicTypes ( SwapFlag(..), isSwapped,
+ infinity, IntWithInf, intGtLimit )
import HsTypes ( HsIPName(..) )
import TcCanonical
import TcFlatten
@@ -133,16 +134,14 @@ that prepareInertsForImplications will discard the insolubles, so we
must keep track of them separately.
-}
-solveSimpleGivens :: [Ct] -> TcS Cts
+solveSimpleGivens :: [Ct] -> TcS ()
solveSimpleGivens givens
| null givens -- Shortcut for common case
- = return emptyCts
+ = return ()
| otherwise
= do { traceTcS "solveSimpleGivens {" (ppr givens)
; go givens
- ; given_insols <- takeGivenInsolubles
- ; traceTcS "End solveSimpleGivens }" (text "Insoluble:" <+> pprCts given_insols)
- ; return given_insols }
+ ; traceTcS "End solveSimpleGivens }" empty }
where
go givens = do { solveSimples (listToBag givens)
; new_givens <- runTcPluginsGiven
@@ -183,7 +182,8 @@ solveSimpleWanteds simples
; if unif_count == 0 && not rerun_plugin
then return (n, wc2) -- Done
- else do { traceTcS "solveSimple going round again:" (ppr rerun_plugin)
+ else do { traceTcS "solveSimple going round again:" $
+ ppr unif_count $$ ppr rerun_plugin
; go (n+1) limit wc2 } } -- Loop
@@ -355,11 +355,8 @@ runTcPlugins plugins all_cts
without = deleteFirstsBy eqCt
eqCt :: Ct -> Ct -> Bool
- eqCt c c' = case (ctEvidence c, ctEvidence c') of
- (CtGiven pred _ _, CtGiven pred' _ _) -> pred `eqType` pred'
- (CtWanted pred _ _, CtWanted pred' _ _) -> pred `eqType` pred'
- (CtDerived pred _ , CtDerived pred' _ ) -> pred `eqType` pred'
- (_ , _ ) -> False
+ eqCt c c' = ctFlavour c == ctFlavour c'
+ && ctPred c `tcEqType` ctPred c'
add :: [(EvTerm,Ct)] -> SolvedCts -> SolvedCts
add xs scs = foldl' addOne scs xs
@@ -386,19 +383,19 @@ runSolverPipeline pipeline workItem
; bumpStepCountTcS -- One step for each constraint processed
; final_res <- run_pipeline pipeline (ContinueWith workItem)
-
- ; final_is <- getTcSInerts
; case final_res of
Stop ev s -> do { traceFireTcS ev s
+ ; final_is <- getTcSInerts
; traceTcS "End solver pipeline (discharged) }"
(text "inerts =" <+> ppr final_is)
; return () }
ContinueWith ct -> do { traceFireTcS (ctEvidence ct) (text "Kept as inert")
+ ; addInertCan ct
+ ; final_is <- getTcSInerts
; traceTcS "End solver pipeline (kept as inert) }" $
vcat [ text "final_item =" <+> ppr ct
, pprTyVars $ tyCoVarsOfCtList ct
- , text "inerts =" <+> ppr final_is]
- ; addInertCan ct }
+ , text "inerts =" <+> ppr final_is] }
}
where run_pipeline :: [(String,SimplifierStage)] -> StopOrContinue Ct
-> TcS (StopOrContinue Ct)
@@ -740,6 +737,7 @@ addFunDepWork inerts work_ev cls
where
work_pred = ctEvPred work_ev
work_loc = ctEvLoc work_ev
+
add_fds inert_ct
= emitFunDepDeriveds $
improveFromAnother derived_loc inert_pred work_pred
@@ -819,7 +817,6 @@ So the inner binding for ?x::Bool *overrides* the outer one.
All this works for the normal cases but it has an odd side effect in
some pathological programs like this:
-
-- This is accepted, the second parameter shadows
f1 :: (?x :: Int, ?x :: Char) => Char
f1 = ?x
@@ -852,57 +849,77 @@ I can think of two ways to fix this:
interactFunEq :: InertCans -> Ct -> TcS (StopOrContinue Ct)
-- Try interacting the work item with the inert set
-interactFunEq inerts workItem@(CFunEqCan { cc_ev = ev, cc_fun = tc
- , cc_tyargs = args, cc_fsk = fsk })
- | Just (CFunEqCan { cc_ev = ev_i
- , cc_fsk = fsk_i }) <- matching_inerts
- = if ev_i `funEqCanDischarge` ev
- then -- Rewrite work-item using inert
- do { traceTcS "reactFunEq (discharge work item):" $
- vcat [ text "workItem =" <+> ppr workItem
- , text "inertItem=" <+> ppr ev_i ]
- ; reactFunEq ev_i fsk_i ev fsk
- ; stopWith ev "Inert rewrites work item" }
- else -- Rewrite inert using work-item
- ASSERT2( ev `funEqCanDischarge` ev_i, ppr ev $$ ppr ev_i )
- do { traceTcS "reactFunEq (rewrite inert item):" $
- vcat [ text "workItem =" <+> ppr workItem
- , text "inertItem=" <+> ppr ev_i ]
- ; updInertFunEqs $ \ feqs -> insertFunEq feqs tc args workItem
- -- Do the updInertFunEqs before the reactFunEq, so that
- -- we don't kick out the inertItem as well as consuming it!
- ; reactFunEq ev fsk ev_i fsk_i
- ; stopWith ev "Work item rewrites inert" }
+interactFunEq inerts work_item@(CFunEqCan { cc_ev = ev, cc_fun = tc
+ , cc_tyargs = args, cc_fsk = fsk })
+ | Just inert_ct@(CFunEqCan { cc_ev = ev_i
+ , cc_fsk = fsk_i })
+ <- findFunEq (inert_funeqs inerts) tc args
+ , pr@(swap_flag, upgrade_flag) <- ev_i `funEqCanDischarge` ev
+ = do { traceTcS "reactFunEq (rewrite inert item):" $
+ vcat [ text "work_item =" <+> ppr work_item
+ , text "inertItem=" <+> ppr ev_i
+ , text "(swap_flag, upgrade)" <+> ppr pr ]
+ ; if isSwapped swap_flag
+ then do { -- Rewrite inert using work-item
+ let work_item' | upgrade_flag = upgradeWanted work_item
+ | otherwise = work_item
+ ; updInertFunEqs $ \ feqs -> insertFunEq feqs tc args work_item'
+ -- Do the updInertFunEqs before the reactFunEq, so that
+ -- we don't kick out the inertItem as well as consuming it!
+ ; reactFunEq ev fsk ev_i fsk_i
+ ; stopWith ev "Work item rewrites inert" }
+ else do { -- Rewrite work-item using inert
+ ; when upgrade_flag $
+ updInertFunEqs $ \ feqs -> insertFunEq feqs tc args
+ (upgradeWanted inert_ct)
+ ; reactFunEq ev_i fsk_i ev fsk
+ ; stopWith ev "Inert rewrites work item" } }
| otherwise -- Try improvement
- = do { improveLocalFunEqs loc inerts tc args fsk
- ; continueWith workItem }
- where
- loc = ctEvLoc ev
- funeqs = inert_funeqs inerts
- matching_inerts = findFunEq funeqs tc args
+ = do { improveLocalFunEqs ev inerts tc args fsk
+ ; continueWith work_item }
-interactFunEq _ workItem = pprPanic "interactFunEq" (ppr workItem)
+interactFunEq _ work_item = pprPanic "interactFunEq" (ppr work_item)
-improveLocalFunEqs :: CtLoc -> InertCans -> TyCon -> [TcType] -> TcTyVar
+upgradeWanted :: Ct -> Ct
+-- We are combining a [W] F tys ~ fmv1 and [D] F tys ~ fmv2
+-- so upgrade the [W] to [WD] before putting it in the inert set
+upgradeWanted ct = ct { cc_ev = upgrade_ev (cc_ev ct) }
+ where
+ upgrade_ev ev = ASSERT2( isWanted ev, ppr ct )
+ ev { ctev_nosh = WDeriv }
+
+improveLocalFunEqs :: CtEvidence -> InertCans -> TyCon -> [TcType] -> TcTyVar
-> TcS ()
-- Generate derived improvement equalities, by comparing
-- the current work item with inert CFunEqs
-- E.g. x + y ~ z, x + y' ~ z => [D] y ~ y'
-improveLocalFunEqs loc inerts fam_tc args fsk
- | not (null improvement_eqns)
- = do { traceTcS "interactFunEq improvements: " $
+--
+-- See Note [FunDep and implicit parameter reactions]
+improveLocalFunEqs ev inerts fam_tc args fsk
+ | isGiven ev -- See Note [No FunEq improvement for Givens]
+ = return ()
+
+ | null improvement_eqns
+ = do { traceTcS "improveLocalFunEqs no improvements: " $
+ vcat [ text "Candidates:" <+> ppr funeqs_for_tc
+ , text "Inert eqs:" <+> ppr ieqs ]
+ ; return () }
+
+ | otherwise
+ = do { traceTcS "improveLocalFunEqs improvements: " $
vcat [ text "Eqns:" <+> ppr improvement_eqns
, text "Candidates:" <+> ppr funeqs_for_tc
- , text "Model:" <+> ppr model ]
+ , text "Inert eqs:" <+> ppr ieqs ]
; mapM_ (unifyDerived loc Nominal) improvement_eqns }
- | otherwise
- = return ()
+
where
- model = inert_model inerts
+ loc = ctEvLoc ev
+ ieqs = inert_eqs inerts
funeqs = inert_funeqs inerts
funeqs_for_tc = findFunEqsByTyCon funeqs fam_tc
- rhs = lookupFlattenTyVar model fsk
+ rhs = lookupFlattenTyVar ieqs fsk
+ fam_inj_info = familyTyConInjectivityInfo fam_tc
--------------------
improvement_eqns
@@ -910,37 +927,30 @@ improveLocalFunEqs loc inerts fam_tc args fsk
= -- Try built-in families, notably for arithmethic
concatMap (do_one_built_in ops) funeqs_for_tc
- | Injective injective_args <- familyTyConInjectivityInfo fam_tc
+ | Injective injective_args <- fam_inj_info
= -- Try improvement from type families with injectivity annotations
- concatMap (do_one_injective injective_args) funeqs_for_tc
+ concatMap (do_one_injective injective_args) funeqs_for_tc
| otherwise
= []
--------------------
do_one_built_in ops (CFunEqCan { cc_tyargs = iargs, cc_fsk = ifsk })
- = sfInteractInert ops args rhs iargs (lookupFlattenTyVar model ifsk)
+ = sfInteractInert ops args rhs iargs (lookupFlattenTyVar ieqs ifsk)
do_one_built_in _ _ = pprPanic "interactFunEq 1" (ppr fam_tc)
--------------------
-- See Note [Type inference for type families with injectivity]
- do_one_injective injective_args
- (CFunEqCan { cc_tyargs = iargs, cc_fsk = ifsk })
- | rhs `tcEqType` lookupFlattenTyVar model ifsk
- = [Pair arg iarg | (arg, iarg, True)
- <- zip3 args iargs injective_args ]
- | otherwise
- = []
+ do_one_injective inj_args (CFunEqCan { cc_tyargs = iargs, cc_fsk = ifsk })
+ | rhs `tcEqType` lookupFlattenTyVar ieqs ifsk
+ = [ Pair arg iarg
+ | (arg, iarg, True) <- zip3 args iargs inj_args ]
+
+ | otherwise = []
+
do_one_injective _ _ = pprPanic "interactFunEq 2" (ppr fam_tc)
-------------
-lookupFlattenTyVar :: InertModel -> TcTyVar -> TcType
--- See Note [lookupFlattenTyVar]
-lookupFlattenTyVar model ftv
- = case lookupDVarEnv model ftv of
- Just (CTyEqCan { cc_rhs = rhs, cc_eq_rel = NomEq }) -> rhs
- _ -> mkTyVarTy ftv
-
reactFunEq :: CtEvidence -> TcTyVar -- From this :: F args1 ~ fsk1
-> CtEvidence -> TcTyVar -- Solve this :: F args2 ~ fsk2
-> TcS ()
@@ -955,26 +965,20 @@ reactFunEq from_this fsk1 solve_this fsk2
; new_ev <- newGivenEvVar loc (fsk_eq_pred, EvCoercion fsk_eq_co)
; emitWorkNC [new_ev] }
- | otherwise
+ | CtDerived { ctev_loc = loc } <- solve_this
+ = do { traceTcS "reactFunEq (Derived)" (ppr from_this $$ ppr fsk1 $$
+ ppr solve_this $$ ppr fsk2)
+ ; emitNewDerivedEq loc Nominal (mkTyVarTy fsk1) (mkTyVarTy fsk2) }
+ -- FunEqs are always at Nominal role
+
+ | otherwise -- Wanted
= do { traceTcS "reactFunEq" (ppr from_this $$ ppr fsk1 $$
ppr solve_this $$ ppr fsk2)
; dischargeFmv solve_this fsk2 (ctEvCoercion from_this) (mkTyVarTy fsk1)
; traceTcS "reactFunEq done" (ppr from_this $$ ppr fsk1 $$
ppr solve_this $$ ppr fsk2) }
-{- Note [lookupFlattenTyVar]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Supppose we have an injective function F and
- inert_funeqs: F t1 ~ fsk1
- F t2 ~ fsk2
- model fsk1 ~ fsk2
-
-We never rewrite the RHS (cc_fsk) of a CFunEqCan. But we /do/ want to
-get the [D] t1 ~ t2 from the injectiveness of F. So we look up the
-cc_fsk of CFunEqCans in the model when trying to find derived
-equalities arising from injectivity.
-
-Note [Type inference for type families with injectivity]
+{- Note [Type inference for type families with injectivity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose we have a type family with an injectivity annotation:
type family F a b = r | r -> b
@@ -1101,24 +1105,19 @@ test when solving pairwise CFunEqCan.
**********************************************************************
-}
-interactTyVarEq :: InertCans -> Ct -> TcS (StopOrContinue Ct)
--- CTyEqCans are always consumed, so always returns Stop
-interactTyVarEq inerts workItem@(CTyEqCan { cc_tyvar = tv
- , cc_rhs = rhs
- , cc_ev = ev
- , cc_eq_rel = eq_rel })
+inertsCanDischarge :: InertCans -> TcTyVar -> TcType -> CtEvidence
+ -> Maybe ( CtEvidence -- The evidence for the inert
+ , SwapFlag -- Whether we need mkSymCo
+ , Bool) -- True <=> keep a [D] version
+ -- of the [WD] constraint
+inertsCanDischarge inerts tv rhs ev
| (ev_i : _) <- [ ev_i | CTyEqCan { cc_ev = ev_i, cc_rhs = rhs_i }
<- findTyEqs inerts tv
, ev_i `eqCanDischarge` ev
, rhs_i `tcEqType` rhs ]
= -- Inert: a ~ ty
-- Work item: a ~ ty
- do { setEvBindIfWanted ev $
- EvCoercion (tcDowngradeRole (eqRelRole eq_rel)
- (ctEvRole ev_i)
- (ctEvCoercion ev_i))
-
- ; stopWith ev "Solved from inert" }
+ Just (ev_i, NotSwapped, keep_deriv ev_i)
| Just tv_rhs <- getTyVar_maybe rhs
, (ev_i : _) <- [ ev_i | CTyEqCan { cc_ev = ev_i, cc_rhs = rhs_i }
@@ -1127,13 +1126,41 @@ interactTyVarEq inerts workItem@(CTyEqCan { cc_tyvar = tv
, rhs_i `tcEqType` mkTyVarTy tv ]
= -- Inert: a ~ b
-- Work item: b ~ a
- do { setEvBindIfWanted ev $
- EvCoercion (mkTcSymCo $
- tcDowngradeRole (eqRelRole eq_rel)
- (ctEvRole ev_i)
- (ctEvCoercion ev_i))
+ Just (ev_i, IsSwapped, keep_deriv ev_i)
+
+ | otherwise
+ = Nothing
+
+ where
+ keep_deriv ev_i
+ | Wanted WOnly <- ctEvFlavour ev_i -- inert is [W]
+ , Wanted WDeriv <- ctEvFlavour ev -- work item is [WD]
+ = True -- Keep a derived verison of the work item
+ | otherwise
+ = False -- Work item is fully discharged
+
+interactTyVarEq :: InertCans -> Ct -> TcS (StopOrContinue Ct)
+-- CTyEqCans are always consumed, so always returns Stop
+interactTyVarEq inerts workItem@(CTyEqCan { cc_tyvar = tv
+ , cc_rhs = rhs
+ , cc_ev = ev
+ , cc_eq_rel = eq_rel })
+ | Just (ev_i, swapped, keep_deriv)
+ <- inertsCanDischarge inerts tv rhs ev
+ = do { setEvBindIfWanted ev $
+ EvCoercion (maybeSym swapped $
+ tcDowngradeRole (eqRelRole eq_rel)
+ (ctEvRole ev_i)
+ (ctEvCoercion ev_i))
+
+ ; let deriv_ev = CtDerived { ctev_pred = ctEvPred ev
+ , ctev_loc = ctEvLoc ev }
+ ; when keep_deriv $
+ emitWork [workItem { cc_ev = deriv_ev }]
+ -- As a Derived it might not be fully rewritten,
+ -- so we emit it as new work
- ; stopWith ev "Solved from inert (r)" }
+ ; stopWith ev "Solved from inert" }
| ReprEq <- eq_rel -- We never solve representational
= unsolved_inert -- equalities by unification
@@ -1159,7 +1186,7 @@ interactTyVarEq inerts workItem@(CTyEqCan { cc_tyvar = tv
<+> text "is" <+> ppr (metaTyVarTcLevel tv))
, text "RHS:" <+> ppr rhs <+> dcolon <+> ppr (typeKind rhs) ])
; addInertEq workItem
- ; return (Stop ev (text "Kept as inert")) }
+ ; stopWith ev "Kept as inert" }
interactTyVarEq _ wi = pprPanic "interactTyVarEq" (ppr wi)
@@ -1233,9 +1260,78 @@ constraint right away. This avoids two dangers
To achieve this required some refactoring of FunDeps.hs (nicer
now!).
+
+Note [FunDep and implicit parameter reactions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Currently, our story of interacting two dictionaries (or a dictionary
+and top-level instances) for functional dependencies, and implicit
+parameters, is that we simply produce new Derived equalities. So for example
+
+ class D a b | a -> b where ...
+ Inert:
+ d1 :g D Int Bool
+ WorkItem:
+ d2 :w D Int alpha
+
+ We generate the extra work item
+ cv :d alpha ~ Bool
+ where 'cv' is currently unused. However, this new item can perhaps be
+ spontaneously solved to become given and react with d2,
+ discharging it in favour of a new constraint d2' thus:
+ d2' :w D Int Bool
+ d2 := d2' |> D Int cv
+ Now d2' can be discharged from d1
+
+We could be more aggressive and try to *immediately* solve the dictionary
+using those extra equalities, but that requires those equalities to carry
+evidence and derived do not carry evidence.
+
+If that were the case with the same inert set and work item we might dischard
+d2 directly:
+
+ cv :w alpha ~ Bool
+ d2 := d1 |> D Int cv
+
+But in general it's a bit painful to figure out the necessary coercion,
+so we just take the first approach. Here is a better example. Consider:
+ class C a b c | a -> b
+And:
+ [Given] d1 : C T Int Char
+ [Wanted] d2 : C T beta Int
+In this case, it's *not even possible* to solve the wanted immediately.
+So we should simply output the functional dependency and add this guy
+[but NOT its superclasses] back in the worklist. Even worse:
+ [Given] d1 : C T Int beta
+ [Wanted] d2: C T beta Int
+Then it is solvable, but its very hard to detect this on the spot.
+
+It's exactly the same with implicit parameters, except that the
+"aggressive" approach would be much easier to implement.
+
+Note [Weird fundeps]
+~~~~~~~~~~~~~~~~~~~~
+Consider class Het a b | a -> b where
+ het :: m (f c) -> a -> m b
+
+ class GHet (a :: * -> *) (b :: * -> *) | a -> b
+ instance GHet (K a) (K [a])
+ instance Het a b => GHet (K a) (K b)
+
+The two instances don't actually conflict on their fundeps,
+although it's pretty strange. So they are both accepted. Now
+try [W] GHet (K Int) (K Bool)
+This triggers fundeps from both instance decls;
+ [D] K Bool ~ K [a]
+ [D] K Bool ~ K beta
+And there's a risk of complaining about Bool ~ [a]. But in fact
+the Wanted matches the second instance, so we never get as far
+as the fundeps.
+
+Trac #7875 is a case in point.
-}
emitFunDepDeriveds :: [FunDepEqn CtLoc] -> TcS ()
+-- See Note [FunDep and implicit parameter reactions]
emitFunDepDeriveds fd_eqns
= mapM_ do_one_FDEqn fd_eqns
where
@@ -1282,121 +1378,45 @@ doTopReact work_item
_ -> -- Any other work item does not react with any top-level equations
continueWith work_item }
---------------------
-doTopReactDict :: InertSet -> Ct -> TcS (StopOrContinue Ct)
--- Try to use type-class instance declarations to simplify the constraint
-doTopReactDict inerts work_item@(CDictCan { cc_ev = fl, cc_class = cls
- , cc_tyargs = xis })
- | isGiven fl -- Never use instances for Given constraints
- = do { try_fundep_improvement
- ; continueWith work_item }
-
- | Just ev <- lookupSolvedDict inerts cls xis -- Cached
- = do { setEvBindIfWanted fl (ctEvTerm ev)
- ; stopWith fl "Dict/Top (cached)" }
-
- | isDerived fl -- Use type-class instances for Deriveds, in the hope
- -- of generating some improvements
- -- C.f. Example 3 of Note [The improvement story]
- -- It's easy because no evidence is involved
- = do { dflags <- getDynFlags
- ; lkup_inst_res <- matchClassInst dflags inerts cls xis dict_loc
- ; case lkup_inst_res of
- GenInst { lir_new_theta = preds
- , lir_safe_over = s } ->
- do { emitNewDeriveds dict_loc preds
- ; unless s $ insertSafeOverlapFailureTcS work_item
- ; stopWith fl "Dict/Top (solved)" }
-
- NoInstance ->
- do { -- If there is no instance, try improvement
- try_fundep_improvement
- ; continueWith work_item } }
-
- | otherwise -- Wanted, but not cached
- = do { dflags <- getDynFlags
- ; lkup_inst_res <- matchClassInst dflags inerts cls xis dict_loc
- ; case lkup_inst_res of
- GenInst { lir_new_theta = theta
- , lir_mk_ev = mk_ev
- , lir_safe_over = s } ->
- do { addSolvedDict fl cls xis
- ; unless s $ insertSafeOverlapFailureTcS work_item
- ; solve_from_instance theta mk_ev }
- NoInstance ->
- do { try_fundep_improvement
- ; continueWith work_item } }
- where
- dict_pred = mkClassPred cls xis
- dict_loc = ctEvLoc fl
- dict_origin = ctLocOrigin dict_loc
- deeper_loc = zap_origin (bumpCtLocDepth dict_loc)
-
- zap_origin loc -- After applying an instance we can set ScOrigin to
- -- infinity, so that prohibitedSuperClassSolve never fires
- | ScOrigin {} <- dict_origin
- = setCtLocOrigin loc (ScOrigin infinity)
- | otherwise
- = loc
-
- solve_from_instance :: [TcPredType]
- -> ([EvTerm] -> EvTerm) -> TcS (StopOrContinue Ct)
- -- Precondition: evidence term matches the predicate workItem
- solve_from_instance theta mk_ev
- | null theta
- = do { traceTcS "doTopReact/found nullary instance for" $ ppr fl
- ; setWantedEvBind (ctEvId fl) (mk_ev [])
- ; stopWith fl "Dict/Top (solved, no new work)" }
- | otherwise
- = do { checkReductionDepth deeper_loc dict_pred
- ; traceTcS "doTopReact/found non-nullary instance for" $ ppr fl
- ; evc_vars <- mapM (newWanted deeper_loc) theta
- ; setWantedEvBind (ctEvId fl) (mk_ev (map getEvTerm evc_vars))
- ; emitWorkNC (freshGoals evc_vars)
- ; stopWith fl "Dict/Top (solved, more work)" }
-
- -- We didn't solve it; so try functional dependencies with
- -- the instance environment, and return
- -- See also Note [Weird fundeps]
- try_fundep_improvement
- = do { traceTcS "try_fundeps" (ppr work_item)
- ; instEnvs <- getInstEnvs
- ; emitFunDepDeriveds $
- improveFromInstEnv instEnvs mk_ct_loc dict_pred }
-
- mk_ct_loc :: PredType -- From instance decl
- -> SrcSpan -- also from instance deol
- -> CtLoc
- mk_ct_loc inst_pred inst_loc
- = dict_loc { ctl_origin = FunDepOrigin2 dict_pred dict_origin
- inst_pred inst_loc }
-
-doTopReactDict _ w = pprPanic "doTopReactDict" (ppr w)
--------------------
doTopReactFunEq :: Ct -> TcS (StopOrContinue Ct)
doTopReactFunEq work_item@(CFunEqCan { cc_ev = old_ev, cc_fun = fam_tc
, cc_tyargs = args, cc_fsk = fsk })
+
+ | fsk `elemVarSet` tyCoVarsOfTypes args
+ = no_reduction -- See Note [FunEq occurs-check principle]
+
+ | otherwise -- Note [Reduction for Derived CFunEqCans]
= do { match_res <- matchFam fam_tc args
-- Look up in top-level instances, or built-in axiom
-- See Note [MATCHING-SYNONYMS]
; case match_res of
- Nothing -> do { improveTopFunEqs (ctEvLoc old_ev) fam_tc args fsk
- ; continueWith work_item }
- Just (ax_co, rhs_ty)
- -> reduce_top_fun_eq old_ev fsk ax_co rhs_ty }
+ Nothing -> no_reduction
+ Just match_info -> reduce_top_fun_eq old_ev fsk match_info }
+ where
+ no_reduction
+ = do { improveTopFunEqs old_ev fam_tc args fsk
+ ; continueWith work_item }
doTopReactFunEq w = pprPanic "doTopReactFunEq" (ppr w)
-reduce_top_fun_eq :: CtEvidence -> TcTyVar -> TcCoercion -> TcType
+reduce_top_fun_eq :: CtEvidence -> TcTyVar -> (TcCoercion, TcType)
-> TcS (StopOrContinue Ct)
--- Found an applicable top-level axiom: use it to reduce
-reduce_top_fun_eq old_ev fsk ax_co rhs_ty
+-- We have found an applicable top-level axiom: use it to reduce
+-- Precondition: fsk is not free in rhs_ty
+-- old_ev is not Derived
+reduce_top_fun_eq old_ev fsk (ax_co, rhs_ty)
+ | isDerived old_ev
+ = do { emitNewDerivedEq loc Nominal (mkTyVarTy fsk) rhs_ty
+ ; stopWith old_ev "Fun/Top (derived)" }
+
| Just (tc, tc_args) <- tcSplitTyConApp_maybe rhs_ty
, isTypeFamilyTyCon tc
, tc_args `lengthIs` tyConArity tc -- Short-cut
- = shortCutReduction old_ev fsk ax_co tc tc_args
- -- Try shortcut; see Note [Short cut for top-level reaction]
+ = -- RHS is another type-family application
+ -- Try shortcut; see Note [Top-level reductions for type functions]
+ shortCutReduction old_ev fsk ax_co tc tc_args
| isGiven old_ev -- Not shortcut
= do { let final_co = mkTcSymCo (ctEvCoercion old_ev) `mkTcTransCo` ax_co
@@ -1406,50 +1426,36 @@ reduce_top_fun_eq old_ev fsk ax_co rhs_ty
; emitWorkNC [new_ev] -- Non-cannonical; that will mean we flatten rhs_ty
; stopWith old_ev "Fun/Top (given)" }
- -- So old_ev is Wanted or Derived
- | not (fsk `elemVarSet` tyCoVarsOfType rhs_ty)
- = do { dischargeFmv old_ev fsk ax_co rhs_ty
+ | otherwise -- So old_ev is Wanted (cannot be Derived)
+ = ASSERT2( not (fsk `elemVarSet` tyCoVarsOfType rhs_ty)
+ , ppr old_ev $$ ppr rhs_ty )
+ -- Guaranteed by Note [FunEq occurs-check principle]
+ do { dischargeFmv old_ev fsk ax_co rhs_ty
; traceTcS "doTopReactFunEq" $
vcat [ text "old_ev:" <+> ppr old_ev
, nest 2 (text ":=") <+> ppr ax_co ]
; stopWith old_ev "Fun/Top (wanted)" }
- | otherwise -- We must not assign ufsk := ...ufsk...!
- = do { alpha_ty <- newFlexiTcSTy (tyVarKind fsk)
- ; new_co <- case old_ev of
- CtWanted {} -> emitNewWantedEq loc Nominal alpha_ty rhs_ty
- CtDerived {} -> do { ev <- newDerivedNC loc pred
- ; updWorkListTcS (extendWorkListDerived loc ev)
- ; return (ctEvCoercion ev) } -- Coercion is bottom
- where pred = mkPrimEqPred alpha_ty rhs_ty
- _ -> pprPanic "reduce_top_fun_eq" (ppr old_ev)
-
- -- By emitting this as non-canonical, we deal with all
- -- flattening, occurs-check, and ufsk := ufsk issues
- ; let final_co = ax_co `mkTcTransCo` mkTcSymCo new_co
- -- ax_co :: fam_tc args ~ rhs_ty
- -- ev :: alpha ~ rhs_ty
- -- ufsk := alpha
- -- final_co :: fam_tc args ~ alpha
- ; dischargeFmv old_ev fsk final_co alpha_ty
- ; traceTcS "doTopReactFunEq (occurs)" $
- vcat [ text "old_ev:" <+> ppr old_ev
- , nest 2 (text ":=") <+>
- if isDerived old_ev then text "(derived)"
- else ppr final_co
- , text "new_co:" <+> ppr new_co ]
- ; stopWith old_ev "Fun/Top (wanted)" }
where
loc = ctEvLoc old_ev
deeper_loc = bumpCtLocDepth loc
-improveTopFunEqs :: CtLoc -> TyCon -> [TcType] -> TcTyVar -> TcS ()
-improveTopFunEqs loc fam_tc args fsk
- = do { model <- getInertModel
+improveTopFunEqs :: CtEvidence -> TyCon -> [TcType] -> TcTyVar -> TcS ()
+-- See Note [FunDep and implicit parameter reactions]
+improveTopFunEqs ev fam_tc args fsk
+ | isGiven ev -- See Note [No FunEq improvement for Givens]
+ = return ()
+
+ | otherwise
+ = do { ieqs <- getInertEqs
; fam_envs <- getFamInstEnvs
; eqns <- improve_top_fun_eqs fam_envs fam_tc args
- (lookupFlattenTyVar model fsk)
+ (lookupFlattenTyVar ieqs fsk)
+ ; traceTcS "improveTopFunEqs" (vcat [ ppr fam_tc <+> ppr args <+> ppr fsk
+ , ppr eqns ])
; mapM_ (unifyDerived loc Nominal) eqns }
+ where
+ loc = ctEvLoc ev
improve_top_fun_eqs :: FamInstEnvs
-> TyCon -> [TcType] -> TcType
@@ -1475,7 +1481,8 @@ improve_top_fun_eqs fam_envs fam_tc args rhs_ty
| otherwise
= return []
- where
+
+ where
buildImprovementData
:: [a] -- axioms for a TF (FamInst or CoAxBranch)
-> (a -> [Type]) -- get LHS of an axiom
@@ -1535,11 +1542,7 @@ shortCutReduction old_ev fsk ax_co fam_tc tc_args
`mkTcTransCo` mkTcSymCo ax_co
`mkTcTransCo` ctEvCoercion old_ev) )
- Derived -> newDerivedNC deeper_loc $
- mkPrimEqPred (mkTyConApp fam_tc xis)
- (mkTyVarTy fsk)
-
- Wanted ->
+ Wanted {} ->
do { (new_ev, new_co) <- newWantedEq deeper_loc Nominal
(mkTyConApp fam_tc xis) (mkTyVarTy fsk)
; setWantedEq (ctev_dest old_ev) $
@@ -1548,6 +1551,8 @@ shortCutReduction old_ev fsk ax_co fam_tc tc_args
`mkTcTransCo` new_co
; return new_ev }
+ Derived -> pprPanic "shortCutReduction" (ppr old_ev)
+
; let new_ct = CFunEqCan { cc_ev = new_ev, cc_fun = fam_tc
, cc_tyargs = xis, cc_fsk = fsk }
; updWorkListTcS (extendWorkListFunEq new_ct)
@@ -1559,19 +1564,21 @@ dischargeFmv :: CtEvidence -> TcTyVar -> TcCoercion -> TcType -> TcS ()
-- (dischargeFmv x fmv co ty)
-- [W] ev :: F tys ~ fmv
-- co :: F tys ~ xi
--- Precondition: fmv is not filled, and fuv `notElem` xi
+-- Precondition: fmv is not filled, and fmv `notElem` xi
+-- ev is Wanted
--
-- Then set fmv := xi,
--- set ev := co
+-- set ev := co
-- kick out any inert things that are now rewritable
--
-- Does not evaluate 'co' if 'ev' is Derived
-dischargeFmv ev fmv co xi
+dischargeFmv ev@(CtWanted { ctev_dest = dest }) fmv co xi
= ASSERT2( not (fmv `elemVarSet` tyCoVarsOfType xi), ppr ev $$ ppr fmv $$ ppr xi )
- do { setEvBindIfWanted ev (EvCoercion co)
+ do { setWantedEvTerm dest (EvCoercion co)
; unflattenFmv fmv xi
; n_kicked <- kickOutAfterUnification fmv
; traceTcS "dischargeFmv" (ppr fmv <+> equals <+> ppr xi $$ ppr_kicked n_kicked) }
+dischargeFmv ev _ _ _ = pprPanic "dischargeFmv" (ppr ev)
{- Note [Top-level reductions for type functions]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1585,15 +1592,14 @@ Here is what we do, in four cases:
instantiate axiom: ax_co : F tys ~ rhs
Then:
- Discharge fmv := alpha
+ Discharge fmv := rhs
Discharge x := ax_co ; sym x2
- New wanted [W] x2 : alpha ~ rhs (Non-canonical)
This is *the* way that fmv's get unified; even though they are
"untouchable".
- NB: it can be the case that fmv appears in the (instantiated) rhs.
- In that case the new Non-canonical wanted will be loopy, but that's
- ok. But it's good reason NOT to claim that it is canonical!
+ NB: Given Note [FunEq occurs-check principle], fmv does not appear
+ in tys, and hence does not appear in the instantiated RHS. So
+ the unification can't make an infinite type.
* Wanteds: short cut firing rule
Applies when the RHS of the axiom is another type-function application
@@ -1625,6 +1631,36 @@ Here is what we do, in four cases:
- Add new Canonical given
[G] (sym (G flat_cos) ; co ; g) : G flat_tys ~ fsk (CFunEqCan)
+Note [FunEq occurs-check principle]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+I have spent a lot of time finding a good way to deal with
+CFunEqCan constraints like
+ F (fuv, a) ~ fuv
+where flatten-skolem occurs on the LHS. Now in principle we
+might may progress by doing a reduction, but in practice its
+hard to find examples where it is useful, and easy to find examples
+where we fall into an infinite reduction loop. A rule that works
+very well is this:
+
+ *** FunEq occurs-check principle ***
+
+ Do not reduce a CFunEqCan
+ F tys ~ fsk
+ if fsk appears free in tys
+ Instead we treat it as stuck.
+
+Examples:
+
+* Trac #5837 has [G] a ~ TF (a,Int), with an instance
+ type instance TF (a,b) = (TF a, TF b)
+ This readily loops when solving givens. But with the FunEq occurs
+ check principle, it rapidly gets stuck which is fine.
+
+* Trac #12444 is a good example, explained in comment:2. We have
+ type instance F (Succ x) = Succ (F x)
+ [W] alpha ~ Succ (F alpha)
+ If we allow the reduction to happen, we get an infinite loop
+
Note [Cached solved FunEqs]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
When trying to solve, say (FunExpensive big-type ~ ty), it's important
@@ -1639,87 +1675,6 @@ When trying to match a dictionary (D tau) to a top-level instance, or a
type family equation (F taus_1 ~ tau_2) to a top-level family instance,
we do *not* need to expand type synonyms because the matcher will do that for us.
-
-Note [RHS-FAMILY-SYNONYMS]
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-The RHS of a family instance is represented as yet another constructor which is
-like a type synonym for the real RHS the programmer declared. Eg:
- type instance F (a,a) = [a]
-Becomes:
- :R32 a = [a] -- internal type synonym introduced
- F (a,a) ~ :R32 a -- instance
-
-When we react a family instance with a type family equation in the work list
-we keep the synonym-using RHS without expansion.
-
-Note [FunDep and implicit parameter reactions]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Currently, our story of interacting two dictionaries (or a dictionary
-and top-level instances) for functional dependencies, and implicit
-parameters, is that we simply produce new Derived equalities. So for example
-
- class D a b | a -> b where ...
- Inert:
- d1 :g D Int Bool
- WorkItem:
- d2 :w D Int alpha
-
- We generate the extra work item
- cv :d alpha ~ Bool
- where 'cv' is currently unused. However, this new item can perhaps be
- spontaneously solved to become given and react with d2,
- discharging it in favour of a new constraint d2' thus:
- d2' :w D Int Bool
- d2 := d2' |> D Int cv
- Now d2' can be discharged from d1
-
-We could be more aggressive and try to *immediately* solve the dictionary
-using those extra equalities, but that requires those equalities to carry
-evidence and derived do not carry evidence.
-
-If that were the case with the same inert set and work item we might dischard
-d2 directly:
-
- cv :w alpha ~ Bool
- d2 := d1 |> D Int cv
-
-But in general it's a bit painful to figure out the necessary coercion,
-so we just take the first approach. Here is a better example. Consider:
- class C a b c | a -> b
-And:
- [Given] d1 : C T Int Char
- [Wanted] d2 : C T beta Int
-In this case, it's *not even possible* to solve the wanted immediately.
-So we should simply output the functional dependency and add this guy
-[but NOT its superclasses] back in the worklist. Even worse:
- [Given] d1 : C T Int beta
- [Wanted] d2: C T beta Int
-Then it is solvable, but its very hard to detect this on the spot.
-
-It's exactly the same with implicit parameters, except that the
-"aggressive" approach would be much easier to implement.
-
-Note [Weird fundeps]
-~~~~~~~~~~~~~~~~~~~~
-Consider class Het a b | a -> b where
- het :: m (f c) -> a -> m b
-
- class GHet (a :: * -> *) (b :: * -> *) | a -> b
- instance GHet (K a) (K [a])
- instance Het a b => GHet (K a) (K b)
-
-The two instances don't actually conflict on their fundeps,
-although it's pretty strange. So they are both accepted. Now
-try [W] GHet (K Int) (K Bool)
-This triggers fundeps from both instance decls;
- [D] K Bool ~ K [a]
- [D] K Bool ~ K beta
-And there's a risk of complaining about Bool ~ [a]. But in fact
-the Wanted matches the second instance, so we never get as far
-as the fundeps.
-
-Trac #7875 is a case in point.
-
Note [Improvement orientation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A very delicate point is the orientation of derived equalities
@@ -1756,10 +1711,170 @@ template. But that's a bit tricky, esp when we remember that the
kinds much match too; so it's easier to let the normal machinery
handle it. Instead we are careful to orient the new derived
equality with the template on the left. Delicate, but it works.
+
+Note [No FunEq improvement for Givens]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We don't do improvements (injectivity etc) for Givens. Why?
+
+* It generates Derived constraints on skolems, which don't do us
+ much good, except perhaps identify inaccessible branches.
+ (They'd be perfectly valid though.)
+
+* For type-nat stuff the derived constraints include type families;
+ e.g. (a < b), (b < c) ==> a < c If we generate a Derived for this,
+ we'll generate a Derived/Wanted CFunEqCan; and, since the same
+ InertCans (after solving Givens) are used for each iteration, that
+ massively confused the unflattening step (TcFlatten.unflatten).
+
+ In fact it led to some infinite loops:
+ indexed-types/should_compile/T10806
+ indexed-types/should_compile/T10507
+ polykinds/T10742
+
+Note [Reduction for Derived CFunEqCans]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You may wonder if it's important to use top-level instances to
+simplify [D] CFunEqCan's. But it is. Here's an example (T10226).
+
+ type instance F Int = Int
+ type instance FInv Int = Int
+
+Suppose we have to solve
+ [WD] FInv (F alpha) ~ alpha
+ [WD] F alpha ~ Int
+
+ --> flatten
+ [WD] F alpha ~ fuv0
+ [WD] FInv fuv0 ~ fuv1 -- (A)
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int -- (B)
+
+ --> Rewwrite (A) with (B), splitting it
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+ [D] FInv Int ~ fuv1 -- (C)
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+
+ --> Reduce (C) with top-level instance
+ **** This is the key step ***
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+ [D] fuv1 ~ Int -- (D)
+ [WD] fuv1 ~ alpha -- (E)
+ [WD] fuv0 ~ Int
+
+ --> Rewrite (D) with (E)
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+ [D] alpha ~ Int -- (F)
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+
+ --> unify (F) alpha := Int, and that solves it
+
+Another example is indexed-types/should_compile/T10634
-}
{- *******************************************************************
* *
+ Top-level reaction for class constraints (CDictCan)
+* *
+**********************************************************************-}
+
+doTopReactDict :: InertSet -> Ct -> TcS (StopOrContinue Ct)
+-- Try to use type-class instance declarations to simplify the constraint
+doTopReactDict inerts work_item@(CDictCan { cc_ev = fl, cc_class = cls
+ , cc_tyargs = xis })
+ | isGiven fl
+ = do { try_fundep_improvement
+ ; continueWith work_item }
+
+ | Just ev <- lookupSolvedDict inerts cls xis -- Cached
+ = do { setEvBindIfWanted fl (ctEvTerm ev)
+ ; stopWith fl "Dict/Top (cached)" }
+
+ | isDerived fl -- Use type-class instances for Deriveds, in the hope
+ -- of generating some improvements
+ -- C.f. Example 3 of Note [The improvement story]
+ -- It's easy because no evidence is involved
+ = do { dflags <- getDynFlags
+ ; lkup_inst_res <- matchClassInst dflags inerts cls xis dict_loc
+ ; case lkup_inst_res of
+ GenInst { lir_new_theta = preds
+ , lir_safe_over = s } ->
+ do { emitNewDeriveds dict_loc preds
+ ; unless s $ insertSafeOverlapFailureTcS work_item
+ ; stopWith fl "Dict/Top (solved)" }
+
+ NoInstance ->
+ do { -- If there is no instance, try improvement
+ try_fundep_improvement
+ ; continueWith work_item } }
+
+ | otherwise -- Wanted, but not cached
+ = do { dflags <- getDynFlags
+ ; lkup_inst_res <- matchClassInst dflags inerts cls xis dict_loc
+ ; case lkup_inst_res of
+ GenInst { lir_new_theta = theta
+ , lir_mk_ev = mk_ev
+ , lir_safe_over = s } ->
+ do { addSolvedDict fl cls xis
+ ; unless s $ insertSafeOverlapFailureTcS work_item
+ ; solve_from_instance theta mk_ev }
+ NoInstance ->
+ do { try_fundep_improvement
+ ; continueWith work_item } }
+ where
+ dict_pred = mkClassPred cls xis
+ dict_loc = ctEvLoc fl
+ dict_origin = ctLocOrigin dict_loc
+ deeper_loc = zap_origin (bumpCtLocDepth dict_loc)
+
+ zap_origin loc -- After applying an instance we can set ScOrigin to
+ -- infinity, so that prohibitedSuperClassSolve never fires
+ | ScOrigin {} <- dict_origin
+ = setCtLocOrigin loc (ScOrigin infinity)
+ | otherwise
+ = loc
+
+ solve_from_instance :: [TcPredType]
+ -> ([EvTerm] -> EvTerm) -> TcS (StopOrContinue Ct)
+ -- Precondition: evidence term matches the predicate workItem
+ solve_from_instance theta mk_ev
+ | null theta
+ = do { traceTcS "doTopReact/found nullary instance for" $ ppr fl
+ ; setWantedEvBind (ctEvId fl) (mk_ev [])
+ ; stopWith fl "Dict/Top (solved, no new work)" }
+ | otherwise
+ = do { checkReductionDepth deeper_loc dict_pred
+ ; traceTcS "doTopReact/found non-nullary instance for" $ ppr fl
+ ; evc_vars <- mapM (newWanted deeper_loc) theta
+ ; setWantedEvBind (ctEvId fl) (mk_ev (map getEvTerm evc_vars))
+ ; emitWorkNC (freshGoals evc_vars)
+ ; stopWith fl "Dict/Top (solved, more work)" }
+
+ -- We didn't solve it; so try functional dependencies with
+ -- the instance environment, and return
+ -- See also Note [Weird fundeps]
+ try_fundep_improvement
+ = do { traceTcS "try_fundeps" (ppr work_item)
+ ; instEnvs <- getInstEnvs
+ ; emitFunDepDeriveds $
+ improveFromInstEnv instEnvs mk_ct_loc dict_pred }
+
+ mk_ct_loc :: PredType -- From instance decl
+ -> SrcSpan -- also from instance deol
+ -> CtLoc
+ mk_ct_loc inst_pred inst_loc
+ = dict_loc { ctl_origin = FunDepOrigin2 dict_pred dict_origin
+ inst_pred inst_loc }
+
+doTopReactDict _ w = pprPanic "doTopReactDict" (ppr w)
+
+
+{- *******************************************************************
+* *
Class lookup
* *
**********************************************************************-}
diff --git a/compiler/typecheck/TcMType.hs b/compiler/typecheck/TcMType.hs
index ff0ee9ea49..c200b4efb8 100644
--- a/compiler/typecheck/TcMType.hs
+++ b/compiler/typecheck/TcMType.hs
@@ -43,7 +43,7 @@ module TcMType (
--------------------------------
-- Creating new evidence variables
newEvVar, newEvVars, newDict,
- newWanted, newWanteds,
+ newWanted, newWanteds, cloneWanted, cloneWC,
emitWanted, emitWantedEq, emitWantedEvVar, emitWantedEvVars,
newTcEvBinds, addTcEvBind,
@@ -170,11 +170,30 @@ newWanted orig t_or_k pty
else EvVarDest <$> newEvVar pty
return $ CtWanted { ctev_dest = d
, ctev_pred = pty
+ , ctev_nosh = WDeriv
, ctev_loc = loc }
newWanteds :: CtOrigin -> ThetaType -> TcM [CtEvidence]
newWanteds orig = mapM (newWanted orig Nothing)
+cloneWanted :: Ct -> TcM CtEvidence
+cloneWanted ct
+ = newWanted (ctEvOrigin ev) Nothing (ctEvPred ev)
+ where
+ ev = ctEvidence ct
+
+cloneWC :: WantedConstraints -> TcM WantedConstraints
+cloneWC wc@(WC { wc_simple = simples, wc_impl = implics })
+ = do { simples' <- mapBagM clone_one simples
+ ; implics' <- mapBagM clone_implic implics
+ ; return (wc { wc_simple = simples', wc_impl = implics' }) }
+ where
+ clone_one ct = do { ev <- cloneWanted ct; return (mkNonCanonical ev) }
+
+ clone_implic implic@(Implic { ic_wanted = inner_wanted })
+ = do { inner_wanted' <- cloneWC inner_wanted
+ ; return (implic { ic_wanted = inner_wanted' }) }
+
-- | Emits a new Wanted. Deals with both equalities and non-equalities.
emitWanted :: CtOrigin -> TcPredType -> TcM EvTerm
emitWanted origin pty
@@ -188,7 +207,8 @@ emitWantedEq origin t_or_k role ty1 ty2
= do { hole <- newCoercionHole
; loc <- getCtLocM origin (Just t_or_k)
; emitSimple $ mkNonCanonical $
- CtWanted { ctev_pred = pty, ctev_dest = HoleDest hole, ctev_loc = loc }
+ CtWanted { ctev_pred = pty, ctev_dest = HoleDest hole
+ , ctev_nosh = WDeriv, ctev_loc = loc }
; return (mkHoleCo hole role ty1 ty2) }
where
pty = mkPrimEqPredRole role ty1 ty2
@@ -201,6 +221,7 @@ emitWantedEvVar origin ty
; loc <- getCtLocM origin Nothing
; let ctev = CtWanted { ctev_dest = EvVarDest new_cv
, ctev_pred = ty
+ , ctev_nosh = WDeriv
, ctev_loc = loc }
; emitSimple $ mkNonCanonical ctev
; return new_cv }
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index ce541c3f54..fd6d74cfe1 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -78,10 +78,8 @@ module TcRnTypes(
ctEvTerm, ctEvCoercion, ctEvId,
tyCoVarsOfCt, tyCoVarsOfCts,
tyCoVarsOfCtList, tyCoVarsOfCtsList,
- toDerivedCt,
WantedConstraints(..), insolubleWC, emptyWC, isEmptyWC,
- toDerivedWC,
andWC, unionsWC, mkSimpleWC, mkImplicWC,
addInsols, getInsolubles, addSimples, addImplics,
tyCoVarsOfWC, dropDerivedWC, dropDerivedSimples, dropDerivedInsols,
@@ -107,7 +105,7 @@ module TcRnTypes(
CtEvidence(..), TcEvDest(..),
mkGivenLoc, mkKindLoc, toKindLoc,
- isWanted, isGiven, isDerived,
+ isWanted, isGiven, isDerived, isGivenOrWDeriv,
ctEvRole,
-- Constraint solver plugins
@@ -115,10 +113,11 @@ module TcRnTypes(
TcPluginM, runTcPluginM, unsafeTcPluginTcM,
getEvBindsTcPluginM,
- CtFlavour(..), ctEvFlavour,
+ CtFlavour(..), ShadowInfo(..), ctEvFlavour,
CtFlavourRole, ctEvFlavourRole, ctFlavourRole,
- eqCanRewrite, eqCanRewriteFR, eqCanDischarge,
- funEqCanDischarge, funEqCanDischargeFR,
+ eqCanRewriteFR, eqMayRewriteFR,
+ eqCanDischarge,
+ funEqCanDischarge, funEqCanDischargeF,
-- Pretty printing
pprEvVarTheta,
@@ -174,6 +173,7 @@ import ListSetOps
import FastString
import qualified GHC.LanguageExtensions as LangExt
import Fingerprint
+import Util
import Control.Monad (ap, liftM, msum)
#if __GLASGOW_HASKELL__ > 710
@@ -1509,7 +1509,8 @@ data Ct
cc_tyvar :: TcTyVar,
cc_rhs :: TcType, -- Not necessarily function-free (hence not Xi)
-- See invariants above
- cc_eq_rel :: EqRel
+
+ cc_eq_rel :: EqRel -- INVARIANT: cc_eq_rel = ctEvEqRel cc_ev
}
| CFunEqCan { -- F xis ~ fsk
@@ -1625,16 +1626,6 @@ ctPred :: Ct -> PredType
-- See Note [Ct/evidence invariant]
ctPred ct = ctEvPred (cc_ev ct)
--- | Convert a Wanted to a Derived
-toDerivedCt :: Ct -> Ct
-toDerivedCt ct
- = case ctEvidence ct of
- CtWanted { ctev_pred = pred, ctev_loc = loc }
- -> ct {cc_ev = CtDerived {ctev_pred = pred, ctev_loc = loc}}
-
- CtDerived {} -> ct
- CtGiven {} -> pprPanic "to_derived" (ppr ct)
-
-- | Makes a new equality predicate with the same role as the given
-- evidence.
mkTcEqPredLikeEv :: CtEvidence -> TcType -> TcType -> TcType
@@ -2074,16 +2065,6 @@ andWC (WC { wc_simple = f1, wc_impl = i1, wc_insol = n1 })
unionsWC :: [WantedConstraints] -> WantedConstraints
unionsWC = foldr andWC emptyWC
--- | Convert all Wanteds into Deriveds (ignoring insolubles)
-toDerivedWC :: WantedConstraints -> WantedConstraints
-toDerivedWC wc@(WC { wc_simple = simples, wc_impl = implics })
- = wc { wc_simple = mapBag toDerivedCt simples
- , wc_impl = mapBag to_derived_implic implics }
- where
- to_derived_implic implic@(Implic { ic_wanted = inner_wanted })
- = implic { ic_wanted = toDerivedWC inner_wanted }
-
-
addSimples :: WantedConstraints -> Bag Ct -> WantedConstraints
addSimples wc cts
= wc { wc_simple = wc_simple wc `unionBags` cts }
@@ -2113,21 +2094,23 @@ isInsolubleStatus _ = False
insolubleImplic :: Implication -> Bool
insolubleImplic ic = isInsolubleStatus (ic_status ic)
-insolubleWC :: TcLevel -> WantedConstraints -> Bool
-insolubleWC tc_lvl (WC { wc_impl = implics, wc_insol = insols })
- = anyBag (trulyInsoluble tc_lvl) insols
+insolubleWC :: WantedConstraints -> Bool
+insolubleWC (WC { wc_impl = implics, wc_insol = insols })
+ = anyBag trulyInsoluble insols
|| anyBag insolubleImplic implics
-trulyInsoluble :: TcLevel -> Ct -> Bool
+trulyInsoluble :: Ct -> Bool
-- Constraints in the wc_insol set which ARE NOT
-- treated as truly insoluble:
-- a) type holes, arising from PartialTypeSignatures,
-- b) "true" expression holes arising from TypedHoles
--
--- Out-of-scope variables masquerading as expression holes
--- ARE treated as truly insoluble.
+-- A "expression hole" or "type hole" constraint isn't really an error
+-- at all; it's a report saying "_ :: Int" here. But an out-of-scope
+-- variable masquerading as expression holes IS treated as truly
+-- insoluble, so that it trumps other errors during error reporting.
-- Yuk!
-trulyInsoluble _tc_lvl insol
+trulyInsoluble insol
| isHoleCt insol = isOutOfScopeCt insol
| otherwise = True
@@ -2342,22 +2325,25 @@ data TcEvDest
-- See Note [Coercion holes] in TyCoRep
data CtEvidence
- = CtGiven { ctev_pred :: TcPredType -- See Note [Ct/evidence invariant]
- , ctev_evar :: EvVar -- See Note [Evidence field of CtEvidence]
- , ctev_loc :: CtLoc }
- -- Truly given, not depending on subgoals
- -- NB: Spontaneous unifications belong here
-
- | CtWanted { ctev_pred :: TcPredType -- See Note [Ct/evidence invariant]
- , ctev_dest :: TcEvDest
- , ctev_loc :: CtLoc }
- -- Wanted goal
-
- | CtDerived { ctev_pred :: TcPredType
- , ctev_loc :: CtLoc }
- -- A goal that we don't really have to solve and can't immediately
- -- rewrite anything other than a derived (there's no evidence!)
- -- but if we do manage to solve it may help in solving other goals.
+ = CtGiven -- Truly given, not depending on subgoals
+ -- NB: Spontaneous unifications belong here
+ { ctev_pred :: TcPredType -- See Note [Ct/evidence invariant]
+ , ctev_evar :: EvVar -- See Note [Evidence field of CtEvidence]
+ , ctev_loc :: CtLoc }
+
+
+ | CtWanted -- Wanted goal
+ { ctev_pred :: TcPredType -- See Note [Ct/evidence invariant]
+ , ctev_dest :: TcEvDest
+ , ctev_nosh :: ShadowInfo -- See Note [Constraint flavours]
+ , ctev_loc :: CtLoc }
+
+ | CtDerived -- A goal that we don't really have to solve and can't
+ -- immediately rewrite anything other than a derived
+ -- (there's no evidence!) but if we do manage to solve
+ -- it may help in solving other goals.
+ { ctev_pred :: TcPredType
+ , ctev_loc :: CtLoc }
ctEvPred :: CtEvidence -> TcPredType
-- The predicate of a flavor
@@ -2399,11 +2385,12 @@ instance Outputable TcEvDest where
ppr (EvVarDest ev) = ppr ev
instance Outputable CtEvidence where
- ppr fl = case fl of
- CtGiven {} -> text "[G]" <+> ppr (ctev_evar fl) <+> ppr_pty
- CtWanted {} -> text "[W]" <+> ppr (ctev_dest fl) <+> ppr_pty
- CtDerived {} -> text "[D]" <+> text "_" <+> ppr_pty
- where ppr_pty = dcolon <+> ppr (ctEvPred fl)
+ ppr ev = ppr (ctEvFlavour ev) <+> pp_ev <+> dcolon <+> ppr (ctEvPred ev)
+ where
+ pp_ev = case ev of
+ CtGiven { ctev_evar = v } -> ppr v
+ CtWanted {ctev_dest = d } -> ppr d
+ CtDerived {} -> text "_"
isWanted :: CtEvidence -> Bool
isWanted (CtWanted {}) = True
@@ -2424,23 +2411,62 @@ isDerived _ = False
%* *
%************************************************************************
-Just an enum type that tracks whether a constraint is wanted, derived,
-or given, when we need to separate that info from the constraint itself.
+Note [Constraint flavours]
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Constraints come in four flavours:
+
+* [G] Given: we have evidence
+
+* [W] Wanted WOnly: we want evidence
+
+* [D] Derived: any solution must satisfy this constraint, but
+ we don't need evidence for it. Examples include:
+ - superclasses of [W] class constraints
+ - equalities arising from functional dependencies
+ or injectivity
+* [WD] Wanted WDeriv: a single constraint that represents
+ both [W] and [D]
+ We keep them paired as one both for efficiency, and because
+ when we have a finite map F tys -> CFunEqCan, it's inconvenient
+ to have two CFunEqCans in the range
+
+The ctev_nosh field of a Wanted distinguishes between [W] and [WD]
+
+Wanted constraints are born as [WD], but are split into [W] and its
+"shadow" [D] in TcSMonad.maybeEmitShadow.
+
+See Note [The improvement story and derived shadows] in TcSMonad
-}
-data CtFlavour = Given | Wanted | Derived
+data CtFlavour -- See Note [Constraint flavours]
+ = Given
+ | Wanted ShadowInfo
+ | Derived
deriving Eq
+data ShadowInfo
+ = WDeriv -- [WD] This Wanted constraint has no Derived shadow,
+ -- so it behaves like a pair of a Wanted and a Derived
+ | WOnly -- [W] It has a separate derived shadow
+ -- See Note [Derived shadows]
+ deriving( Eq )
+
+isGivenOrWDeriv :: CtFlavour -> Bool
+isGivenOrWDeriv Given = True
+isGivenOrWDeriv (Wanted WDeriv) = True
+isGivenOrWDeriv _ = False
+
instance Outputable CtFlavour where
- ppr Given = text "[G]"
- ppr Wanted = text "[W]"
- ppr Derived = text "[D]"
+ ppr Given = text "[G]"
+ ppr (Wanted WDeriv) = text "[WD]"
+ ppr (Wanted WOnly) = text "[W]"
+ ppr Derived = text "[D]"
ctEvFlavour :: CtEvidence -> CtFlavour
-ctEvFlavour (CtWanted {}) = Wanted
-ctEvFlavour (CtGiven {}) = Given
-ctEvFlavour (CtDerived {}) = Derived
+ctEvFlavour (CtWanted { ctev_nosh = nosh }) = Wanted nosh
+ctEvFlavour (CtGiven {}) = Given
+ctEvFlavour (CtDerived {}) = Derived
-- | Whether or not one 'Ct' can rewrite another is determined by its
-- flavour and its equality relation. See also
@@ -2456,7 +2482,7 @@ ctFlavourRole :: Ct -> CtFlavourRole
ctFlavourRole = ctEvFlavourRole . cc_ev
{- Note [eqCanRewrite]
-~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~
(eqCanRewrite ct1 ct2) holds if the constraint ct1 (a CTyEqCan of form
tv ~ ty) can be used to rewrite ct2. It must satisfy the properties of
a can-rewrite relation, see Definition [Can-rewrite relation] in
@@ -2498,9 +2524,31 @@ I thought maybe we could never get Derived ReprEq constraints, but
we can; straight from the Wanteds during improvment. And from a Derived
ReprEq we could conceivably get a Derived NomEq improvment (by decomposing
a type constructor with Nomninal role), and hence unify.
+-}
-Note [funEqCanDischarge]
-~~~~~~~~~~~~~~~~~~~~~~~~~
+eqCanRewriteFR :: CtFlavourRole -> CtFlavourRole -> Bool
+-- Can fr1 actually rewrite fr2?
+-- Very important function!
+-- See Note [eqCanRewrite]
+-- See Note [Wanteds do not rewrite Wanteds]
+-- See Note [Deriveds do rewrite Deriveds]
+eqCanRewriteFR (Given, NomEq) (_, _) = True
+eqCanRewriteFR (Given, ReprEq) (_, ReprEq) = True
+eqCanRewriteFR (Wanted WDeriv, NomEq) (Derived, NomEq) = True
+eqCanRewriteFR (Derived, NomEq) (Derived, NomEq) = True
+eqCanRewriteFR _ _ = False
+
+eqMayRewriteFR :: CtFlavourRole -> CtFlavourRole -> Bool
+-- Is it /possible/ that fr1 can rewrite fr2?
+-- This is used when deciding which inerts to kick out,
+-- at which time a [WD] inert may be split into [W] and [D]
+eqMayRewriteFR (Wanted WDeriv, NomEq) (Wanted WDeriv, NomEq) = True
+eqMayRewriteFR (Derived, NomEq) (Wanted WDeriv, NomEq) = True
+eqMayRewriteFR fr1 fr2 = eqCanRewriteFR fr1 fr2
+
+-----------------
+{- Note [funEqCanDischarge]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose we have two CFunEqCans with the same LHS:
(x1:F ts ~ f1) `funEqCanDischarge` (x2:F ts ~ f2)
Can we drop x2 in favour of x1, either unifying
@@ -2508,12 +2556,37 @@ f2 (if it's a flatten meta-var) or adding a new Given
(f1 ~ f2), if x2 is a Given?
Answer: yes if funEqCanDischarge is true.
+-}
-Note [eqCanDischarge]
-~~~~~~~~~~~~~~~~~~~~~
-Suppose we have two identicla equality constraints
+funEqCanDischarge
+ :: CtEvidence -> CtEvidence
+ -> ( SwapFlag -- NotSwapped => lhs can discharge rhs
+ -- Swapped => rhs can discharge lhs
+ , Bool) -- True <=> upgrade non-discharded one
+ -- from [W] to [WD]
+-- See Note [funEqCanDischarge]
+funEqCanDischarge ev1 ev2
+ = ASSERT2( ctEvEqRel ev1 == NomEq, ppr ev1 )
+ ASSERT2( ctEvEqRel ev2 == NomEq, ppr ev2 )
+ -- CFunEqCans are all Nominal, hence asserts
+ funEqCanDischargeF (ctEvFlavour ev1) (ctEvFlavour ev2)
+
+funEqCanDischargeF :: CtFlavour -> CtFlavour -> (SwapFlag, Bool)
+funEqCanDischargeF Given _ = (NotSwapped, False)
+funEqCanDischargeF _ Given = (IsSwapped, False)
+funEqCanDischargeF (Wanted WDeriv) _ = (NotSwapped, False)
+funEqCanDischargeF _ (Wanted WDeriv) = (IsSwapped, True)
+funEqCanDischargeF (Wanted WOnly) (Wanted WOnly) = (NotSwapped, False)
+funEqCanDischargeF (Wanted WOnly) Derived = (NotSwapped, True)
+funEqCanDischargeF Derived (Wanted WOnly) = (IsSwapped, True)
+funEqCanDischargeF Derived Derived = (NotSwapped, False)
+
+
+{- Note [eqCanDischarge]
+~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose we have two identical CTyEqCan equality constraints
(i.e. both LHS and RHS are the same)
- (x1:s~t) `eqCanDischarge` (xs:s~t)
+ (x1:a~t) `eqCanDischarge` (xs:a~t)
Can we just drop x2 in favour of x1?
Answer: yes if eqCanDischarge is true.
@@ -2525,48 +2598,27 @@ other Deriveds in the model whereas the Wanted cannot.
However a Wanted can certainly discharge an identical Wanted. So
eqCanDischarge does /not/ define a can-rewrite relation in the
sense of Definition [Can-rewrite relation] in TcSMonad.
--}
------------------
-eqCanRewrite :: CtEvidence -> CtEvidence -> Bool
--- Very important function!
--- See Note [eqCanRewrite]
--- See Note [Wanteds do not rewrite Wanteds]
--- See Note [Deriveds do rewrite Deriveds]
-eqCanRewrite ev1 ev2 = eqCanRewriteFR (ctEvFlavourRole ev1)
- (ctEvFlavourRole ev2)
+We /do/ say that a [W] can discharge a [WD]. In evidence terms it
+certainly can, and the /caller/ arranges that the otherwise-lost [D]
+is spat out as a new Derived. -}
-eqCanRewriteFR :: CtFlavourRole -> CtFlavourRole -> Bool
-eqCanRewriteFR (Given, NomEq) (_, _) = True
-eqCanRewriteFR (Given, ReprEq) (_, ReprEq) = True
-eqCanRewriteFR (Derived, NomEq) (Derived, NomEq) = True
-eqCanRewriteFR _ _ = False
-
------------------
-funEqCanDischarge :: CtEvidence -> CtEvidence -> Bool
--- See Note [funEqCanDischarge]
-funEqCanDischarge ev1 ev2 = funEqCanDischargeFR (ctEvFlavourRole ev1)
- (ctEvFlavourRole ev2)
-
-funEqCanDischargeFR :: CtFlavourRole -> CtFlavourRole -> Bool
-funEqCanDischargeFR (_, ReprEq) (_, NomEq) = False
-funEqCanDischargeFR (Given, _) _ = True
-funEqCanDischargeFR (Wanted, _) (Wanted, _) = True
-funEqCanDischargeFR (Wanted, _) (Derived, _) = True
-funEqCanDischargeFR (Derived, _) (Derived, _) = True
-funEqCanDischargeFR _ _ = False
-
------------------
eqCanDischarge :: CtEvidence -> CtEvidence -> Bool
-- See Note [eqCanDischarge]
eqCanDischarge ev1 ev2 = eqCanDischargeFR (ctEvFlavourRole ev1)
(ctEvFlavourRole ev2)
+
eqCanDischargeFR :: CtFlavourRole -> CtFlavourRole -> Bool
-eqCanDischargeFR (_, ReprEq) (_, NomEq) = False
-eqCanDischargeFR (Given, _) (Given,_) = True
-eqCanDischargeFR (Wanted, _) (Wanted, _) = True
-eqCanDischargeFR (Derived, _) (Derived, _) = True
-eqCanDischargeFR _ _ = False
+eqCanDischargeFR (_, ReprEq) (_, NomEq) = False
+eqCanDischargeFR (f1,_) (f2, _) = eqCanDischargeF f1 f2
+
+eqCanDischargeF :: CtFlavour -> CtFlavour -> Bool
+eqCanDischargeF Given _ = True
+eqCanDischargeF (Wanted _) (Wanted _) = True
+eqCanDischargeF (Wanted WDeriv) Derived = True
+eqCanDischargeF Derived Derived = True
+eqCanDischargeF _ _ = False
+
{-
************************************************************************
diff --git a/compiler/typecheck/TcRules.hs b/compiler/typecheck/TcRules.hs
index 4cfccb6bf0..2983ccb612 100644
--- a/compiler/typecheck/TcRules.hs
+++ b/compiler/typecheck/TcRules.hs
@@ -287,16 +287,14 @@ EvVar for the coercion, fill the hole with the invented EvVar, and
then quantify over the EvVar. Not too tricky -- just some
impedence matching, really.
-Note [Simplify *derived* constraints]
+Note [Simplify cloned constraints]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At this stage, we're simplifying constraints only for insolubility
and for unification. Note that all the evidence is quickly discarded.
-We make this explicit by working over derived constraints, for which
-there is no evidence. Using derived constraints also prevents solved
-equalities from being written to coercion holes. If we don't do this,
+We use a clone of the real constraint. If we don't do this,
then RHS coercion-hole constraints get filled in, only to get filled
in *again* when solving the implications emitted from tcRule. That's
-terrible, so we avoid the problem by using derived constraints.
+terrible, so we avoid the problem by cloning the constraints.
-}
@@ -310,15 +308,16 @@ simplifyRule :: RuleName
simplifyRule name lhs_wanted rhs_wanted
= do { -- We allow ourselves to unify environment
-- variables: runTcS runs with topTcLevel
- ; tc_lvl <- getTcLevel
+ ; lhs_clone <- cloneWC lhs_wanted
+ ; rhs_clone <- cloneWC rhs_wanted
; insoluble <- runTcSDeriveds $
do { -- First solve the LHS and *then* solve the RHS
-- See Note [Solve order for RULES]
- -- See Note [Simplify *derived* constraints]
- lhs_resid <- solveWanteds $ toDerivedWC lhs_wanted
- ; rhs_resid <- solveWanteds $ toDerivedWC rhs_wanted
- ; return ( insolubleWC tc_lvl lhs_resid ||
- insolubleWC tc_lvl rhs_resid ) }
+ -- See Note [Simplify cloned constraints]
+ lhs_resid <- solveWanteds lhs_clone
+ ; rhs_resid <- solveWanteds rhs_clone
+ ; return ( insolubleWC lhs_resid ||
+ insolubleWC rhs_resid ) }
; zonked_lhs_simples <- zonkSimples (wc_simple lhs_wanted)
diff --git a/compiler/typecheck/TcSMonad.hs b/compiler/typecheck/TcSMonad.hs
index 0d20122553..dd9a82ba03 100644
--- a/compiler/typecheck/TcSMonad.hs
+++ b/compiler/typecheck/TcSMonad.hs
@@ -49,8 +49,9 @@ module TcSMonad (
InertSet(..), InertCans(..),
updInertTcS, updInertCans, updInertDicts, updInertIrreds,
getNoGivenEqs, setInertCans,
- getInertEqs, getInertCans, getInertModel, getInertGivens,
- emptyInert, getTcSInerts, setTcSInerts, takeGivenInsolubles,
+ getInertEqs, getInertCans, getInertGivens,
+ getInertInsols,
+ emptyInert, getTcSInerts, setTcSInerts,
matchableGivens, prohibitedSuperClassSolve,
getUnsolvedInerts,
removeInertCts, getPendingScDicts,
@@ -58,7 +59,7 @@ module TcSMonad (
emitInsoluble, emitWorkNC, emitWork,
-- The Model
- InertModel, kickOutAfterUnification,
+ kickOutAfterUnification,
-- Inert Safe Haskell safe-overlap failures
addInertSafehask, insertSafeOverlapFailureTcS, updInertSafehask,
@@ -70,6 +71,7 @@ module TcSMonad (
-- Inert CTyEqCans
EqualCtList, findTyEqs, foldTyEqs, isInInertEqs,
+ lookupFlattenTyVar, lookupInertTyVar,
-- Inert solved dictionaries
addSolvedDict, lookupSolvedDict,
@@ -81,7 +83,7 @@ module TcSMonad (
lookupFlatCache, extendFlatCache, newFlattenSkolem, -- Flatten skolems
-- Inert CFunEqCans
- updInertFunEqs, findFunEq, sizeFunEqMap,
+ updInertFunEqs, findFunEq,
findFunEqsByTyCon,
instDFunType, -- Instantiation
@@ -341,8 +343,7 @@ selectNextWorkItem
; try (selectWorkItem wl) $
do { ics <- getInertCans
- ; solve_deriveds <- keepSolvingDeriveds
- ; if inert_count ics == 0 && not solve_deriveds
+ ; if inert_count ics == 0
then return Nothing
else try (selectDerivedWorkItem wl) (return Nothing) } }
@@ -375,13 +376,14 @@ instance Outputable WorkList where
data InertSet
= IS { inert_cans :: InertCans
- -- Canonical Given, Wanted, Derived (no Solved)
+ -- Canonical Given, Wanted, Derived
-- Sometimes called "the inert set"
, inert_flat_cache :: ExactFunEqMap (TcCoercion, TcType, CtFlavour)
-- See Note [Type family equations]
- -- If F tys :-> (co, ty, ev),
- -- then co :: F tys ~ ty
+ -- If F tys :-> (co, rhs, flav),
+ -- then co :: F tys ~ rhs
+ -- flav is [G] or [WD]
--
-- Just a hash-cons cache for use when flattening only
-- These include entirely un-processed goals, so don't use
@@ -410,8 +412,7 @@ emptyInert
, inert_safehask = emptyDicts
, inert_funeqs = emptyFunEqs
, inert_irreds = emptyCts
- , inert_insols = emptyCts
- , inert_model = emptyDVarEnv }
+ , inert_insols = emptyCts }
, inert_flat_cache = emptyExactFunEqs
, inert_solved_dicts = emptyDictMap }
@@ -584,26 +585,23 @@ Result
********************************************************************* -}
data InertCans -- See Note [Detailed InertCans Invariants] for more
- = IC { inert_model :: InertModel
- -- See Note [inert_model: the inert model]
-
- , inert_eqs :: DTyVarEnv EqualCtList
+ = IC { inert_eqs :: InertEqs
-- See Note [inert_eqs: the inert equalities]
- -- All Given/Wanted CTyEqCans; index is the LHS tyvar
+ -- All CTyEqCans; index is the LHS tyvar
+ -- Domain = skolems and untouchables; a touchable would be unified
, inert_funeqs :: FunEqMap Ct
-- All CFunEqCans; index is the whole family head type.
-- All Nominal (that's an invarint of all CFunEqCans)
-- LHS is fully rewritten (modulo eqCanRewrite constraints)
- -- wrt inert_eqs/inert_model
- -- We can get Derived ones from e.g.
- -- (a) flattening derived equalities
- -- (b) emitDerivedShadows
+ -- wrt inert_eqs
+ -- Can include all flavours, [G], [W], [WD], [D]
+ -- See Note [Type family equations]
, inert_dicts :: DictMap Ct
-- Dictionaries only
-- All fully rewritten (modulo flavour constraints)
- -- wrt inert_eqs/inert_model
+ -- wrt inert_eqs
, inert_safehask :: DictMap Ct
-- Failed dictionary resolution due to Safe Haskell overlapping
@@ -627,15 +625,11 @@ data InertCans -- See Note [Detailed InertCans Invariants] for more
-- When non-zero, keep trying to solved
}
-type InertModel = DTyVarEnv Ct
- -- If a -> ct, then ct is a
- -- nominal, Derived, canonical CTyEqCan for [D] (a ~N rhs)
- -- The index of the TyVarEnv is the 'a'
- -- All saturated info for Given, Wanted, Derived is here
-
+type InertEqs = DTyVarEnv EqualCtList
+type EqualCtList = [Ct] -- See Note [EqualCtList invariants]
{- Note [Detailed InertCans Invariants]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The InertCans represents a collection of constraints with the following properties:
* All canonical
@@ -657,69 +651,58 @@ The InertCans represents a collection of constraints with the following properti
* CTyEqCan equalities: see Note [Applying the inert substitution]
in TcFlatten
+Note [EqualCtList invariants]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * All are equalities
+ * All these equalities have the same LHS
+ * The list is never empty
+ * No element of the list can rewrite any other
+ * Derived before Wanted
+
+From the fourth invariant it follows that the list is
+ - A single [G], or
+ - Zero or one [D] or [WD], followd by any number of [W]
+
+The Wanteds can't rewrite anything which is why we put them last
+
Note [Type family equations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Type-family equations, of form (ev : F tys ~ ty), live in three places
+Type-family equations, CFunEqCans, of form (ev : F tys ~ ty),
+live in three places
* The work-list, of course
+ * The inert_funeqs are un-solved but fully processed, and in
+ the InertCans. They can be [G], [W], [WD], or [D].
+
* The inert_flat_cache. This is used when flattening, to get maximal
- sharing. It contains lots of things that are still in the work-list.
+ sharing. Everthing in the inert_flat_cache is [G] or [WD]
+
+ It contains lots of things that are still in the work-list.
E.g Suppose we have (w1: F (G a) ~ Int), and (w2: H (G a) ~ Int) in the
work list. Then we flatten w1, dumping (w3: G a ~ f1) in the work
list. Now if we flatten w2 before we get to w3, we still want to
share that (G a).
-
Because it contains work-list things, DO NOT use the flat cache to solve
a top-level goal. Eg in the above example we don't want to solve w3
using w3 itself!
- * The inert_funeqs are un-solved but fully processed and in the InertCans.
+The CFunEqCan Ownership Invariant:
-Note [inert_model: the inert model]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* Part of the inert set is the “model”
+ * Each [G/W/WD] CFunEqCan has a distinct fsk or fmv
+ It "owns" that fsk/fmv, in the sense that:
+ - reducing a [W/WD] CFunEqCan fills in the fmv
+ - unflattening a [W/WD] CFunEqCan fills in the fmv
+ (in both cases unless an occurs-check would result)
- * The “Model” is an non-idempotent but no-occurs-check
- substitution, reflecting *all* *Nominal* equalities (a ~N ty)
- that are not immediately soluble by unification.
+ * In contrast a [D] CFunEqCan does not "own" its fmv:
+ - reducing a [D] CFunEqCan does not fill in the fmv;
+ it just generates an equality
+ - unflattening ignores [D] CFunEqCans altogether
- * All the constraints in the model are Derived CTyEqCans
- That is if (a -> ty) is in the model, then
- we have an inert constraint [D] a ~N ty.
-
- * There are two sources of constraints in the model:
-
- - Derived constraints arising from functional dependencies, or
- decomposing injective arguments of type functions, and
- suchlike.
-
- - A Derived "shadow copy" for every Wanted (a ~N ty) in
- inert_eqs. (Originally included every Given too; but
- see Note [Add derived shadows only for Wanteds])
-
- * The model is not subject to "kicking-out". Reason: we make a Derived
- shadow copy of any Wanted (a ~ ty), and that Derived copy will
- be fully rewritten by the model before it is added
-
- * The principal reason for maintaining the model is to generate
- equalities that tell us how to unify a variable: that is, what
- Mark Jones calls "improvement". The same idea is sometimes also
- called "saturation"; find all the equalities that must hold in
- any solution.
-
- * Domain of the model = skolems + untouchables.
- A touchable unification variable would have been unified first.
-
- * The inert_eqs are all Given/Wanted. The Derived ones are in the
- inert_model only.
-
- * However inert_dicts, inert_funeqs, inert_irreds
- may well contain derived constraints.
Note [inert_eqs: the inert equalities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
Definition [Can-rewrite relation]
A "can-rewrite" relation between flavours, written f1 >= f2, is a
binary relation with the following properties
@@ -937,26 +920,6 @@ inerts whenever the tyvar of a work item is "exposed", where exposed means
not under some proper data-type constructor, like [] or Maybe. See
isTyVarExposed in TcType. This is encoded in (K3b).
-Note [Stability of flattening]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The inert_eqs and inert_model, *considered separately* are each stable;
-that is, substituting using them will terminate. Considered *together*
-they are not. E.g.
-
- Add: [G] a~[b] to inert set with model [D] b~[a]
-
- We add [G] a~[b] to inert_eqs, and emit [D] a~[b]. At this point
- the combination of inert_eqs and inert_model is not stable.
-
- Then we canonicalise [D] a~[b] to [D] a~[[a]], and add that to
- insolubles as an occurs check.
-
-* When canonicalizing, the flattener respects flavours. In particular,
- when flattening a type variable 'a':
- * Derived: look up 'a' in the inert_model
- * Given/Wanted: look up 'a' in the inert_eqs
-
-
Note [Flavours with roles]
~~~~~~~~~~~~~~~~~~~~~~~~~~
The system described in Note [inert_eqs: the inert equalities]
@@ -979,126 +942,10 @@ T Int Bool. The reason is that T's first parameter has a nominal role, and
thus rewriting a to Int in T a b is wrong. Indeed, this non-congruence of
substitution means that the proof in Note [The inert equalities] may need
to be revisited, but we don't think that the end conclusion is wrong.
-
-Note [Examples of how the inert_model helps completeness]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
------------ Example 2 (indexed-types/should_fail/T4093a)
- Ambiguity check for f: (Foo e ~ Maybe e) => Foo e
-
- We get [G] Foo e ~ Maybe e
- [W] Foo e ~ Foo ee -- ee is a unification variable
- [W] Foo ee ~ Maybe ee
-
- Flatten: [G] Foo e ~ fsk
- [G] fsk ~ Maybe e -- (A)
-
- [W] Foo ee ~ fmv
- [W] fmv ~ fsk -- (B) From Foo e ~ Foo ee
- [W] fmv ~ Maybe ee
-
- --> rewrite (B) with (A)
- [W] Foo ee ~ fmv
- [W] fmv ~ Maybe e
- [W] fmv ~ Maybe ee
-
- But now awe appear to be stuck, since we don't rewrite Wanteds with
- Wanteds. But inert_model to the rescue. In the model we first added
- fmv -> Maybe e
- Then when adding [W] fmv -> Maybe ee to the inert set, we noticed
- that the model can rewrite the constraint, and so emit [D] fmv ~ Maybe ee.
- That canonicalises to
- [D] Maybe e ~ Maybe ee
- and that soon yields ee := e, and all is well
-
------------ Example 3 (typecheck/should_compile/Improvement.hs)
- type instance F Int = Bool
- instance (b~Int) => C Bool b
-
- [W] w1 : C (F alpha) alpha, [W] w2 : F alpha ~ Bool
-
- If we rewrote wanteds with wanteds, we could rewrite w1 to
- C Bool alpha, use the instance to get alpha ~ Int, and solve
- the whole thing.
-
- And that is exactly what happens, in the *Derived* constraints.
- In effect we get
-
- [D] F alpha ~ fmv
- [D] C fmv alpha
- [D] fmv ~ Bool
-
- and now we can rewrite (C fmv alpha) with (fmv ~ Bool), ane
- we are off to the races.
-
------------ Example 4 (Trac #10009, a nasty example):
-
- f :: (UnF (F b) ~ b) => F b -> ()
-
- g :: forall a. (UnF (F a) ~ a) => a -> ()
- g _ = f (undefined :: F a)
-
- For g we get [G] UnF (F a) ~ a
- [W] UnF (F beta) ~ beta
- [W] F a ~ F beta
- Flatten:
- [G] g1: F a ~ fsk1 fsk1 := F a
- [G] g2: UnF fsk1 ~ fsk2 fsk2 := UnF fsk1
- [G] g3: fsk2 ~ a
-
- [W] w1: F beta ~ fmv1
- [W] w2: UnF fmv1 ~ fmv2
- [W] w3: beta ~ fmv2
- [W] w5: fmv1 ~ fsk1 -- From F a ~ F beta using flat-cache
- -- and re-orient to put meta-var on left
-
- Unify beta := fmv2
- [W] w1: F fmv2 ~ fmv1
- [W] w2: UnF fmv1 ~ fmv2
- [W] w5: fmv1 ~ fsk1
-
- In the model, we have the shadow Deriveds of w1 and w2
- (I name them for convenience even though they are anonymous)
- [D] d1: F fmv2 ~ fmv1d
- [D] d2: fmv1d ~ fmv1
- [D] d3: UnF fmv1 ~ fmv2d
- [D] d4: fmv2d ~ fmv2
-
- Now we can rewrite d3 with w5, and match with g2, to get
- fmv2d := fsk2
- [D] d1: F fmv2 ~ fmv1d
- [D] d2: fmv1d ~ fmv1
- [D] d4: fmv2 ~ fsk2
-
- Use g2 to rewrite fsk2 to a.
- [D] d1: F fmv2 ~ fmv1d
- [D] d2: fmv1d ~ fmv1
- [D] d4: fmv2 ~ a
-
- Use d4 to rewrite d1, rewrite with g3,
- match with g1, to get
- fmv1d := fsk1
- [D] d2: fmv1 ~ fsk1
- [D] d4: fmv2 ~ a
-
- At this point we are stuck so we unflatten this set:
- See Note [Orientation of equalities with fmvs] in TcFlatten
- [W] w1: F fmv2 ~ fmv1
- [W] w2: UnF fmv1 ~ fmv2
- [W] w5: fmv1 ~ fsk1
- [D] d4: fmv2 ~ a
-
- Unflattening will discharge w1: fmv1 := F fmv2
- It can't discharge w2, so it is kept. But we can
- unify fmv2 := fsk2, and that is "progress". Result
- [W] w2: UnF (F a) ~ a
- [W] w5: F a ~ fsk1
-
- And now both of these are easily proved in the next iteration. Phew!
-}
instance Outputable InertCans where
- ppr (IC { inert_model = model, inert_eqs = eqs
+ ppr (IC { inert_eqs = eqs
, inert_funeqs = funeqs, inert_dicts = dicts
, inert_safehask = safehask, inert_irreds = irreds
, inert_insols = insols, inert_count = count })
@@ -1116,218 +963,160 @@ instance Outputable InertCans where
text "Irreds =" <+> pprCts irreds
, ppUnless (isEmptyCts insols) $
text "Insolubles =" <+> pprCts insols
- , ppUnless (isEmptyDVarEnv model) $
- text "Model =" <+> pprCts (foldDVarEnv consCts emptyCts model)
, text "Unsolved goals =" <+> int count
]
{- *********************************************************************
* *
- Adding an inert
+ Shadow constraints and improvement
* *
************************************************************************
-Note [Adding an inert canonical constraint the InertCans]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* Adding any constraint c *other* than a CTyEqCan (TcSMonad.addInertCan):
-
- * If c can be rewritten by model, emit the shadow constraint [D] c
- as NonCanonical. See Note [Emitting shadow constraints]
-
- * Reason for non-canonical: a CFunEqCan has a unique fmv on the RHS,
- so we must not duplicate it.
-
-* Adding a *nominal* CTyEqCan (a ~N ty) to the inert set (TcSMonad.addInertEq).
-
- (A) Always (G/W/D) kick out constraints that can be rewritten
- (respecting flavours) by the new constraint. This is done
- by kickOutRewritable.
-
- (B) Applies only to nominal equalities: a ~ ty. Four cases:
-
- [Representational] [G/W/D] a ~R ty:
- Just add it to inert_eqs
-
- [Derived Nominal] [D] a ~N ty:
- 1. Add (a~ty) to the model
- NB: 'a' cannot be in fv(ty), because the constraint is canonical.
-
- 2. (DShadow) Do emitDerivedShadows
- For every inert [W] constraint c, st
- (a) (a~ty) can rewrite c (see Note [Emitting shadow constraints]),
- and
- (b) the model cannot rewrite c
- kick out a Derived *copy*, leaving the original unchanged.
- Reason for (b) if the model can rewrite c, then we have already
- generated a shadow copy
- See Note [Add derived shadows only for Wanteds]
-
- [Given/Wanted Nominal] [G/W] a ~N ty:
- 1. Add it to inert_eqs
- 2. For [W], Emit [D] a~ty
- Step (2) is needed to allow the current model to fully
- rewrite [D] a~ty before adding it using the [Derived Nominal]
- steps above.
- See Note [Add derived shadows only for Wanteds]
-
-* Unifying a:=ty, is like adding [G] a~ty, but we can't make a [D]
- a~ty, as in step (1) of the [G/W] case above. So instead, do
- kickOutAfterUnification:
- - Kick out from the model any equality (b~ty2) that mentions 'a'
- (i.e. a=b or a in ty2). Example:
- [G] a ~ [b], model [D] b ~ [a]
-
-Note [Emitting shadow constraints]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * Given a new model element [D] a ~ ty, we want to emit shadow
- [D] constraints for any inert constraints 'c' that can be
- rewritten [D] a-> ty
+Note [The improvement story and derived shadows]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Because Wanteds cannot rewrite Wanteds (see Note [Wanteds do not
+rewrite Wanteds] in TcRnTypes), we may miss some opportunities for
+solving. Here's a classic example (indexed-types/should_fail/T4093a)
- * And similarly given a new Given/Wanted 'c', we want to emit a
- shadow 'c' if the model can rewrite [D] c
+ Ambiguity check for f: (Foo e ~ Maybe e) => Foo e
-See modelCanRewrite.
+ We get [G] Foo e ~ Maybe e
+ [W] Foo e ~ Foo ee -- ee is a unification variable
+ [W] Foo ee ~ Maybe ee
-NB the use of rewritableTyVars. You might wonder whether, given the new
-constraint [D] fmv ~ ty and the inert [W] F alpha ~ fmv, do we want to
-emit a shadow constraint [D] F alpha ~ fmv? No, we don't, because
-it'll literally be a duplicate (since we do not rewrite the RHS of a
-CFunEqCan) and hence immediately eliminated again. Insetad we simply
-want to *kick-out* the [W] F alpha ~ fmv, so that it is reconsidered
-from a fudep point of view. See Note [Kicking out CFunEqCan for
-fundeps]
+ Flatten: [G] Foo e ~ fsk
+ [G] fsk ~ Maybe e -- (A)
-Note [Kicking out CFunEqCan for fundeps]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Consider:
- New: [D] fmv1 ~ fmv2
- Inert: [W] F alpha ~ fmv1
- [W] F beta ~ fmv2
+ [W] Foo ee ~ fmv
+ [W] fmv ~ fsk -- (B) From Foo e ~ Foo ee
+ [W] fmv ~ Maybe ee
-The new (derived) equality certainly can't rewrite the inerts. But we
-*must* kick out the first one, to get:
+ --> rewrite (B) with (A)
+ [W] Foo ee ~ fmv
+ [W] fmv ~ Maybe e
+ [W] fmv ~ Maybe ee
- New: [W] F alpha ~ fmv1
- Inert: [W] F beta ~ fmv2
- Model: [D] fmv1 ~ fmv2
+ But now we appear to be stuck, since we don't rewrite Wanteds with
+ Wanteds. This is silly because we can see that ee := e is the
+ only solution.
-and now improvement will discover [D] alpha ~ beta. This is important;
-eg in Trac #9587.
--}
+The basic plan is
+ * generate Derived constraints that shadow Wanted constraints
+ * allow Derived to rewrite Derived
+ * in order to cause some unifications to take place
+ * that in turn solve the original Wanteds
-addInertEq :: Ct -> TcS ()
--- This is a key function, because of the kick-out stuff
--- Precondition: item /is/ canonical
-addInertEq ct@(CTyEqCan { cc_tyvar = tv })
- = do { traceTcS "addInertEq {" $
- text "Adding new inert equality:" <+> ppr ct
- ; ics <- getInertCans
+The ONLY reason for all these Derived equalities is to tell us how to
+unify a variable: that is, what Mark Jones calls "improvement".
- ; let (kicked_out, ics1) = kickOutRewritable (ctFlavourRole ct) tv ics
- ; ics2 <- add_inert_eq ics1 ct
+The same idea is sometimes also called "saturation"; find all the
+equalities that must hold in any solution.
- ; setInertCans ics2
+Or, equivalently, you can think of the derived shadows as implementing
+the "model": an non-idempotent but no-occurs-check substitution,
+reflecting *all* *Nominal* equalities (a ~N ty) that are not
+immediately soluble by unification.
- ; unless (isEmptyWorkList kicked_out) $
- do { updWorkListTcS (appendWorkList kicked_out)
- ; csTraceTcS $
- hang (text "Kick out, tv =" <+> ppr tv)
- 2 (vcat [ text "n-kicked =" <+> int (workListSize kicked_out)
- , ppr kicked_out ]) }
+More specifically, here's how it works (Oct 16):
- ; traceTcS "addInertEq }" $ empty }
-addInertEq ct = pprPanic "addInertEq" (ppr ct)
-
-add_inert_eq :: InertCans -> Ct -> TcS InertCans
-add_inert_eq ics@(IC { inert_count = n
- , inert_eqs = old_eqs
- , inert_model = old_model })
- ct@(CTyEqCan { cc_ev = ev, cc_eq_rel = eq_rel, cc_tyvar = tv
- , cc_rhs = _rhs })
- | ReprEq <- eq_rel
- = return new_ics
-
- | isDerived ev
- = do { emitDerivedShadows ics tv
- ; return (ics { inert_model = extendDVarEnv old_model tv ct }) }
-
- | otherwise -- Given/Wanted Nominal equality [W] tv ~N ty
- = do { emitNewDerived loc pred
- ; return new_ics }
- where
- loc = ctEvLoc ev
- pred = ctEvPred ev
- new_ics = ics { inert_eqs = addTyEq old_eqs tv ct
- , inert_count = bumpUnsolvedCount ev n }
-
-add_inert_eq _ ct = pprPanic "addInertEq" (ppr ct)
-
-emitDerivedShadows :: InertCans -> TcTyVar -> TcS ()
-emitDerivedShadows IC { inert_eqs = tv_eqs
- , inert_dicts = dicts
- , inert_safehask = safehask
- , inert_funeqs = funeqs
- , inert_irreds = irreds
- , inert_model = model } new_tv
- | null shadows
- = return ()
- | otherwise
- = do { traceTcS "Emit derived shadows:" $
- vcat [ text "tyvar =" <+> ppr new_tv
- , text "shadows =" <+> vcat (map ppr shadows) ]
- ; emitWork shadows }
- where
- shadows = foldDicts get_ct dicts $
- foldDicts get_ct safehask $
- foldFunEqs get_ct funeqs $
- foldIrreds get_ct irreds $
- foldTyEqs get_ct tv_eqs []
- -- Ignore insolubles
-
- get_ct ct cts | want_shadow ct = mkShadowCt ct : cts
- | otherwise = cts
-
- want_shadow ct
- = isWantedCt ct -- See Note [Add shadows only for Wanteds]
- && (new_tv `elemVarSet` rw_tvs) -- New tv can rewrite ct, yielding a
- -- different ct
- && not (modelCanRewrite model rw_tvs)-- We have not already created a
- -- shadow
- where
- rw_tvs = rewritableTyCoVars ct
-
-mkShadowCt :: Ct -> Ct
--- Produce a Derived shadow constraint from the input
--- If it is a CFunEqCan, make it NonCanonical, to avoid
--- duplicating the flatten-skolems
--- Otherwise keep the canonical shape. This just saves work, but
--- is sometimes important; see Note [Keep CDictCan shadows as CDictCan]
-mkShadowCt ct
- | CFunEqCan {} <- ct = CNonCanonical { cc_ev = derived_ev }
- | otherwise = ct { cc_ev = derived_ev }
- where
- ev = ctEvidence ct
- derived_ev = CtDerived { ctev_pred = ctEvPred ev
- , ctev_loc = ctEvLoc ev }
+* Wanted constraints are born as [WD]; this behaves like a
+ [W] and a [D] paired together.
-{- Note [Add derived shadows only for Wanteds]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We now only add shadows for Wanted constraints. Why add derived
-shadows for Givens? After all, Givens can rewrite Deriveds. But
-Deriveds can't rewrite Givens. So in principle, if we created a
-Derived shadow of a Given, it could be rewritten by other Deriveds,
-and that could, conceivably, lead to a useful unification.
+* When we are about to add a [WD] to the inert set, if it can
+ be rewritten by a [D] a ~ ty, then we split it into [W] and [D],
+ putting the latter into the work list (see maybeEmitShadow).
+
+In the example above, we get to the point where we are stuck:
+ [WD] Foo ee ~ fmv
+ [WD] fmv ~ Maybe e
+ [WD] fmv ~ Maybe ee
+
+But now when [WD] fmv ~ Maybe ee is about to be added, we'll
+split it into [W] and [D], since the inert [WD] fmv ~ Maybe e
+can rewrite it. Then:
+ work item: [D] fmv ~ Maybe ee
+ inert: [W] fmv ~ Maybe ee
+ [WD] fmv ~ Maybe e -- (C)
+ [WD] Foo ee ~ fmv
+
+Now the work item is rewritten by (C) and we soon get ee := e.
+
+Additional notes:
+
+ * The derived shadow equalities live in inert_eqs, along with
+ the Givens and Wanteds; see Note [EqualCtList invariants].
+
+ * We make Derived shadows only for Wanteds, not Givens. So we
+ have only [G], not [GD] and [G] plus splitting. See
+ Note [Add derived shadows only for Wanteds]
+
+ * We also get Derived equalities from functional dependencies
+ and type-function injectivity; see calls to unifyDerived.
+
+ * This splitting business applies to CFunEqCans too; and then
+ we do apply type-function reductions to the [D] CFunEqCan.
+ See Note [Reduction for Derived CFunEqCans]
+
+ * It's worth having [WD] rather than just [W] and [D] because
+ * efficiency: silly to process the same thing twice
+ * inert_funeqs, inert_dicts is a finite map keyed by
+ the type; it's inconvenient for it to map to TWO constraints
+
+Note [Examples of how Derived shadows helps completeness]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Trac #10009, a very nasty example:
+
+ f :: (UnF (F b) ~ b) => F b -> ()
+
+ g :: forall a. (UnF (F a) ~ a) => a -> ()
+ g _ = f (undefined :: F a)
+
+ For g we get [G] UnF (F a) ~ a
+ [WD] UnF (F beta) ~ beta
+ [WD] F a ~ F beta
+ Flatten:
+ [G] g1: F a ~ fsk1 fsk1 := F a
+ [G] g2: UnF fsk1 ~ fsk2 fsk2 := UnF fsk1
+ [G] g3: fsk2 ~ a
+
+ [WD] w1: F beta ~ fmv1
+ [WD] w2: UnF fmv1 ~ fmv2
+ [WD] w3: fmv2 ~ beta
+ [WD] w4: fmv1 ~ fsk1 -- From F a ~ F beta using flat-cache
+ -- and re-orient to put meta-var on left
+
+Rewrite w2 with w4: [D] d1: UnF fsk1 ~ fmv2
+React that with g2: [D] d2: fmv2 ~ fsk2
+React that with w3: [D] beta ~ fsk2
+ and g3: [D] beta ~ a -- Hooray beta := a
+And that is enough to solve everything
+
+Note [Add derived shadows only for Wanteds]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We only add shadows for Wanted constraints. That is, we have
+[WD] but not [GD]; and maybeEmitShaodw looks only at [WD]
+constraints.
+
+It does just possibly make sense ot add a derived shadow for a
+Given. If we created a Derived shadow of a Given, it could be
+rewritten by other Deriveds, and that could, conceivably, lead to a
+useful unification.
But (a) I have been unable to come up with an example of this
-happening and (b) see Trac #12660 for how adding the derived shadows
-of a Given led to an infinite loop. For (b) there may be other
-ways to solve the loop, but simply reraining from adding
-derived shadows of Givens is particularly simple. And it's more
-efficient too!
+ happening
+ (b) see Trac #12660 for how adding the derived shadows
+ of a Given led to an infinite loop.
+ (c) It's unlikely that rewriting derived Givens will lead
+ to a unification becuse Givens don't mention touchable
+ unification variables
+
+For (b) there may be other ways to solve the loop, but simply
+reraining from adding derived shadows of Givens is particularly
+simple. And it's more efficient too!
Still, here's one possible reason for adding derived shadows
for Givens. Consider
- work-item [G] a ~ [b], model has [D] b ~ a.
+ work-item [G] a ~ [b], inerts has [D] b ~ a.
If we added the derived shadow (into the work list)
[D] a ~ [b]
When we process it, we'll rewrite to a ~ [a] and get an
@@ -1358,36 +1147,178 @@ generating superclasses repeatedly will fail.
See Trac #11379 for a case of this.
-}
-modelCanRewrite :: InertModel -> TcTyCoVarSet -> Bool
--- See Note [Emitting shadow constraints]
--- True if there is any intersection between dom(model) and tvs
-modelCanRewrite model tvs = not (disjointUdfmUfm model tvs)
- -- The low-level use of disjointUFM might e surprising.
- -- InertModel = TyVarEnv Ct, and we want to see if its domain
- -- is disjoint from that of a TcTyCoVarSet. So we drop down
- -- to the underlying UniqFM. A bit yukky, but efficient.
+maybeEmitShadow :: InertCans -> Ct -> TcS Ct
+-- See Note [The improvement story and derived shadows]
+maybeEmitShadow ics ct
+ | let ev = ctEvidence ct
+ , CtWanted { ctev_pred = pred, ctev_loc = loc
+ , ctev_nosh = WDeriv } <- ev
+ , shouldSplitWD (inert_eqs ics) ct
+ = do { traceTcS "Emit derived shadow" (ppr ct)
+ ; let derived_ev = CtDerived { ctev_pred = pred
+ , ctev_loc = loc }
+ shadow_ct = ct { cc_ev = derived_ev }
+ -- Te shadow constraint keeps the canonical shape.
+ -- This just saves work, but is sometimes important;
+ -- see Note [Keep CDictCan shadows as CDictCan]
+ ; emitWork [shadow_ct]
+
+ ; let ev' = ev { ctev_nosh = WOnly }
+ ct' = ct { cc_ev = ev' }
+ -- Record that it now has a shadow
+ -- This is /the/ place we set the flag to WOnly
+ ; return ct' }
+
+ | otherwise
+ = return ct
+
+shouldSplitWD :: InertEqs -> Ct -> Bool
+-- Precondition: 'ct' is [WD], and is inert
+-- True <=> we should split ct ito [W] and [D] because
+-- the inert_eqs can make progress on the [D]
+
+shouldSplitWD inert_eqs (CFunEqCan { cc_tyargs = tys })
+ = inert_eqs `intersects_with` rewritableTyVarsOfTypes tys
+ -- We don't need to split if the tv is the RHS fsk
+
+shouldSplitWD inert_eqs ct
+ = inert_eqs `intersects_with` rewritableTyVarsOfType (ctPred ct)
+ -- Otherwise split if the tv is mentioned at all
+
+intersects_with :: InertEqs -> TcTyVarSet -> Bool
+intersects_with inert_eqs free_vars
+ = not (disjointUdfmUfm inert_eqs free_vars)
+ -- Test whether the inert equalities could rewrite
+ -- a derived version of this constraint
+ -- The low-level use of disjointUFM might seem surprising.
+ -- InertEqs = TyVarEnv EqualCtList, and we want to see if its domain
+ -- is disjoint from that of a TcTyCoVarSet. So we drop down
+ -- to the underlying UniqFM. A bit yukky, but efficient.
+
+
+{- *********************************************************************
+* *
+ Inert equalities
+* *
+********************************************************************* -}
+
+addTyEq :: InertEqs -> TcTyVar -> Ct -> InertEqs
+addTyEq old_eqs tv ct
+ = extendDVarEnv_C add_eq old_eqs tv [ct]
+ where
+ add_eq old_eqs _
+ | isWantedCt ct
+ , (eq1 : eqs) <- old_eqs
+ = eq1 : ct : eqs
+ | otherwise
+ = ct : old_eqs
+
+foldTyEqs :: (Ct -> b -> b) -> InertEqs -> b -> b
+foldTyEqs k eqs z
+ = foldDVarEnv (\cts z -> foldr k z cts) z eqs
+
+findTyEqs :: InertCans -> TyVar -> EqualCtList
+findTyEqs icans tv = lookupDVarEnv (inert_eqs icans) tv `orElse` []
+
+delTyEq :: InertEqs -> TcTyVar -> TcType -> InertEqs
+delTyEq m tv t = modifyDVarEnv (filter (not . isThisOne)) m tv
+ where isThisOne (CTyEqCan { cc_rhs = t1 }) = eqType t t1
+ isThisOne _ = False
+
+lookupInertTyVar :: InertEqs -> TcTyVar -> Maybe TcType
+lookupInertTyVar ieqs tv
+ = case lookupDVarEnv ieqs tv of
+ Just (CTyEqCan { cc_rhs = rhs, cc_eq_rel = NomEq } : _ ) -> Just rhs
+ _ -> Nothing
+
+lookupFlattenTyVar :: InertEqs -> TcTyVar -> TcType
+-- See Note [lookupFlattenTyVar]
+lookupFlattenTyVar ieqs ftv
+ = lookupInertTyVar ieqs ftv `orElse` mkTyVarTy ftv
+
+{- Note [lookupFlattenTyVar]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Supppose we have an injective function F and
+ inert_funeqs: F t1 ~ fsk1
+ F t2 ~ fsk2
+ inert_eqs: fsk1 ~ fsk2
+
+We never rewrite the RHS (cc_fsk) of a CFunEqCan. But we /do/ want to
+get the [D] t1 ~ t2 from the injectiveness of F. So we look up the
+cc_fsk of CFunEqCans in the inert_eqs when trying to find derived
+equalities arising from injectivity.
+-}
+
-rewritableTyCoVars :: Ct -> TcTyVarSet
--- The tyvars of a Ct that can be rewritten
-rewritableTyCoVars (CFunEqCan { cc_tyargs = tys }) = tyCoVarsOfTypes tys
-rewritableTyCoVars ct = tyCoVarsOfType (ctPred ct)
+{- *********************************************************************
+* *
+ Adding an inert
+* *
+************************************************************************
+
+Note [Adding an equality to the InertCans]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When adding an equality to the inerts:
+
+* Split [WD] into [W] and [D] if the inerts can rewrite the latter;
+ done by maybeEmitShadow.
+
+* Kick out any constraints that can be rewritten by the thing
+ we are adding. Done by kickOutRewritable.
+
+* Note that unifying a:=ty, is like adding [G] a~ty; just use
+ kickOutRewritable with Nominal, Given. See kickOutAfterUnification.
+
+Note [Kicking out CFunEqCan for fundeps]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider:
+ New: [D] fmv1 ~ fmv2
+ Inert: [W] F alpha ~ fmv1
+ [W] F beta ~ fmv2
+
+where F is injective. The new (derived) equality certainly can't
+rewrite the inerts. But we *must* kick out the first one, to get:
+
+ New: [W] F alpha ~ fmv1
+ Inert: [W] F beta ~ fmv2
+ [D] fmv1 ~ fmv2
+
+and now improvement will discover [D] alpha ~ beta. This is important;
+eg in Trac #9587.
+
+So in kickOutRewritable we look at all the tyvars of the
+CFunEqCan, including the fsk.
+-}
+
+addInertEq :: Ct -> TcS ()
+-- This is a key function, because of the kick-out stuff
+-- Precondition: item /is/ canonical
+-- See Note [Adding an equality to the InertCans]
+addInertEq ct
+ = do { traceTcS "addInertEq {" $
+ text "Adding new inert equality:" <+> ppr ct
+ ; ics <- getInertCans
+
+ ; ct@(CTyEqCan { cc_tyvar = tv, cc_ev = ev }) <- maybeEmitShadow ics ct
+
+ ; (_, ics1) <- kickOutRewritable (ctEvFlavourRole ev) tv ics
+
+ ; let ics2 = ics1 { inert_eqs = addTyEq (inert_eqs ics1) tv ct
+ , inert_count = bumpUnsolvedCount ev (inert_count ics1) }
+ ; setInertCans ics2
+
+ ; traceTcS "addInertEq }" $ empty }
--------------
addInertCan :: Ct -> TcS () -- Constraints *other than* equalities
addInertCan ct
= do { traceTcS "insertInertCan {" $
- text "Trying to insert new inert item:" <+> ppr ct
+ text "Trying to insert new non-eq inert item:" <+> ppr ct
; ics <- getInertCans
+ ; ct <- maybeEmitShadow ics ct
; setInertCans (add_item ics ct)
- -- Emit shadow derived if necessary
- -- See Note [Emitting shadow constraints]
- ; let rw_tvs = rewritableTyCoVars ct
- ; when (isWantedCt ct && modelCanRewrite (inert_model ics) rw_tvs)
- -- See Note [Add shadows only for Wanteds]
- (emitWork [mkShadowCt ct])
-
; traceTcS "addInertCan }" $ empty }
add_item :: InertCans -> Ct -> InertCans
@@ -1419,21 +1350,39 @@ bumpUnsolvedCount ev n | isWanted ev = n+1
-----------------------------------------
-kickOutRewritable :: CtFlavourRole -- Flavour/role of the equality that
- -- is being added to the inert set
- -> TcTyVar -- The new equality is tv ~ ty
- -> InertCans
- -> (WorkList, InertCans)
+kickOutRewritable :: CtFlavourRole -- Flavour/role of the equality that
+ -- is being added to the inert set
+ -> TcTyVar -- The new equality is tv ~ ty
+ -> InertCans
+ -> TcS (Int, InertCans)
+kickOutRewritable new_fr new_tv ics
+ = do { let (kicked_out, ics') = kick_out_rewritable new_fr new_tv ics
+ n_kicked = workListSize kicked_out
+
+ ; unless (n_kicked == 0) $
+ do { updWorkListTcS (appendWorkList kicked_out)
+ ; csTraceTcS $
+ hang (text "Kick out, tv =" <+> ppr new_tv)
+ 2 (vcat [ text "n-kicked =" <+> int n_kicked
+ , text "kicked_out =" <+> ppr kicked_out
+ , text "Residual inerts =" <+> ppr ics' ]) }
+
+ ; return (n_kicked, ics') }
+
+kick_out_rewritable :: CtFlavourRole -- Flavour/role of the equality that
+ -- is being added to the inert set
+ -> TcTyVar -- The new equality is tv ~ ty
+ -> InertCans
+ -> (WorkList, InertCans)
-- See Note [kickOutRewritable]
-kickOutRewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
+kick_out_rewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
, inert_dicts = dictmap
, inert_safehask = safehask
, inert_funeqs = funeqmap
, inert_irreds = irreds
, inert_insols = insols
- , inert_count = n
- , inert_model = model })
- | not (new_fr `eqCanRewriteFR` new_fr)
+ , inert_count = n })
+ | not (new_fr `eqMayRewriteFR` new_fr)
= (emptyWorkList, ics)
-- If new_fr can't rewrite itself, it can't rewrite
-- anything else, so no need to kick out anything.
@@ -1449,9 +1398,7 @@ kickOutRewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
, inert_funeqs = feqs_in
, inert_irreds = irs_in
, inert_insols = insols_in
- , inert_count = n - workListWantedCount kicked_out
- , inert_model = model }
- -- Leave the model unchanged
+ , inert_count = n - workListWantedCount kicked_out }
kicked_out = WL { wl_eqs = tv_eqs_out
, wl_funeqs = feqs_out
@@ -1461,31 +1408,32 @@ kickOutRewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
, wl_implics = emptyBag }
(tv_eqs_out, tv_eqs_in) = foldDVarEnv kick_out_eqs ([], emptyDVarEnv) tv_eqs
- (feqs_out, feqs_in) = partitionFunEqs kick_out_fe funeqmap
+ (feqs_out, feqs_in) = partitionFunEqs kick_out_ct funeqmap
+ -- See Note [Kicking out CFunEqCan for fundeps]
(dicts_out, dicts_in) = partitionDicts kick_out_ct dictmap
(irs_out, irs_in) = partitionBag kick_out_ct irreds
(insols_out, insols_in) = partitionBag kick_out_ct insols
-- Kick out even insolubles; see Note [Kick out insolubles]
- fr_can_rewrite :: CtEvidence -> Bool
- fr_can_rewrite ev = new_fr `eqCanRewriteFR` (ctEvFlavourRole ev)
+ fr_may_rewrite :: CtFlavourRole -> Bool
+ fr_may_rewrite fs = new_fr `eqMayRewriteFR` fs
+ -- Can the new item rewrite the inert item?
kick_out_ct :: Ct -> Bool
-- Kick it out if the new CTyEqCan can rewrite the inert
-- one. See Note [kickOutRewritable]
- kick_out_ct ct
- = fr_can_rewrite ev
- && new_tv `elemVarSet` tyCoVarsOfType (ctEvPred ev)
- where
- ev = ctEvidence ct
+ -- Or if it has no shadow and the shadow
+ kick_out_ct ct = kick_out_ev (ctEvidence ct)
- kick_out_fe :: Ct -> Bool
- kick_out_fe (CFunEqCan { cc_ev = ev, cc_tyargs = tys, cc_fsk = fsk })
- = new_tv == fsk -- If RHS is new_tvs, kick out /regardless of flavour/
- -- See Note [Kicking out CFunEqCan for fundeps]
- || (fr_can_rewrite ev
- && new_tv `elemVarSet` tyCoVarsOfTypes tys)
- kick_out_fe ct = pprPanic "kick_out_fe" (ppr ct)
+ kick_out_ev :: CtEvidence -> Bool
+ -- Kick it out if the new CTyEqCan can rewrite the inert
+ -- one. See Note [kickOutRewritable]
+ kick_out_ev ev = fr_may_rewrite (ctEvFlavourRole ev)
+ && new_tv `elemVarSet` rewritableTyVarsOfType (ctEvPred ev)
+ -- NB: this includes the fsk of a CFunEqCan. It can't
+ -- actually be rewritten, but we need to kick it out
+ -- so we get to take advantage of injectivity
+ -- See Note [Kicking out CFunEqCan for fundeps]
kick_out_eqs :: EqualCtList -> ([Ct], DTyVarEnv EqualCtList)
-> ([Ct], DTyVarEnv EqualCtList)
@@ -1500,19 +1448,19 @@ kickOutRewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
keep_eq (CTyEqCan { cc_tyvar = tv, cc_rhs = rhs_ty, cc_ev = ev
, cc_eq_rel = eq_rel })
| tv == new_tv
- = not (fr_can_rewrite ev) -- (K1)
+ = not (fr_may_rewrite fs) -- (K1)
| otherwise
= check_k2 && check_k3
where
fs = ctEvFlavourRole ev
- check_k2 = not (fs `eqCanRewriteFR` fs) -- (K2a)
- || (fs `eqCanRewriteFR` new_fr) -- (K2b)
- || not (new_fr `eqCanRewriteFR` fs) -- (K2c)
+ check_k2 = not (fs `eqMayRewriteFR` fs) -- (K2a)
+ || (fs `eqMayRewriteFR` new_fr) -- (K2b)
+ || not (fr_may_rewrite fs) -- (K2c)
|| not (new_tv `elemVarSet` tyCoVarsOfType rhs_ty) -- (K2d)
check_k3
- | new_fr `eqCanRewriteFR` fs
+ | fr_may_rewrite fs
= case eq_rel of
NomEq -> not (rhs_ty `eqType` mkTyVarTy new_tv)
ReprEq -> not (isTyVarExposed new_tv rhs_ty)
@@ -1525,42 +1473,13 @@ kickOutRewritable new_fr new_tv ics@(IC { inert_eqs = tv_eqs
kickOutAfterUnification :: TcTyVar -> TcS Int
kickOutAfterUnification new_tv
= do { ics <- getInertCans
- ; let (kicked_out1, ics1) = kickOutModel new_tv ics
- (kicked_out2, ics2) = kickOutRewritable (Given,NomEq)
- new_tv ics1
+ ; (n_kicked, ics2) <- kickOutRewritable (Given,NomEq)
+ new_tv ics
-- Given because the tv := xi is given; NomEq because
-- only nominal equalities are solved by unification
- kicked_out = appendWorkList kicked_out1 kicked_out2
- ; setInertCans ics2
- ; updWorkListTcS (appendWorkList kicked_out)
-
- ; unless (isEmptyWorkList kicked_out) $
- csTraceTcS $
- hang (text "Kick out (unify), tv =" <+> ppr new_tv)
- 2 (vcat [ text "n-kicked =" <+> int (workListSize kicked_out)
- , text "kicked_out =" <+> ppr kicked_out
- , text "Residual inerts =" <+> ppr ics2 ])
- ; return (workListSize kicked_out) }
-
-kickOutModel :: TcTyVar -> InertCans -> (WorkList, InertCans)
-kickOutModel new_tv ics@(IC { inert_model = model, inert_eqs = eqs })
- = (foldDVarEnv add emptyWorkList der_out, ics { inert_model = new_model })
- where
- (der_out, new_model) = partitionDVarEnv kick_out_der model
-
- kick_out_der :: Ct -> Bool
- kick_out_der (CTyEqCan { cc_tyvar = tv, cc_rhs = rhs })
- = new_tv == tv || new_tv `elemVarSet` tyCoVarsOfType rhs
- kick_out_der _ = False
-
- add :: Ct -> WorkList -> WorkList
- -- Don't kick out a Derived if there is a Given or Wanted with
- -- the same predicate. The model is just a shadow copy, and the
- -- Given/Wanted will serve the purpose.
- add (CTyEqCan { cc_ev = ev, cc_tyvar = tv, cc_rhs = rhs }) wl
- | not (isInInertEqs eqs tv rhs) = extendWorkListDerived (ctEvLoc ev) ev wl
- add _ wl = wl
+ ; setInertCans ics2
+ ; return n_kicked }
{- Note [kickOutRewritable]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1582,13 +1501,8 @@ new equality, to maintain the inert-set invariants.
kick out constraints that mention type variables whose kinds
contain this variable!
- - We do not need to kick anything out from the model; we only
- add [D] constraints to the model (in effect) and they are
- fully rewritten by the model, so (K2b) holds
-
- - A Derived equality can kick out [D] constraints in inert_dicts,
- inert_irreds etc. Nothing in inert_eqs because there are no
- Derived constraints in inert_eqs (they are in the model)
+ - A Derived equality can kick out [D] constraints in inert_eqs,
+ inert_dicts, inert_irreds etc.
- We don't kick out constraints from inert_solved_dicts, and
inert_solved_funeqs optimistically. But when we lookup we have to
@@ -1657,35 +1571,6 @@ getInertCans = do { inerts <- getTcSInerts; return (inert_cans inerts) }
setInertCans :: InertCans -> TcS ()
setInertCans ics = updInertTcS $ \ inerts -> inerts { inert_cans = ics }
-takeGivenInsolubles :: TcS Cts
--- See Note [The inert set after solving Givens]
-takeGivenInsolubles
- = updRetInertCans $ \ cans ->
- ( inert_insols cans
- , cans { inert_insols = emptyBag
- , inert_funeqs = filterFunEqs isGivenCt (inert_funeqs cans) } )
-
-{- Note [The inert set after solving Givens]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-After solving the Givens we take two things out of the inert set
-
- a) The insolubles; we return these to report inaccessible code
- We return these separately. We don't want to leave them in
- the inert set, lest we confuse them with insolubles arising from
- solving wanteds
-
- b) Any Derived CFunEqCans. Derived CTyEqCans are in the
- inert_model and do no harm. In contrast, Derived CFunEqCans
- get mixed up with the Wanteds later and confuse the
- post-solve-wanted unflattening (Trac #10507).
- E.g. From [G] 1 <= m, [G] m <= n
- We get [D] 1 <= n, and we must remove it!
- Otherwise we unflatten it more then once, and assign
- to its fmv more than once...disaster.
- It's ok to remove them because they turned out not to
- yield an insoluble, and hence have now done their work.
--}
-
updRetInertCans :: (InertCans -> (a, InertCans)) -> TcS a
-- Modify the inert set with the supplied function
updRetInertCans upd_fn
@@ -1723,8 +1608,8 @@ updInertIrreds upd_fn
getInertEqs :: TcS (DTyVarEnv EqualCtList)
getInertEqs = do { inert <- getInertCans; return (inert_eqs inert) }
-getInertModel :: TcS InertModel
-getInertModel = do { inert <- getInertCans; return (inert_model inert) }
+getInertInsols :: TcS Cts
+getInertInsols = do { inert <- getInertCans; return (inert_insols inert) }
getInertGivens :: TcS [Ct]
-- Returns the Given constraints in the inert set,
@@ -1776,42 +1661,42 @@ getUnsolvedInerts
, inert_irreds = irreds
, inert_dicts = idicts
, inert_insols = insols
- , inert_model = model } <- getInertCans
- ; keep_derived <- keepSolvingDeriveds
+ } <- getInertCans
- ; let der_tv_eqs = foldDVarEnv (add_der_eq keep_derived tv_eqs)
- emptyCts model
- unsolved_tv_eqs = foldTyEqs add_if_unsolved tv_eqs der_tv_eqs
- unsolved_fun_eqs = foldFunEqs add_if_unsolved fun_eqs emptyCts
+ ; let unsolved_tv_eqs = foldTyEqs add_if_unsolved tv_eqs emptyCts
+ unsolved_fun_eqs = foldFunEqs add_if_wanted fun_eqs emptyCts
unsolved_irreds = Bag.filterBag is_unsolved irreds
unsolved_dicts = foldDicts add_if_unsolved idicts emptyCts
- others = unsolved_irreds `unionBags` unsolved_dicts
+ unsolved_others = unsolved_irreds `unionBags` unsolved_dicts
+ unsolved_insols = filterBag is_unsolved insols
; implics <- getWorkListImplics
; traceTcS "getUnsolvedInerts" $
vcat [ text " tv eqs =" <+> ppr unsolved_tv_eqs
, text "fun eqs =" <+> ppr unsolved_fun_eqs
- , text "insols =" <+> ppr insols
- , text "others =" <+> ppr others
+ , text "insols =" <+> ppr unsolved_insols
+ , text "others =" <+> ppr unsolved_others
, text "implics =" <+> ppr implics ]
- ; return ( implics, unsolved_tv_eqs, unsolved_fun_eqs, insols, others) }
- -- Keep even the given insolubles
- -- so that we can report dead GADT pattern match branches
+ ; return ( implics, unsolved_tv_eqs, unsolved_fun_eqs
+ , unsolved_insols, unsolved_others) }
where
- add_der_eq keep_derived tv_eqs ct cts
- -- See Note [Unsolved Derived equalities]
- | CTyEqCan { cc_tyvar = tv, cc_rhs = rhs } <- ct
- , isMetaTyVar tv || keep_derived
- , not (isInInertEqs tv_eqs tv rhs) = ct `consBag` cts
- | otherwise = cts
add_if_unsolved :: Ct -> Cts -> Cts
add_if_unsolved ct cts | is_unsolved ct = ct `consCts` cts
| otherwise = cts
is_unsolved ct = not (isGivenCt ct) -- Wanted or Derived
+ -- For CFunEqCans we ignore the Derived ones, and keep
+ -- only the Wanteds for flattening. The Derived ones
+ -- share a unification variable with the corresponding
+ -- Wanted, so we definitely don't want to to participate
+ -- in unflattening
+ -- See Note [Type family equations]
+ add_if_wanted ct cts | isWantedCt ct = ct `consCts` cts
+ | otherwise = cts
+
isInInertEqs :: DTyVarEnv EqualCtList -> TcTyVar -> TcType -> Bool
-- True if (a ~N ty) is in the inert set, in either Given or Wanted
isInInertEqs eqs tv rhs
@@ -1827,22 +1712,23 @@ isInInertEqs eqs tv rhs
getNoGivenEqs :: TcLevel -- TcLevel of this implication
-> [TcTyVar] -- Skolems of this implication
- -> Cts -- Given insolubles from solveGivens
- -> TcS Bool -- True <=> definitely no residual given equalities
+ -> TcS ( Bool -- True <=> definitely no residual given equalities
+ , Cts ) -- Insoluble constraints arising from givens
-- See Note [When does an implication have given equalities?]
-getNoGivenEqs tclvl skol_tvs given_insols
+getNoGivenEqs tclvl skol_tvs
= do { inerts@(IC { inert_eqs = ieqs, inert_irreds = iirreds
- , inert_funeqs = funeqs })
- <- getInertCans
+ , inert_funeqs = funeqs
+ , inert_insols = insols })
+ <- getInertCans
; let local_fsks = foldFunEqs add_fsk funeqs emptyVarSet
has_given_eqs = foldrBag ((||) . ev_given_here . ctEvidence) False
- (iirreds `unionBags` given_insols)
+ (iirreds `unionBags` insols)
|| anyDVarEnv (eqs_given_here local_fsks) ieqs
; traceTcS "getNoGivenEqs" (vcat [ ppr has_given_eqs, ppr inerts
- , ppr given_insols])
- ; return (not has_given_eqs) }
+ , ppr insols])
+ ; return (not has_given_eqs, insols) }
where
eqs_given_here :: VarSet -> EqualCtList -> Bool
eqs_given_here local_fsks [CTyEqCan { cc_tyvar = tv, cc_ev = ev }]
@@ -1921,17 +1807,10 @@ prohibitedSuperClassSolve from_loc solve_loc
{- Note [Unsolved Derived equalities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In getUnsolvedInerts, we return a derived equality from the model
-for two possible reasons:
-
- * Because it is a candidate for floating out of this implication.
- We only float equalities with a meta-tyvar on the left, so we only
- pull those out here.
-
- * If we are only solving derived constraints (i.e. tcs_need_derived
- is true; see Note [Solving for Derived constraints]), then we
- those Derived constraints are effectively unsolved, and we need
- them!
+In getUnsolvedInerts, we return a derived equality from the inert_eqs
+because it is a candidate for floating out of this implication. We
+only float equalities with a meta-tyvar on the left, so we only pull
+those out here.
Note [When does an implication have given equalities?]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2029,7 +1908,7 @@ lookupFlatCache fam_tc tys
lookup_inerts inert_funeqs
| Just (CFunEqCan { cc_ev = ctev, cc_fsk = fsk, cc_tyargs = xis })
<- findFunEq inert_funeqs fam_tc tys
- , tys `eqTypes` xis -- the lookup might find a near-match; see
+ , tys `eqTypes` xis -- The lookup might find a near-match; see
-- Note [Use loose types in inert set]
= Just (ctEvCoercion ctev, mkTyVarTy fsk, ctEvFlavour ctev)
| otherwise = Nothing
@@ -2076,42 +1955,6 @@ foldIrreds k irreds z = foldrBag k z irreds
{- *********************************************************************
* *
- Type equalities
-* *
-********************************************************************* -}
-
-type EqualCtList = [Ct]
-
-{- Note [EqualCtList invariants]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * All are equalities
- * All these equalities have the same LHS
- * The list is never empty
- * No element of the list can rewrite any other
-
- From the fourth invariant it follows that the list is
- - A single Given, or
- - Any number of Wanteds and/or Deriveds
--}
-
-addTyEq :: DTyVarEnv EqualCtList -> TcTyVar -> Ct -> DTyVarEnv EqualCtList
-addTyEq old_list tv it = extendDVarEnv_C (\old_eqs _new_eqs -> it : old_eqs)
- old_list tv [it]
-
-foldTyEqs :: (Ct -> b -> b) -> DTyVarEnv EqualCtList -> b -> b
-foldTyEqs k eqs z
- = foldDVarEnv (\cts z -> foldr k z cts) z eqs
-
-findTyEqs :: InertCans -> TyVar -> EqualCtList
-findTyEqs icans tv = lookupDVarEnv (inert_eqs icans) tv `orElse` []
-
-delTyEq :: DTyVarEnv EqualCtList -> TcTyVar -> TcType -> DTyVarEnv EqualCtList
-delTyEq m tv t = modifyDVarEnv (filter (not . isThisOne)) m tv
- where isThisOne (CTyEqCan { cc_rhs = t1 }) = eqType t t1
- isThisOne _ = False
-
-{- *********************************************************************
-* *
TcAppMap
* *
************************************************************************
@@ -2245,9 +2088,6 @@ type FunEqMap a = TcAppMap a -- A map whose key is a (TyCon, [Type]) pair
emptyFunEqs :: TcAppMap a
emptyFunEqs = emptyTcAppMap
-sizeFunEqMap :: FunEqMap a -> Int
-sizeFunEqMap m = foldFunEqs (\ _ x -> x+1) m 0
-
findFunEq :: FunEqMap a -> TyCon -> [Type] -> Maybe a
findFunEq m tc tys = findTcApp m (getUnique tc) tys
@@ -2269,8 +2109,8 @@ foldFunEqs = foldTcAppMap
-- mapFunEqs :: (a -> b) -> FunEqMap a -> FunEqMap b
-- mapFunEqs = mapTcApp
-filterFunEqs :: (Ct -> Bool) -> FunEqMap Ct -> FunEqMap Ct
-filterFunEqs = filterTcAppMap
+-- filterFunEqs :: (Ct -> Bool) -> FunEqMap Ct -> FunEqMap Ct
+-- filterFunEqs = filterTcAppMap
insertFunEq :: FunEqMap a -> TyCon -> [Type] -> a -> FunEqMap a
insertFunEq m tc tys val = insertTcApp m (getUnique tc) tys val
@@ -2324,16 +2164,6 @@ All you can do is
Filling in a dictionary evidence variable means to create a binding
for it, so TcS carries a mutable location where the binding can be
added. This is initialised from the innermost implication constraint.
-
-Note [Solving for Derived constraints]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Sometimes we invoke the solver on a bunch of Derived constraints, not to
-generate any evidence, but just to cause unification side effects or to
-produce a simpler set of constraints. If that is what we are doing, we
-should do two things differently:
- a) Don't stop when you've solved all the Wanteds; instead keep going
- if there are any Deriveds in the work queue.
- b) In getInertUnsolved, include Derived ones
-}
data TcSEnv
@@ -2350,11 +2180,7 @@ data TcSEnv
-- The main work-list and the flattening worklist
-- See Note [Work list priorities] and
- tcs_worklist :: IORef WorkList, -- Current worklist
-
- tcs_need_deriveds :: Bool
- -- Keep solving, even if all the unsolved constraints are Derived
- -- See Note [Solving for Derived constraints]
+ tcs_worklist :: IORef WorkList -- Current worklist
}
---------------
@@ -2450,7 +2276,7 @@ runTcS :: TcS a -- What to run
-> TcM (a, EvBindMap)
runTcS tcs
= do { ev_binds_var <- TcM.newTcEvBinds
- ; res <- runTcSWithEvBinds False ev_binds_var tcs
+ ; res <- runTcSWithEvBinds ev_binds_var tcs
; ev_binds <- TcM.getTcEvBindsMap ev_binds_var
; return (res, ev_binds) }
@@ -2460,19 +2286,18 @@ runTcS tcs
runTcSDeriveds :: TcS a -> TcM a
runTcSDeriveds tcs
= do { ev_binds_var <- TcM.newTcEvBinds
- ; runTcSWithEvBinds True ev_binds_var tcs }
+ ; runTcSWithEvBinds ev_binds_var tcs }
-- | This can deal only with equality constraints.
runTcSEqualities :: TcS a -> TcM a
runTcSEqualities thing_inside
= do { ev_binds_var <- TcM.newTcEvBinds
- ; runTcSWithEvBinds False ev_binds_var thing_inside }
+ ; runTcSWithEvBinds ev_binds_var thing_inside }
-runTcSWithEvBinds :: Bool -- ^ keep running even if only Deriveds are left?
- -> EvBindsVar
+runTcSWithEvBinds :: EvBindsVar
-> TcS a
-> TcM a
-runTcSWithEvBinds solve_deriveds ev_binds_var tcs
+runTcSWithEvBinds ev_binds_var tcs
= do { unified_var <- TcM.newTcRef 0
; step_count <- TcM.newTcRef 0
; inert_var <- TcM.newTcRef emptyInert
@@ -2481,8 +2306,7 @@ runTcSWithEvBinds solve_deriveds ev_binds_var tcs
, tcs_unified = unified_var
, tcs_count = step_count
, tcs_inerts = inert_var
- , tcs_worklist = wl_var
- , tcs_need_deriveds = solve_deriveds }
+ , tcs_worklist = wl_var }
-- Run the computation
; res <- unTcS tcs env
@@ -2536,7 +2360,6 @@ nestImplicTcS ref inner_tclvl (TcS thing_inside)
= TcS $ \ TcSEnv { tcs_unified = unified_var
, tcs_inerts = old_inert_var
, tcs_count = count
- , tcs_need_deriveds = solve_deriveds
} ->
do { inerts <- TcM.readTcRef old_inert_var
; let nest_inert = inerts { inert_flat_cache = emptyExactFunEqs }
@@ -2547,8 +2370,7 @@ nestImplicTcS ref inner_tclvl (TcS thing_inside)
, tcs_unified = unified_var
, tcs_count = count
, tcs_inerts = new_inert_var
- , tcs_worklist = new_wl_var
- , tcs_need_deriveds = solve_deriveds }
+ , tcs_worklist = new_wl_var }
; res <- TcM.setTcLevel inner_tclvl $
thing_inside nest_env
@@ -2642,10 +2464,6 @@ updWorkListTcS f
; let new_work = f wl_curr
; wrapTcS (TcM.writeTcRef wl_var new_work) }
--- | Should we keep solving even only deriveds are left?
-keepSolvingDeriveds :: TcS Bool
-keepSolvingDeriveds = TcS (return . tcs_need_deriveds)
-
emitWorkNC :: [CtEvidence] -> TcS ()
emitWorkNC evs
| null evs
@@ -2869,35 +2687,44 @@ which will result in two Deriveds to end up in the insoluble set:
-- Flatten skolems
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
newFlattenSkolem :: CtFlavour -> CtLoc
- -> TcType -- F xis
- -> TcS (CtEvidence, Coercion, TcTyVar) -- [W] x:: F xis ~ fsk
-newFlattenSkolem Given loc fam_ty
- = do { fsk <- newFsk fam_ty
- ; let co = mkNomReflCo fam_ty
- ; ev <- newGivenEvVar loc (mkPrimEqPred fam_ty (mkTyVarTy fsk),
- EvCoercion co)
- ; return (ev, co, fsk) }
-
-newFlattenSkolem Wanted loc fam_ty
- = do { fmv <- newFmv fam_ty
- ; (ev, hole_co) <- newWantedEq loc Nominal fam_ty (mkTyVarTy fmv)
- ; return (ev, hole_co, fmv) }
-
-newFlattenSkolem Derived loc fam_ty
- = do { fmv <- newFmv fam_ty
- ; ev <- newDerivedNC loc (mkPrimEqPred fam_ty (mkTyVarTy fmv))
- ; return (ev, pprPanic "newFlattenSkolem [D]" (ppr fam_ty), fmv) }
-
-newFsk, newFmv :: TcType -> TcS TcTyVar
-newFsk fam_ty = wrapTcS (TcM.newFskTyVar fam_ty)
-newFmv fam_ty = wrapTcS (TcM.newFmvTyVar fam_ty)
+ -> TyCon -> [TcType] -- F xis
+ -> TcS (CtEvidence, Coercion, TcTyVar) -- [G/WD] x:: F xis ~ fsk
+newFlattenSkolem flav loc tc xis
+ = do { stuff@(ev, co, fsk) <- new_skolem
+ ; let fsk_ty = mkTyVarTy fsk
+ ; extendFlatCache tc xis (co, fsk_ty, ctEvFlavour ev)
+ ; return stuff }
+ where
+ fam_ty = mkTyConApp tc xis
+
+ new_skolem
+ | Given <- flav
+ = do { fsk <- wrapTcS (TcM.newFskTyVar fam_ty)
+ ; let co = mkNomReflCo fam_ty
+ ; ev <- newGivenEvVar loc (mkPrimEqPred fam_ty (mkTyVarTy fsk),
+ EvCoercion co)
+ ; return (ev, co, fsk) }
+
+ | otherwise -- Generate a [WD] for both Wanted and Derived
+ -- See Note [No Derived CFunEqCans]
+ = do { fmv <- wrapTcS (TcM.newFmvTyVar fam_ty)
+ ; (ev, hole_co) <- newWantedEq loc Nominal fam_ty (mkTyVarTy fmv)
+ ; return (ev, hole_co, fmv) }
extendFlatCache :: TyCon -> [Type] -> (TcCoercion, TcType, CtFlavour) -> TcS ()
-extendFlatCache tc xi_args stuff
+extendFlatCache tc xi_args stuff@(_, ty, fl)
+ | isGivenOrWDeriv fl -- Maintain the invariant that inert_flat_cache
+ -- only has [G] and [WD] CFunEqCans
= do { dflags <- getDynFlags
; when (gopt Opt_FlatCache dflags) $
- updInertTcS $ \ is@(IS { inert_flat_cache = fc }) ->
- is { inert_flat_cache = insertExactFunEq fc tc xi_args stuff } }
+ do { traceTcS "extendFlatCache" (vcat [ ppr tc <+> ppr xi_args
+ , ppr fl, ppr ty ])
+ -- 'co' can be bottom, in the case of derived items
+ ; updInertTcS $ \ is@(IS { inert_flat_cache = fc }) ->
+ is { inert_flat_cache = insertExactFunEq fc tc xi_args stuff } } }
+
+ | otherwise
+ = return ()
-- Instantiations
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3039,6 +2866,7 @@ newWantedEq loc role ty1 ty2
= do { hole <- wrapTcS $ TcM.newCoercionHole
; traceTcS "Emitting new coercion hole" (ppr hole <+> dcolon <+> ppr pty)
; return ( CtWanted { ctev_pred = pty, ctev_dest = HoleDest hole
+ , ctev_nosh = WDeriv
, ctev_loc = loc}
, mkHoleCo hole role ty1 ty2 ) }
where
@@ -3048,11 +2876,11 @@ newWantedEq loc role ty1 ty2
newWantedEvVarNC :: CtLoc -> TcPredType -> TcS CtEvidence
-- Don't look up in the solved/inerts; we know it's not there
newWantedEvVarNC loc pty
- = do { -- checkReductionDepth loc pty
- ; new_ev <- newEvVar pty
+ = do { new_ev <- newEvVar pty
; traceTcS "Emitting new wanted" (ppr new_ev <+> dcolon <+> ppr pty $$
pprCtLoc loc)
; return (CtWanted { ctev_pred = pty, ctev_dest = EvVarDest new_ev
+ , ctev_nosh = WDeriv
, ctev_loc = loc })}
newWantedEvVar :: CtLoc -> TcPredType -> TcS MaybeNew
diff --git a/compiler/typecheck/TcSimplify.hs b/compiler/typecheck/TcSimplify.hs
index c23b3171ab..a9548f292e 100644
--- a/compiler/typecheck/TcSimplify.hs
+++ b/compiler/typecheck/TcSimplify.hs
@@ -413,8 +413,7 @@ simplifyAmbiguityCheck ty wanteds
-- inaccessible code
; allow_ambiguous <- xoptM LangExt.AllowAmbiguousTypes
; traceTc "reportUnsolved(ambig) {" empty
- ; tc_lvl <- TcM.getTcLevel
- ; unless (allow_ambiguous && not (insolubleWC tc_lvl final_wc))
+ ; unless (allow_ambiguous && not (insolubleWC final_wc))
(discardResult (reportUnsolved final_wc))
; traceTc "reportUnsolved(ambig) }" empty
@@ -431,11 +430,8 @@ simplifyDefault :: ThetaType -- Wanted; has no type variables in it
-> TcM () -- Succeeds if the constraint is soluble
simplifyDefault theta
= do { traceTc "simplifyDefault" empty
- ; loc <- getCtLocM DefaultOrigin Nothing
- ; let wanted = [ CtDerived { ctev_pred = pred
- , ctev_loc = loc }
- | pred <- theta ]
- ; unsolved <- runTcSDeriveds (solveWanteds (mkSimpleWC wanted))
+ ; wanteds <- newWanteds DefaultOrigin theta
+ ; unsolved <- runTcSDeriveds (solveWantedsAndDrop (mkSimpleWC wanteds))
; traceTc "reportUnsolved {" empty
; reportAllUnsolved unsolved
; traceTc "reportUnsolved }" empty
@@ -451,7 +447,8 @@ tcCheckSatisfiability given_ids
do { traceTcS "checkSatisfiability {" (ppr given_ids)
; let given_cts = mkGivens given_loc (bagToList given_ids)
-- See Note [Superclasses and satisfiability]
- ; insols <- solveSimpleGivens given_cts
+ ; solveSimpleGivens given_cts
+ ; insols <- getInertInsols
; insols <- try_harder insols
; traceTcS "checkSatisfiability }" (ppr insols)
; return (isEmptyBag insols) }
@@ -467,7 +464,8 @@ tcCheckSatisfiability given_ids
| otherwise
= do { pending_given <- getPendingScDicts
; new_given <- makeSuperClasses pending_given
- ; solveSimpleGivens new_given }
+ ; solveSimpleGivens new_given
+ ; getInertInsols }
{- Note [Superclasses and satisfiability]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -573,7 +571,7 @@ simplifyInfer rhs_tclvl infer_mode sigs name_taus wanteds
; psig_theta_vars <- mapM TcM.newEvVar psig_theta
; wanted_transformed_incl_derivs
<- setTcLevel rhs_tclvl $
- runTcSWithEvBinds False ev_binds_var $
+ runTcSWithEvBinds ev_binds_var $
do { let loc = mkGivenLoc rhs_tclvl UnkSkol tc_lcl_env
psig_givens = mkGivens loc psig_theta_vars
; _ <- solveSimpleGivens psig_givens
@@ -590,7 +588,7 @@ simplifyInfer rhs_tclvl infer_mode sigs name_taus wanteds
; let wanted_transformed = dropDerivedWC wanted_transformed_incl_derivs
; quant_pred_candidates -- Fully zonked
- <- if insolubleWC rhs_tclvl wanted_transformed_incl_derivs
+ <- if insolubleWC wanted_transformed_incl_derivs
then return [] -- See Note [Quantification with errors]
-- NB: must include derived errors in this test,
-- hence "incl_derivs"
@@ -615,15 +613,11 @@ simplifyInfer rhs_tclvl infer_mode sigs name_taus wanteds
; mapM_ def_tyvar meta_tvs
; mapM_ (promoteTyVar rhs_tclvl) meta_tvs
+ ; clone_wanteds <- mapM cloneWanted (bagToList quant_cand)
; WC { wc_simple = simples }
<- setTcLevel rhs_tclvl $
- runTcSDeriveds $
- solveSimpleWanteds $
- mapBag toDerivedCt quant_cand
- -- NB: we don't want evidence,
- -- so use Derived constraints
-
- ; simples <- TcM.zonkSimples simples
+ simplifyWantedsTcM clone_wanteds
+ -- Discard evidence; result is zonked
; return [ ctEvPred ev | ct <- bagToList simples
, let ev = ctEvidence ct ] }
@@ -1074,7 +1068,7 @@ simplifyWantedsTcM :: [CtEvidence] -> TcM WantedConstraints
-- Postcondition: fully zonked and unflattened constraints
simplifyWantedsTcM wanted
= do { traceTc "simplifyWantedsTcM {" (ppr wanted)
- ; (result, _) <- runTcS (solveWantedsAndDrop $ mkSimpleWC wanted)
+ ; (result, _) <- runTcS (solveWantedsAndDrop (mkSimpleWC wanted))
; result <- TcM.zonkWC result
; traceTc "simplifyWantedsTcM }" (ppr result)
; return result }
@@ -1093,20 +1087,19 @@ solveWanteds :: WantedConstraints -> TcS WantedConstraints
solveWanteds wc@(WC { wc_simple = simples, wc_insol = insols, wc_impl = implics })
= do { traceTcS "solveWanteds {" (ppr wc)
- -- Try the simple bit, including insolubles. Solving insolubles a
- -- second time round is a bit of a waste; but the code is simple
- -- and the program is wrong anyway, and we don't run the danger
- -- of adding Derived insolubles twice; see
- -- TcSMonad Note [Do not add duplicate derived insolubles]
; wc1 <- solveSimpleWanteds simples
- ; (no_new_scs, wc1) <- expandSuperClasses wc1
; let WC { wc_simple = simples1, wc_insol = insols1, wc_impl = implics1 } = wc1
; (floated_eqs, implics2) <- solveNestedImplications (implics `unionBags` implics1)
+ ; (no_new_scs, simples2) <- expandSuperClasses simples1
+
+ ; traceTcS "solveWanteds middle" $ vcat [ text "simples1 =" <+> ppr simples1
+ , text "simples2 =" <+> ppr simples2 ]
; dflags <- getDynFlags
- ; final_wc <- simpl_loop 0 (solverIterations dflags) floated_eqs no_new_scs
- (WC { wc_simple = simples1, wc_impl = implics2
+ ; final_wc <- simpl_loop 0 (solverIterations dflags) floated_eqs
+ no_new_scs
+ (WC { wc_simple = simples2, wc_impl = implics2
, wc_insol = insols `unionBags` insols1 })
; bb <- TcS.getTcEvBindsMap
@@ -1119,9 +1112,9 @@ solveWanteds wc@(WC { wc_simple = simples, wc_insol = insols, wc_impl = implics
simpl_loop :: Int -> IntWithInf -> Cts -> Bool
-> WantedConstraints
-> TcS WantedConstraints
-simpl_loop n limit floated_eqs no_new_scs
+simpl_loop n limit floated_eqs no_new_deriveds
wc@(WC { wc_simple = simples, wc_insol = insols, wc_impl = implics })
- | isEmptyBag floated_eqs && no_new_scs
+ | isEmptyBag floated_eqs && no_new_deriveds
= return wc -- Done!
| n `intGtLimit` limit
@@ -1134,8 +1127,8 @@ simpl_loop n limit floated_eqs no_new_scs
2 (vcat [ text "Unsolved:" <+> ppr wc
, ppUnless (isEmptyBag floated_eqs) $
text "Floated equalities:" <+> ppr floated_eqs
- , ppUnless no_new_scs $
- text "New superclasses found"
+ , ppUnless no_new_deriveds $
+ text "New deriveds found"
, text "Set limit with -fconstraint-solver-iterations=n; n=0 for no limit"
]))
; return wc }
@@ -1144,7 +1137,7 @@ simpl_loop n limit floated_eqs no_new_scs
= do { let n_floated = lengthBag floated_eqs
; csTraceTcS $
text "simpl_loop iteration=" <> int n
- <+> (parens $ hsep [ text "no new scs =" <+> ppr no_new_scs <> comma
+ <+> (parens $ hsep [ text "no new deriveds =" <+> ppr no_new_deriveds <> comma
, int n_floated <+> text "floated eqs" <> comma
, int (lengthBag simples) <+> text "simples to solve" ])
@@ -1155,8 +1148,8 @@ simpl_loop n limit floated_eqs no_new_scs
-- NB: the floated_eqs may include /derived/ equalities
-- arising from fundeps inside an implication
- ; (no_new_scs, wc1) <- expandSuperClasses wc1
; let WC { wc_simple = simples1, wc_insol = insols1, wc_impl = implics1 } = wc1
+ ; (no_new_scs, simples2) <- expandSuperClasses simples1
-- We have already tried to solve the nested implications once
-- Try again only if we have unified some meta-variables
@@ -1167,36 +1160,36 @@ simpl_loop n limit floated_eqs no_new_scs
else solveNestedImplications (implics `unionBags` implics1)
; simpl_loop (n+1) limit floated_eqs2 no_new_scs
- (WC { wc_simple = simples1, wc_impl = implics2
+ (WC { wc_simple = simples2, wc_impl = implics2
, wc_insol = insols `unionBags` insols1 }) }
-expandSuperClasses :: WantedConstraints -> TcS (Bool, WantedConstraints)
--- If there are any unsolved wanteds, expand one step of superclasses for
--- unsolved wanteds or givens
+expandSuperClasses :: Cts -> TcS (Bool, Cts)
+-- If there are any unsolved wanteds, expand one step of
+-- superclasses for deriveds
+-- Returned Bool is True <=> no new superclass constraints added
-- See Note [The superclass story] in TcCanonical
-expandSuperClasses wc@(WC { wc_simple = unsolved, wc_insol = insols })
+expandSuperClasses unsolved
| not (anyBag superClassesMightHelp unsolved)
- = return (True, wc)
+ = return (True, unsolved)
| otherwise
= do { traceTcS "expandSuperClasses {" empty
; let (pending_wanted, unsolved') = mapAccumBagL get [] unsolved
- get acc ct = case isPendingScDict ct of
- Just ct' -> (ct':acc, ct')
- Nothing -> (acc, ct)
+ get acc ct | Just ct' <- isPendingScDict ct
+ = (ct':acc, ct')
+ | otherwise
+ = (acc, ct)
; pending_given <- getPendingScDicts
; if null pending_given && null pending_wanted
then do { traceTcS "End expandSuperClasses no-op }" empty
- ; return (True, wc) }
+ ; return (True, unsolved) }
else
do { new_given <- makeSuperClasses pending_given
- ; new_insols <- solveSimpleGivens new_given
+ ; solveSimpleGivens new_given
; new_wanted <- makeSuperClasses pending_wanted
; traceTcS "End expandSuperClasses }"
(vcat [ text "Given:" <+> ppr pending_given
- , text "Insols from given:" <+> ppr new_insols
, text "Wanted:" <+> ppr new_wanted ])
- ; return (False, wc { wc_simple = unsolved' `unionBags` listToBag new_wanted
- , wc_insol = insols `unionBags` new_insols }) } }
+ ; return (False, unsolved' `unionBags` listToBag new_wanted) } }
solveNestedImplications :: Bag Implication
-> TcS (Cts, Bag Implication)
@@ -1244,17 +1237,17 @@ solveImplication imp@(Implic { ic_tclvl = tclvl
-- Solve the nested constraints
; (no_given_eqs, given_insols, residual_wanted)
- <- nestImplicTcS ev_binds_var tclvl $
+ <- nestImplicTcS ev_binds_var tclvl $
do { let loc = mkGivenLoc tclvl info env
givens = mkGivens loc given_ids
- ; given_insols <- solveSimpleGivens givens
+ ; solveSimpleGivens givens
; residual_wanted <- solveWanteds wanteds
-- solveWanteds, *not* solveWantedsAndDrop, because
-- we want to retain derived equalities so we can float
-- them out in floatEqualities
- ; no_eqs <- getNoGivenEqs tclvl skols given_insols
+ ; (no_eqs, given_insols) <- getNoGivenEqs tclvl skols
-- Call getNoGivenEqs /after/ solveWanteds, because
-- solveWanteds can augment the givens, via expandSuperClasses,
-- to reveal given superclass equalities
@@ -1289,7 +1282,6 @@ setImplicationStatus :: Implication -> TcS (Maybe Implication)
-- Return Nothing if we can discard the implication altogether
setImplicationStatus implic@(Implic { ic_binds = ev_binds_var
, ic_info = info
- , ic_tclvl = tc_lvl
, ic_wanted = wc
, ic_given = givens })
| some_insoluble
@@ -1299,10 +1291,13 @@ setImplicationStatus implic@(Implic { ic_binds = ev_binds_var
, wc_insol = pruned_insols } }
| some_unsolved
- = return $ Just $
+ = do { traceTcS "setImplicationStatus" $
+ vcat [ppr givens $$ ppr simples $$ ppr insols $$ ppr mb_implic_needs]
+ ; return $ Just $
implic { ic_status = IC_Unsolved
, ic_wanted = wc { wc_simple = pruned_simples
, wc_insol = pruned_insols } }
+ }
| otherwise -- Everything is solved; look at the implications
-- See Note [Tracking redundant constraints]
@@ -1342,7 +1337,7 @@ setImplicationStatus implic@(Implic { ic_binds = ev_binds_var
where
WC { wc_simple = simples, wc_impl = implics, wc_insol = insols } = wc
- some_insoluble = insolubleWC tc_lvl wc
+ some_insoluble = insolubleWC wc
some_unsolved = not (isEmptyBag simples && isEmptyBag insols)
|| isNothing mb_implic_needs
@@ -1510,23 +1505,13 @@ works:
----- Shortcomings
-After I introduced -Wredundant-constraints there was extensive discussion
-about cases where it reported a redundant constraint but the programmer
-really wanted it. See
+Consider (see Trac #9939)
+ f2 :: (Eq a, Ord a) => a -> a -> Bool
+ -- Ord a redundant, but Eq a is reported
+ f2 x y = (x == y)
- * #11370 (removed it from -Wdefault)
- * #10635 (removed it from -Wall as well)
- * #12142
- * #11474, #10100 (class not used, but its fundeps are)
- * #11099 (redundant, but still desired)
- * #10183 (constraint necessary to exclude omitted case)
-
- * #9939: f2 :: (Eq a, Ord a) => a -> a -> Bool
- -- Ord a redundant, but Eq a is reported
- f2 x y = (x == y)
-
- We report (Eq a) as redundant, whereas actually (Ord a) is.
- But it's really not easy to detect that!
+We report (Eq a) as redundant, whereas actually (Ord a) is. But it's
+really not easy to detect that!
Note [Cutting off simpl_loop]
@@ -1770,29 +1755,6 @@ beta! Concrete example is in indexed_types/should_fail/ExtraTcsUntch.hs:
in (g1 '3', g2 undefined)
-Note [Solving Family Equations]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-After we are done with simplification we may be left with constraints of the form:
- [Wanted] F xis ~ beta
-If 'beta' is a touchable unification variable not already bound in the TyBinds
-then we'd like to create a binding for it, effectively "defaulting" it to be 'F xis'.
-
-When is it ok to do so?
- 1) 'beta' must not already be defaulted to something. Example:
-
- [Wanted] F Int ~ beta <~ Will default [beta := F Int]
- [Wanted] F Char ~ beta <~ Already defaulted, can't default again. We
- have to report this as unsolved.
-
- 2) However, we must still do an occurs check when defaulting (F xis ~ beta), to
- set [beta := F xis] only if beta is not among the free variables of xis.
-
- 3) Notice that 'beta' can't be bound in ty binds already because we rewrite RHS
- of type family equations. See Inert Set invariants in TcInteract.
-
-This solving is now happening during zonking, see Note [Unflattening while zonking]
-in TcMType.
-
*********************************************************************************
* *
diff --git a/compiler/typecheck/TcType.hs b/compiler/typecheck/TcType.hs
index 99927aab38..e2644c537f 100644
--- a/compiler/typecheck/TcType.hs
+++ b/compiler/typecheck/TcType.hs
@@ -102,6 +102,7 @@ module TcType (
-- * Finding "exact" (non-dead) type variables
exactTyCoVarsOfType, exactTyCoVarsOfTypes,
splitDepVarsOfType, splitDepVarsOfTypes, TcDepVars(..), tcDepVarSet,
+ rewritableTyVarsOfTypes, rewritableTyVarsOfType,
-- * Extracting bound variables
allBoundVariables, allBoundVariabless,
@@ -835,6 +836,23 @@ exactTyCoVarsOfType ty
exactTyCoVarsOfTypes :: [Type] -> TyVarSet
exactTyCoVarsOfTypes tys = mapUnionVarSet exactTyCoVarsOfType tys
+rewritableTyVarsOfTypes :: [TcType] -> TcTyVarSet
+rewritableTyVarsOfTypes tys = mapUnionVarSet rewritableTyVarsOfType tys
+
+rewritableTyVarsOfType :: TcType -> TcTyVarSet
+rewritableTyVarsOfType ty
+ = go ty
+ where
+ go (TyVarTy tv) = unitVarSet tv
+ go (LitTy {}) = emptyVarSet
+ go (TyConApp _ tys) = rewritableTyVarsOfTypes tys
+ go (AppTy fun arg) = go fun `unionVarSet` go arg
+ go (FunTy arg res) = go arg `unionVarSet` go res
+ go ty@(ForAllTy {}) = pprPanic "rewriteableTyVarOfType" (ppr ty)
+ go (CastTy ty _co) = go ty
+ go (CoercionTy _co) = emptyVarSet
+
+
{- *********************************************************************
* *
Bound variables in a type
diff --git a/compiler/typecheck/TcUnify.hs b/compiler/typecheck/TcUnify.hs
index d194321fe4..566594f40b 100644
--- a/compiler/typecheck/TcUnify.hs
+++ b/compiler/typecheck/TcUnify.hs
@@ -1509,8 +1509,10 @@ maybe_sym IsSwapped = mkSymCo
maybe_sym NotSwapped = id
swapOverTyVars :: TcTyVar -> TcTyVar -> Bool
--- See Note [Canonical orientation for tyvar/tyvar equality constraints]
swapOverTyVars tv1 tv2
+ | isFmvTyVar tv1 = False -- See Note [Fmv Orientation Invariant]
+ | isFmvTyVar tv2 = True
+
| Just lvl1 <- metaTyVarTcLevel_maybe tv1
-- If tv1 is touchable, swap only if tv2 is also
-- touchable and it's strictly better to update the latter
@@ -1565,7 +1567,46 @@ canSolveByUnification tclvl tv xi
FlatSkol {} -> False
RuntimeUnk -> True
-{-
+{- Note [Fmv Orientation Invariant]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * We always orient a constraint
+ fmv ~ alpha
+ with fmv on the left, even if alpha is
+ a touchable unification variable
+
+Reason: doing it the other way round would unify alpha:=fmv, but that
+really doesn't add any info to alpha. But a later constraint alpha ~
+Int might unlock everything. Comment:9 of #12526 gives a detailed
+example.
+
+WARNING: I've gone to and fro on this one several times.
+I'm now pretty sure that unifying alpha:=fmv is a bad idea!
+So orienting with fmvs on the left is a good thing.
+
+This example comes from IndTypesPerfMerge. (Others include
+T10226, T10009.)
+ From the ambiguity check for
+ f :: (F a ~ a) => a
+ we get:
+ [G] F a ~ a
+ [WD] F alpha ~ alpha, alpha ~ a
+
+ From Givens we get
+ [G] F a ~ fsk, fsk ~ a
+
+ Now if we flatten we get
+ [WD] alpha ~ fmv, F alpha ~ fmv, alpha ~ a
+
+ Now, if we unified alpha := fmv, we'd get
+ [WD] F fmv ~ fmv, [WD] fmv ~ a
+ And now we are stuck.
+
+So instead the Fmv Orientation Invariant puts te fmv on the
+left, giving
+ [WD] fmv ~ alpha, [WD] F alpha ~ fmv, [WD] alpha ~ a
+
+ Now we get alpha:=a, and everything works out
+
Note [Prevent unification with type families]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We prevent unification with type families because of an uneasy compromise.
diff --git a/testsuite/tests/indexed-types/should_compile/T10226.hs b/testsuite/tests/indexed-types/should_compile/T10226.hs
index 14d3a3eb31..77ce29a69d 100644
--- a/testsuite/tests/indexed-types/should_compile/T10226.hs
+++ b/testsuite/tests/indexed-types/should_compile/T10226.hs
@@ -42,6 +42,30 @@ showFromF fa = undefined
showFromF' :: (Show (FInv b), F (FInv b) ~ b) => b -> String
showFromF' = showFromF
+{- [G] F (FInv b) ~ b
+ [W] FInv (F alpha) ~ alpha
+ [W] F alpha ~ b
+-->
+ [G] g1: FInv b ~ fsk1
+ [G] g2: F fsk1 ~ fsk2
+ [G} g3: fsk2 ~ b
+
+ [W] F alpha ~ fmv1
+ [W] FInv fmv1 ~ fmv2
+ [W] fmv2 ~ alpha
+ [W] fmv1 ~ b
+
+ [D] d1: F alpha ~ fmv1
+ [D] d2: FInv fmv1 ~ fmv2
+ [D] d3: fmv2 ~ alpha
+ [D] d4: fmv1 ~ b
+
+--> d2 + d4: [D] FInv b ~ fmv2
+ + g1: [D] fmv2 ~ b
+--> d3: b ~ alpha, and we are done
+
+-}
+
{-------------------------------------------------------------------------------
In 7.10 the definition of showFromF' is not accepted, but it gets stranger.
In 7.10 we cannot _call_ showFromF at all, even at a concrete type. Below
@@ -57,3 +81,36 @@ type instance FInv Int = Int
test :: String
test = showFromF (0 :: Int)
+
+{-
+
+ [WD] FInv (F alpha) ~ alpha
+ [WD] F alpha ~ Int
+
+-->
+ [WD] F alpha ~ fuv0
+* [WD] FInv fuv0 ~ fuv1
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+
+-->
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+* [D] FInv Int ~ fuv1
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+
+-->
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+* [D] fuv1 ~ Int
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+
+-->
+ [WD] F alpha ~ fuv0
+ [W] FInv fuv0 ~ fuv1
+ [D] alpha := Int
+ [WD] fuv1 ~ alpha
+ [WD] fuv0 ~ Int
+-}
diff --git a/testsuite/tests/indexed-types/should_compile/T10634.hs b/testsuite/tests/indexed-types/should_compile/T10634.hs
index f02cf810da..0b52ef534a 100644
--- a/testsuite/tests/indexed-types/should_compile/T10634.hs
+++ b/testsuite/tests/indexed-types/should_compile/T10634.hs
@@ -21,3 +21,18 @@ instance Convert Int32 where
x :: Int8
x = down 8
+
+{- From x = down 8
+
+[WD] Num alpha
+[WD] Convert alpha
+[WD] Down alpha ~ Int8
+
+--> superclasses
+[D] Up (Down alpha) ~ alpha
+
+--> substitute (Down alpha ~ Int8)
+[D] Up Int8 ~ alpha
+
+--> alpha := Int16
+-}
diff --git a/testsuite/tests/indexed-types/should_compile/T12526.hs b/testsuite/tests/indexed-types/should_compile/T12526.hs
new file mode 100644
index 0000000000..35a653a544
--- /dev/null
+++ b/testsuite/tests/indexed-types/should_compile/T12526.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE TypeFamilies, MonoLocalBinds #-}
+module T12526 where
+
+type family P (s :: * -> *) :: * -> * -> *
+type instance P Signal = Causal
+
+type family S (p :: * -> * -> *) :: * -> *
+type instance S Causal = Signal
+
+class (P (S p) ~ p) => CP p
+instance CP Causal
+
+data Signal a = Signal
+data Causal a b = Causal
+
+shapeModOsci :: CP p => p Float Float
+shapeModOsci = undefined
+
+f :: Causal Float Float -> Bool
+f = undefined
+
+-- This fails
+ping :: Bool
+ping = let osci = shapeModOsci in f osci
+
+
+-- This works
+-- ping :: Bool
+-- ping = f shapeModOsci
+
+
+{-
+
+ osci :: p Float Float
+ [W] CP p, [D] P (S p) ~ p
+-->
+ [W] CP p, [D] P fuv1 ~ fuv2, S p ~ fuv1, fuv2 ~ p
+-->
+ p := fuv2
+ [W] CP fuv2, [D] P fuv1 ~ fuv2, S fuv2 ~ fuv1
+
+-}
+
+-- P (S p) ~ p
+-- p Float Float ~ Causal Float Float
+
+
+{-
+ P (S p) ~ p
+ p Float Float ~ Causal Float Float
+
+--->
+ S p ~ fuv1 (FunEq)
+ P fuv1 ~ fuv2 (FunEq)
+ fuv2 ~ p
+ p F F ~ Causal F F
+
+--->
+ p := fuv2
+
+ fuv2 ~ Causal
+ S fuv2 ~ fuv1 (FunEq)
+ P fuv1 ~ fuv2 (FunEq)
+
+---> unflatten
+ fuv1 := S fuv2
+ fuv2 := Causal
+
+-}
diff --git a/testsuite/tests/indexed-types/should_compile/T12538.hs b/testsuite/tests/indexed-types/should_compile/T12538.hs
new file mode 100644
index 0000000000..9aff36e47d
--- /dev/null
+++ b/testsuite/tests/indexed-types/should_compile/T12538.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module T12538 where
+
+data Tagged t a = Tagged a
+
+type family Tag a where
+ Tag (Tagged t a) = Tagged t a
+ Tag a = Tagged Int a
+
+class (r ~ Tag a) => TagImpl a r | a -> r where
+ tag :: a -> r
+
+instance {-# OVERLAPPING #-} (r ~ Tag (Tagged t a)) => TagImpl (Tagged t a) r where
+ tag = id
+
+#ifdef WRONG
+instance {-# OVERLAPPING #-} (r ~ Tagged t a, r ~ Tag a) => TagImpl a r where
+#else
+instance {-# OVERLAPPING #-} (r ~ Tagged Int a, r ~ Tag a) => TagImpl a r where
+#endif
+ tag = Tagged @Int
+
+data family DF x
+data instance DF (Tagged t a) = DF (Tagged t a)
+
+class ToDF a b | a -> b where
+ df :: a -> b
+
+instance (TagImpl a a', b ~ DF a') => ToDF a b where
+ df = DF . tag
+
+main :: IO ()
+main = pure ()
diff --git a/testsuite/tests/indexed-types/should_compile/T12538.stderr b/testsuite/tests/indexed-types/should_compile/T12538.stderr
new file mode 100644
index 0000000000..ca106246e7
--- /dev/null
+++ b/testsuite/tests/indexed-types/should_compile/T12538.stderr
@@ -0,0 +1,13 @@
+
+T12538.hs:37:8: error:
+ • Could not deduce: a' ~ Tagged Int a
+ from the context: (TagImpl a a', b ~ DF a')
+ bound by the instance declaration at T12538.hs:36:10-46
+ ‘a'’ is a rigid type variable bound by
+ the instance declaration at T12538.hs:36:10-46
+ Expected type: a -> b
+ Actual type: a -> DF (Tagged Int a)
+ • In the expression: DF . tag
+ In an equation for ‘df’: df = DF . tag
+ In the instance declaration for ‘ToDF a b’
+ • Relevant bindings include df :: a -> b (bound at T12538.hs:37:3)
diff --git a/testsuite/tests/indexed-types/should_compile/T3017.stderr b/testsuite/tests/indexed-types/should_compile/T3017.stderr
index 29877bf2aa..b11c95eb6c 100644
--- a/testsuite/tests/indexed-types/should_compile/T3017.stderr
+++ b/testsuite/tests/indexed-types/should_compile/T3017.stderr
@@ -4,7 +4,7 @@ TYPE SIGNATURES
emptyL :: forall a. ListColl a
insert :: forall c. Coll c => Elem c -> c -> c
test2 ::
- forall a b c. (Elem c ~ (a, b), Coll c, Num a, Num b) => c -> c
+ forall a b c. (Elem c ~ (a, b), Num b, Num a, Coll c) => c -> c
TYPE CONSTRUCTORS
class Coll c where
type family Elem c :: * open
diff --git a/testsuite/tests/indexed-types/should_compile/T4338.hs b/testsuite/tests/indexed-types/should_compile/T4338.hs
index 6fa2ae85ac..64e224e11d 100644
--- a/testsuite/tests/indexed-types/should_compile/T4338.hs
+++ b/testsuite/tests/indexed-types/should_compile/T4338.hs
@@ -17,7 +17,40 @@ instance Foo Char Int where
tickle = (+1)
test :: (Foo a b) => a -> a
-test = back . tickle . there
+test x = back (tickle (there x))
main :: IO ()
main = print $ test 'F'
+
+{-
+
+[G] Foo a b
+[G] There a ~ b
+[G] Back b ~ a
+
+[W] Foo a beta -- from 'there'
+[W] Foo alpha beta -- from tickle
+[W] Foo a beta -- from back
+
+[D] There a ~ beta
+[D] Back beta ~ a
+
+[D] There alpha ~ beta
+[D] Back beta ~ alpha
+
+
+-- Hence beta = b
+-- alpha = a
+
+
+
+[W] Foo a (There t_a1jL)
+[W] Foo t_a1jL (There t_a1jL)
+[W] Back (There t_a1jL) ~ t_a1jL
+
+[D] There a ~ There t_a1jL
+ hence There t_a1jL ~ b
+[D] Back (There t_a1jL) ~ a
+[D] There t_a1jL ~ There t_a1jL
+[D] Back (There t_a1jL) ~ t_a1jL
+-}
diff --git a/testsuite/tests/indexed-types/should_compile/all.T b/testsuite/tests/indexed-types/should_compile/all.T
index b093128a41..eb71a2866e 100644
--- a/testsuite/tests/indexed-types/should_compile/all.T
+++ b/testsuite/tests/indexed-types/should_compile/all.T
@@ -278,3 +278,5 @@ test('T12175', normal, compile, [''])
test('T12522', normal, compile, [''])
test('T12522b', normal, compile, [''])
test('T12676', normal, compile, [''])
+test('T12526', normal, compile, [''])
+test('T12538', normal, compile_fail, [''])
diff --git a/testsuite/tests/indexed-types/should_fail/T2544.stderr b/testsuite/tests/indexed-types/should_fail/T2544.stderr
index 6375c8f79e..b943db2087 100644
--- a/testsuite/tests/indexed-types/should_fail/T2544.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T2544.stderr
@@ -1,4 +1,16 @@
+T2544.hs:17:12: error:
+ • Couldn't match type ‘IxMap r’ with ‘IxMap i1’
+ Expected type: IxMap (l :|: r) [Int]
+ Actual type: BiApp (IxMap l) (IxMap i1) [Int]
+ NB: ‘IxMap’ is a type function, and may not be injective
+ The type variable ‘i1’ is ambiguous
+ • In the expression: BiApp empty empty
+ In an equation for ‘empty’: empty = BiApp empty empty
+ In the instance declaration for ‘Ix (l :|: r)’
+ • Relevant bindings include
+ empty :: IxMap (l :|: r) [Int] (bound at T2544.hs:17:4)
+
T2544.hs:17:18: error:
• Couldn't match type ‘IxMap i0’ with ‘IxMap l’
Expected type: IxMap l [Int]
@@ -10,15 +22,3 @@ T2544.hs:17:18: error:
In an equation for ‘empty’: empty = BiApp empty empty
• Relevant bindings include
empty :: IxMap (l :|: r) [Int] (bound at T2544.hs:17:4)
-
-T2544.hs:17:24: error:
- • Couldn't match type ‘IxMap i1’ with ‘IxMap r’
- Expected type: IxMap r [Int]
- Actual type: IxMap i1 [Int]
- NB: ‘IxMap’ is a type function, and may not be injective
- The type variable ‘i1’ is ambiguous
- • In the second argument of ‘BiApp’, namely ‘empty’
- In the expression: BiApp empty empty
- In an equation for ‘empty’: empty = BiApp empty empty
- • Relevant bindings include
- empty :: IxMap (l :|: r) [Int] (bound at T2544.hs:17:4)
diff --git a/testsuite/tests/indexed-types/should_fail/T2627b.stderr b/testsuite/tests/indexed-types/should_fail/T2627b.stderr
index 11e1b8e8fd..63f11b97f1 100644
--- a/testsuite/tests/indexed-types/should_fail/T2627b.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T2627b.stderr
@@ -1,9 +1,9 @@
T2627b.hs:20:24: error:
• Occurs check: cannot construct the infinite type:
- t0 ~ Dual (Dual t0)
+ b0 ~ Dual (Dual b0)
arising from a use of ‘conn’
- The type variable ‘t0’ is ambiguous
+ The type variable ‘b0’ is ambiguous
• In the expression: conn undefined undefined
In an equation for ‘conn’:
conn (Rd k) (Wr a r) = conn undefined undefined
diff --git a/testsuite/tests/indexed-types/should_fail/T3330c.stderr b/testsuite/tests/indexed-types/should_fail/T3330c.stderr
index 8526c17f5e..829bca1400 100644
--- a/testsuite/tests/indexed-types/should_fail/T3330c.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T3330c.stderr
@@ -3,14 +3,14 @@ T3330c.hs:23:43: error:
• Couldn't match kind ‘* -> *’ with ‘*’
When matching types
f1 :: * -> *
- Der f1 x :: *
- Expected type: Der ((->) x) (Der f1 x)
+ f1 x :: *
+ Expected type: Der ((->) x) (f1 x)
Actual type: R f1
• In the first argument of ‘plug’, namely ‘rf’
In the first argument of ‘Inl’, namely ‘(plug rf df x)’
In the expression: Inl (plug rf df x)
• Relevant bindings include
x :: x (bound at T3330c.hs:23:29)
- df :: Der f1 x (bound at T3330c.hs:23:25)
+ df :: f1 x (bound at T3330c.hs:23:25)
rf :: R f1 (bound at T3330c.hs:23:13)
plug' :: R f -> Der f x -> x -> f x (bound at T3330c.hs:23:1)
diff --git a/testsuite/tests/indexed-types/should_fail/T4179.stderr b/testsuite/tests/indexed-types/should_fail/T4179.stderr
index 91244c4ce7..516bdf3802 100644
--- a/testsuite/tests/indexed-types/should_fail/T4179.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T4179.stderr
@@ -1,13 +1,13 @@
T4179.hs:26:16: error:
- • Couldn't match type ‘A3 (x (A2 (FCon x) -> A3 (FCon x)))’
- with ‘A3 (FCon x)’
+ • Couldn't match type ‘A2 (x (A2 (FCon x) -> A3 (FCon x)))’
+ with ‘A2 (FCon x)’
Expected type: x (A2 (FCon x) -> A3 (FCon x))
-> A2 (FCon x) -> A3 (FCon x)
Actual type: x (A2 (FCon x) -> A3 (FCon x))
-> A2 (x (A2 (FCon x) -> A3 (FCon x)))
-> A3 (x (A2 (FCon x) -> A3 (FCon x)))
- NB: ‘A3’ is a type function, and may not be injective
+ NB: ‘A2’ is a type function, and may not be injective
• In the first argument of ‘foldDoC’, namely ‘op’
In the expression: foldDoC op
In an equation for ‘fCon’: fCon = foldDoC op
diff --git a/testsuite/tests/indexed-types/should_fail/T6123.stderr b/testsuite/tests/indexed-types/should_fail/T6123.stderr
index 3c77040f95..0ae1a5e3c1 100644
--- a/testsuite/tests/indexed-types/should_fail/T6123.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T6123.stderr
@@ -1,9 +1,9 @@
T6123.hs:10:14: error:
- • Occurs check: cannot construct the infinite type: t0 ~ Id t0
+ • Occurs check: cannot construct the infinite type: a0 ~ Id a0
arising from a use of ‘cid’
- The type variable ‘t0’ is ambiguous
+ The type variable ‘a0’ is ambiguous
• In the expression: cid undefined
In an equation for ‘cundefined’: cundefined = cid undefined
• Relevant bindings include
- cundefined :: t0 (bound at T6123.hs:10:1)
+ cundefined :: a0 (bound at T6123.hs:10:1)
diff --git a/testsuite/tests/indexed-types/should_fail/T7786.hs b/testsuite/tests/indexed-types/should_fail/T7786.hs
index 839a1fb83d..2a5c7f5983 100644
--- a/testsuite/tests/indexed-types/should_fail/T7786.hs
+++ b/testsuite/tests/indexed-types/should_fail/T7786.hs
@@ -55,17 +55,17 @@ type family Intersect (l :: Inventory a) (r :: Inventory a) :: Inventory a where
Intersect l Empty = Empty
Intersect (More ls l) r = InterAppend (Intersect ls r) r l
-type family InterAppend (l :: Inventory a)
- (r :: Inventory a)
- (one :: a)
+type family InterAppend (l :: Inventory a)
+ (r :: Inventory a)
+ (one :: a)
:: Inventory a where
InterAppend acc Empty one = acc
InterAppend acc (More rs one) one = More acc one
InterAppend acc (More rs r) one = InterAppend acc rs one
-type family BuriedUnder (sub :: Inventory [KeySegment])
- (post :: [KeySegment])
- (inv :: Inventory [KeySegment])
+type family BuriedUnder (sub :: Inventory [KeySegment])
+ (post :: [KeySegment])
+ (inv :: Inventory [KeySegment])
:: Inventory [KeySegment] where
BuriedUnder Empty post inv = inv
BuriedUnder (More ps p) post inv = More ((ps `BuriedUnder` post) inv) (p `Under` post)
@@ -82,9 +82,23 @@ foo :: Database inv
foo db k sub = buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db
-}
+foogle :: Database inv
+ -> Sing post
+ -> Database sub
+ -> Maybe (Sing (Intersect (BuriedUnder sub post 'Empty) inv))
+
+foogle db k sub = return (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
+
+
addSub :: Database inv -> Sing k -> Database sub -> Maybe (Database ((sub `BuriedUnder` k) inv))
-addSub db k sub = do Nil :: Sing xxx <- return (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
+addSub db k sub = do Nil :: Sing xxx <- foogle db k sub
+ return $ Sub db k sub
+
+{-
+addSub :: Database inv -> Sing k -> Database sub -> Maybe (Database ((sub `BuriedUnder` k) inv))
+addSub db k sub = do Nil :: Sing xxx <- foogle db sub k
-- Nil :: Sing ((sub `BuriedUnder` k) Empty `Intersect` inv) <- return (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
-- Nil :: Sing Empty <- return (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
-- Nil <- return (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
return $ Sub db k sub
+-}
diff --git a/testsuite/tests/indexed-types/should_fail/T7786.stderr b/testsuite/tests/indexed-types/should_fail/T7786.stderr
index ca3e9ecbda..8fdb49bd8e 100644
--- a/testsuite/tests/indexed-types/should_fail/T7786.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T7786.stderr
@@ -1,29 +1,29 @@
-T7786.hs:86:49: error:
+T7786.hs:94:41: error:
• Couldn't match type ‘xxx’
with ‘Intersect (BuriedUnder sub k 'Empty) inv’
- Expected type: Sing xxx
- Actual type: Sing (Intersect (BuriedUnder sub k 'Empty) inv)
- • In the first argument of ‘return’, namely
- ‘(buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)’
- In a stmt of a 'do' block:
- Nil :: Sing xxx <- return
- (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db)
+ Expected type: Maybe (Sing xxx)
+ Actual type: Maybe
+ (Sing (Intersect (BuriedUnder sub k 'Empty) inv))
+ • In a stmt of a 'do' block: Nil :: Sing xxx <- foogle db k sub
In the expression:
- do { Nil :: Sing xxx <- return
- (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db);
+ do { Nil :: Sing xxx <- foogle db k sub;
return $ Sub db k sub }
+ In an equation for ‘addSub’:
+ addSub db k sub
+ = do { Nil :: Sing xxx <- foogle db k sub;
+ return $ Sub db k sub }
• Relevant bindings include
- sub :: Database sub (bound at T7786.hs:86:13)
- k :: Sing k (bound at T7786.hs:86:11)
- db :: Database inv (bound at T7786.hs:86:8)
+ sub :: Database sub (bound at T7786.hs:94:13)
+ k :: Sing k (bound at T7786.hs:94:11)
+ db :: Database inv (bound at T7786.hs:94:8)
addSub :: Database inv
-> Sing k
-> Database sub
-> Maybe (Database (BuriedUnder sub k inv))
- (bound at T7786.hs:86:1)
+ (bound at T7786.hs:94:1)
-T7786.hs:90:31: error:
+T7786.hs:95:31: error:
• Could not deduce: Intersect (BuriedUnder sub k 'Empty) inv
~
'Empty
@@ -32,19 +32,18 @@ T7786.hs:90:31: error:
bound by a pattern with constructor: Nil :: forall a. Sing 'Empty,
in a pattern binding in
'do' block
- at T7786.hs:86:22-24
+ at T7786.hs:94:22-24
• In the second argument of ‘($)’, namely ‘Sub db k sub’
In a stmt of a 'do' block: return $ Sub db k sub
In the expression:
- do { Nil :: Sing xxx <- return
- (buryUnder (dbKeys sub) k Nil `intersectPaths` dbKeys db);
+ do { Nil :: Sing xxx <- foogle db k sub;
return $ Sub db k sub }
• Relevant bindings include
- sub :: Database sub (bound at T7786.hs:86:13)
- k :: Sing k (bound at T7786.hs:86:11)
- db :: Database inv (bound at T7786.hs:86:8)
+ sub :: Database sub (bound at T7786.hs:94:13)
+ k :: Sing k (bound at T7786.hs:94:11)
+ db :: Database inv (bound at T7786.hs:94:8)
addSub :: Database inv
-> Sing k
-> Database sub
-> Maybe (Database (BuriedUnder sub k inv))
- (bound at T7786.hs:86:1)
+ (bound at T7786.hs:94:1)
diff --git a/testsuite/tests/indexed-types/should_fail/T8227.stderr b/testsuite/tests/indexed-types/should_fail/T8227.stderr
index 6dec5d0191..88f4732df1 100644
--- a/testsuite/tests/indexed-types/should_fail/T8227.stderr
+++ b/testsuite/tests/indexed-types/should_fail/T8227.stderr
@@ -1,10 +1,11 @@
-T8227.hs:16:44: error:
- • Couldn't match expected type ‘Scalar (V (Scalar (V a)))’
- with actual type ‘Scalar (V a)’
- NB: ‘Scalar’ is a type function, and may not be injective
- • In the first argument of ‘arcLengthToParam’, namely ‘eps’
- In the expression: arcLengthToParam eps eps
+T8227.hs:16:27: error:
+ • Couldn't match type ‘Scalar (V a)’
+ with ‘Scalar (V a) -> Scalar (V a)’
+ Expected type: Scalar (V a)
+ Actual type: Scalar (V (Scalar (V a) -> Scalar (V a)))
+ -> Scalar (V (Scalar (V a) -> Scalar (V a)))
+ • In the expression: arcLengthToParam eps eps
In an equation for ‘absoluteToParam’:
absoluteToParam eps seg = arcLengthToParam eps eps
• Relevant bindings include
diff --git a/testsuite/tests/partial-sigs/should_compile/T10403.stderr b/testsuite/tests/partial-sigs/should_compile/T10403.stderr
index 23c059e720..0588c1b5bc 100644
--- a/testsuite/tests/partial-sigs/should_compile/T10403.stderr
+++ b/testsuite/tests/partial-sigs/should_compile/T10403.stderr
@@ -60,18 +60,3 @@ T10403.hs:28:8: warning: [-Wdeferred-type-errors (in -Wdefault)]
In an equation for ‘app2’: app2 = h2 (H . I) (B ())
• Relevant bindings include
app2 :: H (B t) (bound at T10403.hs:28:1)
-
-T10403.hs:28:20: warning: [-Wdeferred-type-errors (in -Wdefault)]
- • Couldn't match type ‘f0’ with ‘B t’
- because type variable ‘t’ would escape its scope
- This (rigid, skolem) type variable is bound by
- the type signature for:
- app2 :: H (B t)
- at T10403.hs:27:1-15
- Expected type: f0 ()
- Actual type: B t ()
- • In the second argument of ‘h2’, namely ‘(B ())’
- In the expression: h2 (H . I) (B ())
- In an equation for ‘app2’: app2 = h2 (H . I) (B ())
- • Relevant bindings include
- app2 :: H (B t) (bound at T10403.hs:28:1)
diff --git a/testsuite/tests/perf/compiler/T5837.hs b/testsuite/tests/perf/compiler/T5837.hs
index 6ebbd65bd5..0a500fb826 100644
--- a/testsuite/tests/perf/compiler/T5837.hs
+++ b/testsuite/tests/perf/compiler/T5837.hs
@@ -13,26 +13,35 @@ t = undefined
[G] a ~ TF (a,Int) -- a = a_am1
-->
[G] TF (a,Int) ~ fsk -- fsk = fsk_am8
+
inert [G] fsk ~ a
---->
- [G] fsk ~ (TF a, TF Int)
+---> reduce
+ [G] fsk ~ (TF a, TF Int)
+
inert [G] fsk ~ a
---->
- a ~ (TF a, TF Int)
+---> substitute for fsk and flatten
+ [G] TF a ~ fsk1
+ [G] TF Int ~ fsk2
+
inert [G] fsk ~ a
+ [G] a ~ (fsk1, fsk2)
----> (attempting to flatten (TF a) so that it does not mention a
- TF a ~ fsk2
-inert a ~ (fsk2, TF Int)
-inert fsk ~ (fsk2, TF Int)
+---> (substitute for a in first constraint)
+ TF (fsk1, fsk2) ~ fsk1 (C1)
+ TF Int ~ fsk2
----> (substitute for a)
- TF (fsk2, TF Int) ~ fsk2
inert a ~ (fsk2, TF Int)
inert fsk ~ (fsk2, TF Int)
+
+------- At this point we are stuck because of
+-- the recursion in the first constraint C1
+-- Hooray
+
+-- Before, we reduced C1, which led to a loop
+
---> (top-level reduction, re-orient)
fsk2 ~ (TF fsk2, TF Int)
inert a ~ (fsk2, TF Int)
@@ -50,4 +59,4 @@ inert fsk2 ~ (fsk3, TF Int)
inert a ~ ((fsk3, TF Int), TF Int)
inert fsk ~ ((fsk3, TF Int), TF Int)
--} \ No newline at end of file
+-}
diff --git a/testsuite/tests/perf/compiler/T5837.stderr b/testsuite/tests/perf/compiler/T5837.stderr
deleted file mode 100644
index 324e817947..0000000000
--- a/testsuite/tests/perf/compiler/T5837.stderr
+++ /dev/null
@@ -1,91 +0,0 @@
-
-T5837.hs:8:6: error:
- Reduction stack overflow; size = 51
- When simplifying the following type:
- TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- a))))))))))))))))))))))))
- ~ (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- a))))))))))))))))))))))))),
- TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- (TF
- Int))))))))))))))))))))))))))
- Use -freduction-depth=0 to disable this check
- (any upper bound you could choose might fail unpredictably with
- minor updates to GHC, so disabling the check is recommended if
- you're sure that type checking should terminate)
- In the ambiguity check for ‘t’
- In the type signature:
- t :: (a ~ TF (a, Int)) => Int
diff --git a/testsuite/tests/perf/compiler/all.T b/testsuite/tests/perf/compiler/all.T
index 72c58c7592..7c8f55ab24 100644
--- a/testsuite/tests/perf/compiler/all.T
+++ b/testsuite/tests/perf/compiler/all.T
@@ -594,7 +594,7 @@ test('T5837',
# 2014-12-08: 115905208 Constraint solver perf improvements (esp kick-out)
# 2016-04-06: 24199320 (x86/Linux, 64-bit machine) TypeInType
- (wordsize(64), 41832056, 10)])
+ (wordsize(64), 52597024, 10)])
# sample: 3926235424 (amd64/Linux, 15/2/2012)
# 2012-10-02 81879216
# 2012-09-20 87254264 amd64/Linux
@@ -615,8 +615,11 @@ test('T5837',
# for other optimisations
# 2016-09-15 42445672 Linux; fixing #12422
# 2016-09-25 41832056 amd64/Linux, Rework handling of names (D2469)
+ # 2016-10-25 52597024 amd64/Linux, the test now passes (hooray), and so
+ # allocates more because it goes right down the
+ # compilation pipeline
],
- compile_fail,['-freduction-depth=50'])
+ compile, ['-freduction-depth=50'])
test('T6048',
[ only_ways(['optasm']),
diff --git a/testsuite/tests/polykinds/T12444.hs b/testsuite/tests/polykinds/T12444.hs
new file mode 100644
index 0000000000..746c6448ef
--- /dev/null
+++ b/testsuite/tests/polykinds/T12444.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module T12444 where
+
+data Nat = Zero | Succ Nat
+
+data SNat (n :: Nat) where
+ SZero :: SNat Zero
+ SSucc :: SNat n -> SNat (Succ n)
+
+type family (:+:) (a :: Nat) (b :: Nat) :: Nat where
+ m :+: Zero = m
+ m :+: (Succ n) = Succ (m :+: n)
+
+foo :: SNat (Succ c) -> SNat b -> SNat (Succ (c :+: b))
+foo _ x = x
+
+
+{-
+sadd :: ((Succ n1 :+: n) ~ Succ (n1 :+: n), (Succ n1) ~ m)
+ => SNat m -> SNat n -> SNat (m :+: n)
+sadd SZero n = n
+-}
+
+{- [G] a ~ Succ c
+ Succ c + b ~ Succ (c+b)
+ a ~ Zero
+
+-->
+ Zero ~ Succ c TyEq
+ fsk1 ~ Succ fsk2 TyEq
+ fsk1 ~ (Succ c) + b FunEq
+ fsk2 ~ c+b FunEq
+----
+[W] b ~ a+b ---> b ~ Succ (c+b)
+
+
+Derived
+ a ~ Succ c
+ fsk1 ~ Succ fsk2
+ b ~ Succ fsk2
+
+work (Succ c) + b ~ fsk1
+ c+b ~ fsk2
+
+
+-}
+
+{-
+
+[G] a ~ [b]
+--> Derived shadow [D] a ~ [b]
+ When adding this to the model
+ don't kick out a derived shadow again!
+
+[G] (a ~ b) --> sc a ~~ b, a ~# b
+ Silly to then kick out (a~b) and (a~~b)
+ and rewrite them to (a~a) and (a~~a)
+
+Insoluble constraint does not halt solving;
+indeed derived version stays. somehow
+-}
diff --git a/testsuite/tests/polykinds/T12444.stderr b/testsuite/tests/polykinds/T12444.stderr
new file mode 100644
index 0000000000..0ebd2986cf
--- /dev/null
+++ b/testsuite/tests/polykinds/T12444.stderr
@@ -0,0 +1,16 @@
+
+T12444.hs:19:11: error:
+ • Couldn't match type ‘b’ with ‘'Succ (c :+: b)’
+ ‘b’ is a rigid type variable bound by
+ the type signature for:
+ foo :: forall (c :: Nat) (b :: Nat).
+ SNat ('Succ c) -> SNat b -> SNat ('Succ (c :+: b))
+ at T12444.hs:18:1-55
+ Expected type: SNat ('Succ (c :+: b))
+ Actual type: SNat b
+ • In the expression: x
+ In an equation for ‘foo’: foo _ x = x
+ • Relevant bindings include
+ x :: SNat b (bound at T12444.hs:19:7)
+ foo :: SNat ('Succ c) -> SNat b -> SNat ('Succ (c :+: b))
+ (bound at T12444.hs:19:1)
diff --git a/testsuite/tests/polykinds/T9222.stderr b/testsuite/tests/polykinds/T9222.stderr
index df97578029..dc1b92c202 100644
--- a/testsuite/tests/polykinds/T9222.stderr
+++ b/testsuite/tests/polykinds/T9222.stderr
@@ -1,12 +1,12 @@
T9222.hs:13:3: error:
- • Couldn't match type ‘b0’ with ‘b’
- ‘b0’ is untouchable
+ • Couldn't match type ‘c0’ with ‘c’
+ ‘c0’ is untouchable
inside the constraints: a ~ '(b0, c0)
bound by the type of the constructor ‘Want’:
a ~ '(b0, c0) => Proxy b0
at T9222.hs:13:3
- ‘b’ is a rigid type variable bound by
+ ‘c’ is a rigid type variable bound by
the type of the constructor ‘Want’:
forall i1 j1 (a :: (i1, j1)) (b :: i1) (c :: j1).
(a ~ '(b, c) => Proxy b) -> Want a
diff --git a/testsuite/tests/polykinds/all.T b/testsuite/tests/polykinds/all.T
index 6ec2a439e6..0e0d66a676 100644
--- a/testsuite/tests/polykinds/all.T
+++ b/testsuite/tests/polykinds/all.T
@@ -154,3 +154,4 @@ test('T12055a', normal, compile_fail, [''])
test('T12593', normal, compile_fail, [''])
test('T12668', normal, compile, [''])
test('T12718', normal, compile, [''])
+test('T12444', normal, compile_fail, [''])
diff --git a/testsuite/tests/typecheck/should_compile/Improvement.hs b/testsuite/tests/typecheck/should_compile/Improvement.hs
index e7e11901a8..8df81c26a7 100644
--- a/testsuite/tests/typecheck/should_compile/Improvement.hs
+++ b/testsuite/tests/typecheck/should_compile/Improvement.hs
@@ -1,5 +1,15 @@
{-# LANGUAGE TypeFamilies, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+
+-- This one relies on doing instance reduction
+-- on a /derived/ class
+-- [W] C (F a0) a0, F a0 ~ Bool
+-- Currently (Oct 16) I've disabled this because it seems like
+-- overkill.
+--
+-- See Note Note [No reduction for Derived class constraints]
+-- in TcInteract
+
module Foo where
type family F a
@@ -14,5 +24,5 @@ blug = error "Urk"
foo :: Bool
foo = blug undefined
--- [W] C (F a0) a0, F a0 ~ Bool
+
diff --git a/testsuite/tests/typecheck/should_compile/T6018.hs b/testsuite/tests/typecheck/should_compile/T6018.hs
index 5fdb03b220..91a67c5e57 100644
--- a/testsuite/tests/typecheck/should_compile/T6018.hs
+++ b/testsuite/tests/typecheck/should_compile/T6018.hs
@@ -8,6 +8,38 @@
module T6018 where
+{-
+barapp2 :: Int
+barapp2 = bar 1
+
+bar :: Bar a -> Bar a
+bar x = x
+
+type family Bar a = r | r -> a where
+ Bar Int = Bool
+ Bar Bool = Int
+ Bar Bool = Char
+
+type family F a b c = (result :: k) | result -> a b c
+
+type family FClosed a b c = result | result -> a b c where
+ FClosed Int Char Bool = Bool
+ FClosed Char Bool Int = Int
+ FClosed Bool Int Char = Char
+-}
+{-
+g6_use :: [Char]
+g6_use = g6_id "foo"
+
+g6_id :: G6 a -> G6 a
+g6_id x = x
+
+type family G6 a = r | r -> a
+type instance G6 [a] = [Gi a]
+type family Gi a = r | r -> a
+type instance Gi Int = Char
+-}
+
import T6018a -- defines G, identical to F
type family F a b c = (result :: k) | result -> a b c
diff --git a/testsuite/tests/typecheck/should_compile/T6018.stderr b/testsuite/tests/typecheck/should_compile/T6018.stderr
index 91d6b300d6..57eed50c46 100644
--- a/testsuite/tests/typecheck/should_compile/T6018.stderr
+++ b/testsuite/tests/typecheck/should_compile/T6018.stderr
@@ -2,10 +2,10 @@
[2 of 3] Compiling T6018a (.hs -> .o)
[3 of 3] Compiling T6018 (.hs -> .o)
-T6018.hs:75:5: warning:
+T6018.hs:107:5: warning:
Type family instance equation is overlapped:
- Foo Bool = Bool -- Defined at T6018.hs:75:5
+ Foo Bool = Bool -- Defined at T6018.hs:107:5
-T6018.hs:82:5: warning:
+T6018.hs:114:5: warning:
Type family instance equation is overlapped:
- Bar Bool = Char -- Defined at T6018.hs:82:5
+ Bar Bool = Char -- Defined at T6018.hs:114:5
diff --git a/testsuite/tests/typecheck/should_fail/ContextStack2.hs b/testsuite/tests/typecheck/should_fail/ContextStack2.hs
index f3f93eb912..d66103c880 100644
--- a/testsuite/tests/typecheck/should_fail/ContextStack2.hs
+++ b/testsuite/tests/typecheck/should_fail/ContextStack2.hs
@@ -5,6 +5,8 @@ module ContextStack2 where
type family TF a :: *
type instance TF (a,b) = (TF a, TF b)
+-- Succeeds with new approach to fuvs
+-- Aug 2016
t :: (a ~ TF (a,Int)) => Int
t = undefined
diff --git a/testsuite/tests/typecheck/should_fail/ContextStack2.stderr b/testsuite/tests/typecheck/should_fail/ContextStack2.stderr
deleted file mode 100644
index f76d1f486c..0000000000
--- a/testsuite/tests/typecheck/should_fail/ContextStack2.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-
-ContextStack2.hs:8:6: error:
- • Reduction stack overflow; size = 11
- When simplifying the following type:
- TF (TF (TF (TF (TF a))))
- ~ (TF (TF (TF (TF (TF (TF a))))), TF (TF (TF (TF (TF (TF Int))))))
- Use -freduction-depth=0 to disable this check
- (any upper bound you could choose might fail unpredictably with
- minor updates to GHC, so disabling the check is recommended if
- you're sure that type checking should terminate)
- • In the ambiguity check for ‘t’
- In the type signature:
- t :: (a ~ TF (a, Int)) => Int
diff --git a/testsuite/tests/typecheck/should_fail/Makefile b/testsuite/tests/typecheck/should_fail/Makefile
index 9101fbd40a..f994435d03 100644
--- a/testsuite/tests/typecheck/should_fail/Makefile
+++ b/testsuite/tests/typecheck/should_fail/Makefile
@@ -1,3 +1,8 @@
TOP=../../..
include $(TOP)/mk/boilerplate.mk
include $(TOP)/mk/test.mk
+
+.PHONEY: foo
+
+foo:
+ echo hello
diff --git a/testsuite/tests/typecheck/should_fail/T5691.stderr b/testsuite/tests/typecheck/should_fail/T5691.stderr
index 9d4e587166..ad5c7e452f 100644
--- a/testsuite/tests/typecheck/should_fail/T5691.stderr
+++ b/testsuite/tests/typecheck/should_fail/T5691.stderr
@@ -1,12 +1,12 @@
-T5691.hs:15:24: error:
+T5691.hs:14:9: error:
• Couldn't match type ‘p’ with ‘PrintRuleInterp’
Expected type: PrintRuleInterp a
Actual type: p a
- • In the first argument of ‘printRule_’, namely ‘f’
- In the second argument of ‘($)’, namely ‘printRule_ f’
- In the expression: MkPRI $ printRule_ f
- • Relevant bindings include f :: p a (bound at T5691.hs:14:9)
+ • When checking that the pattern signature: p a
+ fits the type of its context: PrintRuleInterp a
+ In the pattern: f :: p a
+ In an equation for ‘test’: test (f :: p a) = MkPRI $ printRule_ f
T5691.hs:24:10: error:
• No instance for (Alternative RecDecParser)
diff --git a/testsuite/tests/typecheck/should_fail/T5853.stderr b/testsuite/tests/typecheck/should_fail/T5853.stderr
index 62385ea1df..719895af6c 100644
--- a/testsuite/tests/typecheck/should_fail/T5853.stderr
+++ b/testsuite/tests/typecheck/should_fail/T5853.stderr
@@ -1,22 +1,22 @@
T5853.hs:15:46: error:
- • Could not deduce: Subst (Subst t2 t) t1 ~ Subst t2 t1
+ • Could not deduce: Subst (Subst fa a) b ~ Subst fa b
arising from a use of ‘<$>’
- from the context: (F t2,
- Elem t2 ~ Elem t2,
- Elem (Subst t2 t1) ~ t1,
- Subst t2 t1 ~ Subst t2 t1,
- Subst (Subst t2 t1) (Elem t2) ~ t2,
- F (Subst t2 t),
- Elem (Subst t2 t) ~ t,
- Elem t2 ~ Elem t2,
- Subst (Subst t2 t) (Elem t2) ~ t2,
- Subst t2 t ~ Subst t2 t)
+ from the context: (F fa,
+ Elem fa ~ Elem fa,
+ Elem (Subst fa b) ~ b,
+ Subst fa b ~ Subst fa b,
+ Subst (Subst fa b) (Elem fa) ~ fa,
+ F (Subst fa a),
+ Elem (Subst fa a) ~ a,
+ Elem fa ~ Elem fa,
+ Subst (Subst fa a) (Elem fa) ~ fa,
+ Subst fa a ~ Subst fa a)
bound by the RULE "map/map" at T5853.hs:15:2-57
NB: ‘Subst’ is a type function, and may not be injective
• In the expression: (f . g) <$> xs
When checking the transformation rule "map/map"
• Relevant bindings include
- f :: Elem t2 -> t1 (bound at T5853.hs:15:19)
- g :: t -> Elem t2 (bound at T5853.hs:15:21)
- xs :: Subst t2 t (bound at T5853.hs:15:23)
+ f :: Elem fa -> b (bound at T5853.hs:15:19)
+ g :: a -> Elem fa (bound at T5853.hs:15:21)
+ xs :: Subst fa a (bound at T5853.hs:15:23)
diff --git a/testsuite/tests/typecheck/should_fail/T8450.stderr b/testsuite/tests/typecheck/should_fail/T8450.stderr
index 8ba84a76f1..7503f4d37e 100644
--- a/testsuite/tests/typecheck/should_fail/T8450.stderr
+++ b/testsuite/tests/typecheck/should_fail/T8450.stderr
@@ -1,11 +1,15 @@
-T8450.hs:8:7: error:
- • Couldn't match expected type ‘a’ with actual type ‘()’
+T8450.hs:8:20: error:
+ • Couldn't match type ‘a’ with ‘Bool’
‘a’ is a rigid type variable bound by
the type signature for:
run :: forall a. a
at T8450.hs:7:1-18
- • In the expression: runEffect $ (undefined :: Either a ())
+ Expected type: Either Bool ()
+ Actual type: Either a ()
+ • In the second argument of ‘($)’, namely
+ ‘(undefined :: Either a ())’
+ In the expression: runEffect $ (undefined :: Either a ())
In an equation for ‘run’:
run = runEffect $ (undefined :: Either a ())
• Relevant bindings include run :: a (bound at T8450.hs:8:1)
diff --git a/testsuite/tests/typecheck/should_fail/T9260.stderr b/testsuite/tests/typecheck/should_fail/T9260.stderr
index 0773da2bf5..f55f474904 100644
--- a/testsuite/tests/typecheck/should_fail/T9260.stderr
+++ b/testsuite/tests/typecheck/should_fail/T9260.stderr
@@ -1,7 +1,8 @@
-T9260.hs:12:8: error:
- • Couldn't match type ‘2’ with ‘1’
- Expected type: Fin 1
- Actual type: Fin (1 + 1)
- • In the expression: Fsucc Fzero
+T9260.hs:12:14: error:
+ • Couldn't match type ‘1’ with ‘0’
+ Expected type: Fin 0
+ Actual type: Fin (0 + 1)
+ • In the first argument of ‘Fsucc’, namely ‘Fzero’
+ In the expression: Fsucc Fzero
In an equation for ‘test’: test = Fsucc Fzero
diff --git a/testsuite/tests/typecheck/should_fail/all.T b/testsuite/tests/typecheck/should_fail/all.T
index 4d58d4f0de..6f99a94ff4 100644
--- a/testsuite/tests/typecheck/should_fail/all.T
+++ b/testsuite/tests/typecheck/should_fail/all.T
@@ -336,7 +336,7 @@ test('T8428', normal, compile_fail, [''])
test('T8450', normal, compile_fail, [''])
test('T8514', normal, compile_fail, [''])
test('ContextStack1', normal, compile_fail, ['-freduction-depth=10'])
-test('ContextStack2', normal, compile_fail, ['-freduction-depth=10'])
+test('ContextStack2', normal, compile, [''])
test('T8570', extra_clean(['T85570a.o', 'T8570a.hi','T85570b.o', 'T8570b.hi']),
multimod_compile_fail, ['T8570', '-v0'])
test('T8603', normal, compile_fail, [''])