@@ -1,28 +1,35 @@
1+{-# LANGUAGE NamedFieldPuns #-}
2+
13-- |
24-- Type class entailment
35--
46module Language.PureScript.TypeChecker.Entailment
57 ( InstanceContext
68 , replaceTypeClassDictionaries
9+ , newDictionaries
710 ) where
811
912import Prelude.Compat
1013
14+import Control.Arrow (second)
1115import Control.Monad.Error.Class (MonadError(..))
1216import Control.Monad.State
1317import Control.Monad.Supply.Class (MonadSupply(..))
1418import Control.Monad.Writer
1519
20+import Data.Foldable (for_)
1621import Data.Function (on)
17−import Data.List (minimumBy, sortBy, groupBy)
18−import Data.Maybe (maybeToList, mapMaybe)
22+import Data.List (minimumBy, nub)
23+import Data.Maybe (fromMaybe, maybeToList, mapMaybe)
1924import qualified Data.Map as M
25+import qualified Data.Set as S
2026
2127import Language.PureScript.AST
2228import Language.PureScript.Crash
29+import Language.PureScript.Environment
2330import Language.PureScript.Errors
2431import Language.PureScript.Names
25−import Language.PureScript.TypeChecker.Monad (CheckState, withErrorMessageHint)
32+import Language.PureScript.TypeChecker.Monad
2633import Language.PureScript.TypeChecker.Unify
2734import Language.PureScript.TypeClassDictionaries
2835import Language.PureScript.Types
@@ -34,24 +41,57 @@ type InstanceContext = M.Map (Maybe ModuleName)
3441 (M.Map (Qualified Ident)
3542 TypeClassDictionaryInScope))
3643
37−-- | Merge two type class contexts
44+-- | A type substitution which makes an instance head match a list of types.
45+--
46+-- Note: we store many types per type variable name. For any name, all types
47+-- should unify if we are going to commit to an instance.
48+type Matching a = M.Map String a
49+
3850combineContexts :: InstanceContext -> InstanceContext -> InstanceContext
3951combineContexts = M.unionWith (M.unionWith M.union)
4052
4153-- | Replace type class dictionary placeholders with inferred type class dictionaries
4254replaceTypeClassDictionaries
43− :: (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
55+ :: forall m
56+ . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
4457 => Bool
45− -> ModuleName
4658 -> Expr
4759 -> m (Expr, [(Ident, Constraint)])
48−replaceTypeClassDictionaries shouldGeneralize mn =
49− let (_, f, _) = everywhereOnValuesTopDownM return (WriterT . go) return
50− in flip evalStateT M.empty . runWriterT . f
60+replaceTypeClassDictionaries shouldGeneralize expr = flip evalStateT M.empty $ do
61+ -- Loop, deferring any unsolved constraints, until there are no more
62+ -- constraints which can be solved, then make a generalization pass.
63+ let loop e = do
64+ (e', solved) <- deferPass e
65+ if getAny solved
66+ then loop e'
67+ else return e'
68+ loop expr >>= generalizePass
5169 where
52− go (TypeClassDictionary constraint dicts hints) =
53− rethrow (addHints hints) $ entails shouldGeneralize mn dicts constraint
54− go other = return (other, [])
70+ -- This pass solves constraints where possible, deferring constraints if not.
71+ deferPass :: Expr -> StateT InstanceContext m (Expr, Any)
72+ deferPass = fmap (second fst) . runWriterT . f where
73+ f :: Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
74+ (_, f, _) = everywhereOnValuesTopDownM return (go True) return
75+
76+ -- This pass generalizes any remaining constraints
77+ generalizePass :: Expr -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
78+ generalizePass = fmap (second snd) . runWriterT . f where
79+ f :: Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
80+ (_, f, _) = everywhereOnValuesTopDownM return (go False) return
81+
82+ go :: Bool -> Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
83+ go deferErrors dict@(TypeClassDictionary _ _ hints) =
84+ rethrow (addHints hints) $ entails shouldGeneralize deferErrors dict
85+ go _ other = return other
86+
87+-- | Three options for how we can handle a constraint, depending on the mode we're in.
88+data EntailsResult a
89+ = Solved a TypeClassDictionaryInScope
90+ -- ^ We solved this constraint
91+ | Unsolved Constraint
92+ -- ^ We couldn't solve this constraint right now, it will be generalized
93+ | Deferred
94+ -- ^ We couldn't solve this constraint right now, so it has been deferred
5595
5696-- |
5797-- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
@@ -61,14 +101,14 @@ entails
61101 :: forall m
62102 . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
63103 => Bool
64− -> ModuleName
65− -> InstanceContext
66− -> Constraint
67− -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
68−entails shouldGeneralize moduleName context = solve
104+ -> Bool
105+ -> Expr
106+ -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
107+entails shouldGeneralize deferErrors (TypeClassDictionary constraint context hints) =
108+ solve constraint
69109 where
70110 forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDictionaryInScope]
71− forClassName ctx cn@(Qualified (Just mn) _) tys = concatMap (findDicts ctx cn) (Nothing : Just mn : map Just (mapMaybe ctorModules tys))
111+ forClassName ctx cn@(Qualified (Just mn) _) tys = concatMap (findDicts ctx cn) (nub (Nothing : Just mn : map Just (mapMaybe ctorModules tys)))
72112 forClassName _ _ _ = internalError "forClassName: expected qualified class name"
73113
74114 ctorModules :: Type -> Maybe ModuleName
@@ -80,139 +120,306 @@ entails shouldGeneralize moduleName context = solve
80120 findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDictionaryInScope]
81121 findDicts ctx cn = maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup ctx
82122
83− solve :: Constraint -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
84− solve con = StateT . (withErrorMessageHint (ErrorSolvingConstraint con) .) . runStateT $ do
85− (dict, unsolved) <- go 0 con
86− return (dictionaryValueToValue dict, unsolved)
123+ valUndefined :: Expr
124+ valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
125+
126+ solve :: Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
127+ solve con = go 0 con
87128 where
88− go :: Int -> Constraint -> StateT InstanceContext m (DictionaryValue, [(Ident, Constraint)])
89− go work (Constraint className' tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
90− go work con'@(Constraint className' tys' _) = do
91− -- Get the inferred constraint context so far, and merge it with the global context
92− inferred <- get
93− let instances = do
94− tcd <- forClassName (combineContexts context inferred) className' tys'
95− -- Make sure the type unifies with the type in the type instance definition
96− subst <- maybeToList . (>>= verifySubstitution) . fmap concat $ zipWithM (typeHeadsAreEqual moduleName) tys' (tcdInstanceTypes tcd)
97− return (subst, tcd)
98− solution <- lift $ unique instances
99− case solution of
100− Left (subst, tcd) -> do
101− -- Solve any necessary subgoals
102− (args, unsolved) <- solveSubgoals subst (tcdDependencies tcd)
103− let match = foldr (\(superclassName, index) dict -> SubclassDictionaryValue dict superclassName index)
104− (mkDictionary (tcdName tcd) args)
105− (tcdPath tcd)
106− return (match, unsolved)
107− Right unsolved@(Constraint unsolvedClassName@(Qualified _ pn) unsolvedTys _) -> do
108− -- Generate a fresh name for the unsolved constraint's new dictionary
109− ident <- freshIdent ("dict" ++ runProperName pn)
110− let qident = Qualified Nothing ident
111− -- Store the new dictionary in the InstanceContext so that we can solve this goal in
112− -- future.
113− let newDict = TypeClassDictionaryInScope qident [] unsolvedClassName unsolvedTys Nothing
114− newContext = M.singleton Nothing (M.singleton unsolvedClassName (M.singleton qident newDict))
115− modify (combineContexts newContext)
116− return (LocalDictionaryValue qident, [(ident, unsolved)])
117− where
129+ go :: Int -> Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
130+ go work (Constraint className' tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
131+ go work con'@(Constraint className' tys' conInfo) = WriterT . StateT . (withErrorMessageHint (ErrorSolvingConstraint con') .) . runStateT . runWriterT $ do
132+ -- We might have unified types by solving other constraints, so we need to
133+ -- apply the latest substitution.
134+ latestSubst <- lift . lift $ gets checkSubstitution
135+ let tys'' = map (substituteType latestSubst) tys'
136+ -- Get the inferred constraint context so far, and merge it with the global context
137+ inferred <- lift get
138+ -- We need information about functional dependencies, so we have to look up the class
139+ -- name in the environment:
140+ let findClass = fromMaybe (internalError "entails: type class not found in environment") . M.lookup className'
141+ TypeClassData{ typeClassDependencies } <- lift . lift $ gets (findClass . typeClasses . checkEnv)
142+ let instances =
143+ [ (substs, tcd)
144+ | tcd <- forClassName (combineContexts context inferred) className' tys''
145+ -- Make sure the type unifies with the type in the type instance definition
146+ , substs <- maybeToList (matches typeClassDependencies tcd tys'')
147+ ]
148+ solution <- lift . lift $ unique tys'' instances
149+ case solution of
150+ Solved substs tcd -> do
151+ -- Note that we solved something.
152+ tell (Any True, mempty)
153+ -- Make sure the substitution is valid:
154+ lift . lift . for_ substs $ pairwiseM unifyTypes
155+ -- Now enforce any functional dependencies, using unification
156+ -- Note: we need to generate fresh types for any unconstrained
157+ -- type variables before unifying.
158+ let subst = fmap head substs
159+ currentSubst <- lift . lift $ gets checkSubstitution
160+ subst' <- lift . lift $ withFreshTypes tcd (fmap (substituteType currentSubst) subst)
161+ lift . lift $ zipWithM_ (\t1 t2 -> do
162+ let inferredType = replaceAllTypeVars (M.toList subst') t1
163+ unifyTypes inferredType t2) (tcdInstanceTypes tcd) tys''
164+ currentSubst' <- lift . lift $ gets checkSubstitution
165+ let subst'' = fmap (substituteType currentSubst') subst'
166+ -- Solve any necessary subgoals
167+ args <- solveSubgoals subst'' (tcdDependencies tcd)
168+ let match = foldr (\(superclassName, index) dict -> subclassDictionaryValue dict superclassName index)
169+ (mkDictionary (tcdName tcd) args)
170+ (tcdPath tcd)
171+ return match
172+ Unsolved unsolved -> do
173+ -- Generate a fresh name for the unsolved constraint's new dictionary
174+ ident <- freshIdent ("dict" ++ runProperName (disqualify (constraintClass unsolved)))
175+ let qident = Qualified Nothing ident
176+ -- Store the new dictionary in the InstanceContext so that we can solve this goal in
177+ -- future.
178+ newDicts <- lift . lift $ newDictionaries [] qident unsolved
179+ let newContext = mkContext newDicts
180+ modify (combineContexts newContext)
181+ -- Mark this constraint for generalization
182+ tell (mempty, [(ident, unsolved)])
183+ return (Var qident)
184+ Deferred ->
185+ -- Constraint was deferred, just return the dictionary unchanged,
186+ -- with no unsolved constraints. Hopefully, we can solve this later.
187+ return (TypeClassDictionary (Constraint className' tys'' conInfo) context hints)
188+ where
189+ -- | When checking functional dependencies, we need to use unification to make
190+ -- sure it is safe to use the selected instance. We will unify the solved type with
191+ -- the type in the instance head under the substition inferred from its instantiation.
192+ -- As an example, when solving MonadState t0 (State Int), we choose the
193+ -- MonadState s (State s) instance, and we unify t0 with Int, since the functional
194+ -- dependency from MonadState dictates that t0 should unify with s\[s -> Int], which is
195+ -- Int. This is fine, but in some cases, the substitution does not remove all TypeVars
196+ -- from the type, so we end up with a unification error. So, any type arguments which
197+ -- appear in the instance head, but not in the substitution need to be replaced with
198+ -- fresh type variables. This function extends a substitution with fresh type variables
199+ -- as necessary, based on the types in the instance head.
200+ withFreshTypes
201+ :: TypeClassDictionaryInScope
202+ -> Matching Type
203+ -> m (Matching Type)
204+ withFreshTypes TypeClassDictionaryInScope{..} subst = do
205+ let onType = everythingOnTypes S.union fromTypeVar
206+ typeVarsInHead = foldMap onType tcdInstanceTypes
207+ <> foldMap (foldMap (foldMap onType . constraintArgs)) tcdDependencies
208+ typeVarsInSubst = S.fromList (M.keys subst)
209+ uninstantiatedTypeVars = typeVarsInHead S.\\ typeVarsInSubst
210+ newSubst <- traverse withFreshType (S.toList uninstantiatedTypeVars)
211+ return (subst <> M.fromList newSubst)
212+ where
213+ fromTypeVar (TypeVar v) = S.singleton v
214+ fromTypeVar _ = S.empty
118215
119− unique :: [(a, TypeClassDictionaryInScope)] -> m (Either (a, TypeClassDictionaryInScope) Constraint)
120− unique [] | shouldGeneralize && all canBeGeneralized tys' = return (Right con')
121− | otherwise = throwError . errorMessage $ NoInstanceFound con'
122− unique [a] = return $ Left a
123− unique tcds | pairwise overlapping (map snd tcds) = do
124− tell . errorMessage $ OverlappingInstances className' tys' (map (tcdName . snd) tcds)
125− return $ Left (head tcds)
126− | otherwise = return $ Left (minimumBy (compare `on` length . tcdPath . snd) tcds)
127−
128− canBeGeneralized :: Type -> Bool
129− canBeGeneralized TUnknown{} = True
130− canBeGeneralized Skolem{} = True
131− canBeGeneralized _ = False
132−
133− -- |
134− -- Check if two dictionaries are overlapping
135− --
136− -- Dictionaries which are subclass dictionaries cannot overlap, since otherwise the overlap would have
137− -- been caught when constructing superclass dictionaries.
138− overlapping :: TypeClassDictionaryInScope -> TypeClassDictionaryInScope -> Bool
139− overlapping TypeClassDictionaryInScope{ tcdPath = _ : _ } _ = False
140− overlapping _ TypeClassDictionaryInScope{ tcdPath = _ : _ } = False
141− overlapping TypeClassDictionaryInScope{ tcdDependencies = Nothing } _ = False
142− overlapping _ TypeClassDictionaryInScope{ tcdDependencies = Nothing } = False
143− overlapping tcd1 tcd2 = tcdName tcd1 /= tcdName tcd2
144−
145− -- Create dictionaries for subgoals which still need to be solved by calling go recursively
146− -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
147− -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
148− solveSubgoals :: [(String, Type)] -> Maybe [Constraint] -> StateT InstanceContext m (Maybe [DictionaryValue], [(Ident, Constraint)])
149− solveSubgoals _ Nothing = return (Nothing, [])
150− solveSubgoals subst (Just subgoals) = do
151− zipped <- traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars subst))) subgoals
152− let (dicts, unsolved) = unzip zipped
153− return (Just dicts, concat unsolved)
154−
155− -- Make a dictionary from subgoal dictionaries by applying the correct function
156− mkDictionary :: Qualified Ident -> Maybe [DictionaryValue] -> DictionaryValue
157− mkDictionary fnName Nothing = LocalDictionaryValue fnName
158− mkDictionary fnName (Just []) = GlobalDictionaryValue fnName
159− mkDictionary fnName (Just dicts) = DependentDictionaryValue fnName dicts
160−
161− -- Turn a DictionaryValue into a Expr
162− dictionaryValueToValue :: DictionaryValue -> Expr
163− dictionaryValueToValue (LocalDictionaryValue fnName) = Var fnName
164− dictionaryValueToValue (GlobalDictionaryValue fnName) = Var fnName
165− dictionaryValueToValue (DependentDictionaryValue fnName dicts) = foldl App (Var fnName) (map dictionaryValueToValue dicts)
166− dictionaryValueToValue (SubclassDictionaryValue dict superclassName index) =
167− App (Accessor (C.__superclass_ ++ showQualified runProperName superclassName ++ "_" ++ show index)
168− (dictionaryValueToValue dict))
169− valUndefined
170− -- Ensure that a substitution is valid
171− verifySubstitution :: [(String, Type)] -> Maybe [(String, Type)]
172− verifySubstitution subst = do
173− let grps = groupBy ((==) `on` fst) . sortBy (compare `on` fst) $ subst
174− guard (all (pairwise unifiesWith . map snd) grps)
175− return $ map head grps
216+ withFreshType s = do
217+ t <- freshType
218+ return (s, t)
176219
177− valUndefined :: Expr
178− valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
220+ unique :: [Type] -> [(a, TypeClassDictionaryInScope)] -> m (EntailsResult a)
221+ unique tyArgs []
222+ | deferErrors = return Deferred
223+ -- We need a special case for nullary type classes, since we want
224+ -- to generalize over Partial constraints.
225+ | shouldGeneralize && (null tyArgs || any canBeGeneralized tyArgs) = return (Unsolved (Constraint className' tyArgs conInfo))
226+ | otherwise = throwError . errorMessage $ NoInstanceFound (Constraint className' tyArgs conInfo)
227+ unique _ [(a, dict)] = return $ Solved a dict
228+ unique tyArgs tcds
229+ | pairwiseAny overlapping (map snd tcds) = do
230+ tell . errorMessage $ OverlappingInstances className' tyArgs (map (tcdName . snd) tcds)
231+ return $ uncurry Solved (head tcds)
232+ | otherwise = return $ uncurry Solved (minimumBy (compare `on` length . tcdPath . snd) tcds)
179233
180−-- |
181−-- Check whether the type heads of two types are equal (for the purposes of type class dictionary lookup),
182−-- and return a substitution from type variables to types which makes the type heads unify.
183−--
184−typeHeadsAreEqual :: ModuleName -> Type -> Type -> Maybe [(String, Type)]
185−typeHeadsAreEqual _ (TUnknown u1) (TUnknown u2) | u1 == u2 = Just []
186−typeHeadsAreEqual _ (Skolem _ s1 _ _) (Skolem _ s2 _ _) | s1 == s2 = Just []
187−typeHeadsAreEqual _ t (TypeVar v) = Just [(v, t)]
188−typeHeadsAreEqual _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = Just []
189−typeHeadsAreEqual _ (TypeLevelString s1) (TypeLevelString s2) | s1 == s2 = Just []
190−typeHeadsAreEqual m (TypeApp h1 t1) (TypeApp h2 t2) = (++) <$> typeHeadsAreEqual m h1 h2
191− <*> typeHeadsAreEqual m t1 t2
192−typeHeadsAreEqual _ REmpty REmpty = Just []
193−typeHeadsAreEqual m r1@RCons{} r2@RCons{} =
194− let (s1, r1') = rowToList r1
195− (s2, r2') = rowToList r2
196−
197− int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
198− sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
199− sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
200− in (++) <$> foldMap (uncurry (typeHeadsAreEqual m)) int
201− <*> go sd1 r1' sd2 r2'
234+ canBeGeneralized :: Type -> Bool
235+ canBeGeneralized TUnknown{} = True
236+ canBeGeneralized Skolem{} = True
237+ canBeGeneralized _ = False
238+
239+ -- |
240+ -- Check if two dictionaries are overlapping
241+ --
242+ -- Dictionaries which are subclass dictionaries cannot overlap, since otherwise the overlap would have
243+ -- been caught when constructing superclass dictionaries.
244+ overlapping :: TypeClassDictionaryInScope -> TypeClassDictionaryInScope -> Bool
245+ overlapping TypeClassDictionaryInScope{ tcdPath = _ : _ } _ = False
246+ overlapping _ TypeClassDictionaryInScope{ tcdPath = _ : _ } = False
247+ overlapping TypeClassDictionaryInScope{ tcdDependencies = Nothing } _ = False
248+ overlapping _ TypeClassDictionaryInScope{ tcdDependencies = Nothing } = False
249+ overlapping tcd1 tcd2 = tcdName tcd1 /= tcdName tcd2
250+
251+ -- Create dictionaries for subgoals which still need to be solved by calling go recursively
252+ -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
253+ -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
254+ solveSubgoals :: Matching Type -> Maybe [Constraint] -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) (Maybe [Expr])
255+ solveSubgoals _ Nothing = return Nothing
256+ solveSubgoals subst (Just subgoals) =
257+ Just <$> traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars (M.toList subst)))) subgoals
258+
259+ -- Make a dictionary from subgoal dictionaries by applying the correct function
260+ mkDictionary :: Qualified Ident -> Maybe [Expr] -> Expr
261+ mkDictionary fnName Nothing = Var fnName
262+ mkDictionary fnName (Just []) = Var fnName
263+ mkDictionary fnName (Just dicts) = foldl App (Var fnName) dicts
264+
265+ -- Turn a DictionaryValue into a Expr
266+ subclassDictionaryValue :: Expr -> Qualified (ProperName a) -> Integer -> Expr
267+ subclassDictionaryValue dict superclassName index =
268+ App (Accessor (C.__superclass_ ++ showQualified runProperName superclassName ++ "_" ++ show index)
269+ dict)
270+ valUndefined
271+entails _ _ _ = internalError "entails: expected TypeClassDictionary"
272+
273+-- Check if an instance matches our list of types, allowing for types
274+-- to be solved via functional dependencies. If the types match, we return a
275+-- substitution which makes them match. If not, we return 'Nothing'.
276+matches :: [FunctionalDependency] -> TypeClassDictionaryInScope -> [Type] -> Maybe (Matching [Type])
277+matches deps TypeClassDictionaryInScope{..} tys = do
278+ -- First, find those types which match exactly
279+ let matched = zipWith typeHeadsAreEqual tys tcdInstanceTypes
280+ -- Now, use any functional dependencies to infer any remaining types
281+ guard $ covers matched
282+ -- Verify that any repeated type variables are unifiable
283+ let determinedSet = foldMap (S.fromList . fdDetermined) deps
284+ solved = map snd . filter ((`S.notMember` determinedSet) . fst) $ zipWith (\(_, ts) i -> (i, ts)) matched [0..]
285+ verifySubstitution (M.unionsWith (++) solved)
202286 where
203− go :: [(String, Type)] -> Type -> [(String, Type)] -> Type -> Maybe [(String, Type)]
204− go [] REmpty [] REmpty = Just []
205− go [] (TUnknown _) _ _ = Just []
206− go [] (TypeVar v1) [] (TypeVar v2) | v1 == v2 = Just []
207− go [] (Skolem _ s1 _ _) [] (Skolem _ s2 _ _) | s1 == s2 = Just []
208− go sd r [] (TypeVar v) = Just [(v, rowFromList (sd, r))]
209− go _ _ _ _ = Nothing
210−typeHeadsAreEqual _ _ _ = Nothing
287+ -- | Find the closure of a set of functional dependencies.
288+ covers :: [(Bool, subst)] -> Bool
289+ covers ms = finalSet == S.fromList [0..length ms - 1]
290+ where
291+ initialSet :: S.Set Int
292+ initialSet = S.fromList . map snd . filter (fst . fst) $ zip ms [0..]
211293
212−-- |
213−-- Check all values in a list pairwise match a predicate
214−--
215−pairwise :: (a -> a -> Bool) -> [a] -> Bool
216−pairwise _ [] = True
217−pairwise _ [_] = True
218−pairwise p (x : xs) = all (p x) xs && pairwise p xs
294+ finalSet :: S.Set Int
295+ finalSet = untilFixedPoint applyAll initialSet
296+
297+ untilFixedPoint :: Eq a => (a -> a) -> a -> a
298+ untilFixedPoint f = go
299+ where
300+ go a | a' == a = a'
301+ | otherwise = go a'
302+ where a' = f a
303+
304+ applyAll :: S.Set Int -> S.Set Int
305+ applyAll s = foldr applyDependency s deps
306+
307+ applyDependency :: FunctionalDependency -> S.Set Int -> S.Set Int
308+ applyDependency FunctionalDependency{..} xs
309+ | S.fromList fdDeterminers `S.isSubsetOf` xs = xs <> S.fromList fdDetermined
310+ | otherwise = xs
311+
312+ --
313+ -- Check whether the type heads of two types are equal (for the purposes of type class dictionary lookup),
314+ -- and return a substitution from type variables to types which makes the type heads unify.
315+ --
316+ typeHeadsAreEqual :: Type -> Type -> (Bool, Matching [Type])
317+ typeHeadsAreEqual (TUnknown u1) (TUnknown u2) | u1 == u2 = (True, M.empty)
318+ typeHeadsAreEqual (Skolem _ s1 _ _) (Skolem _ s2 _ _) | s1 == s2 = (True, M.empty)
319+ typeHeadsAreEqual t (TypeVar v) = (True, M.singleton v [t])
320+ typeHeadsAreEqual (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = (True, M.empty)
321+ typeHeadsAreEqual (TypeLevelString s1) (TypeLevelString s2) | s1 == s2 = (True, M.empty)
322+ typeHeadsAreEqual (TypeApp h1 t1) (TypeApp h2 t2) =
323+ both (typeHeadsAreEqual h1 h2) (typeHeadsAreEqual t1 t2)
324+ typeHeadsAreEqual REmpty REmpty = (True, M.empty)
325+ typeHeadsAreEqual r1@RCons{} r2@RCons{} =
326+ foldr both (go sd1 r1' sd2 r2') (map (uncurry typeHeadsAreEqual) int)
327+ where
328+ (s1, r1') = rowToList r1
329+ (s2, r2') = rowToList r2
330+
331+ int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
332+ sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
333+ sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
334+
335+ go :: [(String, Type)] -> Type -> [(String, Type)] -> Type -> (Bool, Matching [Type])
336+ go [] REmpty [] REmpty = (True, M.empty)
337+ go [] (TUnknown u1) [] (TUnknown u2) | u1 == u2 = (True, M.empty)
338+ go [] (TypeVar v1) [] (TypeVar v2) | v1 == v2 = (True, M.empty)
339+ go [] (Skolem _ sk1 _ _) [] (Skolem _ sk2 _ _) | sk1 == sk2 = (True, M.empty)
340+ go sd r [] (TypeVar v) = (True, M.singleton v [rowFromList (sd, r)])
341+ go _ _ _ _ = (False, M.empty)
342+ typeHeadsAreEqual _ _ = (False, M.empty)
343+
344+ both :: (Bool, Matching [Type]) -> (Bool, Matching [Type]) -> (Bool, Matching [Type])
345+ both (b1, m1) (b2, m2) = (b1 && b2, M.unionWith (++) m1 m2)
346+
347+ -- Ensure that a substitution is valid
348+ verifySubstitution :: Matching [Type] -> Maybe (Matching [Type])
349+ verifySubstitution = traverse meet where
350+ meet ts | pairwiseAll typesAreEqual ts = Just ts
351+ | otherwise = Nothing
352+
353+ -- Note that unknowns are only allowed to unify if they came from a type
354+ -- which was _not_ solved, i.e. one which was inferred by a functional
355+ -- dependency.
356+ typesAreEqual :: Type -> Type -> Bool
357+ typesAreEqual (TUnknown u1) (TUnknown u2) | u1 == u2 = True
358+ typesAreEqual (Skolem _ s1 _ _) (Skolem _ s2 _ _) = s1 == s2
359+ typesAreEqual (TypeVar v1) (TypeVar v2) = v1 == v2
360+ typesAreEqual (TypeLevelString s1) (TypeLevelString s2) = s1 == s2
361+ typesAreEqual (TypeConstructor c1) (TypeConstructor c2) = c1 == c2
362+ typesAreEqual (TypeApp h1 t1) (TypeApp h2 t2) = typesAreEqual h1 h2 && typesAreEqual t1 t2
363+ typesAreEqual REmpty REmpty = True
364+ typesAreEqual r1 r2 | isRCons r1 || isRCons r2 =
365+ let (s1, r1') = rowToList r1
366+ (s2, r2') = rowToList r2
367+
368+ int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
369+ sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
370+ sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
371+ in all (uncurry typesAreEqual) int && go sd1 r1' sd2 r2'
372+ where
373+ go :: [(String, Type)] -> Type -> [(String, Type)] -> Type -> Bool
374+ go [] (TUnknown u1) [] (TUnknown u2) | u1 == u2 = True
375+ go [] (Skolem _ s1 _ _) [] (Skolem _ s2 _ _) = s1 == s2
376+ go [] REmpty [] REmpty = True
377+ go [] (TypeVar v1) [] (TypeVar v2) = v1 == v2
378+ go _ _ _ _ = False
379+ typesAreEqual _ _ = False
380+
381+ isRCons :: Type -> Bool
382+ isRCons RCons{} = True
383+ isRCons _ = False
384+
385+-- | Add a dictionary for the constraint to the scope, and dictionaries
386+-- for all implied superclass instances.
387+newDictionaries
388+ :: MonadState CheckState m
389+ => [(Qualified (ProperName 'ClassName), Integer)]
390+ -> Qualified Ident
391+ -> Constraint
392+ -> m [TypeClassDictionaryInScope]
393+newDictionaries path name (Constraint className instanceTy _) = do
394+ tcs <- gets (typeClasses . checkEnv)
395+ let TypeClassData{..} = fromMaybe (internalError "newDictionaries: type class lookup failed") $ M.lookup className tcs
396+ supDicts <- join <$> zipWithM (\(Constraint supName supArgs _) index ->
397+ newDictionaries ((supName, index) : path)
398+ name
399+ (Constraint supName (instantiateSuperclass (map fst typeClassArguments) supArgs instanceTy) Nothing)
400+ ) typeClassSuperclasses [0..]
401+ return (TypeClassDictionaryInScope name path className instanceTy Nothing : supDicts)
402+ where
403+ instantiateSuperclass :: [String] -> [Type] -> [Type] -> [Type]
404+ instantiateSuperclass args supArgs tys = map (replaceAllTypeVars (zip args tys)) supArgs
405+
406+mkContext :: [TypeClassDictionaryInScope] -> InstanceContext
407+mkContext = foldr combineContexts M.empty . map fromDict where
408+ fromDict d = M.singleton Nothing (M.singleton (tcdClassName d) (M.singleton (tcdName d) d))
409+
410+-- | Check all pairs of values in a list match a predicate
411+pairwiseAll :: (a -> a -> Bool) -> [a] -> Bool
412+pairwiseAll _ [] = True
413+pairwiseAll _ [_] = True
414+pairwiseAll p (x : xs) = all (p x) xs && pairwiseAll p xs
415+
416+-- | Check any pair of values in a list match a predicate
417+pairwiseAny :: (a -> a -> Bool) -> [a] -> Bool
418+pairwiseAny _ [] = False
419+pairwiseAny _ [_] = False
420+pairwiseAny p (x : xs) = any (p x) xs || pairwiseAny p xs
421+
422+pairwiseM :: Applicative m => (a -> a -> m ()) -> [a] -> m ()
423+pairwiseM _ [] = pure ()
424+pairwiseM _ [_] = pure ()
425+pairwiseM p (x : xs) = traverse (p x) xs *> pairwiseM p xs