%
% (c) The University of Glasgow 2006
% (c) The GRASP/AQUA Project, Glasgow University, 2000
%
FunDeps - functional dependencies
It's better to read it as: "if we know these, then we're going to know these"
\begin{code}
module FunDeps (
FDEq (..),
Equation(..), pprEquation,
improveFromInstEnv, improveFromAnother,
checkInstCoverage, checkFunDeps,
growThetaTyVars, pprFundeps
) where
#include "HsVersions.h"
import Name
import Var
import Class
import Type
import Unify
import InstEnv
import VarSet
import VarEnv
import Maybes( firstJusts )
import Outputable
import Util
import FastString
import Data.List ( nubBy )
import Data.Maybe ( isJust )
\end{code}
%************************************************************************
%* *
\subsection{Close type variables}
%* *
%************************************************************************
Note [Growing the tau-tvs using constraints]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(growThetaTyVars insts tvs) is the result of extending the set
of tyvars tvs using all conceivable links from pred
E.g. tvs = {a}, preds = {H [a] b, K (b,Int) c, Eq e}
Then growThetaTyVars preds tvs = {a,b,c}
Notice that
growThetaTyVars is conservative if v might be fixed by vs
=> v `elem` grow(vs,C)
\begin{code}
growThetaTyVars :: ThetaType -> TyVarSet -> TyVarSet
growThetaTyVars theta tvs
| null theta = tvs
| otherwise = fixVarSet mk_next tvs
where
mk_next tvs = foldr grow_one tvs theta
grow_one pred tvs = growPredTyVars pred tvs `unionVarSet` tvs
growPredTyVars :: PredType
-> TyVarSet
-> TyVarSet
growPredTyVars pred tvs
| isIPPred pred = pred_tvs
| pred_tvs `intersectsVarSet` tvs = pred_tvs
| otherwise = emptyVarSet
where
pred_tvs = tyVarsOfType pred
\end{code}
%************************************************************************
%* *
\subsection{Generate equations from functional dependencies}
%* *
%************************************************************************
Each functional dependency with one variable in the RHS is responsible
for generating a single equality. For instance:
class C a b | a -> b
The constraints ([Wanted] C Int Bool) and [Wanted] C Int alpha
FDEq { fd_pos = 1
, fd_ty_left = Bool
, fd_ty_right = alpha }
However notice that a functional dependency may have more than one variable
in the RHS which will create more than one FDEq. Example:
class C a b c | a -> b c
[Wanted] C Int alpha alpha
[Wanted] C Int Bool beta
Will generate:
fd1 = FDEq { fd_pos = 1, fd_ty_left = alpha, fd_ty_right = Bool } and
fd2 = FDEq { fd_pos = 2, fd_ty_left = alpha, fd_ty_right = beta }
We record the paremeter position so that can immediately rewrite a constraint
using the produced FDEqs and remove it from our worklist.
INVARIANT: Corresponding types aren't already equal
That is, there exists at least one non-identity equality in FDEqs.
Assume:
class C a b c | a -> b c
instance C Int x x
And: [Wanted] C Int Bool alpha
We will /match/ the LHS of fundep equations, producing a matching substitution
and create equations for the RHS sides. In our last example we'd have generated:
({x}, [fd1,fd2])
where
fd1 = FDEq 1 Bool x
fd2 = FDEq 2 alpha x
To ``execute'' the equation, make fresh type variable for each tyvar in the set,
instantiate the two types with these fresh variables, and then unify or generate
a new constraint. In the above example we would generate a new unification
variable 'beta' for x and produce the following constraints:
[Wanted] (Bool ~ beta)
[Wanted] (alpha ~ beta)
Notice the subtle difference between the above class declaration and:
class C a b c | a -> b, a -> c
where we would generate:
({x},[fd1]),({x},[fd2])
This means that the template variable would be instantiated to different
unification variables when producing the FD constraints.
Finally, the position parameters will help us rewrite the wanted constraint ``on the spot''
\begin{code}
data Equation
= FDEqn { fd_qtvs :: [TyVar]
, fd_eqs :: [FDEq]
, fd_pred1, fd_pred2 :: PredType }
data FDEq = FDEq { fd_pos :: Int
, fd_ty_left :: Type
, fd_ty_right :: Type }
instance Outputable FDEq where
ppr (FDEq { fd_pos = p, fd_ty_left = tyl, fd_ty_right = tyr })
= parens (int p <> comma <+> ppr tyl <> comma <+> ppr tyr)
\end{code}
Given a bunch of predicates that must hold, such as
C Int t1, C Int t2, C Bool t3, ?x::t4, ?x::t5
improve figures out what extra equations must hold.
For example, if we have
class C a b | a->b where ...
then improve will return
[(t1,t2), (t4,t5)]
NOTA BENE:
* improve does not iterate. It's possible that when we make
t1=t2, for example, that will in turn trigger a new equation.
This would happen if we also had
C t1 t7, C t2 t8
If t1=t2, we also get t7=t8.
improve does *not* do this extra step. It relies on the caller
doing so.
* The equations unify types that are not already equal. So there
is no effect iff the result of improve is empty
\begin{code}
instFD :: FunDep TyVar -> [TyVar] -> [Type] -> FunDep Type
instFD (ls,rs) tvs tys
= (map lookup ls, map lookup rs)
where
env = zipVarEnv tvs tys
lookup tv = lookupVarEnv_NF env tv
instFD_WithPos :: FunDep TyVar -> [TyVar] -> [Type] -> ([Type], [(Int,Type)])
instFD_WithPos (ls,rs) tvs tys
= (map (snd . lookup) ls, map lookup rs)
where
ind_tys = zip [0..] tys
env = zipVarEnv tvs ind_tys
lookup tv = lookupVarEnv_NF env tv
zipAndComputeFDEqs :: (Type -> Type -> Bool)
-> [Type]
-> [(Int,Type)]
-> [FDEq]
zipAndComputeFDEqs discard (ty1:tys1) ((i2,ty2):tys2)
| discard ty1 ty2 = zipAndComputeFDEqs discard tys1 tys2
| otherwise = FDEq { fd_pos = i2
, fd_ty_left = ty1
, fd_ty_right = ty2 } : zipAndComputeFDEqs discard tys1 tys2
zipAndComputeFDEqs _ _ _ = []
improveFromAnother :: PredType
-> PredType
-> [Equation]
improveFromAnother pred1 pred2
| Just (cls1, tys1) <- getClassPredTys_maybe pred1
, Just (cls2, tys2) <- getClassPredTys_maybe pred2
, tys1 `lengthAtLeast` 2 && cls1 == cls2
= [ FDEqn { fd_qtvs = [], fd_eqs = eqs, fd_pred1 = pred1, fd_pred2 = pred2 }
| let (cls_tvs, cls_fds) = classTvsFds cls1
, fd <- cls_fds
, let (ltys1, rs1) = instFD fd cls_tvs tys1
(ltys2, irs2) = instFD_WithPos fd cls_tvs tys2
, eqTypes ltys1 ltys2
, let eqs = zipAndComputeFDEqs eqType rs1 irs2
, not (null eqs) ]
improveFromAnother _ _ = []
pprEquation :: Equation -> SDoc
pprEquation (FDEqn { fd_qtvs = qtvs, fd_eqs = pairs })
= vcat [ptext (sLit "forall") <+> braces (pprWithCommas ppr qtvs),
nest 2 (vcat [ ppr t1 <+> ptext (sLit "~") <+> ppr t2 | (FDEq _ t1 t2) <- pairs])]
improveFromInstEnv :: (InstEnv,InstEnv)
-> PredType
-> [Equation]
improveFromInstEnv _inst_env pred
| not (isClassPred pred)
= panic "improveFromInstEnv: not a class predicate"
improveFromInstEnv inst_env pred
| Just (cls, tys) <- getClassPredTys_maybe pred
, tys `lengthAtLeast` 2
, let (cls_tvs, cls_fds) = classTvsFds cls
instances = classInstances inst_env cls
rough_tcs = roughMatchTcs tys
= [ FDEqn { fd_qtvs = meta_tvs, fd_eqs = eqs, fd_pred1 = p_inst, fd_pred2=pred }
| fd <- cls_fds
, let trimmed_tcs = trimRoughMatchTcs cls_tvs fd rough_tcs
, ispec <- instances
, (meta_tvs, eqs) <- checkClsFD fd cls_tvs ispec
emptyVarSet tys trimmed_tcs
, let p_inst = mkClassPred cls (is_tys ispec)
]
improveFromInstEnv _ _ = []
checkClsFD :: FunDep TyVar -> [TyVar]
-> ClsInst
-> TyVarSet -> [Type] -> [Maybe Name]
-> [([TyVar], [FDEq])]
checkClsFD fd clas_tvs
(ClsInst { is_tvs = qtvs, is_tys = tys_inst, is_tcs = rough_tcs_inst })
extra_qtvs tys_actual rough_tcs_actual
| instanceCantMatch rough_tcs_inst rough_tcs_actual
= []
| otherwise
= ASSERT2( length tys_inst == length tys_actual &&
length tys_inst == length clas_tvs
, ppr tys_inst <+> ppr tys_actual )
case tcUnifyTys bind_fn ltys1 ltys2 of
Nothing -> []
Just subst | isJust (tcUnifyTys bind_fn rtys1' rtys2')
-> []
| null fdeqs
-> []
| otherwise
-> [(meta_tvs, fdeqs)]
where
rtys1' = map (substTy subst) rtys1
irs2' = map (\(i,x) -> (i,substTy subst x)) irs2
rtys2' = map snd irs2'
fdeqs = zipAndComputeFDEqs (\_ _ -> False) rtys1' irs2'
meta_tvs = [ setVarType tv (substTy subst (varType tv))
| tv <- qtvs, tv `notElemTvSubst` subst ]
where
qtv_set = mkVarSet qtvs
bind_fn tv | tv `elemVarSet` qtv_set = BindMe
| tv `elemVarSet` extra_qtvs = BindMe
| otherwise = Skolem
(ltys1, rtys1) = instFD fd clas_tvs tys_inst
(ltys2, irs2) = instFD_WithPos fd clas_tvs tys_actual
\end{code}
%************************************************************************
%* *
The Coverage condition for instance declarations
%* *
%************************************************************************
Note [Coverage condition]
~~~~~~~~~~~~~~~~~~~~~~~~~
Example
class C a b | a -> b
instance theta => C t1 t2
For the coverage condition, we check
(normal) fv(t2) `subset` fv(t1)
(liberal) fv(t2) `subset` oclose(fv(t1), theta)
The liberal version ensures the self-consistency of the instance, but
it does not guarantee termination. Example:
class Mul a b c | a b -> c where
(.*.) :: a -> b -> c
instance Mul Int Int Int where (.*.) = (*)
instance Mul Int Float Float where x .*. y = fromIntegral x * y
instance Mul a b c => Mul a [b] [c] where x .*. v = map (x.*.) v
In the third instance, it's not the case that fv([c]) `subset` fv(a,[b]).
But it is the case that fv([c]) `subset` oclose( theta, fv(a,[b]) )
But it is a mistake to accept the instance because then this defn:
f = \ b x y -> if b then x .*. [y] else y
makes instance inference go into a loop, because it requires the constraint
Mul a [b] b
\begin{code}
checkInstCoverage :: Bool
-> Class -> [PredType] -> [Type]
-> Maybe SDoc
checkInstCoverage be_liberal clas theta inst_taus
= firstJusts (map fundep_ok fds)
where
(tyvars, fds) = classTvsFds clas
fundep_ok fd
| if be_liberal then liberal_ok else conservative_ok
= Nothing
| otherwise
= Just msg
where
(ls,rs) = instFD fd tyvars inst_taus
ls_tvs = closeOverKinds (tyVarsOfTypes ls)
rs_tvs = tyVarsOfTypes rs
conservative_ok = rs_tvs `subVarSet` ls_tvs
liberal_ok = rs_tvs `subVarSet` oclose theta ls_tvs
msg = vcat [ sep [ ptext (sLit "The")
<+> ppWhen be_liberal (ptext (sLit "liberal"))
<+> ptext (sLit "coverage condition fails in class")
<+> quotes (ppr clas)
, nest 2 $ ptext (sLit "for functional dependency:")
<+> quotes (pprFunDep fd) ]
, sep [ ptext (sLit "Reason: lhs type")<>plural ls <+> pprQuotedList ls
, nest 2 $
(if isSingleton ls
then ptext (sLit "does not")
else ptext (sLit "do not jointly"))
<+> ptext (sLit "determine rhs type")<>plural rs
<+> pprQuotedList rs ]
, ppWhen (not be_liberal && liberal_ok) $
ptext (sLit "Using UndecidableInstances might help") ]
\end{code}
Note [Closing over kinds in coverage]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose we have a fundep (a::k) -> b
Then if 'a' is instantiated to (x y), where x:k2->*, y:k2,
then fixing x really fixes k2 as well, and so k2 should be added to
the lhs tyvars in the fundep check.
Example (Trac #8391), using liberal coverage
type Foo a = a -- Foo :: forall k. k -> k
class Bar a b | a -> b
instance Bar a (Foo a)
In the instance decl, (a:k) does fix (Foo k a), but only if we notice
that (a:k) fixes k.
Note [The liberal coverage condition]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(oclose preds tvs) closes the set of type variables tvs,
wrt functional dependencies in preds. The result is a superset
of the argument set. For example, if we have
class C a b | a->b where ...
then
oclose [C (x,y) z, C (x,p) q] {x,y} = {x,y,z}
because if we know x and y then that fixes z.
We also use equality predicates in the predicates; if we have an
assumption `t1 ~ t2`, then we use the fact that if we know `t1` we
also know `t2` and the other way.
eg oclose [C (x,y) z, a ~ x] {a,y} = {a,y,z,x}
oclose is used (only) when checking the coverage condition for
an instance declaration
\begin{code}
oclose :: [PredType] -> TyVarSet -> TyVarSet
oclose preds fixed_tvs
| null tv_fds = fixed_tvs
| otherwise = loop fixed_tvs
where
loop fixed_tvs
| new_fixed_tvs `subVarSet` fixed_tvs = fixed_tvs
| otherwise = loop new_fixed_tvs
where new_fixed_tvs = foldl extend fixed_tvs tv_fds
extend fixed_tvs (ls,rs)
| ls `subVarSet` fixed_tvs = fixed_tvs `unionVarSet` rs
| otherwise = fixed_tvs
tv_fds :: [(TyVarSet,TyVarSet)]
tv_fds = [ (tyVarsOfTypes xs, tyVarsOfTypes ys)
| (xs, ys) <- concatMap determined preds
]
determined :: PredType -> [([Type],[Type])]
determined pred
= case classifyPredType pred of
ClassPred cls tys ->
do let (cls_tvs, cls_fds) = classTvsFds cls
fd <- cls_fds
return (instFD fd cls_tvs tys)
EqPred t1 t2 -> [([t1],[t2]), ([t2],[t1])]
TuplePred ts -> concatMap determined ts
_ -> []
\end{code}
%************************************************************************
%* *
Check that a new instance decl is OK wrt fundeps
%* *
%************************************************************************
Here is the bad case:
class C a b | a->b where ...
instance C Int Bool where ...
instance C Int Char where ...
The point is that a->b, so Int in the first parameter must uniquely
determine the second. In general, given the same class decl, and given
instance C s1 s2 where ...
instance C t1 t2 where ...
Then the criterion is: if U=unify(s1,t1) then U(s2) = U(t2).
Matters are a little more complicated if there are free variables in
the s2/t2.
class D a b c | a -> b
instance D a b => D [(a,a)] [b] Int
instance D a b => D [a] [b] Bool
The instance decls don't overlap, because the third parameter keeps
them separate. But we want to make sure that given any constraint
D s1 s2 s3
if s1 matches
\begin{code}
checkFunDeps :: (InstEnv, InstEnv) -> ClsInst
-> Maybe [ClsInst]
checkFunDeps inst_envs ispec
| null bad_fundeps = Nothing
| otherwise = Just bad_fundeps
where
(ins_tvs, clas, ins_tys) = instanceHead ispec
ins_tv_set = mkVarSet ins_tvs
cls_inst_env = classInstances inst_envs clas
bad_fundeps = badFunDeps cls_inst_env clas ins_tv_set ins_tys
badFunDeps :: [ClsInst] -> Class
-> TyVarSet -> [Type]
-> [ClsInst]
badFunDeps cls_insts clas ins_tv_set ins_tys
= nubBy eq_inst $
[ ispec | fd <- fds,
let trimmed_tcs = trimRoughMatchTcs clas_tvs fd rough_tcs,
ispec <- cls_insts,
notNull (checkClsFD fd clas_tvs ispec ins_tv_set ins_tys trimmed_tcs)
]
where
(clas_tvs, fds) = classTvsFds clas
rough_tcs = roughMatchTcs ins_tys
eq_inst i1 i2 = instanceDFunId i1 == instanceDFunId i2
trimRoughMatchTcs :: [TyVar] -> FunDep TyVar -> [Maybe Name] -> [Maybe Name]
trimRoughMatchTcs clas_tvs (ltvs, _) mb_tcs
= zipWith select clas_tvs mb_tcs
where
select clas_tv mb_tc | clas_tv `elem` ltvs = mb_tc
| otherwise = Nothing
\end{code}