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
10import qualified Data.UnionFind.IO as UF
12import qualified AST.Variable as Var
13import qualified Reporting.Region as R
14import qualified Reporting.Error.Type as Error
15import qualified Type.State as TS
16import Type.Type as Type
20-- KICK OFF UNIFICATION
23unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
24unify hint region expected actual =
25 {-# SCC elm_compiler_type_unify #-}
26 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
31 Left (Mismatch maybeReason) ->
34 do expectedSrcType <- Type.toSrcType expected
35 actualSrcType <- Type.toSrcType actual
36 mergeHelp expected actual Error
37 let info = Error.MismatchInfo hint expectedSrcType actualSrcType maybeReason
38 return (Error.Mismatch info)
40 TS.addError region =<< liftIO mkError
48 ExceptT Mismatch TS.Solver
52 { _orientation :: Orientation
54 , _firstDesc :: Descriptor
56 , _secondDesc :: Descriptor
60data Orientation = ExpectedActual | ActualExpected
63reorient :: Context -> Context
64reorient (Context orientation var1 desc1 var2 desc2) =
68 ExpectedActual -> ActualExpected
69 ActualExpected -> ExpectedActual
71 Context otherOrientation var2 desc2 var1 desc1
79 Mismatch (Maybe Error.Reason)
82mismatch :: Context -> Maybe Error.Reason -> Unify a
83mismatch context@(Context orientation var1 _ var2 _) maybeReason =
84 do args1 <- liftIO $ collectArgs var1
85 args2 <- liftIO $ collectArgs var2
87 let numArgs1 = length args1
88 let numArgs2 = length args2
90 if numArgs1 == numArgs2
92 mismatchHelp orientation maybeReason
94 do zipWithM_ (\arg1 arg2 -> lift $ runExceptT $ subUnify context arg1 arg2) args1 args2
95 mismatchHelp orientation $ Just $ Error.MissingArgs $ abs (numArgs2 - numArgs1)
98mismatchHelp :: Orientation -> Maybe Error.Reason -> Unify a
99mismatchHelp orientation maybeReason =
100 throwError $ Mismatch $
106 Error.flipReason <$> maybeReason
109badRigid :: Maybe Text -> Error.Reason
111 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
114badSuper :: Super -> Error.Reason
116 Error.BadVar (Just (errorSuper super)) Nothing
119badSupers :: Super -> Super -> Error.Reason
120badSupers super1 super2 =
121 Error.BadVar (Just (errorSuper super1)) (Just (errorSuper super2))
124doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
126 Error.BadVar (Just vt1) (Just vt2)
129errorSuper :: Super -> Error.VarType
149merge :: Context -> Content -> Unify ()
150merge (Context _ first _ second _) content =
151 liftIO $ mergeHelp first second content
154mergeHelp :: Variable -> Variable -> Content -> IO ()
155mergeHelp first second content =
156 UF.union' first second $ \desc1 desc2 ->
160 , _rank = min (_rank desc1) (_rank desc2)
166fresh :: Context -> Content -> Unify Variable
167fresh (Context _ _ desc1 _ desc2) content =
172 , _rank = min (_rank desc1) (_rank desc2)
176 lift (TS.register freshVariable)
180-- ACTUALLY UNIFY THINGS
183guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
184guardedUnify orientation left right =
185 do equivalent <- liftIO $ UF.equivalent left right
189 do leftDesc <- liftIO $ UF.descriptor left
190 rightDesc <- liftIO $ UF.descriptor right
191 actuallyUnify (Context orientation left leftDesc right rightDesc)
194subUnify :: Context -> Variable -> Variable -> Unify ()
195subUnify context var1 var2 =
196 guardedUnify (_orientation context) var1 var2
199actuallyUnify :: Context -> Unify ()
200actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
202 firstContent = _content firstDesc
203 secondContent = _content secondDesc
207 -- If there was an error, just pretend it is okay. This lets us avoid
208 -- "cascading" errors where one problem manifests as multiple message.
211 Var Flex Nothing _ ->
212 unifyFlex context secondContent
214 Var Flex (Just super) _ ->
215 unifyFlexSuper context super firstContent secondContent
217 Var Rigid maybeSuper maybeName ->
218 unifyRigid context maybeSuper maybeName firstContent secondContent
221 unifyAtom context name secondContent
223 Alias name args realVar ->
224 unifyAlias context name args realVar secondContent
227 unifyStructure context term firstContent secondContent
231-- UNIFY FLEXIBLE VARIABLES
234unifyFlex :: Context -> Content -> Unify ()
235unifyFlex context otherContent =
241 merge context otherContent
244 merge context otherContent
247 merge context otherContent
250 merge context otherContent
253 merge context otherContent
257-- UNIFY RIGID VARIABLES
260unifyRigid :: Context -> Maybe Super -> Maybe Text -> Content -> Content -> Unify ()
261unifyRigid context maybeSuper maybeName content otherContent =
266 Var Flex otherMaybeSuper _ ->
267 case (maybeSuper, otherMaybeSuper) of
269 merge context content
272 mismatch context (Just (badRigid maybeName))
274 (Just super, Just otherSuper) ->
275 if combineRigidSupers super otherSuper then
276 merge context content
278 mismatch context (Just (badRigid maybeName))
280 Var Rigid _ otherMaybeName ->
281 mismatch context $ Just $
282 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
285 mismatch context (Just (badRigid maybeName))
288 mismatch context (Just (badRigid maybeName))
291 mismatch context (Just (badRigid maybeName))
295-- UNIFY SUPER VARIABLES
298unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
299unifyFlexSuper context super content otherContent =
302 unifyFlexSuperStructure context super term
305 if atomMatchesSuper super name then
306 merge context otherContent
308 mismatch context (Just (badSuper super))
310 Var Rigid Nothing maybeName ->
311 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
313 Var Rigid (Just otherSuper) maybeName ->
314 if combineRigidSupers otherSuper super then
315 merge context otherContent
317 mismatch context $ Just $ badRigid maybeName
319 Var Flex Nothing _ ->
320 merge context content
322 Var Flex (Just otherSuper) _ ->
326 Number -> merge context content
327 Comparable -> merge context content
328 _ -> mismatch context $ Just $ badSupers super otherSuper
332 Comparable -> merge context otherContent
333 Number -> merge context otherContent
334 Appendable -> merge context (Var Flex (Just CompAppend) Nothing)
335 CompAppend -> merge context otherContent
339 Appendable -> merge context otherContent
340 Comparable -> merge context (Var Flex (Just CompAppend) Nothing)
341 CompAppend -> merge context otherContent
342 Number -> mismatch context $ Just $ badSupers super otherSuper
346 Comparable -> merge context content
347 Appendable -> merge context content
348 CompAppend -> merge context content
349 Number -> mismatch context $ Just $ badSupers super otherSuper
352 subUnify context (_first context) realVar
358combineRigidSupers :: Super -> Super -> Bool
359combineRigidSupers rigid flex =
361 || (rigid == Number && flex == Comparable)
362 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
365isPrimitiveFrom :: [Text] -> Var.Canonical -> Bool
366isPrimitiveFrom prims var =
367 any (\p -> Var.isPrim p var) prims
370atomMatchesSuper :: Super -> Var.Canonical -> Bool
371atomMatchesSuper super name =
374 isPrimitiveFrom ["Int", "Float"] name
377 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
380 Var.isPrim "String" name
383 Var.isPrim "String" name
386unifyFlexSuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
387unifyFlexSuperStructure context super term =
388 do appStructure <- liftIO (collectApps (Structure term))
391 mismatch context (Just (badSuper super))
396 mismatch context (Just (badSuper super))
399 merge context (Structure term)
402 do merge context (Structure term)
403 unifyComparableRecursive (_orientation context) variable
406 do merge context (Structure term)
407 unifyComparableRecursive (_orientation context) variable
412 mismatch context (Just (badSuper super))
415 mismatch context (Just (badSuper super))
418 if length entries > 6 then
419 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
422 do merge context (Structure term)
423 mapM_ (unifyComparableRecursive (_orientation context)) entries
426 mismatch context (Just (badSuper super))
429unifyComparableRecursive :: Orientation -> Variable -> Unify ()
430unifyComparableRecursive orientation var =
433 do desc <- UF.descriptor var
436 { _content = Var Flex (Just Comparable) Nothing
442 guardedUnify orientation compVar var
451collectApps :: Content -> IO AppStructure
453 collectAppsHelp [] content
456collectAppsHelp :: [Variable] -> Content -> IO AppStructure
457collectAppsHelp args content =
458 case (content, args) of
459 (Structure (App1 func arg), _) ->
460 collectAppsHelp (args ++ [arg]) =<< getContent func
462 (Atom name, [arg]) | Var.isList name ->
465 (Atom name, _) | Var.isTuple name ->
472collectArgs :: Variable -> IO [Variable]
473collectArgs variable =
474 collectArgsHelp [] variable
477collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
478collectArgsHelp revArgs variable =
479 do content <- getContent variable
481 Structure (Fun1 arg returnType) ->
482 collectArgsHelp (arg : revArgs) returnType
485 return $ variable : revArgs
488getContent :: Variable -> IO Content
490 _content <$> UF.descriptor variable
497unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
498unifyAtom context name otherContent =
503 Var Flex Nothing _ ->
504 merge context (Atom name)
506 Var Flex (Just super) _ ->
507 if atomMatchesSuper super name then
508 merge context (Atom name)
511 mismatch context (Just ((Error.flipReason (badSuper super))))
513 Var Rigid _ maybeName ->
514 mismatch context (Just (Error.flipReason (badRigid maybeName)))
517 if name == otherName then
518 merge context otherContent
522 if isIntFloat name otherName || isIntFloat otherName name then
528 subUnify context (_first context) realVar
531 mismatch context Nothing
534isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
535isIntFloat name otherName =
536 Var.isPrim "Int" name && Var.isPrim "Float" otherName
543unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
544unifyAlias context name args realVar otherContent =
549 Var Flex Nothing _ ->
550 merge context (Alias name args realVar)
553 subUnify context realVar (_second context)
556 subUnify context realVar (_second context)
558 Alias otherName otherArgs otherRealVar ->
559 if name == otherName then
560 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
561 merge context otherContent
564 subUnify context realVar otherRealVar
567 subUnify context realVar (_second context)
574unifyStructure :: Context -> Term1 Variable -> Content -> Content -> Unify ()
575unifyStructure context term content otherContent =
580 Var Flex Nothing _ ->
581 merge context content
583 Var Flex (Just super) _ ->
584 unifyFlexSuperStructure (reorient context) super term
586 Var Rigid _ maybeName ->
587 mismatch context (Just (Error.flipReason (badRigid maybeName)))
590 mismatch context Nothing
593 subUnify context (_first context) realVar
595 Structure otherTerm ->
596 case (term, otherTerm) of
597 (App1 func arg, App1 otherFunc otherArg) ->
598 do subUnify context func otherFunc
599 subUnify context arg otherArg
600 merge context otherContent
602 (Fun1 arg result, Fun1 otherArg otherResult) ->
603 do subUnify context arg otherArg
604 subUnify context result otherResult
605 merge context otherContent
607 (EmptyRecord1, EmptyRecord1) ->
608 merge context otherContent
610 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
611 subUnify context ext (_second context)
613 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
614 subUnify context (_first context) ext
616 (Record1 fields extension, Record1 otherFields otherExtension) ->
617 do firstStructure <- liftIO $ gatherFields context fields extension
618 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
619 unifyRecord context firstStructure secondStructure
622 mismatch context Nothing
629unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
630unifyRecord context firstStructure secondStructure =
631 do let (RecordStructure expFields expVar expStruct) = firstStructure
632 let (RecordStructure actFields actVar actStruct) = secondStructure
634 let sharedFields = Map.intersectionWith (,) expFields actFields
636 -- call after unifying extension, make sure record shape matches before
637 -- looking into whether the particular field types match.
638 let unifySharedFields otherFields ext =
639 do unifySharedFieldsHelp context sharedFields
640 let allFields = Map.union (Map.map fst sharedFields) otherFields
641 merge context (Structure (Record1 allFields ext))
643 let uniqueExpFields = Map.difference expFields actFields
644 let uniqueActFields = Map.difference actFields expFields
646 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
647 (_, True, _, True) ->
648 do subUnify context expVar actVar
649 unifySharedFields Map.empty expVar
651 (Empty, _, _, False) ->
652 mismatch context $ Just $
653 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
655 (_, False, Empty, _) ->
656 mismatch context $ Just $
657 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
659 (_, False, _, True) ->
660 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
661 subUnify context subRecord actVar
662 unifySharedFields Map.empty subRecord
664 (_, True, _, False) ->
665 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
666 subUnify context expVar subRecord
667 unifySharedFields Map.empty subRecord
669 (Extension, False, Extension, False) ->
670 do let subFields = Map.union uniqueExpFields uniqueActFields
671 subExt <- fresh context (Var Flex Nothing Nothing)
672 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
673 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
674 subUnify context expVar expRecord
675 subUnify context actRecord actVar
676 unifySharedFields subFields subExt
680unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
681unifySharedFieldsHelp context sharedFields =
682 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
683 case Maybe.catMaybes maybeBadFields of
688 mismatch context (Just (Error.BadFields (reverse badFields)))
691unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
692unifyField context (field, (expected, actual)) =
693 do result <- lift $ runExceptT $ subUnify context expected actual
698 Left (Mismatch maybeReason) ->
699 return $ Just (field, maybeReason)
703-- GATHER RECORD STRUCTURE
706data RecordStructure = RecordStructure
707 { _fields :: Map.Map Text Variable
708 , _extVar :: Variable
709 , _extStruct :: ExtensionStructure
713data ExtensionStructure
718gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
719gatherFields context fields variable =
720 do desc <- UF.descriptor variable
721 case _content desc of
722 Structure (Record1 subFields subExt) ->
723 gatherFields context (Map.union fields subFields) subExt
725 Structure EmptyRecord1 ->
726 return (RecordStructure fields variable Empty)
729 -- TODO may be dropping useful alias info here
730 gatherFields context fields var
733 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