1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Type.Unify (unify) where
5import Control.Monad (liftM2, when, 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)
33 do expectedSrcType <- liftIO $ Type.toSrcType expected
34 actualSrcType <- liftIO $ Type.toSrcType actual
35 liftIO $ mergeHelp expected actual (Error "?") noRank
40 Error.MismatchInfo hint expectedSrcType actualSrcType Nothing
44 Error.MismatchInfo hint expectedSrcType actualSrcType (Just reason)
47 Error.InfiniteType (Left hint) expectedSrcType
55 ExceptT Problem TS.Solver
60 { _orientation :: Orientation
62 , _firstDesc :: Descriptor
64 , _secondDesc :: Descriptor
68data Orientation = ExpectedActual | ActualExpected
71reorient :: Context -> Context
72reorient (Context orientation var1 desc1 var2 desc2) =
76 ExpectedActual -> ActualExpected
77 ActualExpected -> ExpectedActual
79 Context otherOrientation var2 desc2 var1 desc1
88 | Special Error.Reason
92mismatch :: Context -> Maybe Error.Reason -> Unify a
93mismatch context@(Context orientation var1 _ var2 _) maybeReason =
94 do anyInfinite <- liftIO $ liftM2 (||) (Occurs.occurs var1) (Occurs.occurs var2)
95 when anyInfinite $ throwError Infinite
97 args1 <- liftIO $ collectArgs var1
98 args2 <- liftIO $ collectArgs var2
100 let numArgs1 = length args1
101 let numArgs2 = length args2
103 if numArgs1 == numArgs2
105 throwError $ reasonToProblem orientation maybeReason
107 do lift $ zipWithM_ (\a1 a2 -> runExceptT $ subUnify context a1 a2) args1 args2
108 throwError $ reasonToProblem orientation $ Just $
109 Error.MissingArgs $ abs (numArgs2 - numArgs1)
112reasonToProblem :: Orientation -> Maybe Error.Reason -> Problem
113reasonToProblem orientation maybeReason =
124 Special (Error.flipReason reason)
127badRigid :: Maybe Text -> Error.Reason
129 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
132badSuper :: Super -> Error.Reason
134 Error.BadVar (Just (errorSuper super)) Nothing
137badSupers :: Super -> Super -> Error.Reason
138badSupers super1 super2 =
139 Error.BadVar (Just (errorSuper super1)) (Just (errorSuper super2))
142doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
144 Error.BadVar (Just vt1) (Just vt2)
147errorSuper :: Super -> Error.VarType
167merge :: Context -> Content -> Unify ()
168merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
169 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
172mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
173mergeHelp var1 var2 newContent newRank =
176 { _content = newContent
183fresh :: Context -> Content -> Unify Variable
184fresh (Context _ _ desc1 _ desc2) content =
189 , _rank = min (_rank desc1) (_rank desc2)
193 lift (TS.register freshVariable)
197-- ACTUALLY UNIFY THINGS
200guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
201guardedUnify orientation left right =
202 do equivalent <- liftIO $ UF.equivalent left right
206 do leftDesc <- liftIO $ UF.descriptor left
207 rightDesc <- liftIO $ UF.descriptor right
208 actuallyUnify (Context orientation left leftDesc right rightDesc)
211subUnify :: Context -> Variable -> Variable -> Unify ()
212subUnify context var1 var2 =
213 guardedUnify (_orientation context) var1 var2
216actuallyUnify :: Context -> Unify ()
217actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
219 firstContent = _content firstDesc
220 secondContent = _content secondDesc
224 -- If there was an error, just pretend it is okay. This lets us avoid
225 -- "cascading" errors where one problem manifests as multiple message.
228 Var Flex Nothing _ ->
229 unifyFlex context secondContent
231 Var Flex (Just super) _ ->
232 unifyFlexSuper context super firstContent secondContent
234 Var Rigid maybeSuper maybeName ->
235 unifyRigid context maybeSuper maybeName firstContent secondContent
237 Alias name args realVar ->
238 unifyAlias context name args realVar secondContent
241 unifyStructure context term firstContent secondContent
245-- UNIFY FLEXIBLE VARIABLES
248unifyFlex :: Context -> Content -> Unify ()
249unifyFlex context otherContent =
255 merge context otherContent
258 merge context otherContent
261 merge context otherContent
264 merge context otherContent
268-- UNIFY RIGID VARIABLES
271unifyRigid :: Context -> Maybe Super -> Maybe Text -> Content -> Content -> Unify ()
272unifyRigid context maybeSuper maybeName content otherContent =
277 Var Flex otherMaybeSuper _ ->
278 case (maybeSuper, otherMaybeSuper) of
280 merge context content
283 mismatch context (Just (badRigid maybeName))
285 (Just super, Just otherSuper) ->
286 if combineRigidSupers super otherSuper then
287 merge context content
289 mismatch context (Just (badRigid maybeName))
291 Var Rigid _ otherMaybeName ->
292 mismatch context $ Just $
293 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
296 mismatch context (Just (badRigid maybeName))
299 mismatch context (Just (badRigid maybeName))
303-- UNIFY SUPER VARIABLES
306unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
307unifyFlexSuper context super content otherContent =
310 unifyFlexSuperStructure context super term
312 Var Rigid Nothing maybeName ->
313 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
315 Var Rigid (Just otherSuper) maybeName ->
316 if combineRigidSupers otherSuper super then
317 merge context otherContent
319 mismatch context $ Just $ badRigid maybeName
321 Var Flex Nothing _ ->
322 merge context content
324 Var Flex (Just otherSuper) _ ->
328 Number -> merge context content
329 Comparable -> merge context content
330 _ -> mismatch context $ Just $ badSupers super otherSuper
334 Comparable -> merge context otherContent
335 Number -> merge context otherContent
336 Appendable -> merge context (Var Flex (Just CompAppend) Nothing)
337 CompAppend -> merge context otherContent
341 Appendable -> merge context otherContent
342 Comparable -> merge context (Var Flex (Just CompAppend) Nothing)
343 CompAppend -> merge context otherContent
344 Number -> mismatch context $ Just $ badSupers super otherSuper
348 Comparable -> merge context content
349 Appendable -> merge context content
350 CompAppend -> merge context content
351 Number -> mismatch context $ Just $ badSupers super otherSuper
354 subUnify context (_first context) realVar
360combineRigidSupers :: Super -> Super -> Bool
361combineRigidSupers rigid flex =
363 || (rigid == Number && flex == Comparable)
364 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
367atomMatchesSuper :: Super -> Var.Canonical -> Bool
368atomMatchesSuper super name =
371 elem name [ Var.int, Var.float ]
374 elem name [ Var.string, Var.int, Var.float, Var.char]
383unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
384unifyFlexSuperStructure context super term =
387 if atomMatchesSuper super name then
388 merge context (Structure term)
390 mismatch context (Just (badSuper super))
392 App1 name [variable] | name == Var.list ->
395 mismatch context (Just (badSuper super))
398 merge context (Structure term)
401 do comparableOccursCheck context
402 merge context (Structure term)
403 unifyComparableRecursive (_orientation context) variable
406 do comparableOccursCheck context
407 merge context (Structure term)
408 unifyComparableRecursive (_orientation context) variable
410 App1 name args | Var.isTuple name ->
413 mismatch context (Just (badSuper super))
416 mismatch context (Just (badSuper super))
419 if length args > 6 then
420 mismatch context (Just (Error.TooLongComparableTuple (length args)))
423 do comparableOccursCheck context
424 merge context (Structure term)
425 mapM_ (unifyComparableRecursive (_orientation context)) args
428 mismatch context (Just (badSuper super))
431 mismatch context (Just (badSuper super))
434-- TODO: is there some way to avoid doing this?
435-- Do type classes require occurs checks?
436comparableOccursCheck :: Context -> Unify ()
437comparableOccursCheck (Context _ _ _ var _) =
438 do hasOccurred <- liftIO $ Occurs.occurs var
440 then throwError Infinite
444unifyComparableRecursive :: Orientation -> Variable -> Unify ()
445unifyComparableRecursive orientation var =
448 do desc <- UF.descriptor var
451 { _content = Var Flex (Just Comparable) Nothing
457 guardedUnify orientation compVar var
460collectArgs :: Variable -> IO [Variable]
461collectArgs variable =
462 collectArgsHelp [] variable
465collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
466collectArgsHelp revArgs variable =
467 do content <- getContent variable
469 Structure (Fun1 arg returnType) ->
470 collectArgsHelp (arg : revArgs) returnType
473 return $ variable : revArgs
476getContent :: Variable -> IO Content
478 _content <$> UF.descriptor variable
485unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
486unifyAlias context name args realVar otherContent =
491 Var Flex Nothing _ ->
492 merge context (Alias name args realVar)
495 subUnify context realVar (_second context)
497 Alias otherName otherArgs otherRealVar ->
498 if name == otherName then
499 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
500 merge context otherContent
503 subUnify context realVar otherRealVar
506 subUnify context realVar (_second context)
513unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
514unifyStructure context term content otherContent =
519 Var Flex Nothing _ ->
520 merge context content
522 Var Flex (Just super) _ ->
523 unifyFlexSuperStructure (reorient context) super term
525 Var Rigid _ maybeName ->
526 mismatch context (Just (Error.flipReason (badRigid maybeName)))
529 subUnify context (_first context) realVar
531 Structure otherTerm ->
532 case (term, otherTerm) of
533 (App1 name args, App1 otherName otherArgs) ->
534 if name == otherName then
535 do zipWithM_ (subUnify context) args otherArgs
536 merge context otherContent
538 else if isIntFloat name otherName || isIntFloat otherName name then
539 mismatch context (Just Error.IntFloat)
542 mismatch context Nothing
544 (Fun1 arg result, Fun1 otherArg otherResult) ->
545 do subUnify context arg otherArg
546 subUnify context result otherResult
547 merge context otherContent
549 (EmptyRecord1, EmptyRecord1) ->
550 merge context otherContent
552 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
553 subUnify context ext (_second context)
555 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
556 subUnify context (_first context) ext
558 (Record1 fields extension, Record1 otherFields otherExtension) ->
559 do firstStructure <- liftIO $ gatherFields context fields extension
560 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
561 unifyRecord context firstStructure secondStructure
564 mismatch context Nothing
567isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
568isIntFloat name otherName =
569 Var.isPrim "Int" name && Var.isPrim "Float" otherName
576unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
577unifyRecord context firstStructure secondStructure =
578 do let (RecordStructure expFields expVar expStruct) = firstStructure
579 let (RecordStructure actFields actVar actStruct) = secondStructure
581 let sharedFields = Map.intersectionWith (,) expFields actFields
583 -- call after unifying extension, make sure record shape matches before
584 -- looking into whether the particular field types match.
585 let unifySharedFields otherFields ext =
586 do unifySharedFieldsHelp context sharedFields
587 let allFields = Map.union (Map.map fst sharedFields) otherFields
588 merge context (Structure (Record1 allFields ext))
590 let uniqueExpFields = Map.difference expFields actFields
591 let uniqueActFields = Map.difference actFields expFields
593 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
594 (_, True, _, True) ->
595 do subUnify context expVar actVar
596 unifySharedFields Map.empty expVar
598 (Empty, _, _, False) ->
599 mismatch context $ Just $
600 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
602 (_, False, Empty, _) ->
603 mismatch context $ Just $
604 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
606 (_, False, _, True) ->
607 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
608 subUnify context subRecord actVar
609 unifySharedFields Map.empty subRecord
611 (_, True, _, False) ->
612 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
613 subUnify context expVar subRecord
614 unifySharedFields Map.empty subRecord
616 (Extension, False, Extension, False) ->
617 do let subFields = Map.union uniqueExpFields uniqueActFields
618 subExt <- fresh context (Var Flex Nothing Nothing)
619 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
620 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
621 subUnify context expVar expRecord
622 subUnify context actRecord actVar
623 unifySharedFields subFields subExt
627unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
628unifySharedFieldsHelp context sharedFields =
629 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
630 case Maybe.catMaybes maybeBadFields of
635 mismatch context (Just (Error.BadFields (reverse badFields)))
638unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
639unifyField context (field, (expected, actual)) =
640 do result <- lift $ runExceptT $ subUnify context expected actual
646 return $ Just $ (,) field $
659-- GATHER RECORD STRUCTURE
662data RecordStructure = RecordStructure
663 { _fields :: Map.Map Text Variable
664 , _extVar :: Variable
665 , _extStruct :: ExtensionStructure
669data ExtensionStructure
674gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
675gatherFields context fields variable =
676 do desc <- UF.descriptor variable
677 case _content desc of
678 Structure (Record1 subFields subExt) ->
679 gatherFields context (Map.union fields subFields) subExt
681 Structure EmptyRecord1 ->
682 return (RecordStructure fields variable Empty)
685 -- TODO may be dropping useful alias info here
686 gatherFields context fields var
689 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