Type families: new algorithm to solve equalities
[ghc-hetmet.git] / compiler / typecheck / TcTyFuns.lhs
index bfe5207..188a29e 100644 (file)
@@ -3,15 +3,20 @@ normalisation and entailment checking of equality constraints.
 
 \begin{code}
 module TcTyFuns (
 
 \begin{code}
 module TcTyFuns (
-       tcNormaliseFamInst,
+  -- type normalisation wrt to toplevel equalities only
+  tcNormaliseFamInst,
 
 
-       normaliseGivenEqs, normaliseGivenDicts, 
-       normaliseWantedEqs, normaliseWantedDicts,
-       solveWantedEqs,
-       substEqInDictInsts,
+  -- normalisation and solving of equalities
+  EqConfig,
+  normaliseEqs, propagateEqs, finaliseEqs, normaliseDicts,
+
+  -- errors
+  misMatchMsg, failWithMisMatch,
+
+  -- DEPRECATED: interface for the ICFP'08 algorithm
+  normaliseGivenEqs, normaliseGivenDicts, 
+  normaliseWantedEqs, normaliseWantedDicts,
        
        
-        -- errors
-        eqInstMisMatch, misMatchMsg,
   ) where
 
 
   ) where
 
 
@@ -31,22 +36,24 @@ import TypeRep      ( Type(..) )
 import TyCon
 import HsSyn
 import VarEnv
 import TyCon
 import HsSyn
 import VarEnv
+import VarSet
 import Var
 import Name
 import Bag
 import Outputable
 import SrcLoc  ( Located(..) )
 import Maybes
 import Var
 import Name
 import Bag
 import Outputable
 import SrcLoc  ( Located(..) )
 import Maybes
+import FastString
 
 -- standard
 import Data.List
 
 -- standard
 import Data.List
-import Control.Monad (liftM)
+import Control.Monad
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
 \end{code}
 
 
 %************************************************************************
 %*                                                                     *
-               Normalisation of types
+               Normalisation of types wrt toplevel equality schemata
 %*                                                                     *
 %************************************************************************
 
 %*                                                                     *
 %************************************************************************
 
@@ -92,6 +99,10 @@ possible (ie, we treat family instances as a TRS).  Also zonk meta variables.
        then   co : ty ~ ty'
 
 \begin{code}
        then   co : ty ~ ty'
 
 \begin{code}
+-- |Normalise the given type as far as possible with toplevel equalities.
+-- This results in a coercion witnessing the type equality, in addition to the
+-- normalised type.
+--
 tcNormaliseFamInst :: TcType -> TcM (CoercionI, TcType)
 tcNormaliseFamInst = tcGenericNormaliseFamInst tcUnfoldSynFamInst
 
 tcNormaliseFamInst :: TcType -> TcM (CoercionI, TcType)
 tcNormaliseFamInst = tcGenericNormaliseFamInst tcUnfoldSynFamInst
 
@@ -99,11 +110,781 @@ tcNormaliseFamInstPred :: TcPredType -> TcM (CoercionI, TcPredType)
 tcNormaliseFamInstPred = tcGenericNormaliseFamInstPred tcUnfoldSynFamInst
 \end{code}
 
 tcNormaliseFamInstPred = tcGenericNormaliseFamInstPred tcUnfoldSynFamInst
 \end{code}
 
