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
9import qualified Data.Text as Text
10import Data.Text (Text)
11import qualified Data.UnionFind.IO as UF
13import qualified AST.Variable as Var
14import qualified Reporting.Region as R
15import qualified Reporting.Error.Type as Error
16import qualified Type.State as TS
17import Type.Type as Type
21-- KICK OFF UNIFICATION
24unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
25unify hint region expected actual =
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
119doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
121 Error.BadVar (Just vt1) (Just vt2)
124errorSuper :: Super -> Error.VarType
144merge :: Context -> Content -> Unify ()
145merge (Context _ first _ second _) content =
146 liftIO $ mergeHelp first second content
149mergeHelp :: Variable -> Variable -> Content -> IO ()
150mergeHelp first second content =
151 UF.union' first second $ \desc1 desc2 ->
155 , _rank = min (_rank desc1) (_rank desc2)
161fresh :: Context -> Content -> Unify Variable
162fresh (Context _ _ desc1 _ desc2) content =
167 , _rank = min (_rank desc1) (_rank desc2)
171 lift (TS.register freshVariable)
175-- ACTUALLY UNIFY THINGS
178guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
179guardedUnify orientation left right =
180 do equivalent <- liftIO $ UF.equivalent left right
184 do leftDesc <- liftIO $ UF.descriptor left
185 rightDesc <- liftIO $ UF.descriptor right
186 actuallyUnify (Context orientation left leftDesc right rightDesc)
189subUnify :: Context -> Variable -> Variable -> Unify ()
190subUnify context var1 var2 =
191 guardedUnify (_orientation context) var1 var2
194actuallyUnify :: Context -> Unify ()
195actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
197 secondContent = _content secondDesc
199 case _content firstDesc of
201 -- If there was an error, just pretend it is okay. This lets us avoid
202 -- "cascading" errors where one problem manifests as multiple message.
205 Var Flex Nothing _ ->
206 unifyFlex context secondContent
208 Var Flex (Just super) _ ->
209 unifySuper context super secondContent
211 Var Rigid maybeSuper maybeName ->
212 unifyRigid context maybeSuper maybeName secondContent
215 unifyAtom context name secondContent
217 Alias name args realVar ->
218 unifyAlias context name args realVar secondContent
221 unifyStructure context term secondContent
225-- UNIFY FLEXIBLE VARIABLES
228unifyFlex :: Context -> Content -> Unify ()
229unifyFlex context otherContent =
235 merge context otherContent
238 merge context otherContent
241 merge context otherContent
244 merge context otherContent
247 merge context otherContent
251-- UNIFY RIGID VARIABLES
254unifyRigid :: Context -> Maybe Super -> Maybe Text -> Content -> Unify ()
255unifyRigid context maybeSuper maybeName otherContent =
260 Var Flex otherMaybeSuper _ ->
261 case (maybeSuper, otherMaybeSuper) of
263 merge context (Var Rigid maybeSuper maybeName)
266 mismatch context (Just (badRigid maybeName))
268 (Just super, Just otherSuper) ->
269 case combineSupers super otherSuper of
270 Right newSuper | newSuper == otherSuper ->
271 merge context otherContent
274 mismatch context (Just (badRigid maybeName))
276 Var Rigid _ otherMaybeName ->
277 mismatch context $ Just $
278 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
281 mismatch context (Just (badRigid maybeName))
284 mismatch context (Just (badRigid maybeName))
287 mismatch context (Just (badRigid maybeName))
291-- UNIFY SUPER VARIABLES
294unifySuper :: Context -> Super -> Content -> Unify ()
295unifySuper context super otherContent =
298 unifySuperStructure context super term
301 if atomMatchesSuper super name then
302 merge context otherContent
304 mismatch context (Just (badSuper super))
306 Var Rigid Nothing maybeName ->
307 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
309 Var Rigid (Just otherSuper) maybeName ->
310 case combineSupers super otherSuper of
311 Right newSuper | newSuper == super ->
312 merge context otherContent
315 mismatch context $ Just $
316 doubleBad (errorSuper super) (Error.Rigid maybeName)
318 Var Flex Nothing _ ->
319 merge context (Var Flex (Just super) Nothing)
321 Var Flex (Just otherSuper) _ ->
322 case combineSupers super otherSuper of
324 mismatch context (Just reason)
327 merge context (Var Flex (Just newSuper) Nothing)
330 subUnify context (_first context) realVar
336combineSupers :: Super -> Super -> Either Error.Reason Super
337combineSupers firstSuper secondSuper =
338 case (firstSuper, secondSuper) of
339 (Number , Number ) -> Right Number
340 (Comparable, Number ) -> Right Number
341 (Number , Comparable) -> Right Number
343 (Comparable, Comparable) -> Right Comparable
344 (Appendable, Appendable) -> Right Appendable
346 (Appendable, Comparable) -> Right CompAppend
347 (Comparable, Appendable) -> Right CompAppend
349 (CompAppend, CompAppend) -> Right CompAppend
350 (CompAppend, Comparable) -> Right CompAppend
351 (Comparable, CompAppend) -> Right CompAppend
352 (CompAppend, Appendable) -> Right CompAppend
353 (Appendable, CompAppend) -> Right CompAppend
356 Left $ doubleBad (errorSuper firstSuper) (errorSuper secondSuper)
359isPrimitiveFrom :: [Text] -> Var.Canonical -> Bool
360isPrimitiveFrom prims var =
361 any (\p -> Var.isPrim p var) prims
364atomMatchesSuper :: Super -> Var.Canonical -> Bool
365atomMatchesSuper super name =
368 isPrimitiveFrom ["Int", "Float"] name
371 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
374 Var.isPrim "String" name
377 Var.isPrim "String" name
380unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
381unifySuperStructure context super term =
382 do appStructure <- liftIO (collectApps (Structure term))
385 mismatch context (Just (badSuper super))
390 mismatch context (Just (badSuper super))
393 merge context (Structure term)
396 do merge context (Structure term)
397 unifyComparableRecursive (_orientation context) variable
400 do merge context (Structure term)
401 unifyComparableRecursive (_orientation context) variable
406 mismatch context (Just (badSuper super))
409 mismatch context (Just (badSuper super))
412 if length entries > 6 then
413 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
416 do merge context (Structure term)
417 mapM_ (unifyComparableRecursive (_orientation context)) entries
420 mismatch context (Just (badSuper super))
423unifyComparableRecursive :: Orientation -> Variable -> Unify ()
424unifyComparableRecursive orientation var =
427 do desc <- UF.descriptor var
430 { _content = Var Flex (Just Comparable) Nothing
436 guardedUnify orientation compVar var
445collectApps :: Content -> IO AppStructure
447 collectAppsHelp [] content
450collectAppsHelp :: [Variable] -> Content -> IO AppStructure
451collectAppsHelp args content =
452 case (content, args) of
453 (Structure (App1 func arg), _) ->
454 collectAppsHelp (args ++ [arg]) =<< getContent func
456 (Atom name, [arg]) | Var.isList name ->
459 (Atom name, _) | Var.isTuple name ->
466collectArgs :: Variable -> IO [Variable]
467collectArgs variable =
468 collectArgsHelp [] variable
471collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
472collectArgsHelp revArgs variable =
473 do content <- getContent variable
475 Structure (Fun1 arg returnType) ->
476 collectArgsHelp (arg : revArgs) returnType
479 return $ variable : revArgs
482getContent :: Variable -> IO Content
484 _content <$> UF.descriptor variable
491unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
492unifyAtom context name otherContent =
497 Var Flex Nothing _ ->
498 merge context (Atom name)
500 Var Flex (Just super) _ ->
501 if atomMatchesSuper super name then
502 merge context (Atom name)
505 mismatch context (Just ((Error.flipReason (badSuper super))))
507 Var Rigid _ maybeName ->
508 mismatch context (Just (Error.flipReason (badRigid maybeName)))
511 if name == otherName then
512 merge context otherContent
516 if isIntFloat name otherName || isIntFloat otherName name then
522 subUnify context (_first context) realVar
525 mismatch context Nothing
528isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
529isIntFloat name otherName =
530 Var.isPrim "Int" name && Var.isPrim "Float" otherName
537unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
538unifyAlias context name args realVar otherContent =
543 Var Flex Nothing _ ->
544 merge context (Alias name args realVar)
547 subUnify context realVar (_second context)
550 subUnify context realVar (_second context)
552 Alias otherName otherArgs otherRealVar ->
553 if name == otherName then
554 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
555 merge context otherContent
558 subUnify context realVar otherRealVar
561 subUnify context realVar (_second context)
568unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
569unifyStructure context term otherContent =
574 Var Flex Nothing _ ->
575 merge context (Structure term)
577 Var Flex (Just super) _ ->
578 unifySuper (reorient context) super (Structure term)
580 Var Rigid _ maybeName ->
581 mismatch context (Just (Error.flipReason (badRigid maybeName)))
584 mismatch context Nothing
587 subUnify context (_first context) realVar
589 Structure otherTerm ->
590 case (term, otherTerm) of
591 (App1 func arg, App1 otherFunc otherArg) ->
592 do subUnify context func otherFunc
593 subUnify context arg otherArg
594 merge context otherContent
596 (Fun1 arg result, Fun1 otherArg otherResult) ->
597 do subUnify context arg otherArg
598 subUnify context result otherResult
599 merge context otherContent
601 (EmptyRecord1, EmptyRecord1) ->
602 merge context otherContent
604 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
605 subUnify context ext (_second context)
607 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
608 subUnify context (_first context) ext
610 (Record1 fields extension, Record1 otherFields otherExtension) ->
611 do firstStructure <- gatherFields context fields extension
612 secondStructure <- gatherFields context otherFields otherExtension
613 unifyRecord context firstStructure secondStructure
616 mismatch context Nothing
623unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
624unifyRecord context firstStructure secondStructure =
625 do let (RecordStructure expFields expVar expStruct) = firstStructure
626 let (RecordStructure actFields actVar actStruct) = secondStructure
628 let sharedFields = Map.intersectionWith (,) expFields actFields
630 -- call after unifying extension, make sure record shape matches before
631 -- looking into whether the particular field types match.
632 let unifySharedFields otherFields ext =
633 do unifySharedFieldsHelp context sharedFields
634 let allFields = Map.union (Map.map fst sharedFields) otherFields
635 merge context (Structure (Record1 allFields ext))
637 let uniqueExpFields = Map.difference expFields actFields
638 let uniqueActFields = Map.difference actFields expFields
640 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
641 (_, True, _, True) ->
642 do subUnify context expVar actVar
643 unifySharedFields Map.empty expVar
645 (Empty, _, _, False) ->
646 mismatch context $ Just $
647 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
649 (_, False, Empty, _) ->
650 mismatch context $ Just $
651 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
653 (_, False, _, True) ->
654 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
655 subUnify context subRecord actVar
656 unifySharedFields Map.empty subRecord
658 (_, True, _, False) ->
659 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
660 subUnify context expVar subRecord
661 unifySharedFields Map.empty subRecord
663 (Extension, False, Extension, False) ->
664 do let subFields = Map.union uniqueExpFields uniqueActFields
665 subExt <- fresh context (Var Flex Nothing Nothing)
666 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
667 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
668 subUnify context expVar expRecord
669 subUnify context actRecord actVar
670 unifySharedFields subFields subExt
674unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
675unifySharedFieldsHelp context sharedFields =
676 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
677 case Maybe.catMaybes maybeBadFields of
682 mismatch context (Just (Error.BadFields (reverse badFields)))
685unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (String, Maybe Error.Reason))
686unifyField context (field, (expected, actual)) =
687 do result <- lift $ runExceptT $ subUnify context expected actual
692 Left (Mismatch maybeReason) ->
693 return $ Just (Text.unpack field, maybeReason)
697-- GATHER RECORD STRUCTURE
700data RecordStructure = RecordStructure
701 { _fields :: Map.Map Text Variable
702 , _extVar :: Variable
703 , _extStruct :: ExtensionStructure
707data ExtensionStructure
712gatherFields :: Context -> Map.Map Text Variable -> Variable -> Unify RecordStructure
713gatherFields context fields variable =
714 do desc <- liftIO (UF.descriptor variable)
715 case _content desc of
716 Structure (Record1 subFields subExt) ->
717 gatherFields context (Map.union fields subFields) subExt
719 Structure EmptyRecord1 ->
720 return (RecordStructure fields variable Empty)
723 -- TODO may be dropping useful alias info here
724 gatherFields context fields var
727 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