import TypeRep ( Type(..) )
import TyCon
import HsSyn
+import Id
import VarEnv
import VarSet
import Var
, binds = emptyBag
, skolems = emptyVarSet
}
+
+instance Outputable EqConfig where
+ ppr (EqConfig {eqs = eqs, locals = locals, wanteds = wanteds, binds = binds})
+ = vcat [ppr eqs, ppr locals, ppr wanteds, ppr binds]
\end{code}
The set of operations on an equality configuration. We obtain the initialise
--
normaliseEqs :: [Inst] -> TcM EqConfig
normaliseEqs eqs
- = do { (eqss, skolemss) <- mapAndUnzipM normEqInst eqs
+ = do { ASSERTM2( allM isValidWantedEqInst eqs, ppr eqs )
+ ; traceTc $ ptext (sLit "Entering normaliseEqs")
+
+ ; (eqss, skolemss) <- mapAndUnzipM normEqInst eqs
; return $ emptyEqConfig { eqs = concat eqss
, skolems = unionVarSets skolemss
}
--
normaliseDicts :: Bool -> [Inst] -> TcM EqConfig
normaliseDicts isWanted insts
- = do { (insts', eqss, bindss, skolemss) <- mapAndUnzip4M (normDict isWanted)
+ = do { traceTc $ ptext (sLit "Entering normaliseDicts") <+>
+ ptext (if isWanted then sLit "[Wanted]" else sLit "[Local]")
+ ; (insts', eqss, bindss, skolemss) <- mapAndUnzip4M (normDict isWanted)
insts
; return $ emptyEqConfig { eqs = concat eqss
, locals = if isWanted then [] else insts'
--
propagateEqs :: EqConfig -> TcM EqConfig
propagateEqs eqCfg@(EqConfig {eqs = todoEqs})
- = propagate todoEqs (eqCfg {eqs = []})
+ = do { traceTc $ hang (ptext (sLit "Entering propagateEqs:"))
+ 4 (ppr eqCfg)
+
+ ; propagate todoEqs (eqCfg {eqs = []})
+ }
-- |Finalise a set of equalities and associated dictionaries after
-- propagation. The returned Boolean value is `True' iff any flexible
-- set of instances are the locals (without equalities) and the second set are
-- all residual wanteds, including equalities.
--
+-- Remove all identity dictinary bindings (i.e., those whose source and target
+-- dictionary are the same). This is important for termination, as
+-- TcSimplify.reduceContext takes the presence of dictionary bindings as an
+-- indicator that there was some improvement.
+--
finaliseEqsAndDicts :: EqConfig
-> TcM ([Inst], [Inst], TcDictBinds, Bool)
finaliseEqsAndDicts (EqConfig { eqs = eqs
, wanteds = wanteds
, binds = binds
})
- = do { (eqs', subst_binds, locals', wanteds') <- substitute eqs locals wanteds
- ; (eqs'', improved) <- instantiateAndExtract eqs'
- ; return (locals',
- eqs'' ++ wanteds',
- subst_binds `unionBags` binds,
- improved)
+ = do { traceTc $ ptext (sLit "finaliseEqsAndDicts")
+ ; (eqs', subst_binds, locals', wanteds') <- substitute eqs locals wanteds
+ ; (eqs'', improved) <- instantiateAndExtract eqs' (null locals)
+ ; final_binds <- filterM nonTrivialDictBind $
+ bagToList (subst_binds `unionBags` binds)
+
+ ; ASSERTM2( allM isValidWantedEqInst eqs'', ppr eqs'' )
+ ; return (locals', eqs'' ++ wanteds', listToBag final_binds, improved)
}
+ where
+ nonTrivialDictBind (L _ (VarBind { var_id = ide1
+ , var_rhs = L _ (HsWrap _ (HsVar ide2))}))
+ = do { ty1 <- zonkTcType (idType ide1)
+ ; ty2 <- zonkTcType (idType ide2)
+ ; return $ not (ty1 `tcEqType` ty2)
+ }
+ nonTrivialDictBind _ = return True
\end{code}
\begin{code}
data RewriteInst
= RewriteVar -- Form (2) above
- { rwi_var :: TyVar -- may be rigid or flexible
- , rwi_right :: TcType -- contains no synonym family applications
- , rwi_co :: EqInstCo -- the wanted or given coercion
- , rwi_loc :: InstLoc
- , rwi_name :: Name -- no semantic significance (cf. TcRnTypes.EqInst)
+ { rwi_var :: TyVar -- may be rigid or flexible
+ , rwi_right :: TcType -- contains no synonym family applications
+ , rwi_co :: EqInstCo -- the wanted or given coercion
+ , rwi_loc :: InstLoc
+ , rwi_name :: Name -- no semantic significance (cf. TcRnTypes.EqInst)
+ , rwi_swapped :: Bool -- swapped orientation of original EqInst
}
| RewriteFam -- Forms (1) above
- { rwi_fam :: TyCon -- synonym family tycon
- , rwi_args :: [Type] -- contain no synonym family applications
- , rwi_right :: TcType -- contains no synonym family applications
- , rwi_co :: EqInstCo -- the wanted or given coercion
- , rwi_loc :: InstLoc
- , rwi_name :: Name -- no semantic significance (cf. TcRnTypes.EqInst)
+ { rwi_fam :: TyCon -- synonym family tycon
+ , rwi_args :: [Type] -- contain no synonym family applications
+ , rwi_right :: TcType -- contains no synonym family applications
+ , rwi_co :: EqInstCo -- the wanted or given coercion
+ , rwi_loc :: InstLoc
+ , rwi_name :: Name -- no semantic significance (cf. TcRnTypes.EqInst)
+ , rwi_swapped :: Bool -- swapped orientation of original EqInst
}
isWantedRewriteInst :: RewriteInst -> Bool
isWantedRewriteInst = isWantedCo . rwi_co
-rewriteInstToInst :: RewriteInst -> Inst
+rewriteInstToInst :: RewriteInst -> TcM Inst
rewriteInstToInst eq@(RewriteVar {rwi_var = tv})
- = EqInst
- { tci_left = mkTyVarTy tv
- , tci_right = rwi_right eq
- , tci_co = rwi_co eq
- , tci_loc = rwi_loc eq
- , tci_name = rwi_name eq
- }
+ = deriveEqInst eq (mkTyVarTy tv) (rwi_right eq) (rwi_co eq)
rewriteInstToInst eq@(RewriteFam {rwi_fam = fam, rwi_args = args})
- = EqInst
- { tci_left = mkTyConApp fam args
- , tci_right = rwi_right eq
- , tci_co = rwi_co eq
- , tci_loc = rwi_loc eq
- , tci_name = rwi_name eq
- }
+ = deriveEqInst eq (mkTyConApp fam args) (rwi_right eq) (rwi_co eq)
+
+-- Derive an EqInst based from a RewriteInst, possibly swapping the types
+-- around.
+--
+deriveEqInst :: RewriteInst -> TcType -> TcType -> EqInstCo -> TcM Inst
+deriveEqInst rewrite ty1 ty2 co
+ = do { co_adjusted <- if not swapped then return co
+ else mkSymEqInstCo co (ty2, ty1)
+ ; return $ EqInst
+ { tci_left = left
+ , tci_right = right
+ , tci_co = co_adjusted
+ , tci_loc = rwi_loc rewrite
+ , tci_name = rwi_name rewrite
+ }
+ }
+ where
+ swapped = rwi_swapped rewrite
+ (left, right) = if not swapped then (ty1, ty2) else (ty2, ty1)
+
+instance Outputable RewriteInst where
+ ppr (RewriteFam {rwi_fam = fam, rwi_args = args, rwi_right = rhs, rwi_co =co})
+ = hsep [ pprEqInstCo co <+> text "::"
+ , ppr (mkTyConApp fam args)
+ , text "~>"
+ , ppr rhs
+ ]
+ ppr (RewriteVar {rwi_var = tv, rwi_right = rhs, rwi_co =co})
+ = hsep [ pprEqInstCo co <+> text "::"
+ , ppr tv
+ , text "~>"
+ , ppr rhs
+ ]
+
+pprEqInstCo :: EqInstCo -> SDoc
+pprEqInstCo (Left cotv) = ptext (sLit "Wanted") <+> ppr cotv
+pprEqInstCo (Right co) = ptext (sLit "Local") <+> ppr co
\end{code}
The following functions turn an arbitrary equality into a set of normal
extracting any synonym family applications and generation appropriate normal
equalities.
+Whenever we encounter a loopy equality (of the form a ~ T .. (F ...a...) ...),
+we drop that equality and raise an error if it is a wanted or a warning if it
+is a local.
+
\begin{code}
normEqInst :: Inst -> TcM ([RewriteInst], TyVarSet)
-- Normalise one equality.
-- left-to-right rule with type family head
go (TyConApp con args) ty2 co
| isOpenSynTyCon con
- = mkRewriteFam con args ty2 co
+ = mkRewriteFam False con args ty2 co
-- right-to-left rule with type family head
go ty1 ty2@(TyConApp con args) co
| isOpenSynTyCon con
= do { co' <- mkSymEqInstCo co (ty2, ty1)
- ; mkRewriteFam con args ty1 co'
+ ; mkRewriteFam True con args ty1 co'
}
-- no outermost family
= do { (ty1', co1, ty1_eqs, ty1_skolems) <- flattenType inst ty1
; (ty2', co2, ty2_eqs, ty2_skolems) <- flattenType inst ty2
; let ty12_eqs = ty1_eqs ++ ty2_eqs
- rewriteCo = co1 `mkTransCoercion` mkSymCoercion co2
+ sym_co2 = mkSymCoercion co2
eqTys = (ty1', ty2')
- ; (co', ty12_eqs') <- adjustCoercions co rewriteCo eqTys ty12_eqs
+ ; (co', ty12_eqs') <- adjustCoercions co co1 sym_co2 eqTys ty12_eqs
; eqs <- checkOrientation ty1' ty2' co' inst
- ; return $ (eqs ++ ty12_eqs',
- ty1_skolems `unionVarSet` ty2_skolems)
+ ; if isLoopyEquality eqs ty12_eqs'
+ then do { if isWantedCo (tci_co inst)
+ then
+ addErrCtxt (ptext (sLit "Rejecting loopy equality")) $
+ eqInstMisMatch inst
+ else
+ warnDroppingLoopyEquality ty1 ty2
+ ; return ([], emptyVarSet) -- drop the equality
+ }
+ else
+ return (eqs ++ ty12_eqs',
+ ty1_skolems `unionVarSet` ty2_skolems)
}
- mkRewriteFam con args ty2 co
+ mkRewriteFam swapped con args ty2 co
= do { (args', cargs, args_eqss, args_skolemss)
<- mapAndUnzip4M (flattenType inst) args
; (ty2', co2, ty2_eqs, ty2_skolems) <- flattenType inst ty2
- ; let rewriteCo = mkTyConApp con cargs `mkTransCoercion`
- mkSymCoercion co2
+ ; let co1 = mkTyConApp con cargs
+ sym_co2 = mkSymCoercion co2
all_eqs = concat args_eqss ++ ty2_eqs
eqTys = (mkTyConApp con args', ty2')
- ; (co', all_eqs') <- adjustCoercions co rewriteCo eqTys all_eqs
+ ; (co', all_eqs') <- adjustCoercions co co1 sym_co2 eqTys all_eqs
; let thisRewriteFam = RewriteFam
- { rwi_fam = con
- , rwi_args = args'
- , rwi_right = ty2'
- , rwi_co = co'
- , rwi_loc = tci_loc inst
- , rwi_name = tci_name inst
+ { rwi_fam = con
+ , rwi_args = args'
+ , rwi_right = ty2'
+ , rwi_co = co'
+ , rwi_loc = tci_loc inst
+ , rwi_name = tci_name inst
+ , rwi_swapped = swapped
}
; return $ (thisRewriteFam : all_eqs',
unionVarSets (ty2_skolems:args_skolemss))
}
+ -- If the original equality has the form a ~ T .. (F ...a...) ..., we will
+ -- have a variable equality with 'a' on the lhs as the first equality.
+ -- Then, check whether 'a' occurs in the lhs of any family equality
+ -- generated by flattening.
+ isLoopyEquality (RewriteVar {rwi_var = tv}:_) eqs
+ = any inRewriteFam eqs
+ where
+ inRewriteFam (RewriteFam {rwi_args = args})
+ = tv `elemVarSet` tyVarsOfTypes args
+ inRewriteFam _ = False
+ isLoopyEquality _ _ = False
+
normDict :: Bool -> Inst -> TcM (Inst, [RewriteInst], TcDictBinds, TyVarSet)
-- Normalise one dictionary or IP constraint.
normDict isWanted inst@(Dict {tci_pred = ClassP clas args})
; let rewriteCo = PredTy $ ClassP clas cargs
eqs = concat args_eqss
pred' = ClassP clas args'
- ; (inst', bind, eqs') <- mkDictBind inst isWanted rewriteCo pred' eqs
+ ; if null eqs
+ then -- don't generate a binding if there is nothing to flatten
+ return (inst, [], emptyBag, emptyVarSet)
+ else do {
+ ; (inst', bind) <- mkDictBind inst isWanted rewriteCo pred'
+ ; eqs' <- if isWanted then return eqs else mapM wantedToLocal eqs
; return (inst', eqs', bind, unionVarSets args_skolemss)
- }
-normDict isWanted inst
+ }}
+normDict _isWanted inst
= return (inst, [], emptyBag, emptyVarSet)
-- !!!TODO: Still need to normalise IP constraints.
-- NB: We cannot assume that the two types already have outermost type
-- synonyms expanded due to the recursion in the case of type applications.
checkOrientation ty1 ty2 co inst
- = go ty1 ty2
+ = do { traceTc $ ptext (sLit "checkOrientation of ") <+>
+ pprEqInstCo co <+> text "::" <+>
+ ppr ty1 <+> text "~" <+> ppr ty2
+ ; eqs <- go ty1 ty2
+ ; traceTc $ ptext (sLit "checkOrientation returns") <+> ppr eqs
+ ; return eqs
+ }
where
-- look through synonyms
go ty1 ty2 | Just ty1' <- tcView ty1 = go ty1' ty2
-- two tvs, left greater => unchanged
go ty1@(TyVarTy tv1) ty2@(TyVarTy tv2)
| tv1 > tv2
- = mkRewriteVar tv1 ty2 co
+ = mkRewriteVar False tv1 ty2 co
-- two tvs, right greater => swap
| otherwise
= do { co' <- mkSymEqInstCo co (ty2, ty1)
- ; mkRewriteVar tv2 ty1 co'
+ ; mkRewriteVar True tv2 ty1 co'
}
-- only lhs is a tv => unchanged
| ty1 `tcPartOfType` ty2 -- occurs check!
= occurCheckErr ty1 ty2
| otherwise
- = mkRewriteVar tv1 ty2 co
+ = mkRewriteVar False tv1 ty2 co
-- only rhs is a tv => swap
go ty1 ty2@(TyVarTy tv2)
= occurCheckErr ty2 ty1
| otherwise
= do { co' <- mkSymEqInstCo co (ty2, ty1)
- ; mkRewriteVar tv2 ty1 co'
+ ; mkRewriteVar True tv2 ty1 co'
}
-- type applications => decompose
= ASSERT( (not . isForAllTy $ ty1) && (not . isForAllTy $ ty2) )
eqInstMisMatch inst
- mkRewriteVar tv ty co = return [RewriteVar
- { rwi_var = tv
- , rwi_right = ty
- , rwi_co = co
- , rwi_loc = tci_loc inst
- , rwi_name = tci_name inst
- }]
+ mkRewriteVar swapped tv ty co = return [RewriteVar
+ { rwi_var = tv
+ , rwi_right = ty
+ , rwi_co = co
+ , rwi_loc = tci_loc inst
+ , rwi_name = tci_name inst
+ , rwi_swapped = swapped
+ }]
flattenType :: Inst -- context to get location & name
-> Type -- the type to flatten
= go ty
where
-- look through synonyms
- go ty | Just ty' <- tcView ty = go ty'
+ go ty | Just ty' <- tcView ty
+ = do { (ty_flat, co, eqs, skolems) <- go ty'
+ ; if null eqs
+ then -- unchanged, keep the old type with folded synonyms
+ return (ty, ty, [], emptyVarSet)
+ else
+ return (ty_flat, co, eqs, skolems)
+ }
+
+ -- type variable => nothing to do
+ go ty@(TyVarTy _)
+ = return (ty, ty, [] , emptyVarSet)
-- type family application
-- => flatten to "gamma :: F t1'..tn' ~ alpha" (alpha & gamma fresh)
; let alphaTy = mkTyVarTy alpha
; cotv <- newMetaCoVar (mkTyConApp con args') alphaTy
; let thisRewriteFam = RewriteFam
- { rwi_fam = con
- , rwi_args = args'
- , rwi_right = alphaTy
- , rwi_co = mkWantedCo cotv
- , rwi_loc = tci_loc inst
- , rwi_name = tci_name inst
+ { rwi_fam = con
+ , rwi_args = args'
+ , rwi_right = alphaTy
+ , rwi_co = mkWantedCo cotv
+ , rwi_loc = tci_loc inst
+ , rwi_name = tci_name inst
+ , rwi_swapped = True
}
; return (alphaTy,
mkTyConApp con cargs `mkTransCoercion` mkTyVarTy cotv,
-- data constructor application => flatten subtypes
-- NB: Special cased for efficiency - could be handled as type application
- go (TyConApp con args)
+ go ty@(TyConApp con args)
= do { (args', cargs, args_eqss, args_skolemss) <- mapAndUnzip4M go args
- ; return (mkTyConApp con args',
- mkTyConApp con cargs,
- concat args_eqss,
- unionVarSets args_skolemss)
+ ; if null args_eqss
+ then -- unchanged, keep the old type with folded synonyms
+ return (ty, ty, [], emptyVarSet)
+ else
+ return (mkTyConApp con args',
+ mkTyConApp con cargs,
+ concat args_eqss,
+ unionVarSets args_skolemss)
}
-- function type => flatten subtypes
-- NB: Special cased for efficiency - could be handled as type application
- go (FunTy ty_l ty_r)
+ go ty@(FunTy ty_l ty_r)
= do { (ty_l', co_l, eqs_l, skolems_l) <- go ty_l
; (ty_r', co_r, eqs_r, skolems_r) <- go ty_r
- ; return (mkFunTy ty_l' ty_r',
- mkFunTy co_l co_r,
- eqs_l ++ eqs_r,
- skolems_l `unionVarSet` skolems_r)
+ ; if null eqs_l && null eqs_r
+ then -- unchanged, keep the old type with folded synonyms
+ return (ty, ty, [], emptyVarSet)
+ else
+ return (mkFunTy ty_l' ty_r',
+ mkFunTy co_l co_r,
+ eqs_l ++ eqs_r,
+ skolems_l `unionVarSet` skolems_r)
}
-- type application => flatten subtypes
- go (AppTy ty_l ty_r)
--- | Just (ty_l, ty_r) <- repSplitAppTy_maybe ty
+ go ty@(AppTy ty_l ty_r)
= do { (ty_l', co_l, eqs_l, skolems_l) <- go ty_l
; (ty_r', co_r, eqs_r, skolems_r) <- go ty_r
- ; return (mkAppTy ty_l' ty_r',
- mkAppTy co_l co_r,
- eqs_l ++ eqs_r,
- skolems_l `unionVarSet` skolems_r)
+ ; if null eqs_l && null eqs_r
+ then -- unchanged, keep the old type with folded synonyms
+ return (ty, ty, [], emptyVarSet)
+ else
+ return (mkAppTy ty_l' ty_r',
+ mkAppTy co_l co_r,
+ eqs_l ++ eqs_r,
+ skolems_l `unionVarSet` skolems_r)
}
- -- free of type families => leave as is
- go ty
- = ASSERT( not . isForAllTy $ ty )
- return (ty, ty, [] , emptyVarSet)
+ -- forall type => panic if the body contains a type family
+ -- !!!TODO: As long as the family does not contain a quantified variable
+ -- we might pull it out, but what if it does contain a quantified
+ -- variable???
+ go ty@(ForAllTy _ body)
+ | null (tyFamInsts body)
+ = return (ty, ty, [] , emptyVarSet)
+ | otherwise
+ = panic "TcTyFuns.flattenType: synonym family in a rank-n type"
+
+ -- we should never see a predicate type
+ go (PredTy _)
+ = panic "TcTyFuns.flattenType: unexpected PredType"
adjustCoercions :: EqInstCo -- coercion of original equality
- -> Coercion -- coercion witnessing the rewrite
+ -> Coercion -- coercion witnessing the left rewrite
+ -> Coercion -- coercion witnessing the right rewrite
-> (Type, Type) -- types of flattened equality
-> [RewriteInst] -- equalities from flattening
-> TcM (EqInstCo, -- coercion for flattened equality
-- Depending on whether we flattened a local or wanted equality, that equality's
-- coercion and that of the new equalities produced during flattening are
-- adjusted .
-adjustCoercions co rewriteCo eqTys all_eqs
-
+adjustCoercions (Left cotv) co1 co2 (ty_l, ty_r) all_eqs
-- wanted => generate a fresh coercion variable for the flattened equality
- | isWantedCo co
- = do { co' <- mkRightTransEqInstCo co rewriteCo eqTys
- ; return (co', all_eqs)
+ = do { cotv' <- newMetaCoVar ty_l ty_r
+ ; writeMetaTyVar cotv $
+ (co1 `mkTransCoercion` TyVarTy cotv' `mkTransCoercion` co2)
+ ; return (Left cotv', all_eqs)
}
+adjustCoercions co@(Right _) _co1 _co2 _eqTys all_eqs
-- local => turn all new equalities into locals and update (but not zonk)
-- the skolem
- | otherwise
= do { all_eqs' <- mapM wantedToLocal all_eqs
; return (co, all_eqs')
}
-> Bool -- is this a wanted contraint?
-> Coercion -- coercion witnessing the rewrite
-> PredType -- coerced predicate
- -> [RewriteInst] -- equalities from flattening
-> TcM (Inst, -- new inst
- TcDictBinds, -- binding for coerced dictionary
- [RewriteInst]) -- final equalities from flattening
-mkDictBind dict _isWanted _rewriteCo _pred []
- = return (dict, emptyBag, []) -- don't generate binding for an id coercion
-mkDictBind dict isWanted rewriteCo pred eqs
+ TcDictBinds) -- binding for coerced dictionary
+mkDictBind dict isWanted rewriteCo pred
= do { dict' <- newDictBndr loc pred
-- relate the old inst to the new one
-- target_dict = source_dict `cast` st_co
cast_expr = HsWrap (WpCast st_co) expr
rhs = L (instLocSpan loc) cast_expr
binds = instToDictBind target_dict rhs
- ; eqs' <- if isWanted then return eqs else mapM wantedToLocal eqs
- ; return (dict', binds, eqs')
+ ; return (dict', binds)
}
where
loc = tci_loc dict
Nothing -> return Nothing
Just (lhs, rewrite_co)
-> do { co' <- mkRightTransEqInstCo co rewrite_co (lhs, rhs)
- ; let eq' = EqInst
- { tci_left = lhs
- , tci_right = rhs
- , tci_co = co'
- , tci_loc = rwi_loc eq
- , tci_name = rwi_name eq
- }
+ ; eq' <- deriveEqInst eq lhs rhs co'
; liftM Just $ normEqInst eq'
}
}
-- !!!Check whether anything breaks by making tcEqTypes look through synonyms.
-- !!!Should be ok and we don't want three type equalities.
= do { co2' <- mkRightTransEqInstCo co2 co1 (lhs, rhs)
- ; let eq2' = EqInst
- { tci_left = lhs
- , tci_right = rhs
- , tci_co = co2'
- , tci_loc = rwi_loc eq2
- , tci_name = rwi_name eq2
- }
+ ; eq2' <- deriveEqInst eq2 lhs rhs co2'
; liftM Just $ normEqInst eq2'
}
where
| tv1 == tv2 &&
(isWantedRewriteInst eq2 || not (isWantedRewriteInst eq1))
= do { co2' <- mkRightTransEqInstCo co2 co1 (lhs, rhs)
- ; let eq2' = EqInst
- { tci_left = lhs
- , tci_right = rhs
- , tci_co = co2'
- , tci_loc = rwi_loc eq2
- , tci_name = rwi_name eq2
- }
+ ; eq2' <- deriveEqInst eq2 lhs rhs co2'
; liftM Just $ normEqInst eq2'
}
where
= return (res, binds, locals, wanteds)
subst (eq@(RewriteVar {rwi_var = tv, rwi_right = ty, rwi_co = co}):eqs)
res binds locals wanteds
- = do { let coSubst = zipOpenTvSubst [tv] [eqInstCoType co]
+ = do { traceTc $ ptext (sLit "TcTyFuns.substitute:") <+> ppr eq
+ ; let coSubst = zipOpenTvSubst [tv] [eqInstCoType co]
tySubst = zipOpenTvSubst [tv] [ty]
; eqs' <- mapM (substEq eq coSubst tySubst) eqs
; res' <- mapM (substEq eq coSubst tySubst) res
-- We have, co :: tv ~ ty
-- => apply [ty/tv] to right-hand side of eq2
-- (but only if tv actually occurs in the right-hand side of eq2)
- substEq (RewriteVar {rwi_var = tv, rwi_right = ty, rwi_co = co})
+ substEq (RewriteVar {rwi_var = tv, rwi_right = ty})
coSubst tySubst eq2
| tv `elemVarSet` tyVarsOfType (rwi_right eq2)
= do { let co1Subst = mkSymCoercion $ substTy coSubst (rwi_right eq2)
-- We have, co :: tv ~ ty
-- => apply [ty/tv] to dictionary predicate
-- (but only if tv actually occurs in the predicate)
- substDict (RewriteVar {rwi_var = tv, rwi_right = ty, rwi_co = co})
+ substDict (RewriteVar {rwi_var = tv})
coSubst tySubst isWanted dict
| isClassDict dict
, tv `elemVarSet` tyVarsOfPred (tci_pred dict)
- = do { let co1Subst = mkSymCoercion $
- PredTy (substPred coSubst (tci_pred dict))
+ = do { let co1Subst = PredTy (substPred coSubst (tci_pred dict))
pred' = substPred tySubst (tci_pred dict)
- ; (dict', binds, _) <- mkDictBind dict isWanted co1Subst pred' []
+ ; (dict', binds) <- mkDictBind dict isWanted co1Subst pred'
; return (binds, dict')
}
if at least one instantiation of a flexible was performed.
\begin{code}
-instantiateAndExtract :: [RewriteInst] -> TcM ([Inst], Bool)
-instantiateAndExtract eqs
- = do { let wanteds = filter (isWantedCo . rwi_co) eqs
- ; wanteds' <- mapM inst wanteds
+instantiateAndExtract :: [RewriteInst] -> Bool -> TcM ([Inst], Bool)
+instantiateAndExtract eqs localsEmpty
+ = do { wanteds' <- mapM inst wanteds
; let residuals = catMaybes wanteds'
improved = length wanteds /= length residuals
- ; return (map rewriteInstToInst residuals, improved)
+ ; residuals' <- mapM rewriteInstToInst residuals
+ ; return (residuals', improved)
}
where
+ wanteds = filter (isWantedCo . rwi_co) eqs
+ checkingMode = length eqs > length wanteds || not localsEmpty
+ -- no local equalities or dicts => checking mode
+
inst eq@(RewriteVar {rwi_var = tv1, rwi_right = ty2, rwi_co = co})
-- co :: alpha ~ t
| isMetaTyVar tv1
- = doInst tv1 ty2 co eq
+ = doInst (rwi_swapped eq) tv1 ty2 co eq
-- co :: a ~ alpha
| Just tv2 <- tcGetTyVar_maybe ty2
, isMetaTyVar tv2
- = doInst tv2 (mkTyVarTy tv1) co eq
+ = doInst (not $ rwi_swapped eq) tv2 (mkTyVarTy tv1) co eq
+
+ -- co :: F args ~ alpha, and we are in checking mode (ie, no locals)
+ inst eq@(RewriteFam {rwi_fam = fam, rwi_args = args, rwi_right = ty2,
+ rwi_co = co})
+ | checkingMode
+ , Just tv2 <- tcGetTyVar_maybe ty2
+ , isMetaTyVar tv2
+ = doInst (not $ rwi_swapped eq) tv2 (mkTyConApp fam args) co eq
inst eq = return $ Just eq
- doInst _ _ (Right ty) _eq = pprPanic "TcTyFuns.doInst: local eq: "
- (ppr ty)
- doInst tv ty (Left cotv) eq = do { lookupTV <- lookupTcTyVar tv
- ; uMeta False tv lookupTV ty cotv
- }
+ doInst _swapped _tv _ty (Right ty) _eq
+ = pprPanic "TcTyFuns.doInst: local eq: " (ppr ty)
+ doInst swapped tv ty (Left cotv) eq
+ = do { lookupTV <- lookupTcTyVar tv
+ ; uMeta swapped tv lookupTV ty cotv
+ }
where
-- meta variable has been filled already
-- => ignore (must be a skolem that was introduced by flattening locals)
ppr_extra env _ty = (env, empty) -- Normal case
\end{code}
+
+Warn of loopy local equalities that were dropped.
+
+\begin{code}
+warnDroppingLoopyEquality :: TcType -> TcType -> TcM ()
+warnDroppingLoopyEquality ty1 ty2
+ = do { env0 <- tcInitTidyEnv
+ ; ty1 <- zonkTcType ty1
+ ; ty2 <- zonkTcType ty2
+ ; let (env1 , tidy_ty1) = tidyOpenType env0 ty1
+ (_env2, tidy_ty2) = tidyOpenType env1 ty2
+ ; addWarnTc $ hang (ptext (sLit "Dropping loopy given equality"))
+ 2 (quotes (ppr tidy_ty1 <+> text "~" <+> ppr tidy_ty2))
+ }
+\end{code}