+%************************************************************************
+%*                                                                     *
+               Equality Configurations
+%*                                                                     *
+%************************************************************************
+
+We maintain normalised equalities together with the skolems introduced as
+intermediates during flattening of equalities.
+
+!!!TODO: Do we really need to keep track of the skolem variables?  They are at
+the moment not used in instantiateAndExtract, but it is hard to say until we
+know exactly how finalisation will fianlly look like.
+
+\begin{code}
+-- |Configuration of normalised equalities used during solving.
+--
+data EqConfig = EqConfig { eqs     :: [RewriteInst]
+                         , skolems :: TyVarSet
+                         }
+
+addSkolems :: EqConfig -> TyVarSet -> EqConfig
+addSkolems eqCfg newSkolems 
+  = eqCfg {skolems = skolems eqCfg `unionVarSet` newSkolems}
+
+addEq :: EqConfig -> RewriteInst -> EqConfig
+addEq eqCfg eq = eqCfg {eqs = eq : eqs eqCfg}
+\end{code}
+
+The set of operations on an equality configuration.  We obtain the initialise
+configuration by normalisation ('normaliseEqs'), solve the equalities by
+propagation ('propagateEqs'), and eventually finalise the configuration when
+no further propoagation is possible.
+
+!!!TODO: Eventually, normalisation of dictionaries and dictionary
+simplification should be included in propagation.
+
+\begin{code}
+-- |Turn a set of equalities into an equality configuration for solving.
+--
+-- Precondition: The Insts are zonked.
+--
+normaliseEqs :: [Inst] -> TcM EqConfig
+normaliseEqs eqs 
+  = do { (eqss, skolemss) <- mapAndUnzipM normEqInst eqs
+       ; return $ EqConfig { eqs = concat eqss
+                           , skolems = unionVarSets skolemss 
+                           }
+       }
+
+-- |Solves the equalities as far as possible by applying propagation rules.
+--
+propagateEqs :: EqConfig -> TcM EqConfig
+propagateEqs eqCfg@(EqConfig {eqs = todoEqs}) 
+  = propagate todoEqs (eqCfg {eqs = []})
+
+-- |Finalise a set of equalities after propagation.  The Boolean value is
+-- `True' iff any flexible variables, except those introduced by flattening
+-- (i.e., those in the `skolems' component of the argument) where instantiated.
+-- The returned set of instances are all residual wanteds.
+--
+finaliseEqs :: EqConfig -> TcM ([Inst], Bool)
+finaliseEqs (EqConfig {eqs = eqs, skolems = skolems})
+  = do { eqs' <- substitute eqs
+       ; instantiateAndExtract eqs' skolems
+       }
+
+-- |Normalise a set of class instances under a given equality configuration.
+-- Both the class instances and the equality configuration may change.  The
+-- function returns 'Nothing' if neither changes.
+--
+normaliseDicts :: EqConfig -> [Inst] -> TcM (Maybe (EqConfig, [Inst]))
+normaliseDicts = error "TcTyFuns.normaliseDicts"
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+               Normalisation of equalities
+%*                                                                     *
+%************************************************************************
+
+A normal equality is a properly oriented equality with associated coercion
+that contains at most one family equality (in its left-hand side) is oriented
+such that it may be used as a reqrite rule.  It has one of the following two 
+forms:
+
+(1) co :: F t1..tn ~ t  (family equalities)
+(2) co :: x ~ t         (variable equalities)
+
+Variable equalities fall again in two classes:
+
+(2a) co :: x ~ t, where t is *not* a variable, or
+(2b) co :: x ~ y, where x > y.
+
+The types t, t1, ..., tn may not contain any occurrences of synonym
+families.  Moreover, in Forms (2) & (3), the left-hand side may not occur in
+the right-hand side, and the relation x > y is an arbitrary, but total order
+on type variables
+
+!!!TODO: We may need to keep track of swapping for error messages (and to
+re-orient on finilisation).
+
+\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)
+    }
+  | 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)
+    }
+
+isWantedRewriteInst :: RewriteInst -> Bool
+isWantedRewriteInst = isWantedCo . rwi_co
+
+rewriteInstToInst :: RewriteInst -> 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
+    }
+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
+    }
+\end{code}
+
+The following functions turn an arbitrary equality into a set of normal
+equalities.
+
+\begin{code}
+normEqInst :: Inst -> TcM ([RewriteInst], TyVarSet)
+normEqInst inst
+  = ASSERT( isEqInst inst )
+    go ty1 ty2 (eqInstCoercion inst)
+  where
+    (ty1, ty2) = eqInstTys inst
+
+      -- look through synonyms
+    go ty1 ty2 co | Just ty1' <- tcView ty1 = go ty1' ty2 co
+    go ty1 ty2 co | Just ty2' <- tcView ty2 = go ty1 ty2' co
+
+      -- left-to-right rule with type family head
+    go (TyConApp con args) ty2 co 
+      | isOpenSynTyCon con
+      = mkRewriteFam 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'
+           }
+
+      -- no outermost family
+    go ty1 ty2 co
+      = 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
+                 eqTys     = (ty1', ty2')
+           ; (co', ty12_eqs') <- adjustCoercions co rewriteCo eqTys ty12_eqs
+           ; eqs <- checkOrientation ty1' ty2' co' inst
+           ; return $ (eqs ++ ty12_eqs',
+                       ty1_skolems `unionVarSet` ty2_skolems)
+           }
+
+    mkRewriteFam 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
+                 all_eqs   = concat args_eqss ++ ty2_eqs
+                 eqTys     = (mkTyConApp con args', ty2')
+           ; (co', all_eqs') <- adjustCoercions co rewriteCo 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
+                                  }
+           ; return $ (thisRewriteFam : all_eqs',
+                       unionVarSets (ty2_skolems:args_skolemss))
+           }
+
+checkOrientation :: Type -> Type -> EqInstCo -> Inst -> TcM [RewriteInst]
+-- Performs the occurs check, decomposition, and proper orientation
+-- (returns a singleton, or an empty list in case of a trivial equality)
+-- 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
+  where
+      -- look through synonyms
+    go ty1 ty2 | Just ty1' <- tcView ty1 = go ty1' ty2
+    go ty1 ty2 | Just ty2' <- tcView ty2 = go ty1 ty2'
+
+      -- identical types => trivial
+    go ty1 ty2
+      | ty1 `tcEqType` ty2
+      = do { mkIdEqInstCo co ty1
+           ; return []
+           }
+
+      -- two tvs, left greater => unchanged
+    go ty1@(TyVarTy tv1) ty2@(TyVarTy tv2)
+      | tv1 > tv2
+      = mkRewriteVar tv1 ty2 co
+
+      -- two tvs, right greater => swap
+      | otherwise
+      = do { co' <- mkSymEqInstCo co (ty2, ty1)
+           ; mkRewriteVar tv2 ty1 co'
+           }
+
+      -- only lhs is a tv => unchanged
+    go ty1@(TyVarTy tv1) ty2
+      | ty1 `tcPartOfType` ty2      -- occurs check!
+      = occurCheckErr ty1 ty2
+      | otherwise 
+      = mkRewriteVar tv1 ty2 co
+
+      -- only rhs is a tv => swap
+    go ty1 ty2@(TyVarTy tv2)
+      | ty2 `tcPartOfType` ty1      -- occurs check!
+      = occurCheckErr ty2 ty1
+      | otherwise 
+      = do { co' <- mkSymEqInstCo co (ty2, ty1)
+           ; mkRewriteVar tv2 ty1 co'
+           }
+
+      -- type applications => decompose
+    go ty1 ty2 
+      | Just (ty1_l, ty1_r) <- repSplitAppTy_maybe ty1   -- won't split fam apps
+      , Just (ty2_l, ty2_r) <- repSplitAppTy_maybe ty2
+      = do { (co_l, co_r) <- mkAppEqInstCo co (ty1_l, ty2_l) (ty1_r, ty2_r)
+           ; eqs_l <- checkOrientation ty1_l ty2_l co_l inst
+           ; eqs_r <- checkOrientation ty1_r ty2_r co_r inst
+           ; return $ eqs_l ++ eqs_r
+           }
+-- !!!TODO: would be more efficient to handle the FunApp and the data
+-- constructor application explicitly.
+
+      -- inconsistency => type error
+    go ty1 ty2
+      = 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
+                                    }]
+
+flattenType :: Inst     -- context to get location  & name
+            -> Type     -- the type to flatten
+            -> TcM (Type,           -- the flattened type
+                    Coercion,       -- coercion witness of flattening wanteds
+                    [RewriteInst],  -- extra equalities
+                    TyVarSet)       -- new intermediate skolems
+-- Removes all family synonyms from a type by moving them into extra equalities
+flattenType inst ty
+  = go ty
+  where
+      -- look through synonyms
+    go ty | Just ty' <- tcView ty = go ty'
+
+      -- type family application => flatten to "id :: F t1'..tn' ~ alpha"
+    go ty@(TyConApp con args)
+      | isOpenSynTyCon con
+      = do { (args', cargs, args_eqss, args_skolemss) <- mapAndUnzip4M go args
+           ; alpha <- newFlexiTyVar (typeKind ty)
+           ; 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
+                                  }
+           ; return (alphaTy,
+                     mkTyConApp con cargs `mkTransCoercion` mkTyVarTy cotv,
+                     thisRewriteFam : concat args_eqss,
+                     unionVarSets args_skolemss `extendVarSet` alpha)
+           }           -- adding new unflatten var inst
+
+      -- data constructor application => flatten subtypes
+      -- NB: Special cased for efficiency - could be handled as type application
+    go (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)
+           }
+
+      -- function type => flatten subtypes
+      -- NB: Special cased for efficiency - could be handled as type application
+    go (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)
+           }
+
+      -- type application => flatten subtypes
+    go (AppTy ty_l ty_r)
+--      | Just (ty_l, ty_r) <- repSplitAppTy_maybe ty
+      = 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)
+           }
+
+      -- free of type families => leave as is
+    go ty
+      = ASSERT( not . isForAllTy $ ty )
+        return (ty, ty, [] , emptyVarSet)
+
+adjustCoercions :: EqInstCo            -- coercion of original equality
+                -> Coercion            -- coercion witnessing the rewrite
+                -> (Type, Type)        -- type sof flattened equality
+                -> [RewriteInst]       -- equalities from flattening
+                -> TcM (EqInstCo,      -- coercion for flattened equality
+                        [RewriteInst]) -- final equalities from flattening
+-- Depending on whether we flattened a local or wanted equality, that equality's
+-- coercion and that of the new ones are adjusted
+adjustCoercions co rewriteCo eqTys all_eqs
+  | isWantedCo co 
+  = do { co' <- mkRightTransEqInstCo co rewriteCo eqTys
+       ; return (co', all_eqs)
+       }
+  | otherwise
+  = return (co, map wantedToLocal all_eqs)
+  where
+    wantedToLocal eq = eq {rwi_co = mkGivenCo (rwi_right eq)}
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+               Propagation of equalities
+%*                                                                     *
+%************************************************************************
+
+Apply the propagation rules exhaustively.
+
+\begin{code}
+propagate :: [RewriteInst] -> EqConfig -> TcM EqConfig
+propagate []       eqCfg = return eqCfg
+propagate (eq:eqs) eqCfg
+  = do { optEqs <- applyTop eq
+       ; case optEqs of
+
+              -- Top applied to 'eq' => retry with new equalities
+           Just (eqs2, skolems2) 
+             -> propagate (eqs2 ++ eqs) (eqCfg `addSkolems` skolems2)
+
+              -- Top doesn't apply => try subst rules with all other
+              --   equalities, after that 'eq' can go into the residual list
+           Nothing
+             -> do { (eqs', eqCfg') <- applySubstRules eq eqs eqCfg
+                   ; propagate eqs' (eqCfg' `addEq` eq)
+                   }
+   }
+
+applySubstRules :: RewriteInst                    -- currently considered eq
+                -> [RewriteInst]                  -- todo eqs list
+                -> EqConfig                       -- residual
+                -> TcM ([RewriteInst], EqConfig)  -- new todo & residual
+applySubstRules eq todoEqs (eqConfig@EqConfig {eqs = resEqs})
+  = do { (newEqs_t, unchangedEqs_t, skolems_t) <- mapSubstRules eq todoEqs
+       ; (newEqs_r, unchangedEqs_r, skolems_r) <- mapSubstRules eq resEqs
+       ; return (newEqs_t ++ newEqs_r ++ unchangedEqs_t,
+                 eqConfig {eqs = unchangedEqs_r} 
+                   `addSkolems` (skolems_t `unionVarSet` skolems_r))
+       }
+
+mapSubstRules :: RewriteInst     -- try substituting this equality
+              -> [RewriteInst]   -- into these equalities
+              -> TcM ([RewriteInst], [RewriteInst], TyVarSet)
+mapSubstRules eq eqs
+  = do { (newEqss, unchangedEqss, skolemss) <- mapAndUnzip3M (substRules eq) eqs
+       ; return (concat newEqss, concat unchangedEqss, unionVarSets skolemss)
+       }
+  where
+    substRules eq1 eq2
+      = do {   -- try the SubstFam rule
+             optEqs <- applySubstFam eq1 eq2
+           ; case optEqs of
+               Just (eqs, skolems) -> return (eqs, [], skolems)
+               Nothing             -> do 
+           {   -- try the SubstVarVar rule
+             optEqs <- applySubstVarVar eq1 eq2
+           ; case optEqs of
+               Just (eqs, skolems) -> return (eqs, [], skolems)
+               Nothing             -> do 
+           {   -- try the SubstVarFam rule
+             optEqs <- applySubstVarFam eq1 eq2
+           ; case optEqs of
+               Just eq -> return ([eq], [], emptyVarSet)
+               Nothing -> return ([], [eq2], emptyVarSet)
+                 -- if no rule matches, we return the equlity we tried to
+                 -- substitute into unchanged
+           }}}
+\end{code}
+
+Attempt to apply the Top rule.  The rule is
+
+  co :: F t1..tn ~ t
+  =(Top)=>
+  co' :: [s1/x1, .., sm/xm]s ~ t with co = g s1..sm |> co'  
+
+where g :: forall x1..xm. F u1..um ~ s and [s1/x1, .., sm/xm]u1 == t1.
+
+Returns Nothing if the rule could not be applied.  Otherwise, the resulting
+equality is normalised and a list of the normal equalities is returned.
+
+\begin{code}
+applyTop :: RewriteInst -> TcM (Maybe ([RewriteInst], TyVarSet))
+
+applyTop eq@(RewriteFam {rwi_fam = fam, rwi_args = args})
+  = do { optTyCo <- tcUnfoldSynFamInst (TyConApp fam args)
+       ; case optTyCo of
+           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
+                               }
+                   ; liftM Just $ normEqInst eq'
+                   }
+       }
+  where
+    co  = rwi_co eq
+    rhs = rwi_right eq
+
+applyTop _ = return Nothing
+\end{code}
+
+Attempt to apply the SubstFam rule.  The rule is
+
+  co1 :: F t1..tn ~ t  &  co2 :: F t1..tn ~ s
+  =(SubstFam)=>
+  co1 :: F t1..tn ~ t  &  co2' :: t ~ s with co2 = co1 |> co2'
+
+where co1 may be a wanted only if co2 is a wanted, too.
+
+Returns Nothing if the rule could not be applied.  Otherwise, the equality
+co2' is normalised and a list of the normal equalities is returned.  (The
+equality co1 is not returned as it remain unaltered.)
+
+\begin{code}
+applySubstFam :: RewriteInst 
+              -> RewriteInst 
+              -> TcM (Maybe ([RewriteInst], TyVarSet))
+applySubstFam eq1@(RewriteFam {rwi_fam = fam1, rwi_args = args1})
+              eq2@(RewriteFam {rwi_fam = fam2, rwi_args = args2})
+  | fam1 == fam2 && tcEqTypes args1 args2 &&
+    (isWantedRewriteInst eq2 || not (isWantedRewriteInst eq1))
+-- !!!TODO: tcEqTypes is insufficient as it does not look through type synonyms
+-- !!!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
+                    }
+       ; liftM Just $ normEqInst eq2'
+       }
+  where
+    lhs = rwi_right eq1
+    rhs = rwi_right eq2
+    co1 = eqInstCoType (rwi_co eq1)
+    co2 = rwi_co eq2
+applySubstFam _ _ = return Nothing
+\end{code}
+
+Attempt to apply the SubstVarVar rule.  The rule is
+
+  co1 :: x ~ t  &  co2 :: x ~ s
+  =(SubstVarVar)=>
+  co1 :: x ~ t  &  co2' :: t ~ s with co2 = co1 |> co2'
+
+where co1 may be a wanted only if co2 is a wanted, too.
+
+Returns Nothing if the rule could not be applied.  Otherwise, the equality
+co2' is normalised and a list of the normal equalities is returned.  (The
+equality co1 is not returned as it remain unaltered.)
+
+\begin{code}
+applySubstVarVar :: RewriteInst 
+                 -> RewriteInst 
+                 -> TcM (Maybe ([RewriteInst], TyVarSet))
+applySubstVarVar eq1@(RewriteVar {rwi_var = tv1})
+                 eq2@(RewriteVar {rwi_var = tv2})
+  | 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
+                    }
+       ; liftM Just $ normEqInst eq2'
+       }
+  where
+    lhs = rwi_right eq1
+    rhs = rwi_right eq2
+    co1 = eqInstCoType (rwi_co eq1)
+    co2 = rwi_co eq2
+applySubstVarVar _ _ = return Nothing
+\end{code}
+
+Attempt to apply the SubstVarFam rule.  The rule is
+
+  co1 :: x ~ t  &  co2 :: F s1..sn ~ s
+  =(SubstVarFam)=>
+  co1 :: x ~ t  &  co2' :: [t/x](F s1..sn) ~ s 
+    with co2 = [co1/x](F s1..sn) |> co2'
+
+where x occurs in F s1..sn. (co1 may be local or wanted.)
+
+Returns Nothing if the rule could not be applied.  Otherwise, the equality
+co2' is returned.  (The equality co1 is not returned as it remain unaltered.)
+
+\begin{code}
+applySubstVarFam :: RewriteInst -> RewriteInst -> TcM (Maybe RewriteInst)
+applySubstVarFam eq1@(RewriteVar {rwi_var = tv1})
+                 eq2@(RewriteFam {rwi_fam = fam2, rwi_args = args2})
+  | tv1 `elemVarSet` tyVarsOfTypes args2
+  = do { let co1Subst = substTyWith [tv1] [co1] (mkTyConApp fam2 args2)
+             args2'   = substTysWith [tv1] [rhs1] args2
+             lhs2     = mkTyConApp fam2 args2'
+       ; co2' <- mkRightTransEqInstCo co2 co1Subst (lhs2, rhs2)
+       ; return $ Just (eq2 {rwi_args = args2', rwi_co = co2'})
+       }
+  where
+    rhs1 = rwi_right eq1
+    rhs2 = rwi_right eq2
+    co1  = eqInstCoType (rwi_co eq1)
+    co2  = rwi_co eq2
+applySubstVarFam _ _ = return Nothing
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+               Finalisation of equalities
+%*                                                                     *
+%************************************************************************
+
+Exhaustive substitution of all variable equalities of the form co :: x ~ t
+(both local and wanted) into the left-hand sides all other equalities.  This
+may lead to recursive equalities; i.e., (1) we need to apply the substitution
+implied by one variable equality exhaustively before turning to the next and
+(2) we need an occurs check.
+
+NB: Gievn that we apply the substitution corresponding to a single equality
+exhaustively, before turning to the next, and because we eliminate recursive
+eqaulities, all opportunities for subtitution will have been exhausted after
+we have considered each equality once.
+
+\begin{code}
+substitute :: [RewriteInst] -> TcM [RewriteInst]
+substitute eqs = subst eqs []
+  where
+    subst []       res = return res
+    subst (eq:eqs) res 
+      = do { eqs' <- mapM (substOne eq) eqs
+           ; res' <- mapM (substOne eq) res
+           ; subst eqs' (eq:res')
+           }
+
+      -- apply [ty/tv] to left-hand side of eq2
+    substOne (RewriteVar {rwi_var = tv, rwi_right = ty, rwi_co = co}) eq2
+      = do { let co1Subst = mkSymCoercion $
+                              substTyWith [tv] [eqInstCoType co] (rwi_right eq2)
+                 right2'  = substTyWith [tv] [ty] (rwi_right eq2)
+                 left2    = case eq2 of
+                              RewriteVar {rwi_var = tv2}   -> mkTyVarTy tv2
+                              RewriteFam {rwi_fam = fam,
+                                          rwi_args = args} ->mkTyConApp fam args
+           ; co2' <- mkLeftTransEqInstCo (rwi_co eq2) co1Subst (left2, right2')
+           ; case eq2 of
+               RewriteVar {rwi_var = tv2} | tv2 `elemVarSet` tyVarsOfType ty
+                 -> occurCheckErr left2 right2'
+               _ -> return $ eq2 {rwi_right = right2', rwi_co = co2'}
+           }
+
+      -- changed
+    substOne _ eq2
+      = return eq2
+\end{code}
+
+For any *wanted* variable equality of the form co :: alpha ~ t or co :: a ~
+alpha, we instantiate alpha with t or a, respectively, and set co := id.
+Return all remaining wanted equalities.  The Boolean result component is True
+if at least one instantiation of a flexible was performed.
+
+\begin{code}
+instantiateAndExtract :: [RewriteInst] -> TyVarSet -> TcM ([Inst], Bool)
+instantiateAndExtract eqs _skolems
+  = do { let wanteds = filter (isWantedCo . rwi_co) eqs
+       ; wanteds' <- mapM inst wanteds
+       ; let residuals = catMaybes wanteds'
+             improved  = length wanteds /= length residuals
+       ; return (map rewriteInstToInst residuals, improved)
+       }
+  where
+    inst eq@(RewriteVar {rwi_var = tv1, rwi_right = ty2, rwi_co = co})
+
+        -- co :: alpha ~ t
+      | isMetaTyVar tv1
+      = doInst tv1 ty2 co eq
+
+        -- co :: a ~ alpha
+      | Just tv2 <- tcGetTyVar_maybe ty2
+      , isMetaTyVar tv2
+      = doInst tv2 (mkTyVarTy tv1) 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
+                                      }
+      where
+        -- meta variable has been filled already
+        -- => panic (all equalities should have been zonked on normalisation)
+        uMeta _swapped _tv (IndirectTv _) _ty _cotv
+          = panic "TcTyFuns.uMeta: expected zonked equalities"
+
+        -- type variable meets type variable
+        -- => check that tv2 hasn't been updated yet and choose which to update
+        uMeta swapped tv1 (DoneTv details1) (TyVarTy tv2) cotv
+          | tv1 == tv2
+          = panic "TcTyFuns.uMeta: normalisation shouldn't allow x ~ x"
+
+          | otherwise
+          = do { lookupTV2 <- lookupTcTyVar tv2
+               ; case lookupTV2 of
+                   IndirectTv ty   -> 
+                     uMeta swapped tv1 (DoneTv details1) ty cotv
+                   DoneTv details2 -> 
+                     uMetaVar swapped tv1 details1 tv2 details2 cotv
+               }
+
+        ------ Beyond this point we know that ty2 is not a type variable
+
+        -- signature skolem meets non-variable type
+        -- => cannot update (retain the equality)!
+        uMeta _swapped _tv (DoneTv (MetaTv (SigTv _) _)) _non_tv_ty _cotv
+          = return $ Just eq
+
+        -- updatable meta variable meets non-variable type
+        -- => occurs check, monotype check, and kinds match check, then update
+        uMeta swapped tv (DoneTv (MetaTv _ ref)) non_tv_ty cotv
+          = do {   -- occurs + monotype check
+               ; mb_ty' <- checkTauTvUpdate tv non_tv_ty    
+                             
+               ; case mb_ty' of
+                   Nothing  -> 
+                     -- normalisation shouldn't leave families in non_tv_ty
+                     panic "TcTyFuns.uMeta: unexpected synonym family"
+                   Just ty' ->
+                     do { checkUpdateMeta swapped tv ref ty'  -- update meta var
+                        ; writeMetaTyVar cotv ty'             -- update co var
+                        ; return Nothing
+                        }
+               }
+
+        uMeta _ _ _ _ _ = panic "TcTyFuns.uMeta"
+
+        -- uMetaVar: unify two type variables
+        -- meta variable meets skolem 
+        -- => just update
+        uMetaVar swapped tv1 (MetaTv _ ref) tv2 (SkolemTv _) cotv
+          = do { checkUpdateMeta swapped tv1 ref (mkTyVarTy tv2)
+               ; writeMetaTyVar cotv (mkTyVarTy tv2)
+               ; return Nothing
+               }
+
+        -- meta variable meets meta variable 
+        -- => be clever about which of the two to update 
+        --   (from TcUnify.uUnfilledVars minus boxy stuff)
+        uMetaVar swapped tv1 (MetaTv info1 ref1) tv2 (MetaTv info2 ref2) cotv
+          = do { case (info1, info2) of
+                   -- Avoid SigTvs if poss
+                   (SigTv _, _      ) | k1_sub_k2 -> update_tv2
+                   (_,       SigTv _) | k2_sub_k1 -> update_tv1
+
+                   (_,   _) | k1_sub_k2 -> if k2_sub_k1 && nicer_to_update_tv1
+                                           then update_tv1     -- Same kinds
+                                           else update_tv2
+                            | k2_sub_k1 -> update_tv1
+                            | otherwise -> kind_err
+              -- Update the variable with least kind info
+              -- See notes on type inference in Kind.lhs
+              -- The "nicer to" part only applies if the two kinds are the same,
+              -- so we can choose which to do.
+
+               ; writeMetaTyVar cotv (mkTyVarTy tv2)
+               ; return Nothing
+               }
+          where
+                -- Kinds should be guaranteed ok at this point
+            update_tv1 = updateMeta tv1 ref1 (mkTyVarTy tv2)
+            update_tv2 = updateMeta tv2 ref2 (mkTyVarTy tv1)
+
+            kind_err = addErrCtxtM (unifyKindCtxt swapped tv1 (mkTyVarTy tv2)) $
+                       unifyKindMisMatch k1 k2
+
+            k1 = tyVarKind tv1
+            k2 = tyVarKind tv2
+            k1_sub_k2 = k1 `isSubKind` k2
+            k2_sub_k1 = k2 `isSubKind` k1
+
+            nicer_to_update_tv1 = isSystemName (Var.varName tv1)
+                -- Try to update sys-y type variables in preference to ones
+                -- gotten (say) by instantiating a polymorphic function with
+                -- a user-written type sig 
+
+        uMetaVar _ _ _ _ _ _ = panic "uMetaVar"
+\end{code}
+
+
+
+==================== CODE FOR THE OLD ICFP'08 ALGORITHM ======================
+
 An elementary rewrite is a properly oriented equality with associated coercion
 that has one of the following two forms:
 
 (1) co :: F t1..tn ~ t
 An elementary rewrite is a properly oriented equality with associated coercion
 that has one of the following two forms:
 
 (1) co :: F t1..tn ~ t
