1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Type.Unify (unify) where
5import Control.Monad (zipWithM_)
6import Control.Monad.Except (ExceptT, lift, liftIO, throwError, runExceptT)
7import qualified Data.Map as Map
8import qualified Data.Maybe as Maybe
11import qualified AST.Variable as Var
12import qualified Reporting.Region as R
13import qualified Reporting.Error.Type as Error
14import qualified Type.Occurs as Occurs
15import qualified Type.State as TS
16import Type.Type as Type
17import qualified Type.UnionFind as UF
21-- KICK OFF UNIFICATION
24unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
25unify hint region expected actual =
26 {-# SCC elm_compiler_type_unify #-}
27 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
32 Left (Mismatch maybeReason) ->
35 do expectedSrcType <- Type.toSrcType expected
36 actualSrcType <- Type.toSrcType actual
37 mergeHelp expected actual Error noRank
38 let info = Error.MismatchInfo hint expectedSrcType actualSrcType maybeReason
39 return (Error.Mismatch info)
41 TS.addError region =<< liftIO mkError
49 ExceptT Mismatch TS.Solver
54 { _orientation :: Orientation
56 , _firstDesc :: Descriptor
58 , _secondDesc :: Descriptor
62data Orientation = ExpectedActual | ActualExpected
65reorient :: Context -> Context
66reorient (Context orientation var1 desc1 var2 desc2) =
70 ExpectedActual -> ActualExpected
71 ActualExpected -> ExpectedActual
73 Context otherOrientation var2 desc2 var1 desc1
81 Mismatch (Maybe Error.Reason)
84mismatch :: Context -> Maybe Error.Reason -> Unify a
85mismatch context@(Context orientation var1 _ var2 _) maybeReason =
86 do args1 <- liftIO $ collectArgs var1
87 args2 <- liftIO $ collectArgs var2
89 let numArgs1 = length args1
90 let numArgs2 = length args2
92 if numArgs1 == numArgs2
94 mismatchHelp orientation maybeReason
96 do zipWithM_ (\arg1 arg2 -> lift $ runExceptT $ subUnify context arg1 arg2) args1 args2
97 mismatchHelp orientation $ Just $ Error.MissingArgs $ abs (numArgs2 - numArgs1)
100mismatchHelp :: Orientation -> Maybe Error.Reason -> Unify a
101mismatchHelp orientation maybeReason =
102 throwError $ Mismatch $
108 Error.flipReason <$> maybeReason
111badRigid :: Maybe Text -> Error.Reason
113 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
116badSuper :: Super -> Error.Reason
118 Error.BadVar (Just (errorSuper super)) Nothing
121badSupers :: Super -> Super -> Error.Reason
122badSupers super1 super2 =
123 Error.BadVar (Just (errorSuper super1)) (Just (errorSuper super2))
126doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
128 Error.BadVar (Just vt1) (Just vt2)
131errorSuper :: Super -> Error.VarType
151merge :: Context -> Content -> Unify ()
152merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
153 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
156mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
157mergeHelp var1 var2 newContent newRank =
160 { _content = newContent
167fresh :: Context -> Content -> Unify Variable
168fresh (Context _ _ desc1 _ desc2) content =
173 , _rank = min (_rank desc1) (_rank desc2)
177 lift (TS.register freshVariable)
181-- ACTUALLY UNIFY THINGS
184guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
185guardedUnify orientation left right =
186 do equivalent <- liftIO $ UF.equivalent left right
190 do leftDesc <- liftIO $ UF.descriptor left
191 rightDesc <- liftIO $ UF.descriptor right
192 actuallyUnify (Context orientation left leftDesc right rightDesc)
195subUnify :: Context -> Variable -> Variable -> Unify ()
196subUnify context var1 var2 =
197 guardedUnify (_orientation context) var1 var2
200actuallyUnify :: Context -> Unify ()
201actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
203 firstContent = _content firstDesc
204 secondContent = _content secondDesc
208 -- If there was an error, just pretend it is okay. This lets us avoid
209 -- "cascading" errors where one problem manifests as multiple message.
212 Var Flex Nothing _ ->
213 unifyFlex context secondContent
215 Var Flex (Just super) _ ->
216 unifyFlexSuper context super firstContent secondContent
218 Var Rigid maybeSuper maybeName ->
219 unifyRigid context maybeSuper maybeName firstContent secondContent
222 unifyAtom context name secondContent
224 Alias name args realVar ->
225 unifyAlias context name args realVar secondContent
228 unifyStructure context term firstContent secondContent
232-- UNIFY FLEXIBLE VARIABLES
235unifyFlex :: Context -> Content -> Unify ()
236unifyFlex context otherContent =
242 merge context otherContent
245 merge context otherContent
248 merge context otherContent
251 merge context otherContent
254 merge context otherContent
258-- UNIFY RIGID VARIABLES
261unifyRigid :: Context -> Maybe Super -> Maybe Text -> Content -> Content -> Unify ()
262unifyRigid context maybeSuper maybeName content otherContent =
267 Var Flex otherMaybeSuper _ ->
268 case (maybeSuper, otherMaybeSuper) of
270 merge context content
273 mismatch context (Just (badRigid maybeName))
275 (Just super, Just otherSuper) ->
276 if combineRigidSupers super otherSuper then
277 merge context content
279 mismatch context (Just (badRigid maybeName))
281 Var Rigid _ otherMaybeName ->
282 mismatch context $ Just $
283 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
286 mismatch context (Just (badRigid maybeName))
289 mismatch context (Just (badRigid maybeName))
292 mismatch context (Just (badRigid maybeName))
296-- UNIFY SUPER VARIABLES
299unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
300unifyFlexSuper context super content otherContent =
303 unifyFlexSuperStructure context super term
306 if atomMatchesSuper super name then
307 merge context otherContent
309 mismatch context (Just (badSuper super))
311 Var Rigid Nothing maybeName ->
312 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
314 Var Rigid (Just otherSuper) maybeName ->
315 if combineRigidSupers otherSuper super then
316 merge context otherContent
318 mismatch context $ Just $ badRigid maybeName
320 Var Flex Nothing _ ->
321 merge context content
323 Var Flex (Just otherSuper) _ ->
327 Number -> merge context content
328 Comparable -> merge context content
329 _ -> mismatch context $ Just $ badSupers super otherSuper
333 Comparable -> merge context otherContent
334 Number -> merge context otherContent
335 Appendable -> merge context (Var Flex (Just CompAppend) Nothing)
336 CompAppend -> merge context otherContent
340 Appendable -> merge context otherContent
341 Comparable -> merge context (Var Flex (Just CompAppend) Nothing)
342 CompAppend -> merge context otherContent
343 Number -> mismatch context $ Just $ badSupers super otherSuper
347 Comparable -> merge context content
348 Appendable -> merge context content
349 CompAppend -> merge context content
350 Number -> mismatch context $ Just $ badSupers super otherSuper
353 subUnify context (_first context) realVar
359combineRigidSupers :: Super -> Super -> Bool
360combineRigidSupers rigid flex =
362 || (rigid == Number && flex == Comparable)
363 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
366isPrimitiveFrom :: [Text] -> Var.Canonical -> Bool
367isPrimitiveFrom prims var =
368 any (\p -> Var.isPrim p var) prims
371atomMatchesSuper :: Super -> Var.Canonical -> Bool
372atomMatchesSuper super name =
375 isPrimitiveFrom ["Int", "Float"] name
378 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
381 Var.isPrim "String" name
384 Var.isPrim "String" name
387unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
388unifyFlexSuperStructure context super term =
389 do appStructure <- liftIO (collectApps term)
392 mismatch context (Just (badSuper super))
397 mismatch context (Just (badSuper super))
400 merge context (Structure term)
403 do comparableOccursCheck context
404 merge context (Structure term)
405 unifyComparableRecursive (_orientation context) variable
408 do comparableOccursCheck context
409 merge context (Structure term)
410 unifyComparableRecursive (_orientation context) variable
415 mismatch context (Just (badSuper super))
418 mismatch context (Just (badSuper super))
421 if length entries > 6 then
422 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
425 do comparableOccursCheck context
426 merge context (Structure term)
427 mapM_ (unifyComparableRecursive (_orientation context)) entries
430 mismatch context (Just (badSuper super))
433-- TODO: is there some way to avoid doing this?
434-- Do type classes require occurs checks?
435comparableOccursCheck :: Context -> Unify ()
436comparableOccursCheck (Context _ _ _ var _) =
437 do hasOccurred <- liftIO $ Occurs.occurs var
439 then throwError (Mismatch Nothing)
443unifyComparableRecursive :: Orientation -> Variable -> Unify ()
444unifyComparableRecursive orientation var =
447 do desc <- UF.descriptor var
450 { _content = Var Flex (Just Comparable) Nothing
456 guardedUnify orientation compVar var
465collectApps :: FlatType -> IO AppStructure
469 collectAppsHelp [arg] =<< getContent func
475collectAppsHelp :: [Variable] -> Content -> IO AppStructure
476collectAppsHelp args content =
477 case (content, args) of
478 (Structure (App1 func arg), _) ->
479 collectAppsHelp (arg : args) =<< getContent func
481 (Atom name, [arg]) | Var.isList name ->
484 (Atom name, _) | Var.isTuple name ->
485 return (Tuple (reverse args))
491collectArgs :: Variable -> IO [Variable]
492collectArgs variable =
493 collectArgsHelp [] variable
496collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
497collectArgsHelp revArgs variable =
498 do content <- getContent variable
500 Structure (Fun1 arg returnType) ->
501 collectArgsHelp (arg : revArgs) returnType
504 return $ variable : revArgs
507getContent :: Variable -> IO Content
509 _content <$> UF.descriptor variable
516unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
517unifyAtom context name otherContent =
522 Var Flex Nothing _ ->
523 merge context (Atom name)
525 Var Flex (Just super) _ ->
526 if atomMatchesSuper super name then
527 merge context (Atom name)
530 mismatch context (Just ((Error.flipReason (badSuper super))))
532 Var Rigid _ maybeName ->
533 mismatch context (Just (Error.flipReason (badRigid maybeName)))
536 if name == otherName then
537 merge context otherContent
541 if isIntFloat name otherName || isIntFloat otherName name then
547 subUnify context (_first context) realVar
550 mismatch context Nothing
553isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
554isIntFloat name otherName =
555 Var.isPrim "Int" name && Var.isPrim "Float" otherName
562unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
563unifyAlias context name args realVar otherContent =
568 Var Flex Nothing _ ->
569 merge context (Alias name args realVar)
572 subUnify context realVar (_second context)
575 subUnify context realVar (_second context)
577 Alias otherName otherArgs otherRealVar ->
578 if name == otherName then
579 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
580 merge context otherContent
583 subUnify context realVar otherRealVar
586 subUnify context realVar (_second context)
593unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
594unifyStructure context term content otherContent =
599 Var Flex Nothing _ ->
600 merge context content
602 Var Flex (Just super) _ ->
603 unifyFlexSuperStructure (reorient context) super term
605 Var Rigid _ maybeName ->
606 mismatch context (Just (Error.flipReason (badRigid maybeName)))
609 mismatch context Nothing
612 subUnify context (_first context) realVar
614 Structure otherTerm ->
615 case (term, otherTerm) of
616 (App1 func arg, App1 otherFunc otherArg) ->
617 do subUnify context func otherFunc
618 subUnify context arg otherArg
619 merge context otherContent
621 (Fun1 arg result, Fun1 otherArg otherResult) ->
622 do subUnify context arg otherArg
623 subUnify context result otherResult
624 merge context otherContent
626 (EmptyRecord1, EmptyRecord1) ->
627 merge context otherContent
629 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
630 subUnify context ext (_second context)
632 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
633 subUnify context (_first context) ext
635 (Record1 fields extension, Record1 otherFields otherExtension) ->
636 do firstStructure <- liftIO $ gatherFields context fields extension
637 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
638 unifyRecord context firstStructure secondStructure
641 mismatch context Nothing
648unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
649unifyRecord context firstStructure secondStructure =
650 do let (RecordStructure expFields expVar expStruct) = firstStructure
651 let (RecordStructure actFields actVar actStruct) = secondStructure
653 let sharedFields = Map.intersectionWith (,) expFields actFields
655 -- call after unifying extension, make sure record shape matches before
656 -- looking into whether the particular field types match.
657 let unifySharedFields otherFields ext =
658 do unifySharedFieldsHelp context sharedFields
659 let allFields = Map.union (Map.map fst sharedFields) otherFields
660 merge context (Structure (Record1 allFields ext))
662 let uniqueExpFields = Map.difference expFields actFields
663 let uniqueActFields = Map.difference actFields expFields
665 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
666 (_, True, _, True) ->
667 do subUnify context expVar actVar
668 unifySharedFields Map.empty expVar
670 (Empty, _, _, False) ->
671 mismatch context $ Just $
672 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
674 (_, False, Empty, _) ->
675 mismatch context $ Just $
676 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
678 (_, False, _, True) ->
679 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
680 subUnify context subRecord actVar
681 unifySharedFields Map.empty subRecord
683 (_, True, _, False) ->
684 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
685 subUnify context expVar subRecord
686 unifySharedFields Map.empty subRecord
688 (Extension, False, Extension, False) ->
689 do let subFields = Map.union uniqueExpFields uniqueActFields
690 subExt <- fresh context (Var Flex Nothing Nothing)
691 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
692 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
693 subUnify context expVar expRecord
694 subUnify context actRecord actVar
695 unifySharedFields subFields subExt
699unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
700unifySharedFieldsHelp context sharedFields =
701 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
702 case Maybe.catMaybes maybeBadFields of
707 mismatch context (Just (Error.BadFields (reverse badFields)))
710unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
711unifyField context (field, (expected, actual)) =
712 do result <- lift $ runExceptT $ subUnify context expected actual
717 Left (Mismatch maybeReason) ->
718 return $ Just (field, maybeReason)
722-- GATHER RECORD STRUCTURE
725data RecordStructure = RecordStructure
726 { _fields :: Map.Map Text Variable
727 , _extVar :: Variable
728 , _extStruct :: ExtensionStructure
732data ExtensionStructure
737gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
738gatherFields context fields variable =
739 do desc <- UF.descriptor variable
740 case _content desc of
741 Structure (Record1 subFields subExt) ->
742 gatherFields context (Map.union fields subFields) subExt
744 Structure EmptyRecord1 ->
745 return (RecordStructure fields variable Empty)
748 -- TODO may be dropping useful alias info here
749 gatherFields context fields var
752 return (RecordStructure fields variable Extension)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list