@@ -1,28 +1,35 @@
1+{-# LANGUAGE NamedFieldPuns #-}
2-- Type class entailment
4-- Type class entailment
4module Language.PureScript.TypeChecker.Entailment
6module Language.PureScript.TypeChecker.Entailment
5 ( InstanceContext
7 ( InstanceContext
6 , replaceTypeClassDictionaries
8 , replaceTypeClassDictionaries
9import Prelude.Compat
12import Prelude.Compat
14+import Control.Arrow (second)
11import Control.Monad.Error.Class (MonadError(..))
15import Control.Monad.Error.Class (MonadError(..))
12import Control.Monad.State
16import Control.Monad.State
13import Control.Monad.Supply.Class (MonadSupply(..))
17import Control.Monad.Supply.Class (MonadSupply(..))
14import Control.Monad.Writer
18import Control.Monad.Writer
20+import Data.Foldable (for_)
16import Data.Function (on)
21import Data.Function (on)
17−import Data.List (minimumBy, sortBy, groupBy)
22+import Data.List (minimumBy, nub)
18−import Data.Maybe (maybeToList, mapMaybe)
23+import Data.Maybe (fromMaybe, maybeToList, mapMaybe)
19import qualified Data.Map as M
24import qualified Data.Map as M
25+import qualified Data.Set as S
21import Language.PureScript.AST
27import Language.PureScript.AST
22import Language.PureScript.Crash
28import Language.PureScript.Crash
29+import Language.PureScript.Environment
23import Language.PureScript.Errors
30import Language.PureScript.Errors
24import Language.PureScript.Names
31import Language.PureScript.Names
25−import Language.PureScript.TypeChecker.Monad (CheckState, withErrorMessageHint)
32+import Language.PureScript.TypeChecker.Monad
26import Language.PureScript.TypeChecker.Unify
33import Language.PureScript.TypeChecker.Unify
27import Language.PureScript.TypeClassDictionaries
34import Language.PureScript.TypeClassDictionaries
28import Language.PureScript.Types
35import Language.PureScript.Types
@@ -34,24 +41,57 @@ type InstanceContext = M.Map (Maybe ModuleName)
34 (M.Map (Qualified Ident)
41 (M.Map (Qualified Ident)
35 TypeClassDictionaryInScope))
42 TypeClassDictionaryInScope))
37−-- | Merge two type class contexts
44+-- | A type substitution which makes an instance head match a list of types.
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
38combineContexts :: InstanceContext -> InstanceContext -> InstanceContext
50combineContexts :: InstanceContext -> InstanceContext -> InstanceContext
39combineContexts = M.unionWith (M.unionWith M.union)
51combineContexts = M.unionWith (M.unionWith M.union)
41-- | Replace type class dictionary placeholders with inferred type class dictionaries
53-- | Replace type class dictionary placeholders with inferred type class dictionaries
42replaceTypeClassDictionaries
54replaceTypeClassDictionaries
43− :: (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
56+ . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
47 -> m (Expr, [(Ident, Constraint)])
59 -> m (Expr, [(Ident, Constraint)])
48−replaceTypeClassDictionaries shouldGeneralize mn =
60+replaceTypeClassDictionaries shouldGeneralize expr = flip evalStateT M.empty $ do
49− let (_, f, _) = everywhereOnValuesTopDownM return (WriterT . go) return
61+ -- Loop, deferring any unsolved constraints, until there are no more
50− in flip evalStateT M.empty . runWriterT . f
62+ -- constraints which can be solved, then make a generalization pass.
64+ (e', solved) <- deferPass e
68+ loop expr >>= generalizePass
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
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
82+ go :: Bool -> Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
52− go (TypeClassDictionary constraint dicts hints) =
83+ go deferErrors dict@(TypeClassDictionary _ _ hints) =
53− rethrow (addHints hints) $ entails shouldGeneralize mn dicts constraint
84+ rethrow (addHints hints) $ entails shouldGeneralize deferErrors dict
54− go other = return (other, [])
85+ go _ other = return other
87+-- | Three options for how we can handle a constraint, depending on the mode we're in.
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
94+ -- ^ We couldn't solve this constraint right now, so it has been deferred
57-- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
97-- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
@@ -61,14 +101,14 @@ entails
61 :: forall m
101 :: forall m
62 . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
102 . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
64− -> ModuleName
104+ -> Bool
65− -> InstanceContext
105+ -> Expr
66− -> Constraint
106+ -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
67− -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
68−entails shouldGeneralize moduleName context = solve
107+entails shouldGeneralize deferErrors (TypeClassDictionary constraint context hints) =
70 forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDictionaryInScope]
110 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)))
72 forClassName _ _ _ = internalError "forClassName: expected qualified class name"
112 forClassName _ _ _ = internalError "forClassName: expected qualified class name"
74 ctorModules :: Type -> Maybe ModuleName
114 ctorModules :: Type -> Maybe ModuleName
@@ -80,139 +120,306 @@ entails shouldGeneralize moduleName context = solve
80 findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDictionaryInScope]
120 findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDictionaryInScope]
81 findDicts ctx cn = maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup ctx
121 findDicts ctx cn = maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup ctx
123+ valUndefined :: Expr
124+ valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
83− solve :: Constraint -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
126+ solve :: Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
84− solve con = StateT . (withErrorMessageHint (ErrorSolvingConstraint con) .) . runStateT $ do
127+ solve con = go 0 con
85− (dict, unsolved) <- go 0 con
86− return (dictionaryValueToValue dict, unsolved)
88− go :: Int -> Constraint -> StateT InstanceContext m (DictionaryValue, [(Ident, Constraint)])
129+ go :: Int -> Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
89− go work (Constraint className' tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
130+ go work (Constraint className' tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
90− go work con'@(Constraint className' tys' _) = do
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'
91− -- Get the inferred constraint context so far, and merge it with the global context
136+ -- Get the inferred constraint context so far, and merge it with the global context
92− inferred <- get
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)
93− let instances = do
142+ let instances =
94− tcd <- forClassName (combineContexts context inferred) className' tys'
144+ | tcd <- forClassName (combineContexts context inferred) className' tys''
95− -- Make sure the type unifies with the type in the type instance definition
145+ -- 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)
146+ , substs <- maybeToList (matches typeClassDependencies tcd tys'')
97− return (subst, tcd)
147+ ]
98− solution <- lift $ unique instances
148+ solution <- lift . lift $ unique tys'' instances
99− case solution of
149+ case solution of
100− Left (subst, tcd) -> do
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'
101− -- Solve any necessary subgoals
166+ -- Solve any necessary subgoals
102− (args, unsolved) <- solveSubgoals subst (tcdDependencies tcd)
167+ args <- solveSubgoals subst'' (tcdDependencies tcd)
103− let match = foldr (\(superclassName, index) dict -> SubclassDictionaryValue dict superclassName index)
168+ let match = foldr (\(superclassName, index) dict -> subclassDictionaryValue dict superclassName index)
104− (mkDictionary (tcdName tcd) args)
169+ (mkDictionary (tcdName tcd) args)
105− (tcdPath tcd)
170+ (tcdPath tcd)
106− return (match, unsolved)
171+ return match
107− Right unsolved@(Constraint unsolvedClassName@(Qualified _ pn) unsolvedTys _) -> do
172+ Unsolved unsolved -> do
108− -- Generate a fresh name for the unsolved constraint's new dictionary
173+ -- Generate a fresh name for the unsolved constraint's new dictionary
109− ident <- freshIdent ("dict" ++ runProperName pn)
174+ ident <- freshIdent ("dict" ++ runProperName (disqualify (constraintClass unsolved)))
110− let qident = Qualified Nothing ident
175+ let qident = Qualified Nothing ident
111− -- Store the new dictionary in the InstanceContext so that we can solve this goal in
176+ -- Store the new dictionary in the InstanceContext so that we can solve this goal in
112− -- future.
177+ -- future.
113− let newDict = TypeClassDictionaryInScope qident [] unsolvedClassName unsolvedTys Nothing
178+ newDicts <- lift . lift $ newDictionaries [] qident unsolved
114− newContext = M.singleton Nothing (M.singleton unsolvedClassName (M.singleton qident newDict))
179+ let newContext = mkContext newDicts
115− modify (combineContexts newContext)
180+ modify (combineContexts newContext)
116− return (LocalDictionaryValue qident, [(ident, unsolved)])
181+ -- Mark this constraint for generalization
182+ tell (mempty, [(ident, unsolved)])
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)
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.
201+ :: TypeClassDictionaryInScope
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)
213+ fromTypeVar (TypeVar v) = S.singleton v
214+ fromTypeVar _ = S.empty
119− unique :: [(a, TypeClassDictionaryInScope)] -> m (Either (a, TypeClassDictionaryInScope) Constraint)
216+ withFreshType s = do
120− unique [] | shouldGeneralize && all canBeGeneralized tys' = return (Right con')
217+ t <- freshType
121− | otherwise = throwError . errorMessage $ NoInstanceFound con'
218+ return (s, t)
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)
128− canBeGeneralized :: Type -> Bool
129− canBeGeneralized TUnknown{} = True
130− canBeGeneralized Skolem{} = True
131− canBeGeneralized _ = False
134− -- Check if two dictionaries are overlapping
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
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)
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
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))
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
177− valUndefined :: Expr
220+ unique :: [Type] -> [(a, TypeClassDictionaryInScope)] -> m (EntailsResult a)
178− valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
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
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)
180−-- |
234+ canBeGeneralized :: Type -> Bool
181−-- Check whether the type heads of two types are equal (for the purposes of type class dictionary lookup),
235+ canBeGeneralized TUnknown{} = True
182−-- and return a substitution from type variables to types which makes the type heads unify.
236+ canBeGeneralized Skolem{} = True
183−--
237+ canBeGeneralized _ = False
184−typeHeadsAreEqual :: ModuleName -> Type -> Type -> Maybe [(String, Type)]
238+
185−typeHeadsAreEqual _ (TUnknown u1) (TUnknown u2) | u1 == u2 = Just []
239+ -- |
186−typeHeadsAreEqual _ (Skolem _ s1 _ _) (Skolem _ s2 _ _) | s1 == s2 = Just []
240+ -- Check if two dictionaries are overlapping
187−typeHeadsAreEqual _ t (TypeVar v) = Just [(v, t)]
241+ --
188−typeHeadsAreEqual _ (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = Just []
242+ -- Dictionaries which are subclass dictionaries cannot overlap, since otherwise the overlap would have
189−typeHeadsAreEqual _ (TypeLevelString s1) (TypeLevelString s2) | s1 == s2 = Just []
243+ -- been caught when constructing superclass dictionaries.
190−typeHeadsAreEqual m (TypeApp h1 t1) (TypeApp h2 t2) = (++) <$> typeHeadsAreEqual m h1 h2
244+ overlapping :: TypeClassDictionaryInScope -> TypeClassDictionaryInScope -> Bool
191− <*> typeHeadsAreEqual m t1 t2
245+ overlapping TypeClassDictionaryInScope{ tcdPath = _ : _ } _ = False
192−typeHeadsAreEqual _ REmpty REmpty = Just []
246+ overlapping _ TypeClassDictionaryInScope{ tcdPath = _ : _ } = False
193−typeHeadsAreEqual m r1@RCons{} r2@RCons{} =
247+ overlapping TypeClassDictionaryInScope{ tcdDependencies = Nothing } _ = False
194− let (s1, r1') = rowToList r1
248+ overlapping _ TypeClassDictionaryInScope{ tcdDependencies = Nothing } = False
195− (s2, r2') = rowToList r2
249+ overlapping tcd1 tcd2 = tcdName tcd1 /= tcdName tcd2
197− int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
251+ -- Create dictionaries for subgoals which still need to be solved by calling go recursively
198− sd1 = [ (name, t1) | (name, t1) <- s1, name `notElem` map fst s2 ]
252+ -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
199− sd2 = [ (name, t2) | (name, t2) <- s2, name `notElem` map fst s1 ]
253+ -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
200− in (++) <$> foldMap (uncurry (typeHeadsAreEqual m)) int
254+ solveSubgoals :: Matching Type -> Maybe [Constraint] -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) (Maybe [Expr])
201− <*> go sd1 r1' sd2 r2'
255+ solveSubgoals _ Nothing = return Nothing
256+ solveSubgoals subst (Just subgoals) =
257+ Just <$> traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars (M.toList subst)))) subgoals
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
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)
271+entails _ _ _ = internalError "entails: expected TypeClassDictionary"
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)
203− go :: [(String, Type)] -> Type -> [(String, Type)] -> Type -> Maybe [(String, Type)]
287+ -- | Find the closure of a set of functional dependencies.
204− go [] REmpty [] REmpty = Just []
288+ covers :: [(Bool, subst)] -> Bool
205− go [] (TUnknown _) _ _ = Just []
289+ covers ms = finalSet == S.fromList [0..length ms - 1]
206− go [] (TypeVar v1) [] (TypeVar v2) | v1 == v2 = Just []
290+ where
207− go [] (Skolem _ s1 _ _) [] (Skolem _ s2 _ _) | s1 == s2 = Just []
291+ initialSet :: S.Set Int
208− go sd r [] (TypeVar v) = Just [(v, rowFromList (sd, r))]
292+ initialSet = S.fromList . map snd . filter (fst . fst) $ zip ms [0..]
209− go _ _ _ _ = Nothing
210−typeHeadsAreEqual _ _ _ = Nothing
212−-- |
294+ finalSet :: S.Set Int
295+ finalSet = untilFixedPoint applyAll initialSet
297+ untilFixedPoint :: Eq a => (a -> a) -> a -> a
298+ untilFixedPoint f = go
304+ applyAll :: S.Set Int -> S.Set Int
305+ applyAll s = foldr applyDependency s deps
307+ applyDependency :: FunctionalDependency -> S.Set Int -> S.Set Int
308+ applyDependency FunctionalDependency{..} xs
309+ | S.fromList fdDeterminers `S.isSubsetOf` xs = xs <> S.fromList fdDetermined
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.
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)
328+ (s1, r1') = rowToList r1
329+ (s2, r2') = rowToList r2
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 ]
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)
344+ both :: (Bool, Matching [Type]) -> (Bool, Matching [Type]) -> (Bool, Matching [Type])
345+ both (b1, m1) (b2, m2) = (b1 && b2, M.unionWith (++) m1 m2)
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
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
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
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'
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
379+ typesAreEqual _ _ = False
381+ isRCons :: Type -> Bool
382+ isRCons RCons{} = True
385+-- | Add a dictionary for the constraint to the scope, and dictionaries
386+-- for all implied superclass instances.
388+ :: MonadState CheckState m
389+ => [(Qualified (ProperName 'ClassName), Integer)]
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)
399+ (Constraint supName (instantiateSuperclass (map fst typeClassArguments) supArgs instanceTy) Nothing)
400+ ) typeClassSuperclasses [0..]
401+ return (TypeClassDictionaryInScope name path className instanceTy Nothing : supDicts)
403+ instantiateSuperclass :: [String] -> [Type] -> [Type] -> [Type]
404+ instantiateSuperclass args supArgs tys = map (replaceAllTypeVars (zip args tys)) supArgs
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))
213−-- Check all values in a list pairwise match a predicate
410+-- | Check all pairs of values in a list match a predicate
215−pairwise :: (a -> a -> Bool) -> [a] -> Bool
411+pairwiseAll :: (a -> a -> Bool) -> [a] -> Bool
216−pairwise _ [] = True
412+pairwiseAll _ [] = True
217−pairwise _ [_] = True
413+pairwiseAll _ [_] = True
218−pairwise p (x : xs) = all (p x) xs && pairwise p xs
414+pairwiseAll p (x : xs) = all (p x) xs && pairwiseAll p xs
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
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