-(2) co :: a ~ t         , where t /= F t1..tn
+(2) co :: a ~ t         , where t /= F t1..tn and a is a skolem tyvar
+
+NB: We do *not* use equalities of the form a ~ t where a is a meta tyvar as a
+reqrite rule.  Instead, such equalities are solved by unification.  This is
+essential; cf Note [skolemOccurs loop].
 
 The following functions takes an equality instance and turns it into an
 elementary rewrite if possible.
 
 The following functions takes an equality instance and turns it into an
 elementary rewrite if possible.
@@ -113,37 +894,41 @@ data Rewrite = Rewrite TcType           -- lhs of rewrite rule
                        TcType           -- rhs of rewrite rule
                        TcType           -- coercion witnessing the rewrite rule
 
                        TcType           -- rhs of rewrite rule
                        TcType           -- coercion witnessing the rewrite rule
 
-eqInstToRewrite :: Inst -> Maybe Rewrite
+eqInstToRewrite :: Inst -> Maybe (Rewrite, Bool)
+                                           -- True iff rewrite swapped equality
 eqInstToRewrite inst
   = ASSERT( isEqInst inst )
 eqInstToRewrite inst
   = ASSERT( isEqInst inst )
-    go (eqInstLeftTy inst) (eqInstRightTy inst) (eqInstType inst)
+    go ty1 ty2 (eqInstType inst)
   where
   where
