\begin{code}
module TcSMonad (
WorkList(..), isEmptyWorkList, emptyWorkList,
workListFromEq, workListFromNonEq, workListFromCt,
extendWorkListEq, extendWorkListFunEq,
extendWorkListNonEq, extendWorkListCt,
extendWorkListCts, extendWorkListEqs, appendWorkList, selectWorkItem,
withWorkList, workListSize,
updWorkListTcS, updWorkListTcS_return,
updTcSImplics,
Ct(..), Xi, tyVarsOfCt, tyVarsOfCts,
emitInsoluble,
isWanted, isDerived,
isGivenCt, isWantedCt, isDerivedCt,
canRewrite, canSolve,
mkGivenLoc,
TcS, runTcS, runTcSWithEvBinds, failTcS, panicTcS, traceTcS,
traceFireTcS, bumpStepCountTcS,
tryTcS, nestTcS, nestImplicTcS, recoverTcS,
wrapErrTcS, wrapWarnTcS,
addSolvedDict, addSolvedFunEq, getFlattenSkols,
deferTcSForAllEq,
setEvBind,
XEvTerm(..),
MaybeNew (..), isFresh, freshGoals, getEvTerms,
xCtFlavor,
rewriteCtFlavor,
newWantedEvVar, newWantedEvVarNC, instDFunConstraints,
newDerived,
setWantedTyBind,
getInstEnvs, getFamInstEnvs,
getTopEnv, getGblEnv, getTcEvBinds, getUntouchables,
getTcEvBindsMap, getTcSTyBinds, getTcSTyBindsMap,
lookupFlatEqn, newFlattenSkolem,
Deque(..), insertDeque, emptyDeque,
InertSet(..), InertCans(..),
getInertEqs,
emptyInert, getTcSInerts, lookupInInerts,
getInertUnsolved, checkAllSolved,
prepareInertsForImplications,
modifyInertTcS,
insertInertItemTcS, partitionCCanMap, partitionEqMap,
getRelevantCts, extractRelevantInerts,
CCanMap(..), CtTypeMap, CtFamHeadMap, CtPredMap,
PredMap, FamHeadMap,
partCtFamHeadMap, lookupFamHead, lookupSolvedDict,
filterSolved,
instDFunType,
newFlexiTcSTy, instFlexiTcS, instFlexiTcSHelperTcS,
cloneMetaTyVar,
Untouchables, isTouchableMetaTyVarTcS, isFilledMetaTyVar_maybe,
zonkTyVarsAndFV,
getDefaultInfo, getDynFlags,
matchFam, matchOpenFam,
checkWellStagedDFun,
pprEq
) where
#include "HsVersions.h"
import HscTypes
import Inst
import InstEnv
import FamInst
import FamInstEnv
import qualified TcRnMonad as TcM
import qualified TcMType as TcM
import qualified TcEnv as TcM
( checkWellStaged, topIdLvl, tcGetDefaultTys )
import Kind
import TcType
import DynFlags
import Type
import TcEvidence
import Class
import TyCon
import Name
import Var
import VarEnv
import Outputable
import Bag
import MonadUtils
import FastString
import Util
import Id
import TcRnTypes
import Unique
import UniqFM
import Maybes ( orElse, catMaybes, firstJust )
import Pair ( pSnd )
import Control.Monad( unless, when, zipWithM )
import Data.IORef
import TrieMap
#ifdef DEBUG
import StaticFlags( opt_PprStyle_Debug )
import VarSet
import Digraph
#endif
\end{code}
%************************************************************************
%* *
%* Worklists *
%* Canonical and non-canonical constraints that the simplifier has to *
%* work on. Including their simplification depths. *
%* *
%* *
%************************************************************************
Note [WorkList priorities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
A WorkList contains canonical and non-canonical items (of all flavors).
Notice that each Ct now has a simplification depth. We may
consider using this depth for prioritization as well in the future.
As a simple form of priority queue, our worklist separates out
equalities (wl_eqs) from the rest of the canonical constraints,
so that it's easier to deal with them first, but the separation
is not strictly necessary. Notice that non-canonical constraints
are also parts of the worklist.
Note [NonCanonical Semantics]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that canonical constraints involve a CNonCanonical constructor. In the worklist
we use this constructor for constraints that have not yet been canonicalized such as
[Int] ~ [a]
In other words, all constraints start life as NonCanonicals.
On the other hand, in the Inert Set (see below) the presence of a NonCanonical somewhere
means that we have a ``frozen error''.
NonCanonical constraints never interact directly with other constraints -- but they can
be rewritten by equalities (for instance if a non canonical exists in the inert, we'd
better rewrite it as much as possible before reporting it as an error to the user)
\begin{code}
data Deque a = DQ [a] [a]
instance Outputable a => Outputable (Deque a) where
ppr (DQ as bs) = ppr (as ++ reverse bs)
emptyDeque :: Deque a
emptyDeque = DQ [] []
isEmptyDeque :: Deque a -> Bool
isEmptyDeque (DQ as bs) = null as && null bs
dequeSize :: Deque a -> Int
dequeSize (DQ as bs) = length as + length bs
insertDeque :: a -> Deque a -> Deque a
insertDeque b (DQ as bs) = DQ as (b:bs)
appendDeque :: Deque a -> Deque a -> Deque a
appendDeque (DQ as1 bs1) (DQ as2 bs2) = DQ (as1 ++ reverse bs1 ++ as2) bs2
extractDeque :: Deque a -> Maybe (Deque a, a)
extractDeque (DQ [] []) = Nothing
extractDeque (DQ (a:as) bs) = Just (DQ as bs, a)
extractDeque (DQ [] bs) = case reverse bs of
(a:as) -> Just (DQ as [], a)
[] -> panic "extractDeque"
data WorkList = WorkList { wl_eqs :: [Ct]
, wl_funeqs :: Deque Ct
, wl_rest :: [Ct]
}
appendWorkList :: WorkList -> WorkList -> WorkList
appendWorkList new_wl orig_wl
= WorkList { wl_eqs = wl_eqs new_wl ++ wl_eqs orig_wl
, wl_funeqs = wl_funeqs new_wl `appendDeque` wl_funeqs orig_wl
, wl_rest = wl_rest new_wl ++ wl_rest orig_wl }
workListSize :: WorkList -> Int
workListSize (WorkList { wl_eqs = eqs, wl_funeqs = funeqs, wl_rest = rest })
= length eqs + dequeSize funeqs + length rest
extendWorkListEq :: Ct -> WorkList -> WorkList
extendWorkListEq ct wl
| Just {} <- isCFunEqCan_Maybe ct
= extendWorkListFunEq ct wl
| otherwise
= wl { wl_eqs = ct : wl_eqs wl }
extendWorkListFunEq :: Ct -> WorkList -> WorkList
extendWorkListFunEq ct wl
= wl { wl_funeqs = insertDeque ct (wl_funeqs wl) }
extendWorkListEqs :: [Ct] -> WorkList -> WorkList
extendWorkListEqs cts wl = foldr extendWorkListEq wl cts
extendWorkListNonEq :: Ct -> WorkList -> WorkList
extendWorkListNonEq ct wl
= wl { wl_rest = ct : wl_rest wl }
extendWorkListCt :: Ct -> WorkList -> WorkList
extendWorkListCt ct wl
| isEqPred (ctPred ct) = extendWorkListEq ct wl
| otherwise = extendWorkListNonEq ct wl
extendWorkListCts :: [Ct] -> WorkList -> WorkList
extendWorkListCts cts wl = foldr extendWorkListCt wl cts
isEmptyWorkList :: WorkList -> Bool
isEmptyWorkList wl
= null (wl_eqs wl) && null (wl_rest wl) && isEmptyDeque (wl_funeqs wl)
emptyWorkList :: WorkList
emptyWorkList = WorkList { wl_eqs = [], wl_rest = [], wl_funeqs = emptyDeque }
workListFromEq :: Ct -> WorkList
workListFromEq ct = extendWorkListEq ct emptyWorkList
workListFromNonEq :: Ct -> WorkList
workListFromNonEq ct = extendWorkListNonEq ct emptyWorkList
workListFromCt :: Ct -> WorkList
workListFromCt ct | isEqPred (ctPred ct) = workListFromEq ct
| otherwise = workListFromNonEq ct
selectWorkItem :: WorkList -> (Maybe Ct, WorkList)
selectWorkItem wl@(WorkList { wl_eqs = eqs, wl_funeqs = feqs, wl_rest = rest })
= case (eqs,feqs,rest) of
(ct:cts,_,_) -> (Just ct, wl { wl_eqs = cts })
(_,fun_eqs,_) | Just (fun_eqs', ct) <- extractDeque fun_eqs
-> (Just ct, wl { wl_funeqs = fun_eqs' })
(_,_,(ct:cts)) -> (Just ct, wl { wl_rest = cts })
(_,_,_) -> (Nothing,wl)
instance Outputable WorkList where
ppr wl = vcat [ text "WorkList (eqs) = " <+> ppr (wl_eqs wl)
, text "WorkList (funeqs)= " <+> ppr (wl_funeqs wl)
, text "WorkList (rest) = " <+> ppr (wl_rest wl)
]
data CCanMap a
= CCanMap { cts_given :: UniqFM Cts
, cts_derived :: UniqFM Cts
, cts_wanted :: UniqFM Cts }
keepGivenCMap :: CCanMap a -> CCanMap a
keepGivenCMap cc = emptyCCanMap { cts_given = cts_given cc }
instance Outputable (CCanMap a) where
ppr (CCanMap given derived wanted) = ptext (sLit "CCanMap") <+> (ppr given) <+> (ppr derived) <+> (ppr wanted)
cCanMapToBag :: CCanMap a -> Cts
cCanMapToBag cmap = foldUFM unionBags rest_wder (cts_given cmap)
where rest_wder = foldUFM unionBags rest_der (cts_wanted cmap)
rest_der = foldUFM unionBags emptyCts (cts_derived cmap)
emptyCCanMap :: CCanMap a
emptyCCanMap = CCanMap { cts_given = emptyUFM, cts_derived = emptyUFM, cts_wanted = emptyUFM }
updCCanMap:: Uniquable a => (a,Ct) -> CCanMap a -> CCanMap a
updCCanMap (a,ct) cmap
= case cc_ev ct of
CtWanted {} -> cmap { cts_wanted = insert_into (cts_wanted cmap) }
CtGiven {} -> cmap { cts_given = insert_into (cts_given cmap) }
CtDerived {} -> cmap { cts_derived = insert_into (cts_derived cmap) }
where
insert_into m = addToUFM_C unionBags m a (singleCt ct)
getRelevantCts :: Uniquable a => a -> CCanMap a -> (Cts, CCanMap a)
getRelevantCts a cmap
= let relevant = lookup (cts_wanted cmap) `unionBags`
lookup (cts_given cmap) `unionBags`
lookup (cts_derived cmap)
residual_map = cmap { cts_wanted = delFromUFM (cts_wanted cmap) a
, cts_given = delFromUFM (cts_given cmap) a
, cts_derived = delFromUFM (cts_derived cmap) a }
in (relevant, residual_map)
where
lookup map = lookupUFM map a `orElse` emptyCts
lookupCCanMap :: Uniquable a => a -> (CtEvidence -> Bool) -> CCanMap a -> Maybe CtEvidence
lookupCCanMap a pick_me map
= findEvidence pick_me possible_cts
where
possible_cts = lookupUFM (cts_given map) a `plus` (
lookupUFM (cts_wanted map) a `plus` (
lookupUFM (cts_derived map) a `plus` emptyCts))
plus Nothing cts2 = cts2
plus (Just cts1) cts2 = cts1 `unionBags` cts2
findEvidence :: (CtEvidence -> Bool) -> Cts -> Maybe CtEvidence
findEvidence pick_me cts
= foldrBag pick Nothing cts
where
pick :: Ct -> Maybe CtEvidence -> Maybe CtEvidence
pick ct deflt | let ctev = cc_ev ct, pick_me ctev = Just ctev
| otherwise = deflt
partitionCCanMap :: (Ct -> Bool) -> CCanMap a -> (Cts,CCanMap a)
partitionCCanMap pred cmap
= let (ws_map,ws) = foldUFM_Directly aux (emptyUFM,emptyCts) (cts_wanted cmap)
(ds_map,ds) = foldUFM_Directly aux (emptyUFM,emptyCts) (cts_derived cmap)
(gs_map,gs) = foldUFM_Directly aux (emptyUFM,emptyCts) (cts_given cmap)
in (ws `andCts` ds `andCts` gs, cmap { cts_wanted = ws_map
, cts_given = gs_map
, cts_derived = ds_map })
where aux k this_cts (mp,acc_cts) = (new_mp, new_acc_cts)
where new_mp = addToUFM mp k cts_keep
new_acc_cts = acc_cts `andCts` cts_out
(cts_out, cts_keep) = partitionBag pred this_cts
partitionEqMap :: (Ct -> Bool) -> TyVarEnv (Ct,TcCoercion) -> ([Ct], TyVarEnv (Ct,TcCoercion))
partitionEqMap pred isubst
= let eqs_out = foldVarEnv extend_if_pred [] isubst
eqs_in = filterVarEnv_Directly (\_ (ct,_) -> not (pred ct)) isubst
in (eqs_out, eqs_in)
where extend_if_pred (ct,_) cts = if pred ct then ct : cts else cts
extractUnsolvedCMap :: CCanMap a -> Cts
extractUnsolvedCMap cmap = foldUFM unionBags emptyCts (cts_wanted cmap)
`unionBags` foldUFM unionBags emptyCts (cts_derived cmap)
type CtTypeMap = TypeMap Ct
type CtPredMap = PredMap Ct
type CtFamHeadMap = FamHeadMap Ct
newtype PredMap a = PredMap { unPredMap :: TypeMap a }
newtype FamHeadMap a = FamHeadMap { unFamHeadMap :: TypeMap a }
instance Outputable a => Outputable (PredMap a) where
ppr (PredMap m) = ppr (foldTM (:) m [])
instance Outputable a => Outputable (FamHeadMap a) where
ppr (FamHeadMap m) = ppr (foldTM (:) m [])
sizePredMap :: PredMap a -> Int
sizePredMap (PredMap m) = foldTypeMap (\_ x -> x+1) 0 m
emptyFamHeadMap :: FamHeadMap a
emptyFamHeadMap = FamHeadMap emptyTM
sizeFamHeadMap :: FamHeadMap a -> Int
sizeFamHeadMap (FamHeadMap m) = foldTypeMap (\_ x -> x+1) 0 m
ctTypeMapCts :: TypeMap Ct -> Cts
ctTypeMapCts ctmap = foldTM (\ct cts -> extendCts cts ct) ctmap emptyCts
lookupFamHead :: FamHeadMap a -> TcType -> Maybe a
lookupFamHead (FamHeadMap m) key = lookupTM key m
insertFamHead :: FamHeadMap a -> TcType -> a -> FamHeadMap a
insertFamHead (FamHeadMap m) key value = FamHeadMap (alterTM key (const (Just value)) m)
delFamHead :: FamHeadMap a -> TcType -> FamHeadMap a
delFamHead (FamHeadMap m) key = FamHeadMap (alterTM key (const Nothing) m)
anyFamHeadMap :: (Ct -> Bool) -> CtFamHeadMap -> Bool
anyFamHeadMap f ctmap = foldTM ((||) . f) (unFamHeadMap ctmap) False
partCtFamHeadMap :: (Ct -> Bool)
-> CtFamHeadMap
-> (Cts, CtFamHeadMap)
partCtFamHeadMap f ctmap
= let (cts,tymap_final) = foldTM upd_acc tymap_inside (emptyBag, tymap_inside)
in (cts, FamHeadMap tymap_final)
where
tymap_inside = unFamHeadMap ctmap
upd_acc ct (cts,acc_map)
| f ct = (extendCts cts ct, alterTM ct_key (\_ -> Nothing) acc_map)
| otherwise = (cts,acc_map)
where ct_key | EqPred ty1 _ <- classifyPredType (ctPred ct)
= ty1
| otherwise
= panic "partCtFamHeadMap, encountered non equality!"
filterSolved :: (CtEvidence -> Bool) -> PredMap CtEvidence -> PredMap CtEvidence
filterSolved p (PredMap mp) = PredMap (foldTM upd mp emptyTM)
where upd a m = if p a then alterTM (ctEvPred a) (\_ -> Just a) m
else m
\end{code}
%************************************************************************
%* *
%* Inert Sets *
%* *
%* *
%************************************************************************
Note [Detailed InertCans Invariants]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The InertCans represents a collection of constraints with the following properties:
1 All canonical
2 All Given or Wanted or Derived. No (partially) Solved
3 No two dictionaries with the same head
4 No two family equations with the same head
NB: This is enforced by construction since we use a CtFamHeadMap for inert_funeqs
5 Family equations inert wrt top-level family axioms
6 Dictionaries have no matching top-level instance
7 Non-equality constraints are fully rewritten with respect to the equalities (CTyEqCan)
8 Equalities _do_not_ form an idempotent substitution, but they are
guaranteed to not have any occurs errors. Additional notes:
- The lack of idempotence of the inert substitution implies
that we must make sure that when we rewrite a constraint we
apply the substitution /recursively/ to the types
involved. Currently the one AND ONLY way in the whole
constraint solver that we rewrite types and constraints wrt
to the inert substitution is TcCanonical/flattenTyVar.
- In the past we did try to have the inert substitution as
idempotent as possible but this would only be true for
constraints of the same flavor, so in total the inert
substitution could not be idempotent, due to flavor-related
issued. Note [Non-idempotent inert substitution] explains
what is going on.
- Whenever a constraint ends up in the worklist we do
recursively apply exhaustively the inert substitution to it
to check for occurs errors. But if an equality is already in
the inert set and we can guarantee that adding a new equality
will not cause the first equality to have an occurs check
then we do not rewrite the inert equality. This happens in
TcInteract, rewriteInertEqsFromInertEq.
See Note [Delicate equality kick-out] to see which inert
equalities can safely stay in the inert set and which must be
kicked out to be rewritten and re-checked for occurs errors.
9 Given family or dictionary constraints don't mention touchable unification variables
Note [Solved constraints]
~~~~~~~~~~~~~~~~~~~~~~~~~
When we take a step to simplify a constraint 'c', we call the original constraint "solved".
For example: Wanted: ev :: [s] ~ [t]
New goal: ev1 :: s ~ t
Then 'ev' is now "solved".
The reason for all this is simply to avoid re-solving goals we have solved already.
* A solved Wanted may depend on as-yet-unsolved goals, so (for example) we should not
use it to rewrite a Given; in that sense the solved goal is still a Wanted
* A solved Given is just given
* A solved Derived in inert_solved is possible; purpose is to avoid
creating tons of identical Derived goals.
But there are no solved Deriveds in inert_solved_funeqs
Note [Type family equations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Type-family equations, of form (ev : F tys ~ ty), live in four places
* The work-list, of course
* 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.
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_solved_funeqs. These are all "solved" goals (see Note [Solved constraints]),
the result of using a top-level type-family instance.
* THe inert_funeqs are un-solved but fully processed and in the InertCans.
\begin{code}
data InertCans
= IC { inert_eqs :: TyVarEnv Ct
, inert_dicts :: CCanMap Class
, inert_funeqs :: CtFamHeadMap
, inert_irreds :: Cts
, inert_insols :: Cts
}
data InertSet
= IS { inert_cans :: InertCans
, inert_flat_cache :: FamHeadMap (CtEvidence, TcType)
, inert_fsks :: [TcTyVar]
, inert_solved_funeqs :: FamHeadMap (CtEvidence, TcType)
, inert_solved_dicts :: PredMap CtEvidence
}
instance Outputable InertCans where
ppr ics = vcat [ ptext (sLit "Equalities:")
<+> vcat (map ppr (varEnvElts (inert_eqs ics)))
, ptext (sLit "Type-function equalities:")
<+> vcat (map ppr (Bag.bagToList $
ctTypeMapCts (unFamHeadMap $ inert_funeqs ics)))
, ptext (sLit "Dictionaries:")
<+> vcat (map ppr (Bag.bagToList $ cCanMapToBag (inert_dicts ics)))
, ptext (sLit "Irreds:")
<+> vcat (map ppr (Bag.bagToList $ inert_irreds ics))
, text "Insolubles =" <+>
braces (vcat (map ppr (Bag.bagToList $ inert_insols ics)))
]
instance Outputable InertSet where
ppr is = vcat [ ppr $ inert_cans is
, text "Solved dicts" <+> int (sizePredMap (inert_solved_dicts is))
, text "Solved funeqs" <+> int (sizeFamHeadMap (inert_solved_funeqs is))]
emptyInert :: InertSet
emptyInert
= IS { inert_cans = IC { inert_eqs = emptyVarEnv
, inert_dicts = emptyCCanMap
, inert_funeqs = emptyFamHeadMap
, inert_irreds = emptyCts
, inert_insols = emptyCts }
, inert_fsks = []
, inert_flat_cache = emptyFamHeadMap
, inert_solved_dicts = PredMap emptyTM
, inert_solved_funeqs = emptyFamHeadMap }
insertInertItem :: Ct -> InertSet -> InertSet
insertInertItem item is
=
is { inert_cans = upd_inert_cans (inert_cans is) item }
where upd_inert_cans :: InertCans -> Ct -> InertCans
upd_inert_cans ics item
| isCTyEqCan item
= let upd_err a b = pprPanic "insertInertItem" $
vcat [ text "Multiple inert equalities:"
, text "Old (already inert):" <+> ppr a
, text "Trying to insert :" <+> ppr b ]
eqs' = extendVarEnv_C upd_err (inert_eqs ics)
(cc_tyvar item) item
in ics { inert_eqs = eqs' }
| isCIrredEvCan item
= ics { inert_irreds = inert_irreds ics `Bag.snocBag` item }
| Just cls <- isCDictCan_Maybe item
= ics { inert_dicts = updCCanMap (cls,item) (inert_dicts ics) }
| Just _tc <- isCFunEqCan_Maybe item
= let fam_head = mkTyConApp (cc_fun item) (cc_tyargs item)
upd_funeqs Nothing = Just item
upd_funeqs (Just _already_there)
= panic "insertInertItem: item already there!"
in ics { inert_funeqs = FamHeadMap
(alterTM fam_head upd_funeqs $
(unFamHeadMap $ inert_funeqs ics)) }
| otherwise
= pprPanic "upd_inert set: can't happen! Inserting " $
ppr item
insertInertItemTcS :: Ct -> TcS ()
insertInertItemTcS item
= do { traceTcS "insertInertItemTcS {" $
text "Trying to insert new inert item:" <+> ppr item
; updInertTcS (insertInertItem item)
; traceTcS "insertInertItemTcS }" $ empty }
addSolvedDict :: CtEvidence -> TcS ()
addSolvedDict item
| isIPPred (ctEvPred item)
= return ()
| otherwise
= do { traceTcS "updSolvedSetTcs:" $ ppr item
; updInertTcS upd_solved_dicts }
where
upd_solved_dicts is
= is { inert_solved_dicts = PredMap $ alterTM pred upd_solved $
unPredMap $ inert_solved_dicts is }
pred = ctEvPred item
upd_solved _ = Just item
addSolvedFunEq :: TcType -> CtEvidence -> TcType -> TcS ()
addSolvedFunEq fam_ty ev rhs_ty
= updInertTcS $ \ inert ->
inert { inert_solved_funeqs = insertFamHead (inert_solved_funeqs inert)
fam_ty (ev, rhs_ty) }
modifyInertTcS :: (InertSet -> (a,InertSet)) -> TcS a
modifyInertTcS upd
= do { is_var <- getTcSInertsRef
; curr_inert <- wrapTcS (TcM.readTcRef is_var)
; let (a, new_inert) = upd curr_inert
; wrapTcS (TcM.writeTcRef is_var new_inert)
; return a }
updInertTcS :: (InertSet -> InertSet) -> TcS ()
updInertTcS upd
= do { is_var <- getTcSInertsRef
; curr_inert <- wrapTcS (TcM.readTcRef is_var)
; let new_inert = upd curr_inert
; wrapTcS (TcM.writeTcRef is_var new_inert) }
prepareInertsForImplications :: InertSet -> InertSet
prepareInertsForImplications is
= is { inert_cans = getGivens (inert_cans is)
, inert_fsks = []
, inert_flat_cache = emptyFamHeadMap }
where
getGivens (IC { inert_eqs = eqs
, inert_irreds = irreds
, inert_funeqs = FamHeadMap funeqs
, inert_dicts = dicts })
= IC { inert_eqs = filterVarEnv_Directly (\_ ct -> isGivenCt ct) eqs
, inert_funeqs = FamHeadMap (mapTM given_from_wanted funeqs)
, inert_irreds = Bag.filterBag isGivenCt irreds
, inert_dicts = keepGivenCMap dicts
, inert_insols = emptyCts }
given_from_wanted funeq
| isGiven ev = funeq
| otherwise = funeq { cc_ev = given_ev }
where
ev = ctEvidence funeq
given_ev = CtGiven { ctev_evtm = EvId (ctev_evar ev)
, ctev_pred = ctev_pred ev }
\end{code}
Note [Preparing inert set for implications]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before solving the nested implications, we trim the inert set,
retaining only Givens. These givens can be used when solving
the inner implications.
With one wrinkle! We take all *wanted* *funeqs*, and turn them into givens.
Consider (Trac #4935)
type instance F True a b = a
type instance F False a b = b
[w] F c a b ~ gamma
(c ~ True) => a ~ gamma
(c ~ False) => b ~ gamma
Obviously this is soluble with gamma := F c a b. But
Since solveCTyFunEqs happens at the very end of solving, the only way
to solve the two implications is temporarily consider (F c a b ~ gamma)
as Given and push it inside the implications. Now, when we come
out again at the end, having solved the implications solveCTyFunEqs
will solve this equality.
Turning type-function equalities into Givens is easy becase they
*stay inert*. No need to re-process them.
We don't try to turn any *other* Wanteds into Givens:
* For example, we should not push given dictionaries in because
of example LongWayOverlapping.hs, where we might get strange
overlap errors between far-away constraints in the program.
There might be cases where interactions between wanteds can help
to solve a constraint. For example
class C a b | a -> b
(C Int alpha), (forall d. C d blah => C Int a)
If we push the (C Int alpha) inwards, as a given, it can produce a
fundep (alpha~a) and this can float out again and be used to fix
alpha. (In general we can't float class constraints out just in case
(C d blah) might help to solve (C Int a).) But we ignore this possiblity.
\begin{code}
getInertEqs :: TcS (TyVarEnv Ct)
getInertEqs = do { inert <- getTcSInerts
; return (inert_eqs (inert_cans inert)) }
getInertUnsolved :: TcS (Cts, Cts)
getInertUnsolved
= do { is <- getTcSInerts
; let icans = inert_cans is
unsolved_irreds = Bag.filterBag is_unsolved (inert_irreds icans)
unsolved_dicts = extractUnsolvedCMap (inert_dicts icans)
(unsolved_funeqs,_) = partCtFamHeadMap is_unsolved (inert_funeqs icans)
unsolved_eqs = foldVarEnv add_if_unsolved emptyCts (inert_eqs icans)
unsolved_flats = unsolved_eqs `unionBags` unsolved_irreds `unionBags`
unsolved_dicts `unionBags` unsolved_funeqs
; return (unsolved_flats, inert_insols icans) }
where
add_if_unsolved ct cts
| is_unsolved ct = cts `extendCts` ct
| otherwise = cts
is_unsolved ct = not (isGivenCt ct)
checkAllSolved :: TcS Bool
checkAllSolved
= do { is <- getTcSInerts
; let icans = inert_cans is
unsolved_irreds = Bag.anyBag isWantedCt (inert_irreds icans)
unsolved_dicts = not (isNullUFM (cts_wanted (inert_dicts icans)))
unsolved_funeqs = anyFamHeadMap isWantedCt (inert_funeqs icans)
unsolved_eqs = foldVarEnv ((||) . isWantedCt) False (inert_eqs icans)
; return (not (unsolved_eqs || unsolved_irreds
|| unsolved_dicts || unsolved_funeqs
|| not (isEmptyBag (inert_insols icans)))) }
extractRelevantInerts :: Ct -> TcS Cts
extractRelevantInerts wi
= modifyInertTcS (extract_relevants wi)
where
extract_relevants :: Ct -> InertSet -> (Cts,InertSet)
extract_relevants wi is
= let (cts,ics') = extract_ics_relevants wi (inert_cans is)
in (cts, is { inert_cans = ics' })
extract_ics_relevants :: Ct -> InertCans -> (Cts, InertCans)
extract_ics_relevants (CDictCan {cc_class = cl}) ics =
let (cts,dict_map) = getRelevantCts cl (inert_dicts ics)
in (cts, ics { inert_dicts = dict_map })
extract_ics_relevants ct@(CFunEqCan {}) ics@(IC { inert_funeqs = funeq_map })
| Just ct <- lookupFamHead funeq_map fam_head
= (singleCt ct, ics { inert_funeqs = delFamHead funeq_map fam_head })
| otherwise
= (emptyCts, ics)
where
fam_head = mkTyConApp (cc_fun ct) (cc_tyargs ct)
extract_ics_relevants (CHoleCan {}) ics
= pprPanic "extractRelevantInerts" (ppr wi)
extract_ics_relevants (CIrredEvCan { }) ics =
let cts = inert_irreds ics
in (cts, ics { inert_irreds = emptyCts })
extract_ics_relevants _ ics = (emptyCts,ics)
lookupFlatEqn :: TcType -> TcS (Maybe (CtEvidence, TcType))
lookupFlatEqn fam_ty
= do { IS { inert_solved_funeqs = solved_funeqs
, inert_flat_cache = flat_cache
, inert_cans = IC { inert_funeqs = inert_funeqs } } <- getTcSInerts
; return (lookupFamHead solved_funeqs fam_ty `firstJust`
lookup_in_inerts inert_funeqs `firstJust`
lookupFamHead flat_cache fam_ty) }
where
lookup_in_inerts inert_funeqs
= case lookupFamHead inert_funeqs fam_ty of
Nothing -> Nothing
Just ct -> Just (ctEvidence ct, cc_rhs ct)
lookupInInerts :: TcPredType -> TcS (Maybe CtEvidence)
lookupInInerts pty
= do { inerts <- getTcSInerts
; case lookupSolvedDict inerts pty of
Just ctev -> return (Just ctev)
Nothing -> return (lookupInInertCans inerts pty) }
lookupSolvedDict :: InertSet -> TcPredType -> Maybe CtEvidence
lookupSolvedDict (IS { inert_solved_dicts = solved }) pty
= lookupTM pty (unPredMap solved)
lookupInInertCans :: InertSet -> TcPredType -> Maybe CtEvidence
lookupInInertCans (IS { inert_cans = ics }) pty
= case (classifyPredType pty) of
ClassPred cls _
-> lookupCCanMap cls (\ct -> ctEvPred ct `eqType` pty) (inert_dicts ics)
EqPred ty1 _ty2
| Just tv <- getTyVar_maybe ty1
, Just ct <- lookupVarEnv (inert_eqs ics) tv
, let ctev = ctEvidence ct
, ctEvPred ctev `eqType` pty
-> Just ctev
| Just _ <- splitTyConApp_maybe ty1
, Just ct <- lookupTM ty1 (unFamHeadMap $ inert_funeqs ics)
, let ctev = ctEvidence ct
, ctEvPred ctev `eqType` pty
-> Just ctev
IrredPred {} -> findEvidence (\ct -> ctEvPred ct `eqType` pty) (inert_irreds ics)
_other -> Nothing
\end{code}
%************************************************************************
%* *
%* The TcS solver monad *
%* *
%************************************************************************
Note [The TcS monad]
~~~~~~~~~~~~~~~~~~~~
The TcS monad is a weak form of the main Tc monad
All you can do is
* fail
* allocate new variables
* fill in evidence variables
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.
\begin{code}
data TcSEnv
= TcSEnv {
tcs_ev_binds :: EvBindsVar,
tcs_ty_binds :: IORef (TyVarEnv (TcTyVar, TcType)),
tcs_count :: IORef Int,
tcs_inerts :: IORef InertSet,
tcs_worklist :: IORef WorkList,
tcs_implics :: IORef (Bag Implication)
}
\end{code}
\begin{code}
newtype TcS a = TcS { unTcS :: TcSEnv -> TcM a }
instance Functor TcS where
fmap f m = TcS $ fmap f . unTcS m
instance Monad TcS where
return x = TcS (\_ -> return x)
fail err = TcS (\_ -> fail err)
m >>= k = TcS (\ebs -> unTcS m ebs >>= \r -> unTcS (k r) ebs)
wrapTcS :: TcM a -> TcS a
wrapTcS = TcS . const
wrapErrTcS :: TcM a -> TcS a
wrapErrTcS = wrapTcS
wrapWarnTcS :: TcM a -> TcS a
wrapWarnTcS = wrapTcS
failTcS, panicTcS :: SDoc -> TcS a
failTcS = wrapTcS . TcM.failWith
panicTcS doc = pprPanic "TcCanonical" doc
traceTcS :: String -> SDoc -> TcS ()
traceTcS herald doc = wrapTcS (TcM.traceTc herald doc)
instance HasDynFlags TcS where
getDynFlags = wrapTcS getDynFlags
bumpStepCountTcS :: TcS ()
bumpStepCountTcS = TcS $ \env -> do { let ref = tcs_count env
; n <- TcM.readTcRef ref
; TcM.writeTcRef ref (n+1) }
traceFireTcS :: Ct -> SDoc -> TcS ()
traceFireTcS ct doc
= TcS $ \env ->
TcM.whenDOptM Opt_D_dump_cs_trace $
do { n <- TcM.readTcRef (tcs_count env)
; let msg = int n <> brackets (int (ctLocDepth (cc_loc ct))) <+> doc
; TcM.dumpTcRn msg }
runTcS :: TcS a
-> TcM (a, Bag EvBind)
runTcS tcs
= do { ev_binds_var <- TcM.newTcEvBinds
; res <- runTcSWithEvBinds ev_binds_var tcs
; ev_binds <- TcM.getTcEvBinds ev_binds_var
; return (res, ev_binds) }
runTcSWithEvBinds :: EvBindsVar
-> TcS a
-> TcM a
runTcSWithEvBinds ev_binds_var tcs
= do { ty_binds_var <- TcM.newTcRef emptyVarEnv
; step_count <- TcM.newTcRef 0
; inert_var <- TcM.newTcRef is
; let env = TcSEnv { tcs_ev_binds = ev_binds_var
, tcs_ty_binds = ty_binds_var
, tcs_count = step_count
, tcs_inerts = inert_var
, tcs_worklist = panic "runTcS: worklist"
, tcs_implics = panic "runTcS: implics" }
; res <- unTcS tcs env
; ty_binds <- TcM.readTcRef ty_binds_var
; mapM_ do_unification (varEnvElts ty_binds)
#ifdef DEBUG
; count <- TcM.readTcRef step_count
; when (opt_PprStyle_Debug && count > 0) $
TcM.debugDumpTcRn (ptext (sLit "Constraint solver steps =") <+> int count )
; ev_binds <- TcM.getTcEvBinds ev_binds_var
; checkForCyclicBinds ev_binds
#endif
; return res }
where
do_unification (tv,ty) = TcM.writeMetaTyVar tv ty
is = emptyInert
#ifdef DEBUG
checkForCyclicBinds :: Bag EvBind -> TcM ()
checkForCyclicBinds ev_binds
| null cycles
= return ()
| null coercion_cycles
= TcM.traceTc "Cycle in evidence binds" $ ppr cycles
| otherwise
= pprPanic "Cycle in coercion bindings" $ ppr coercion_cycles
where
cycles :: [[EvBind]]
cycles = [c | CyclicSCC c <- stronglyConnCompFromEdgedVertices edges]
coercion_cycles = [c | c <- cycles, any is_co_bind c]
is_co_bind (EvBind b _) = isEqVar b
edges :: [(EvBind, EvVar, [EvVar])]
edges = [(bind, bndr, varSetElems (evVarsOfTerm rhs)) | bind@(EvBind bndr rhs) <- bagToList ev_binds]
#endif
nestImplicTcS :: EvBindsVar -> Untouchables -> InertSet -> TcS a -> TcS a
nestImplicTcS ref inner_untch inerts (TcS thing_inside)
= TcS $ \ TcSEnv { tcs_ty_binds = ty_binds
, tcs_count = count } ->
do { new_inert_var <- TcM.newTcRef inerts
; let nest_env = TcSEnv { tcs_ev_binds = ref
, tcs_ty_binds = ty_binds
, tcs_count = count
, tcs_inerts = new_inert_var
, tcs_worklist = panic "nextImplicTcS: worklist"
, tcs_implics = panic "nextImplicTcS: implics"
}
; res <- TcM.setUntouchables inner_untch $
thing_inside nest_env
#ifdef DEBUG
; ev_binds <- TcM.getTcEvBinds ref
; checkForCyclicBinds ev_binds
#endif
; return res }
recoverTcS :: TcS a -> TcS a -> TcS a
recoverTcS (TcS recovery_code) (TcS thing_inside)
= TcS $ \ env ->
TcM.recoverM (recovery_code env) (thing_inside env)
nestTcS :: TcS a -> TcS a
nestTcS (TcS thing_inside)
= TcS $ \ env@(TcSEnv { tcs_inerts = inerts_var }) ->
do { inerts <- TcM.readTcRef inerts_var
; new_inert_var <- TcM.newTcRef inerts
; let nest_env = env { tcs_inerts = new_inert_var
, tcs_worklist = panic "nextImplicTcS: worklist"
, tcs_implics = panic "nextImplicTcS: implics" }
; thing_inside nest_env }
tryTcS :: TcS a -> TcS a
tryTcS (TcS thing_inside)
= TcS $ \env ->
do { is_var <- TcM.newTcRef emptyInert
; ty_binds_var <- TcM.newTcRef emptyVarEnv
; ev_binds_var <- TcM.newTcEvBinds
; let nest_env = env { tcs_ev_binds = ev_binds_var
, tcs_ty_binds = ty_binds_var
, tcs_inerts = is_var
, tcs_worklist = panic "nextImplicTcS: worklist"
, tcs_implics = panic "nextImplicTcS: implics" }
; thing_inside nest_env }
getTcSInertsRef :: TcS (IORef InertSet)
getTcSInertsRef = TcS (return . tcs_inerts)
getTcSWorkListRef :: TcS (IORef WorkList)
getTcSWorkListRef = TcS (return . tcs_worklist)
getTcSInerts :: TcS InertSet
getTcSInerts = getTcSInertsRef >>= wrapTcS . (TcM.readTcRef)
updWorkListTcS :: (WorkList -> WorkList) -> TcS ()
updWorkListTcS f
= do { wl_var <- getTcSWorkListRef
; wl_curr <- wrapTcS (TcM.readTcRef wl_var)
; let new_work = f wl_curr
; wrapTcS (TcM.writeTcRef wl_var new_work) }
updWorkListTcS_return :: (WorkList -> (a,WorkList)) -> TcS a
updWorkListTcS_return f
= do { wl_var <- getTcSWorkListRef
; wl_curr <- wrapTcS (TcM.readTcRef wl_var)
; let (res,new_work) = f wl_curr
; wrapTcS (TcM.writeTcRef wl_var new_work)
; return res }
withWorkList :: Cts -> TcS () -> TcS (Bag Implication)
withWorkList work_items (TcS thing_inside)
= TcS $ \ tcs_env ->
do { let init_work_list = foldrBag extendWorkListCt emptyWorkList work_items
; new_wl_var <- TcM.newTcRef init_work_list
; new_implics_var <- TcM.newTcRef emptyBag
; thing_inside (tcs_env { tcs_worklist = new_wl_var
, tcs_implics = new_implics_var })
; final_wl <- TcM.readTcRef new_wl_var
; implics <- TcM.readTcRef new_implics_var
; ASSERT( isEmptyWorkList final_wl )
return implics }
updTcSImplics :: (Bag Implication -> Bag Implication) -> TcS ()
updTcSImplics f
= do { impl_ref <- getTcSImplicsRef
; wrapTcS $ do { implics <- TcM.readTcRef impl_ref
; TcM.writeTcRef impl_ref (f implics) } }
emitInsoluble :: Ct -> TcS ()
emitInsoluble ct
= do { traceTcS "Emit insoluble" (ppr ct)
; updInertTcS add_insol }
where
this_pred = ctPred ct
add_insol is@(IS { inert_cans = ics@(IC { inert_insols = old_insols }) })
| already_there = is
| otherwise = is { inert_cans = ics { inert_insols = extendCts old_insols ct } }
where
already_there = not (isWantedCt ct) && anyBag (eqType this_pred . ctPred) old_insols
getTcSImplicsRef :: TcS (IORef (Bag Implication))
getTcSImplicsRef = TcS (return . tcs_implics)
getTcEvBinds :: TcS EvBindsVar
getTcEvBinds = TcS (return . tcs_ev_binds)
getUntouchables :: TcS Untouchables
getUntouchables = wrapTcS TcM.getUntouchables
getFlattenSkols :: TcS [TcTyVar]
getFlattenSkols = do { is <- getTcSInerts; return (inert_fsks is) }
getTcSTyBinds :: TcS (IORef (TyVarEnv (TcTyVar, TcType)))
getTcSTyBinds = TcS (return . tcs_ty_binds)
getTcSTyBindsMap :: TcS (TyVarEnv (TcTyVar, TcType))
getTcSTyBindsMap = getTcSTyBinds >>= wrapTcS . (TcM.readTcRef)
getTcEvBindsMap :: TcS EvBindMap
getTcEvBindsMap
= do { EvBindsVar ev_ref _ <- getTcEvBinds
; wrapTcS $ TcM.readTcRef ev_ref }
setWantedTyBind :: TcTyVar -> TcType -> TcS ()
setWantedTyBind tv ty
= ASSERT2( isMetaTyVar tv, ppr tv )
do { ref <- getTcSTyBinds
; wrapTcS $
do { ty_binds <- TcM.readTcRef ref
; when debugIsOn $
TcM.checkErr (not (tv `elemVarEnv` ty_binds)) $
vcat [ text "TERRIBLE ERROR: double set of meta type variable"
, ppr tv <+> text ":=" <+> ppr ty
, text "Old value =" <+> ppr (lookupVarEnv_NF ty_binds tv)]
; TcM.traceTc "setWantedTyBind" (ppr tv <+> text ":=" <+> ppr ty)
; TcM.writeTcRef ref (extendVarEnv ty_binds tv (tv,ty)) } }
\end{code}
\begin{code}
getDefaultInfo :: TcS ([Type], (Bool, Bool))
getDefaultInfo = wrapTcS TcM.tcGetDefaultTys
getInstEnvs :: TcS (InstEnv, InstEnv)
getInstEnvs = wrapTcS $ Inst.tcGetInstEnvs
getFamInstEnvs :: TcS (FamInstEnv, FamInstEnv)
getFamInstEnvs = wrapTcS $ FamInst.tcGetFamInstEnvs
getTopEnv :: TcS HscEnv
getTopEnv = wrapTcS $ TcM.getTopEnv
getGblEnv :: TcS TcGblEnv
getGblEnv = wrapTcS $ TcM.getGblEnv
checkWellStagedDFun :: PredType -> DFunId -> CtLoc -> TcS ()
checkWellStagedDFun pred dfun_id loc
= wrapTcS $ TcM.setCtLoc loc $
do { use_stage <- TcM.getStage
; TcM.checkWellStaged pp_thing bind_lvl (thLevel use_stage) }
where
pp_thing = ptext (sLit "instance for") <+> quotes (ppr pred)
bind_lvl = TcM.topIdLvl dfun_id
pprEq :: TcType -> TcType -> SDoc
pprEq ty1 ty2 = pprType $ mkEqPred ty1 ty2
isTouchableMetaTyVarTcS :: TcTyVar -> TcS Bool
isTouchableMetaTyVarTcS tv
= do { untch <- getUntouchables
; return $ isTouchableMetaTyVar untch tv }
isFilledMetaTyVar_maybe :: TcTyVar -> TcS (Maybe Type)
isFilledMetaTyVar_maybe tv
= ASSERT2( isTcTyVar tv, ppr tv )
case tcTyVarDetails tv of
MetaTv { mtv_ref = ref }
-> do { cts <- wrapTcS (TcM.readTcRef ref)
; case cts of
Indirect ty -> return (Just ty)
Flexi -> return Nothing }
_ -> return Nothing
zonkTyVarsAndFV :: TcTyVarSet -> TcS TcTyVarSet
zonkTyVarsAndFV tvs = wrapTcS (TcM.zonkTyVarsAndFV tvs)
\end{code}
Note [Do not add duplicate derived insolubles]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In general we *must* add an insoluble (Int ~ Bool) even if there is
one such there already, because they may come from distinct call
sites. Not only do we want an error message for each, but with
-fdefer-type-errors we must generate evidence for each. But for
*derived* insolubles, we only want to report each one once. Why?
(a) A constraint (C r s t) where r -> s, say, may generate the same fundep
equality many times, as the original constraint is sucessively rewritten.
(b) Ditto the successive iterations of the main solver itself, as it traverses
the constraint tree. See example below.
Also for *given* insolubles we may get repeated errors, as we
repeatedly traverse the constraint tree. These are relatively rare
anyway, so removing duplicates seems ok. (Alternatively we could take
the SrcLoc into account.)
Note that the test does not need to be particularly efficient because
it is only used if the program has a type error anyway.
Example of (b): assume a top-level class and instance declaration:
class D a b | a -> b
instance D [a] [a]
Assume we have started with an implication:
forall c. Eq c => { wc_flat = D [c] c [W] }
which we have simplified to:
forall c. Eq c => { wc_flat = D [c] c [W]
, wc_insols = (c ~ [c]) [D] }
For some reason, e.g. because we floated an equality somewhere else,
we might try to re-solve this implication. If we do not do a
dropDerivedWC, then we will end up trying to solve the following
constraints the second time:
(D [c] c) [W]
(c ~ [c]) [D]
which will result in two Deriveds to end up in the insoluble set:
wc_flat = D [c] c [W]
wc_insols = (c ~ [c]) [D], (c ~ [c]) [D]
\begin{code}
newFlattenSkolem :: CtFlavour
-> TcType
-> TcS (CtEvidence, TcType)
newFlattenSkolem Given fam_ty
= do { tv <- wrapTcS $
do { uniq <- TcM.newUnique
; let name = TcM.mkTcTyVarName uniq (fsLit "f")
; return $ mkTcTyVar name (typeKind fam_ty) (FlatSkol fam_ty) }
; traceTcS "New Flatten Skolem Born" $
ppr tv <+> text "[:= " <+> ppr fam_ty <+> text "]"
; let rhs_ty = mkTyVarTy tv
ctev = CtGiven { ctev_pred = mkTcEqPred fam_ty rhs_ty
, ctev_evtm = EvCoercion (mkTcReflCo fam_ty) }
; dflags <- getDynFlags
; updInertTcS $ \ is@(IS { inert_fsks = fsks }) ->
extendFlatCache dflags fam_ty ctev rhs_ty
is { inert_fsks = tv : fsks }
; return (ctev, rhs_ty) }
newFlattenSkolem _ fam_ty
= do { rhs_ty <- newFlexiTcSTy (typeKind fam_ty)
; ctev <- newWantedEvVarNC (mkTcEqPred fam_ty rhs_ty)
; dflags <- getDynFlags
; updInertTcS $ extendFlatCache dflags fam_ty ctev rhs_ty
; return (ctev, rhs_ty) }
extendFlatCache :: DynFlags -> TcType -> CtEvidence -> TcType
-> InertSet -> InertSet
extendFlatCache dflags
| not (gopt Opt_FlatCache dflags)
= \ _ _ _ is -> is
| otherwise
= \ fam_ty ctev rhs_ty is@(IS { inert_flat_cache = fc }) ->
is { inert_flat_cache = insertFamHead fc fam_ty (ctev,rhs_ty) }
instDFunType :: DFunId -> [DFunInstType] -> TcS ([TcType], TcType)
instDFunType dfun_id mb_inst_tys
= wrapTcS $ go dfun_tvs mb_inst_tys (mkTopTvSubst [])
where
(dfun_tvs, dfun_phi) = tcSplitForAllTys (idType dfun_id)
go :: [TyVar] -> [DFunInstType] -> TvSubst -> TcM ([TcType], TcType)
go [] [] subst = return ([], substTy subst dfun_phi)
go (tv:tvs) (Just ty : mb_tys) subst
= do { (tys, phi) <- go tvs mb_tys (extendTvSubst subst tv ty)
; return (ty : tys, phi) }
go (tv:tvs) (Nothing : mb_tys) subst
= do { ty <- instFlexiTcSHelper (tyVarName tv) (substTy subst (tyVarKind tv))
; (tys, phi) <- go tvs mb_tys (extendTvSubst subst tv ty)
; return (ty : tys, phi) }
go _ _ _ = pprPanic "instDFunTypes" (ppr dfun_id $$ ppr mb_inst_tys)
newFlexiTcSTy :: Kind -> TcS TcType
newFlexiTcSTy knd = wrapTcS (TcM.newFlexiTyVarTy knd)
cloneMetaTyVar :: TcTyVar -> TcS TcTyVar
cloneMetaTyVar tv = wrapTcS (TcM.cloneMetaTyVar tv)
instFlexiTcS :: [TKVar] -> TcS (TvSubst, [TcType])
instFlexiTcS tvs = wrapTcS (mapAccumLM inst_one emptyTvSubst tvs)
where
inst_one subst tv
= do { ty' <- instFlexiTcSHelper (tyVarName tv)
(substTy subst (tyVarKind tv))
; return (extendTvSubst subst tv ty', ty') }
instFlexiTcSHelper :: Name -> Kind -> TcM TcType
instFlexiTcSHelper tvname kind
= do { uniq <- TcM.newUnique
; details <- TcM.newMetaDetails TauTv
; let name = setNameUnique tvname uniq
; return (mkTyVarTy (mkTcTyVar name kind details)) }
instFlexiTcSHelperTcS :: Name -> Kind -> TcS TcType
instFlexiTcSHelperTcS n k = wrapTcS (instFlexiTcSHelper n k)
data XEvTerm =
XEvTerm { ev_comp :: [EvTerm] -> EvTerm
, ev_decomp :: EvTerm -> [EvTerm]
}
data MaybeNew = Fresh CtEvidence | Cached EvTerm
isFresh :: MaybeNew -> Bool
isFresh (Fresh {}) = True
isFresh _ = False
getEvTerm :: MaybeNew -> EvTerm
getEvTerm (Fresh ctev) = ctEvTerm ctev
getEvTerm (Cached tm) = tm
getEvTerms :: [MaybeNew] -> [EvTerm]
getEvTerms = map getEvTerm
freshGoals :: [MaybeNew] -> [CtEvidence]
freshGoals mns = [ ctev | Fresh ctev <- mns ]
setEvBind :: EvVar -> EvTerm -> TcS ()
setEvBind the_ev tm
= do { traceTcS "setEvBind" $ vcat [ text "ev =" <+> ppr the_ev
, text "tm =" <+> ppr tm ]
; tc_evbinds <- getTcEvBinds
; wrapTcS $ TcM.addTcEvBind tc_evbinds the_ev tm }
newGivenEvVar :: TcPredType -> EvTerm -> TcS CtEvidence
newGivenEvVar pred rhs
= do { new_ev <- wrapTcS $ TcM.newEvVar pred
; setEvBind new_ev rhs
; return (CtGiven { ctev_pred = pred, ctev_evtm = EvId new_ev }) }
newWantedEvVarNC :: TcPredType -> TcS CtEvidence
newWantedEvVarNC pty
= do { new_ev <- wrapTcS $ TcM.newEvVar pty
; return (CtWanted { ctev_pred = pty, ctev_evar = new_ev })}
newWantedEvVar :: TcPredType -> TcS MaybeNew
newWantedEvVar pty
= do { mb_ct <- lookupInInerts pty
; case mb_ct of
Just ctev | not (isDerived ctev)
-> do { traceTcS "newWantedEvVar/cache hit" $ ppr ctev
; return (Cached (ctEvTerm ctev)) }
_ -> do { ctev <- newWantedEvVarNC pty
; traceTcS "newWantedEvVar/cache miss" $ ppr ctev
; return (Fresh ctev) } }
newDerived :: TcPredType -> TcS (Maybe CtEvidence)
newDerived pty
= do { mb_ct <- lookupInInerts pty
; return (case mb_ct of
Just {} -> Nothing
Nothing -> Just (CtDerived { ctev_pred = pty })) }
instDFunConstraints :: TcThetaType -> TcS [MaybeNew]
instDFunConstraints = mapM newWantedEvVar
\end{code}
Note [xCFlavor]
~~~~~~~~~~~~~~~
A call might look like this:
xCtFlavor ev subgoal-preds evidence-transformer
ev is Given => use ev_decomp to create new Givens for subgoal-preds,
and return them
ev is Wanted => create new wanteds for subgoal-preds,
use ev_comp to bind ev,
return fresh wanteds (ie ones not cached in inert_cans or solved)
ev is Derived => create new deriveds for subgoal-preds
(unless cached in inert_cans or solved)
Note: The [CtEvidence] returned is a subset of the subgoal-preds passed in
Ones that are already cached are not returned
Example
ev : Tree a b ~ Tree c d
xCtFlavor ev [a~c, b~d] (XEvTerm { ev_comp = \[c1 c2]. c1 c2
, ev_decomp = \c. [nth 1 c, nth 2 c] })
(\fresh-goals. stuff)
Note [Bind new Givens immediately]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For Givens we make new EvVars and bind them immediately. We don't worry
about caching, but we don't expect complicated calculations among Givens.
It is important to bind each given:
class (a~b) => C a b where ....
f :: C a b => ....
Then in f's Givens we have g:(C a b) and the superclass sc(g,0):a~b.
But that superclass selector can't (yet) appear in a coercion
(see evTermCoercion), so the easy thing is to bind it to an Id.
See Note [Coercion evidence terms] in TcEvidence.
\begin{code}
xCtFlavor :: CtEvidence
-> [TcPredType]
-> XEvTerm
-> TcS [CtEvidence]
xCtFlavor (CtGiven { ctev_evtm = tm }) ptys xev
= ASSERT( equalLength ptys (ev_decomp xev tm) )
zipWithM newGivenEvVar ptys (ev_decomp xev tm)
xCtFlavor ctev@(CtWanted { ctev_evar = evar }) ptys xev
= do { new_evars <- mapM newWantedEvVar ptys
; setEvBind evar (ev_comp xev (getEvTerms new_evars))
; return (freshGoals new_evars) }
xCtFlavor (CtDerived {}) ptys _xev
= do { ders <- mapM newDerived ptys
; return (catMaybes ders) }
rewriteCtFlavor :: CtEvidence
-> TcPredType
-> TcCoercion
-> TcS (Maybe CtEvidence)
rewriteCtFlavor (CtDerived {}) new_pred _co
=
newDerived new_pred
rewriteCtFlavor old_ev new_pred co
| isTcReflCo co
= return (Just (if ctEvPred old_ev `eqType` new_pred
then old_ev
else old_ev { ctev_pred = new_pred }))
rewriteCtFlavor (CtGiven { ctev_evtm = old_tm }) new_pred co
= do { new_ev <- newGivenEvVar new_pred new_tm
; return (Just new_ev) }
where
new_tm = mkEvCast old_tm (mkTcSymCo co)
rewriteCtFlavor (CtWanted { ctev_evar = evar, ctev_pred = old_pred }) new_pred co
= do { new_evar <- newWantedEvVar new_pred
; setEvBind evar (mkEvCast (getEvTerm new_evar) co)
; case new_evar of
Fresh ctev -> return (Just ctev)
_ -> return Nothing }
matchOpenFam :: TyCon -> [Type] -> TcS (Maybe FamInstMatch)
matchOpenFam tycon args = wrapTcS $ tcLookupFamInst tycon args
matchFam :: TyCon -> [Type] -> TcS (Maybe (TcCoercion, TcType))
matchFam tycon args
| isOpenSynFamilyTyCon tycon
= do { maybe_match <- matchOpenFam tycon args
; case maybe_match of
Nothing -> return Nothing
Just (FamInstMatch { fim_instance = famInst
, fim_tys = inst_tys })
-> let co = mkTcUnbranchedAxInstCo (famInstAxiom famInst) inst_tys
ty = pSnd $ tcCoercionKind co
in return $ Just (co, ty) }
| Just ax <- isClosedSynFamilyTyCon_maybe tycon
, Just (ind, inst_tys) <- chooseBranch ax args
= let co = mkTcAxInstCo ax ind inst_tys
ty = pSnd (tcCoercionKind co)
in return $ Just (co, ty)
| otherwise
= return Nothing
\end{code}
\begin{code}
deferTcSForAllEq :: (CtLoc,EvVar)
-> ([TyVar],TcType)
-> ([TyVar],TcType)
-> TcS ()
deferTcSForAllEq (loc,orig_ev) (tvs1,body1) (tvs2,body2)
= do { (subst1, skol_tvs) <- wrapTcS $ TcM.tcInstSkolTyVars tvs1
; let tys = mkTyVarTys skol_tvs
phi1 = Type.substTy subst1 body1
phi2 = Type.substTy (zipTopTvSubst tvs2 tys) body2
skol_info = UnifyForAllSkol skol_tvs phi1
; mev <- newWantedEvVar (mkTcEqPred phi1 phi2)
; coe_inside <- case mev of
Cached ev_tm -> return (evTermCoercion ev_tm)
Fresh ctev -> do { ev_binds_var <- wrapTcS $ TcM.newTcEvBinds
; env <- wrapTcS $ TcM.getLclEnv
; let ev_binds = TcEvBinds ev_binds_var
new_ct = mkNonCanonical loc ctev
new_co = evTermCoercion (ctEvTerm ctev)
new_untch = pushUntouchables (tcl_untch env)
; let wc = WC { wc_flat = singleCt new_ct
, wc_impl = emptyBag
, wc_insol = emptyCts }
imp = Implic { ic_untch = new_untch
, ic_skols = skol_tvs
, ic_fsks = []
, ic_given = []
, ic_wanted = wc
, ic_insol = False
, ic_binds = ev_binds_var
, ic_env = env
, ic_info = skol_info }
; updTcSImplics (consBag imp)
; return (TcLetCo ev_binds new_co) }
; setEvBind orig_ev $
EvCoercion (foldr mkTcForAllCo coe_inside skol_tvs)
}
\end{code}