1{-# OPTIONS_GHC -Wall #-}
2module Type.Unify (unify) where
4import Control.Monad (zipWithM_)
5import Control.Monad.Except (ExceptT, lift, liftIO, throwError, runExceptT)
6import qualified Data.Map as Map
7import qualified Data.UnionFind.IO as UF
9import qualified AST.Variable as Var
10import qualified Reporting.Region as R
11import qualified Reporting.Error.Type as Error
12import qualified Type.State as TS
13import Type.Type as Type
17-- KICK OFF UNIFICATION
20unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
21unify hint region expected actual =
22 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
27 Left (Mismatch maybeReason) ->
30 do expectedSrcType <- Type.toSrcType expected
31 actualSrcType <- Type.toSrcType actual
32 mergeHelp expected actual Error
33 let info = Error.MismatchInfo hint expectedSrcType actualSrcType maybeReason
34 return (Error.Mismatch info)
36 TS.addError region =<< liftIO mkError
44 ExceptT Mismatch TS.Solver
48 { _orientation :: Orientation
50 , _firstDesc :: Descriptor
52 , _secondDesc :: Descriptor
56data Orientation = ExpectedActual | ActualExpected
59reorient :: Context -> Context
60reorient (Context orientation var1 desc1 var2 desc2) =
64 ExpectedActual -> ActualExpected
65 ActualExpected -> ExpectedActual
67 Context otherOrientation var2 desc2 var1 desc1
75 Mismatch (Maybe Error.Reason)
78mismatch :: Context -> Maybe Error.Reason -> Unify a
79mismatch (Context orientation var1 _ var2 _) maybeReason =
80 do numArgs1 <- liftIO $ countArgs var1
81 numArgs2 <- liftIO $ countArgs var2
82 mismatchHelp orientation $
83 if numArgs1 == numArgs2 then
86 Just (Error.MissingArgs (abs (numArgs2 - numArgs1)))
89mismatchHelp :: Orientation -> Maybe Error.Reason -> Unify a
90mismatchHelp orientation maybeReason =
91 throwError $ Mismatch $
97 Error.flipReason <$> maybeReason
100badRigid :: Maybe String -> Error.Reason
102 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
105badSuper :: Super -> Error.Reason
107 Error.BadVar (Just (errorSuper super)) Nothing
110doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
112 Error.BadVar (Just vt1) (Just vt2)
115errorSuper :: Super -> Error.VarType
135merge :: Context -> Content -> Unify ()
136merge (Context _ first _ second _) content =
137 liftIO $ mergeHelp first second content
140mergeHelp :: Variable -> Variable -> Content -> IO ()
141mergeHelp first second content =
142 UF.union' first second $ \desc1 desc2 ->
146 , _rank = min (_rank desc1) (_rank desc2)
152fresh :: Context -> Content -> Unify Variable
153fresh (Context _ _ desc1 _ desc2) content =
158 , _rank = min (_rank desc1) (_rank desc2)
162 lift (TS.register freshVariable)
166-- ACTUALLY UNIFY THINGS
169guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
170guardedUnify orientation left right =
171 do equivalent <- liftIO $ UF.equivalent left right
175 do leftDesc <- liftIO $ UF.descriptor left
176 rightDesc <- liftIO $ UF.descriptor right
177 actuallyUnify (Context orientation left leftDesc right rightDesc)
180subUnify :: Context -> Variable -> Variable -> Unify ()
181subUnify context var1 var2 =
182 guardedUnify (_orientation context) var1 var2
185actuallyUnify :: Context -> Unify ()
186actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
188 secondContent = _content secondDesc
190 case _content firstDesc of
192 -- If there was an error, just pretend it is okay. This lets us avoid
193 -- "cascading" errors where one problem manifests as multiple message.
196 Var Flex Nothing _ ->
197 unifyFlex context secondContent
199 Var Flex (Just super) _ ->
200 unifySuper context super secondContent
202 Var Rigid maybeSuper maybeName ->
203 unifyRigid context maybeSuper maybeName secondContent
206 unifyAtom context name secondContent
208 Alias name args realVar ->
209 unifyAlias context name args realVar secondContent
212 unifyStructure context term secondContent
216-- UNIFY FLEXIBLE VARIABLES
219unifyFlex :: Context -> Content -> Unify ()
220unifyFlex context otherContent =
226 merge context otherContent
229 merge context otherContent
232 merge context otherContent
235 merge context otherContent
238 merge context otherContent
242-- UNIFY RIGID VARIABLES
245unifyRigid :: Context -> Maybe Super -> Maybe String -> Content -> Unify ()
246unifyRigid context maybeSuper maybeName otherContent =
251 Var Flex otherMaybeSuper _ ->
252 case (maybeSuper, otherMaybeSuper) of
254 merge context (Var Rigid maybeSuper maybeName)
257 mismatch context (Just (badRigid maybeName))
259 (Just super, Just otherSuper) ->
260 case combineSupers super otherSuper of
261 Right newSuper | newSuper == otherSuper ->
262 merge context otherContent
265 mismatch context (Just (badRigid maybeName))
267 Var Rigid _ otherMaybeName ->
268 mismatch context $ Just $
269 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
272 mismatch context (Just (badRigid maybeName))
275 mismatch context (Just (badRigid maybeName))
278 mismatch context (Just (badRigid maybeName))
282-- UNIFY SUPER VARIABLES
285unifySuper :: Context -> Super -> Content -> Unify ()
286unifySuper context super otherContent =
289 unifySuperStructure context super term
292 if atomMatchesSuper super name then
293 merge context otherContent
295 mismatch context (Just (badSuper super))
297 Var Rigid Nothing maybeName ->
298 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
300 Var Rigid (Just otherSuper) maybeName ->
301 case combineSupers super otherSuper of
302 Right newSuper | newSuper == super ->
303 merge context otherContent
306 mismatch context $ Just $
307 doubleBad (errorSuper super) (Error.Rigid maybeName)
309 Var Flex Nothing _ ->
310 merge context (Var Flex (Just super) Nothing)
312 Var Flex (Just otherSuper) _ ->
313 case combineSupers super otherSuper of
315 mismatch context (Just reason)
318 merge context (Var Flex (Just newSuper) Nothing)
321 subUnify context (_first context) realVar
327combineSupers :: Super -> Super -> Either Error.Reason Super
328combineSupers firstSuper secondSuper =
329 case (firstSuper, secondSuper) of
330 (Number , Number ) -> Right Number
331 (Comparable, Number ) -> Right Number
332 (Number , Comparable) -> Right Number
334 (Comparable, Comparable) -> Right Comparable
335 (Appendable, Appendable) -> Right Appendable
337 (Appendable, Comparable) -> Right CompAppend
338 (Comparable, Appendable) -> Right CompAppend
340 (CompAppend, CompAppend) -> Right CompAppend
341 (CompAppend, Comparable) -> Right CompAppend
342 (Comparable, CompAppend) -> Right CompAppend
343 (CompAppend, Appendable) -> Right CompAppend
344 (Appendable, CompAppend) -> Right CompAppend
347 Left $ doubleBad (errorSuper firstSuper) (errorSuper secondSuper)
350isPrimitiveFrom :: [String] -> Var.Canonical -> Bool
351isPrimitiveFrom prims var =
352 any (\p -> Var.isPrim p var) prims
355atomMatchesSuper :: Super -> Var.Canonical -> Bool
356atomMatchesSuper super name =
359 isPrimitiveFrom ["Int", "Float"] name
362 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
365 Var.isPrim "String" name
368 Var.isPrim "String" name
371unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
372unifySuperStructure context super term =
373 do appStructure <- liftIO (collectApps (Structure term))
376 mismatch context (Just (badSuper super))
381 mismatch context (Just (badSuper super))
384 merge context (Structure term)
387 do merge context (Structure term)
388 unifyComparableRecursive (_orientation context) variable
391 do merge context (Structure term)
392 unifyComparableRecursive (_orientation context) variable
397 mismatch context (Just (badSuper super))
400 mismatch context (Just (badSuper super))
403 if length entries > 6 then
404 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
407 do merge context (Structure term)
408 mapM_ (unifyComparableRecursive (_orientation context)) entries
411 mismatch context (Just (badSuper super))
414unifyComparableRecursive :: Orientation -> Variable -> Unify ()
415unifyComparableRecursive orientation var =
418 do desc <- UF.descriptor var
421 { _content = Var Flex (Just Comparable) Nothing
427 guardedUnify orientation compVar var
436collectApps :: Content -> IO AppStructure
438 collectAppsHelp [] content
441collectAppsHelp :: [Variable] -> Content -> IO AppStructure
442collectAppsHelp args content =
443 case (content, args) of
444 (Structure (App1 func arg), _) ->
445 collectAppsHelp (args ++ [arg]) =<< getContent func
447 (Atom name, [arg]) | Var.isList name ->
450 (Atom name, _) | Var.isTuple name ->
457countArgs :: Variable -> IO Int
459 do content <- getContent variable
461 Structure (Fun1 _ returnType) ->
462 (+1) <$> countArgs returnType
468getContent :: Variable -> IO Content
470 _content <$> UF.descriptor variable
477unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
478unifyAtom context name otherContent =
483 Var Flex Nothing _ ->
484 merge context (Atom name)
486 Var Flex (Just super) _ ->
487 if atomMatchesSuper super name then
488 merge context (Atom name)
491 mismatch context (Just ((Error.flipReason (badSuper super))))
493 Var Rigid _ maybeName ->
494 mismatch context (Just (Error.flipReason (badRigid maybeName)))
497 if name == otherName then
498 merge context otherContent
502 if isIntFloat name otherName || isIntFloat otherName name then
508 subUnify context (_first context) realVar
511 mismatch context Nothing
514isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
515isIntFloat name otherName =
516 Var.isPrim "Int" name && Var.isPrim "Float" otherName
523unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
524unifyAlias context name args realVar otherContent =
529 Var Flex Nothing _ ->
530 merge context (Alias name args realVar)
533 subUnify context realVar (_second context)
536 subUnify context realVar (_second context)
539 Alias otherName otherArgs otherRealVar ->
540 if name == otherName then
541 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
542 merge context otherContent
545 subUnify context realVar otherRealVar
548 subUnify context realVar (_second context)
555unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
556unifyStructure context term otherContent =
561 Var Flex Nothing _ ->
562 merge context (Structure term)
564 Var Flex (Just super) _ ->
565 unifySuper (reorient context) super (Structure term)
567 Var Rigid _ maybeName ->
568 mismatch context (Just (Error.flipReason (badRigid maybeName)))
571 mismatch context Nothing
574 subUnify context (_first context) realVar
576 Structure otherTerm ->
577 case (term, otherTerm) of
578 (App1 func arg, App1 otherFunc otherArg) ->
579 do subUnify context func otherFunc
580 subUnify context arg otherArg
581 merge context otherContent
583 (Fun1 arg result, Fun1 otherArg otherResult) ->
584 do subUnify context arg otherArg
585 subUnify context result otherResult
586 merge context otherContent
588 (EmptyRecord1, EmptyRecord1) ->
589 merge context otherContent
591 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
592 subUnify context ext (_second context)
594 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
595 subUnify context (_first context) ext
597 (Record1 fields extension, Record1 otherFields otherExtension) ->
598 do firstStructure <- gatherFields context fields extension
599 secondStructure <- gatherFields context otherFields otherExtension
600 unifyRecord context firstStructure secondStructure
603 mismatch context Nothing
610unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
611unifyRecord context firstStructure secondStructure =
612 do let (RecordStructure expFields expVar expStruct) = firstStructure
613 let (RecordStructure actFields actVar actStruct) = secondStructure
615 -- call after unifying extension, make sure record shape matches before
616 -- looking into whether the particular field types match.
617 let unifySharedFields otherFields ext =
618 do let sharedFields = Map.intersectionWith (,) expFields actFields
619 _ <- traverse (uncurry (subUnify context)) sharedFields
620 let allFields = Map.union (Map.map fst sharedFields) otherFields
621 merge context (Structure (Record1 allFields ext))
623 let uniqueExpFields = Map.difference expFields actFields
624 let uniqueActFields = Map.difference actFields expFields
626 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
627 (_, True, _, True) ->
628 do subUnify context expVar actVar
629 unifySharedFields Map.empty expVar
631 (Empty, _, _, False) ->
632 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
634 (_, False, Empty, _) ->
635 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
637 (_, False, _, True) ->
638 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
639 subUnify context subRecord actVar
640 unifySharedFields Map.empty subRecord
642 (_, True, _, False) ->
643 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
644 subUnify context expVar subRecord
645 unifySharedFields Map.empty subRecord
647 (Extension, False, Extension, False) ->
648 do let subFields = Map.union uniqueExpFields uniqueActFields
649 subExt <- fresh context (Var Flex Nothing Nothing)
650 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
651 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
652 subUnify context expVar expRecord
653 subUnify context actRecord actVar
654 unifySharedFields subFields subExt
658-- GATHER RECORD STRUCTURE
661data RecordStructure = RecordStructure
662 { _fields :: Map.Map String Variable
663 , _extVar :: Variable
664 , _extStruct :: ExtensionStructure
668data ExtensionStructure
673gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
674gatherFields context fields variable =
675 do desc <- liftIO (UF.descriptor variable)
676 case _content desc of
677 Structure (Record1 subFields subExt) ->
678 gatherFields context (Map.union fields subFields) subExt
680 Structure EmptyRecord1 ->
681 return (RecordStructure fields variable Empty)
684 -- TODO may be dropping useful alias info here
685 gatherFields context fields var
688 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