+    (ty1,ty2) = eqInstTys inst
+
     -- look through synonyms
     go ty1 ty2 co | Just ty1' <- tcView ty1 = go ty1' ty2 co
     go ty1 ty2 co | Just ty2' <- tcView ty2 = go ty1 ty2' co
 
     -- look through synonyms
     go ty1 ty2 co | Just ty1' <- tcView ty1 = go ty1' ty2 co
     go ty1 ty2 co | Just ty2' <- tcView ty2 = go ty1 ty2' co
 
-    -- rewrite type family applications
+    -- left-to-right rule with type family head
     go ty1@(TyConApp con _) ty2 co 
       | isOpenSynTyCon con
     go ty1@(TyConApp con _) ty2 co 
       | isOpenSynTyCon con
-      = Just $ Rewrite ty1 ty2 co
+      = Just (Rewrite ty1 ty2 co, False)                     -- not swapped
 
 
-    -- rewrite skolems
+    -- left-to-right rule with type variable head
     go ty1@(TyVarTy tv) ty2 co 
       | isSkolemTyVar tv
     go ty1@(TyVarTy tv) ty2 co 
       | isSkolemTyVar tv
-      = Just $ Rewrite ty1 ty2 co
+      = Just (Rewrite ty1 ty2 co, False)                     -- not swapped
 
 
-    -- rewrite type family applications from right-to-left, only after
+    -- right-to-left rule with type family head, only after
     -- having checked whether we can work left-to-right
     go ty1 ty2@(TyConApp con _) co 
       | isOpenSynTyCon con
     -- having checked whether we can work left-to-right
     go ty1 ty2@(TyConApp con _) co 
       | isOpenSynTyCon con
-      = Just $ Rewrite ty2 ty1 (mkSymCoercion co)
+      = Just (Rewrite ty2 ty1 (mkSymCoercion co), True)      -- swapped
 
 
-    -- rewrite skolems from right-to-left, only after having checked
-    -- whether we can work left-to-right 
+    -- right-to-left rule with type variable head, only after 
+    -- having checked whether we can work left-to-right 
     go ty1 ty2@(TyVarTy tv) co 
       | isSkolemTyVar tv
     go ty1 ty2@(TyVarTy tv) co 
       | isSkolemTyVar tv
-      = Just $ Rewrite ty2 ty1 (mkSymCoercion co)
+      = Just (Rewrite ty2 ty1 (mkSymCoercion co), True)      -- swapped
 
 
+    -- this equality is not a rewrite rule => ignore
     go _ _ _ = Nothing
 \end{code}
 
     go _ _ _ = Nothing
 \end{code}
 
@@ -156,8 +941,8 @@ explicitly given elementary rewrite.
 tcEqInstNormaliseFamInst :: Inst -> TcType -> TcM (CoercionI, TcType)
 tcEqInstNormaliseFamInst inst ty
   = case eqInstToRewrite inst of
 tcEqInstNormaliseFamInst :: Inst -> TcType -> TcM (CoercionI, TcType)
 tcEqInstNormaliseFamInst inst ty
   = case eqInstToRewrite inst of
-      Just rewrite -> tcEqRuleNormaliseFamInst rewrite ty
-      Nothing      -> return (IdCo, ty)
+      Just (rewrite, _) -> tcEqRuleNormaliseFamInst rewrite ty
+      Nothing           -> return (IdCo, ty)
 
 -- Rewrite by equality rewrite rule
 tcEqRuleNormaliseFamInst :: Rewrite                     -- elementary rewrite
 
 -- Rewrite by equality rewrite rule
 tcEqRuleNormaliseFamInst :: Rewrite                     -- elementary rewrite
@@ -223,10 +1008,6 @@ tcGenericNormaliseFamInst fun (ForAllTy tyvar ty1)
   = do         { (coi,nty1) <- tcGenericNormaliseFamInst fun ty1
        ; return (mkForAllTyCoI tyvar coi, mkForAllTy tyvar nty1)
        }
   = do         { (coi,nty1) <- tcGenericNormaliseFamInst fun ty1
        ; return (mkForAllTyCoI tyvar coi, mkForAllTy tyvar nty1)
        }
-tcGenericNormaliseFamInst fun (NoteTy note ty1)
-  = do { (coi,nty1) <- tcGenericNormaliseFamInst fun ty1
-       ; return (mkNoteTyCoI note coi, NoteTy note nty1)
-       }
 tcGenericNormaliseFamInst fun ty@(TyVarTy tv)
   | isTcTyVar tv
   = do { traceTc (text "tcGenericNormaliseFamInst" <+> ppr ty)
 tcGenericNormaliseFamInst fun ty@(TyVarTy tv)
   | isTcTyVar tv
   = do { traceTc (text "tcGenericNormaliseFamInst" <+> ppr ty)
@@ -303,38 +1084,12 @@ normaliseGivenEqs givens
 \end{code}
 
 \begin{code}
 \end{code}
 
 \begin{code}
-normaliseWantedEqs :: [Inst] -> TcM [Inst]
-normaliseWantedEqs insts 
-  = do { traceTc (text "normaliseWantedEqs <-" <+> ppr insts)
-       ; result <- liftM fst $ rewriteToFixedPoint Nothing
-                    [ ("(ZONK)",    dontRerun $ zonkInsts)
-                    , ("(TRIVIAL)", dontRerun $ trivialRule)
-                    , ("(DECOMP)",  decompRule)
-                    , ("(TOP)",     topRule)
-                    , ("(UNIFY)",   unifyMetaRule)      -- incl. occurs check
-                    , ("(SUBST)",   substRule)          -- incl. occurs check
-                     ] insts
-       ; traceTc (text "normaliseWantedEqs ->" <+> ppr result)
-       ; return result
-       }
-\end{code}
-
-
-%************************************************************************
-%*                                                                     *
-\section{Solving of wanted constraints with respect to a given set}
-%*                                                                     *
-%************************************************************************
-
-The set of given equalities must have been normalised already.
-
-\begin{code}
-solveWantedEqs :: [Inst]        -- givens
-            -> [Inst]          -- wanteds
-            -> TcM [Inst]      -- irreducible wanteds
-solveWantedEqs givens wanteds 
-  = do { traceTc $ text "solveWantedEqs <-" <+> ppr wanteds <+> text "with" <+> 
-                   ppr givens
+normaliseWantedEqs :: [Inst]        -- givens
+                  -> [Inst]        -- wanteds
+                  -> TcM [Inst]    -- irreducible wanteds
+normaliseWantedEqs givens wanteds 
+  = do { traceTc $ text "normaliseWantedEqs <-" <+> ppr wanteds 
+                   <+> text "with" <+> ppr givens
        ; result <- liftM fst $ rewriteToFixedPoint Nothing
                      [ ("(ZONK)",    dontRerun $ zonkInsts)
                      , ("(TRIVIAL)", dontRerun $ trivialRule)
        ; result <- liftM fst $ rewriteToFixedPoint Nothing
                      [ ("(ZONK)",    dontRerun $ zonkInsts)
                      , ("(TRIVIAL)", dontRerun $ trivialRule)
@@ -342,8 +1097,9 @@ solveWantedEqs givens wanteds
                      , ("(TOP)",     topRule)
                      , ("(GIVEN)",   substGivens givens) -- incl. occurs check
                      , ("(UNIFY)",   unifyMetaRule)      -- incl. occurs check
                      , ("(TOP)",     topRule)
                      , ("(GIVEN)",   substGivens givens) -- incl. occurs check
                      , ("(UNIFY)",   unifyMetaRule)      -- incl. occurs check
+                    , ("(SUBST)",   substRule)          -- incl. occurs check
                      ] wanteds
                      ] wanteds
-       ; traceTc (text "solveWantedEqs ->" <+> ppr result)
+       ; traceTc (text "normaliseWantedEqs ->" <+> ppr result)
        ; return result
        }
   where
        ; return result
        }
   where
@@ -380,14 +1136,18 @@ normalise_dicts
                        -- Fals <=> they are given
        -> TcM ([Inst],TcDictBinds)
 normalise_dicts given_eqs dicts is_wanted
                        -- Fals <=> they are given
        -> TcM ([Inst],TcDictBinds)
 normalise_dicts given_eqs dicts is_wanted
-  = do { traceTc $ text "normalise???Dicts <-" <+> ppr dicts <+> 
+  = do { traceTc $ let name | is_wanted = "normaliseWantedDicts <-"
+                             | otherwise = "normaliseGivenDicts <-"
+                    in
+                    text name <+> ppr dicts <+> 
                     text "with" <+> ppr given_eqs
        ; (dicts0, binds0)  <- normaliseInsts is_wanted dicts
                     text "with" <+> ppr given_eqs
        ; (dicts0, binds0)  <- normaliseInsts is_wanted dicts
-       ; (dicts1, binds1)  <- substEqInDictInsts given_eqs dicts0
+       ; (dicts1, binds1)  <- substEqInDictInsts is_wanted given_eqs dicts0
        ; let binds01 = binds0 `unionBags` binds1
        ; if isEmptyBag binds1
          then return (dicts1, binds01)
        ; let binds01 = binds0 `unionBags` binds1
        ; if isEmptyBag binds1
          then return (dicts1, binds01)
-         else do { (dicts2, binds2) <- normaliseGivenDicts given_eqs dicts1
+         else do { (dicts2, binds2) <- 
+                      normalise_dicts given_eqs dicts1 is_wanted
                  ; return (dicts2, binds01 `unionBags` binds2) } }
 \end{code}
 
                  ; return (dicts2, binds01 `unionBags` binds2) } }
 \end{code}
 
@@ -489,7 +1249,7 @@ oriented properly, like
 
      F a ~ [G (F a)]
  or even
 
      F a ~ [G (F a)]
  or even
-     a ~ [G a]
+     a ~ [G a]          , where a is a skolem tyvar
 
 The left-to-right orientiation is not suitable because it does not
 terminate. The right-to-left orientation is not suitable because it 
 
 The left-to-right orientiation is not suitable because it does not
 terminate. The right-to-left orientation is not suitable because it 
@@ -534,6 +1294,45 @@ NB: We perform this transformation for multiple occurences of ty1 under one
     rule doesn't need to be applied multiple times at a single inst).  As a 
     result we can get two or more insts back.
 
     rule doesn't need to be applied multiple times at a single inst).  As a 
     result we can get two or more insts back.
 
+Note [skolemOccurs loop]
+~~~~~~~~~~~~~~~~~~~~~~~~
+You might think that under
+
+  type family F a 
+  type instance F [a] = [F a]
+
+a signature such as
+
+  foo :: (F [a] ~ a) => a
+
+will get us into a loop.  However, this is *not* the case.  Here is why:
+
+    F [a<sk>] ~ a<sk>
+
+    -->(TOP)
+
+    [F a<sk>] ~ a<sk>
+
+    -->(SkolemOccurs)
+
+    [b<tau>] ~ a<sk>
+    F [b<tau>] ~ b<tau>   , with b := F a
+
+    -->(TOP)
+
+    [b<tau>] ~ a<sk>
+    [F b<tau>] ~ b<tau>   , with b := F a
+
+At this point (SkolemOccurs) does *not* apply anymore, as 
+
+  [F b<tau>] ~ b<tau>
+
+is not used as a rewrite rule.  The variable b<tau> is not a skolem (cf
+eqInstToRewrite). 
+
+(The regression test indexed-types/should_compile/Simple20 checks that the
+described property of the system does not change.)
+
 \begin{code}
 skolemOccurs :: PrecondRule
 skolemOccurs insts
 \begin{code}
 skolemOccurs :: PrecondRule
 skolemOccurs insts
@@ -543,38 +1342,21 @@ skolemOccurs insts
   where
     oneSkolemOccurs inst
       = ASSERT( isEqInst inst )
   where
     oneSkolemOccurs inst
       = ASSERT( isEqInst inst )
-        isRewriteRule (eqInstLeftTy inst) (eqInstRightTy inst)
+        case eqInstToRewrite inst of
+          Just (rewrite, swapped) -> breakRecursion rewrite swapped
+          Nothing                 -> return ([inst], return ())
       where
       where
+        -- inst is an elementary rewrite rule, check whether we need to break
+        -- it up
+        breakRecursion (Rewrite pat body _) swapped
 
 
-        -- look through synonyms
-        isRewriteRule ty1 ty2 | Just ty1' <- tcView ty1 = isRewriteRule ty1' ty2
-        isRewriteRule ty1 ty2 | Just ty2' <- tcView ty2 = isRewriteRule ty1 ty2'
-
-        -- left-to-right rule with type family head
-        isRewriteRule ty1@(TyConApp con _) ty2
-          | isOpenSynTyCon con
-          = breakRecursion ty1 ty2 False    -- not swapped
-
-        -- left-to-right rule with type variable head
-        isRewriteRule ty1@(TyVarTy _) ty2
-          = breakRecursion ty1 ty2 False    -- not swapped
-
-        -- right-to-left rule with type family head
-        isRewriteRule ty1 ty2@(TyConApp con _)
-          | isOpenSynTyCon con
-          = breakRecursion ty2 ty1 True     -- swapped
-
-        -- right-to-left rule with type variable head
-        isRewriteRule ty1 ty2@(TyVarTy _)
-          = breakRecursion ty2 ty1 True     -- swapped
-
-        -- this equality is not a rewrite rule => ignore
-        isRewriteRule _ _ = return ([inst], return ())
-
-        ------------------
-        breakRecursion pat body swapped
+          -- skolemOccurs does not apply, leave as is
           | null tysToLiftOut 
           | null tysToLiftOut 
-          = return ([inst], return ())
+          = do { traceTc $ text "oneSkolemOccurs: no tys to lift out"
+               ; return ([inst], return ())
+               }
+
+          -- recursive occurence of pat in body under a type family application
           | otherwise
           = do { traceTc $ text "oneSkolemOccurs[TLO]:" <+> ppr tysToLiftOut
                ; skTvs <- mapM (newMetaTyVar TauTv . typeKind) tysToLiftOut
           | otherwise
           = do { traceTc $ text "oneSkolemOccurs[TLO]:" <+> ppr tysToLiftOut
                ; skTvs <- mapM (newMetaTyVar TauTv . typeKind) tysToLiftOut
@@ -629,7 +1411,7 @@ The following rules exploits the reflexivity of equality:
 \begin{code}
 trivialRule :: IdemRewriteRule
 trivialRule insts 
 \begin{code}
 trivialRule :: IdemRewriteRule
 trivialRule insts 
-  = liftM catMaybes $ mappM trivial insts
+  = liftM catMaybes $ mapM trivial insts
   where
     trivial inst
       | ASSERT( isEqInst inst )
   where
     trivial inst
       | ASSERT( isEqInst inst )
@@ -642,8 +1424,7 @@ trivialRule insts
       | otherwise
       = return $ Just inst
       where
       | otherwise
       = return $ Just inst
       where
-        ty1 = eqInstLeftTy  inst
-        ty2 = eqInstRightTy inst
+        (ty1,ty2) = eqInstTys inst
 \end{code}
 
 
 \end{code}
 
 
@@ -674,8 +1455,9 @@ decompRule insts
   where
     decomp inst
       = ASSERT( isEqInst inst )
   where
     decomp inst
       = ASSERT( isEqInst inst )
-        go (eqInstLeftTy inst) (eqInstRightTy inst)
+        go ty1 ty2
       where
       where
+       (ty1,ty2) = eqInstTys inst
         go ty1 ty2             
           | Just ty1' <- tcView ty1 = go ty1' ty2 
           | Just ty2' <- tcView ty2 = go ty1 ty2' 
         go ty1 ty2             
           | Just ty1' <- tcView ty1 = go ty1' ty2 
           | Just ty2' <- tcView ty2 = go ty1 ty2' 
@@ -794,8 +1576,7 @@ topRule insts
                    }
             }
       where
                    }
             }
       where
-        ty1 = eqInstLeftTy  inst
-       ty2 = eqInstRightTy inst
+        (ty1,ty2) = eqInstTys inst
 \end{code}
 
 
 \end{code}
 
 
@@ -848,8 +1629,8 @@ substRule insts = tryAllInsts insts []
 substInst :: Inst -> [Inst] -> TcM ([Inst], Bool)
 substInst inst insts
   = case eqInstToRewrite inst of
 substInst :: Inst -> [Inst] -> TcM ([Inst], Bool)
 substInst inst insts
   = case eqInstToRewrite inst of
-      Just rewrite -> substEquality rewrite insts
-      Nothing      -> return (insts, False)
+      Just (rewrite, _) -> substEquality rewrite insts
+      Nothing           -> return (insts, False)
   where
     substEquality :: Rewrite            -- elementary rewrite
                   -> [Inst]             -- insts to rewrite
   where
     substEquality :: Rewrite            -- elementary rewrite
                   -> [Inst]             -- insts to rewrite
@@ -893,8 +1674,7 @@ substInst inst insts
                     }
               }
          where 
                     }
               }
          where 
-            ty1 = eqInstLeftTy  inst
-            ty2 = eqInstRightTy inst
+            (ty1,ty2) = eqInstTys inst
 \end{code}
 
 
 \end{code}
 
 
@@ -931,9 +1711,10 @@ unifyMetaRule insts
   where
     unifyMeta inst
       = ASSERT( isEqInst inst )
   where
     unifyMeta inst
       = ASSERT( isEqInst inst )
-        go (eqInstLeftTy inst) (eqInstRightTy inst)
+        go ty1 ty2
            (fromWantedCo "unifyMetaRule" $ eqInstCoercion inst)
       where
            (fromWantedCo "unifyMetaRule" $ eqInstCoercion inst)
       where
+       (ty1,ty2) = eqInstTys inst
         go ty1 ty2 cotv
           | Just ty1' <- tcView ty1 = go ty1' ty2 cotv
           | Just ty2' <- tcView ty2 = go ty1 ty2' cotv
         go ty1 ty2 cotv
           | Just ty1' <- tcView ty1 = go ty1' ty2 cotv
           | Just ty2' <- tcView ty2 = go ty1 ty2' cotv
@@ -953,26 +1734,30 @@ unifyMetaRule insts
         uMeta _swapped _tv (IndirectTv _) _ty _cotv
           = return ([inst], False)
 
         uMeta _swapped _tv (IndirectTv _) _ty _cotv
           = return ([inst], False)
 
-        -- signature skolem meets non-variable type
-        -- => cannot update!
-        uMeta _swapped _tv (DoneTv (MetaTv (SigTv _) _)) ty _cotv
-          | not $ isTyVarTy ty
-          = return ([inst], False)
-
         -- type variable meets type variable
         -- => check that tv2 hasn't been updated yet and choose which to update
        uMeta swapped tv1 (DoneTv details1) (TyVarTy tv2) cotv
         -- type variable meets type variable
         -- => check that tv2 hasn't been updated yet and choose which to update
        uMeta swapped tv1 (DoneTv details1) (TyVarTy tv2) cotv
+         | tv1 == tv2
+         = return ([inst], False)      -- The two types are already identical
+
+         | otherwise
          = do { lookupTV2 <- lookupTcTyVar tv2
                ; case lookupTV2 of
          = do { lookupTV2 <- lookupTcTyVar tv2
                ; case lookupTV2 of
-                   IndirectTv ty  -> uMeta swapped tv1 (DoneTv details1) ty cotv
-                   DoneTv details2 -> 
-                     uMetaVar swapped tv1 details1 tv2 details2 cotv
+                   IndirectTv ty   -> uMeta swapped tv1 (DoneTv details1) ty cotv
+                   DoneTv details2 -> uMetaVar swapped tv1 details1 tv2 details2 cotv
               }
 
               }
 
+       ------ Beyond this point we know that ty2 is not a type variable
+
+        -- signature skolem meets non-variable type
+        -- => cannot update!
+        uMeta _swapped _tv (DoneTv (MetaTv (SigTv _) _)) _non_tv_ty _cotv
+          = return ([inst], False)
+
         -- updatable meta variable meets non-variable type
         -- => occurs check, monotype check, and kinds match check, then update
         -- updatable meta variable meets non-variable type
         -- => occurs check, monotype check, and kinds match check, then update
-       uMeta swapped tv (DoneTv (MetaTv _ ref)) ty cotv
-         = do { mb_ty' <- checkTauTvUpdate tv ty    -- occurs + monotype check
+       uMeta swapped tv (DoneTv (MetaTv _ ref)) non_tv_ty cotv
+         = do { mb_ty' <- checkTauTvUpdate tv non_tv_ty    -- occurs + monotype check
                ; case mb_ty' of
                    Nothing  -> return ([inst], False)  -- tv occurs in faminst
                    Just ty' ->
                ; case mb_ty' of
                    Nothing  -> return ([inst], False)  -- tv occurs in faminst
                    Just ty' ->
@@ -984,6 +1769,7 @@ unifyMetaRule insts
 
         uMeta _ _ _ _ _ = panic "uMeta"
 
 
         uMeta _ _ _ _ _ = panic "uMeta"
 
+       -- uMetaVar: unify two type variables
         -- meta variable meets skolem 
         -- => just update
         uMetaVar swapped tv1 (MetaTv _ ref) tv2 (SkolemTv _) cotv
         -- meta variable meets skolem 
         -- => just update
         uMetaVar swapped tv1 (MetaTv _ ref) tv2 (SkolemTv _) cotv
@@ -1051,10 +1837,11 @@ form
 where F is a type family.
 
 \begin{code}
 where F is a type family.
 
 \begin{code}
-substEqInDictInsts :: [Inst]    -- given equalities (used as rewrite rules)
+substEqInDictInsts :: Bool      -- whether the *dictionaries* are wanted/given
+                   -> [Inst]    -- given equalities (used as rewrite rules)
                    -> [Inst]    -- dictinaries to be normalised
                    -> TcM ([Inst], TcDictBinds)
                    -> [Inst]    -- dictinaries to be normalised
                    -> TcM ([Inst], TcDictBinds)
-substEqInDictInsts eqInsts dictInsts 
+substEqInDictInsts isWanted eqInsts dictInsts 
   = do { traceTc (text "substEqInDictInst <-" <+> ppr dictInsts)
        ; dictInsts' <- 
            foldlM rewriteWithOneEquality (dictInsts, emptyBag) eqInsts
   = do { traceTc (text "substEqInDictInst <-" <+> ppr dictInsts)
        ; dictInsts' <- 
            foldlM rewriteWithOneEquality (dictInsts, emptyBag) eqInsts
@@ -1068,7 +1855,7 @@ substEqInDictInsts eqInsts dictInsts
                                            tci_right = target})
       | isOpenSynTyConApp pattern || isTyVarTy pattern
       = do { (dictInsts', moreDictBinds) <- 
                                            tci_right = target})
       | isOpenSynTyConApp pattern || isTyVarTy pattern
       = do { (dictInsts', moreDictBinds) <- 
-               genericNormaliseInsts True {- wanted -} applyThisEq dictInsts
+               genericNormaliseInsts isWanted applyThisEq dictInsts
            ; return (dictInsts', dictBinds `unionBags` moreDictBinds)
            }
       where
            ; return (dictInsts', dictBinds `unionBags` moreDictBinds)
            }
       where
@@ -1143,11 +1930,17 @@ genericNormaliseInsts isWanted fun insts
                              --        else
                              --          dict' = dict  `cast` co
                          expr      = HsVar $ instToId source_dict
                              --        else
                              --          dict' = dict  `cast` co
                          expr      = HsVar $ instToId source_dict
-                         cast_expr = HsWrap (WpCo st_co) expr
+                         cast_expr = HsWrap (WpCast st_co) expr
                          rhs       = L (instLocSpan loc) cast_expr
                          binds     = instToDictBind target_dict rhs
                      -- return the new inst
                          rhs       = L (instLocSpan loc) cast_expr
                          binds     = instToDictBind target_dict rhs
                      -- return the new inst
-                   ; traceTc $ text "genericNormaliseInst ->" <+> ppr dict'
+                   ; traceTc $ let name | isWanted 
+                                         = "genericNormaliseInst (wanted) ->"
+                                         | otherwise
+                                         = "genericNormaliseInst (given) ->"
+                                in
+                                text name <+> ppr dict' <+>
+                                text "with" <+> ppr binds
                     ; return (dict', binds)
                    }
           }
                     ; return (dict', binds)
                    }
           }
@@ -1155,6 +1948,8 @@ genericNormaliseInsts isWanted fun insts
        -- TOMDO: What do we have to do about ImplicInst, Method, and LitInst??
     normaliseOneInst _isWanted _fun inst
       = do { inst' <- zonkInst inst
        -- TOMDO: What do we have to do about ImplicInst, Method, and LitInst??
     normaliseOneInst _isWanted _fun inst
       = do { inst' <- zonkInst inst
+           ; traceTc $ text "*** TcTyFuns.normaliseOneInst: Skipping" <+>
+                       ppr inst
           ; return (inst', emptyBag)
           }
 \end{code}
           ; return (inst', emptyBag)
           }
 \end{code}
@@ -1173,45 +1968,49 @@ somethingdifferent message.
 eqInstMisMatch :: Inst -> TcM a
 eqInstMisMatch inst
   = ASSERT( isEqInst inst )
 eqInstMisMatch :: Inst -> TcM a
 eqInstMisMatch inst
   = ASSERT( isEqInst inst )
-    do { (env, msg) <- misMatchMsg ty_act ty_exp
-       ; setErrCtxt ctxt $
-           failWithTcM (env, msg)
-       }
+    setErrCtxt ctxt $ failWithMisMatch ty_act ty_exp
   where
   where
-    ty_act           = eqInstLeftTy  inst
-    ty_exp           = eqInstRightTy inst
-    InstLoc _ _ ctxt = instLoc       inst
+    (ty_act, ty_exp) = eqInstTys inst
+    InstLoc _ _ ctxt = instLoc   inst
 
 -----------------------
 
 -----------------------
-misMatchMsg :: TcType -> TcType -> TcM (TidyEnv, SDoc)
+failWithMisMatch :: TcType -> TcType -> TcM a
 -- Generate the message when two types fail to match,
 -- going to some trouble to make it helpful.
 -- The argument order is: actual type, expected type
 -- Generate the message when two types fail to match,
 -- going to some trouble to make it helpful.
 -- The argument order is: actual type, expected type
-misMatchMsg ty_act ty_exp
+failWithMisMatch ty_act ty_exp
   = do { env0 <- tcInitTidyEnv
         ; ty_exp <- zonkTcType ty_exp
         ; ty_act <- zonkTcType ty_act
   = do { env0 <- tcInitTidyEnv
         ; ty_exp <- zonkTcType ty_exp
         ; ty_act <- zonkTcType ty_act
-       ; (env1, pp_exp, extra_exp) <- ppr_ty env0 ty_exp
-       ; (env2, pp_act, extra_act) <- ppr_ty env1 ty_act
-       ; return (env2, 
-                  sep [sep [ptext SLIT("Couldn't match expected type") <+> pp_exp, 
-                           nest 7 $
-                              ptext SLIT("against inferred type") <+> pp_act],
-                      nest 2 (extra_exp $$ extra_act)]) }
-
-ppr_ty :: TidyEnv -> TcType -> TcM (TidyEnv, SDoc, SDoc)
-ppr_ty env ty
-  = do { let (env1, tidy_ty) = tidyOpenType env ty
-       ; (env2, extra) <- ppr_extra env1 tidy_ty
-       ; return (env2, quotes (ppr tidy_ty), extra) }
-
--- (ppr_extra env ty) shows extra info about 'ty'
-ppr_extra :: TidyEnv -> Type -> TcM (TidyEnv, SDoc)
-ppr_extra env (TyVarTy tv)
-  | isSkolemTyVar tv || isSigTyVar tv
-  = return (env1, pprSkolTvBinding tv1)
+        ; failWithTcM (misMatchMsg env0 (ty_act, ty_exp))
+       }
+
+misMatchMsg :: TidyEnv -> (TcType, TcType) -> (TidyEnv, SDoc)
+misMatchMsg env0 (ty_act, ty_exp)
+  = let (env1, pp_exp, extra_exp) = ppr_ty env0 ty_exp
+       (env2, pp_act, extra_act) = ppr_ty env1 ty_act
+        msg = sep [sep [ptext (sLit "Couldn't match expected type") <+> pp_exp, 
+                       nest 7 $
+                              ptext (sLit "against inferred type") <+> pp_act],
+                  nest 2 (extra_exp $$ extra_act)]
+    in
+    (env2, msg)
+
   where
   where
-    (env1, tv1) = tidySkolemTyVar env tv
+    ppr_ty :: TidyEnv -> TcType -> (TidyEnv, SDoc, SDoc)
+    ppr_ty env ty
+      = let (env1, tidy_ty) = tidyOpenType env ty
+           (env2, extra)  = ppr_extra env1 tidy_ty
+       in
+       (env2, quotes (ppr tidy_ty), extra)
+
+    -- (ppr_extra env ty) shows extra info about 'ty'
+    ppr_extra :: TidyEnv -> Type -> (TidyEnv, SDoc)
+    ppr_extra env (TyVarTy tv)
+      | isTcTyVar tv && (isSkolemTyVar tv || isSigTyVar tv) && not (isUnk tv)
+      = (env1, pprSkolTvBinding tv1)
+      where
+        (env1, tv1) = tidySkolemTyVar env tv
 
 
-ppr_extra env _ty = return (env, empty)                -- Normal case
+    ppr_extra env _ty = (env, empty)           -- Normal case
 \end{code}
 \end{code}