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.Set as Set
8import qualified Data.UnionFind.IO as UF
10import qualified AST.Variable as Var
11import qualified Reporting.Region as R
12import qualified Reporting.Error.Type as Error
13import qualified Type.State as TS
14import Type.Type as Type
18-- KICK OFF UNIFICATION
21unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
22unify hint region expected actual =
23 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
28 Left (Mismatch _subExpected _subActual maybeReason) ->
31 do expectedSrcType <- Type.toSrcType expected
32 actualSrcType <- Type.toSrcType actual
33 mergeHelp expected actual Error
34 let info = Error.MismatchInfo hint expectedSrcType actualSrcType maybeReason
35 return (Error.Mismatch info)
37 TS.addError region =<< liftIO mkError
45 ExceptT Mismatch TS.Solver
49 { _orientation :: Orientation
51 , _firstDesc :: Descriptor
53 , _secondDesc :: Descriptor
57data Orientation = ExpectedActual | ActualExpected
60reorient :: Context -> Context
61reorient (Context orientation var1 desc1 var2 desc2) =
65 ExpectedActual -> ActualExpected
66 ActualExpected -> ExpectedActual
68 Context otherOrientation var2 desc2 var1 desc1
76 = Mismatch Variable Variable (Maybe Error.Reason)
79mismatch :: Context -> Maybe Error.Reason -> Unify a
80mismatch (Context orientation first _ second _) maybeReason =
82 (expected, actual, orientedReason) =
85 (first, second, maybeReason)
88 (second, first, Error.flipReason <$> maybeReason)
90 throwError (Mismatch expected actual orientedReason)
93badRigid :: Maybe String -> Error.Reason
95 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
98badSuper :: Super -> Error.Reason
100 Error.BadVar (Just (errorSuper super)) Nothing
103doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
105 Error.BadVar (Just vt1) (Just vt2)
108errorSuper :: Super -> Error.VarType
128merge :: Context -> Content -> Unify ()
129merge (Context _ first _ second _) content =
130 liftIO $ mergeHelp first second content
133mergeHelp :: Variable -> Variable -> Content -> IO ()
134mergeHelp first second content =
135 UF.union' first second $ \desc1 desc2 ->
139 , _rank = min (_rank desc1) (_rank desc2)
145fresh :: Context -> Content -> Unify Variable
146fresh (Context _ _ desc1 _ desc2) content =
151 , _rank = min (_rank desc1) (_rank desc2)
155 lift (TS.register freshVariable)
159-- ACTUALLY UNIFY THINGS
162guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
163guardedUnify orientation left right =
164 do equivalent <- liftIO $ UF.equivalent left right
168 do leftDesc <- liftIO $ UF.descriptor left
169 rightDesc <- liftIO $ UF.descriptor right
170 actuallyUnify (Context orientation left leftDesc right rightDesc)
173subUnify :: Context -> Variable -> Variable -> Unify ()
174subUnify context var1 var2 =
175 guardedUnify (_orientation context) var1 var2
178actuallyUnify :: Context -> Unify ()
179actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
181 secondContent = _content secondDesc
183 case _content firstDesc of
185 -- If there was an error, just pretend it is okay. This lets us avoid
186 -- "cascading" errors where one problem manifests as multiple message.
189 Var Flex Nothing _ ->
190 unifyFlex context secondContent
192 Var Flex (Just super) _ ->
193 unifySuper context super secondContent
195 Var Rigid maybeSuper maybeName ->
196 unifyRigid context maybeSuper maybeName secondContent
199 unifyAtom context name secondContent
201 Alias name args realVar ->
202 unifyAlias context name args realVar secondContent
205 unifyStructure context term secondContent
209-- UNIFY FLEXIBLE VARIABLES
212unifyFlex :: Context -> Content -> Unify ()
213unifyFlex context otherContent =
219 merge context otherContent
222 merge context otherContent
225 merge context otherContent
228 merge context otherContent
231 merge context otherContent
235-- UNIFY RIGID VARIABLES
238unifyRigid :: Context -> Maybe Super -> Maybe String -> Content -> Unify ()
239unifyRigid context maybeSuper maybeName otherContent =
244 Var Flex otherMaybeSuper _ ->
245 case (maybeSuper, otherMaybeSuper) of
247 merge context (Var Rigid maybeSuper maybeName)
250 mismatch context (Just (badRigid maybeName))
252 (Just super, Just otherSuper) ->
253 case combineSupers super otherSuper of
254 Right newSuper | newSuper == otherSuper ->
255 merge context otherContent
258 mismatch context (Just (badRigid maybeName))
260 Var Rigid _ otherMaybeName ->
261 mismatch context $ Just $
262 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
265 mismatch context (Just (badRigid maybeName))
268 mismatch context (Just (badRigid maybeName))
271 mismatch context (Just (badRigid maybeName))
274-- UNIFY SUPER VARIABLES
277unifySuper :: Context -> Super -> Content -> Unify ()
278unifySuper context super otherContent =
281 unifySuperStructure context super term
284 if atomMatchesSuper super name then
285 merge context otherContent
287 mismatch context (Just (badSuper super))
289 Var Rigid Nothing maybeName ->
290 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
292 Var Rigid (Just otherSuper) maybeName ->
293 case combineSupers super otherSuper of
294 Right newSuper | newSuper == super ->
295 merge context otherContent
298 mismatch context $ Just $
299 doubleBad (errorSuper super) (Error.Rigid maybeName)
301 Var Flex Nothing _ ->
302 merge context (Var Flex (Just super) Nothing)
304 Var Flex (Just otherSuper) _ ->
305 case combineSupers super otherSuper of
307 mismatch context (Just reason)
310 merge context (Var Flex (Just newSuper) Nothing)
313 subUnify context (_first context) realVar
319combineSupers :: Super -> Super -> Either Error.Reason Super
320combineSupers firstSuper secondSuper =
321 case (firstSuper, secondSuper) of
322 (Number , Number ) -> Right Number
323 (Comparable, Number ) -> Right Number
324 (Number , Comparable) -> Right Number
326 (Comparable, Comparable) -> Right Comparable
327 (Appendable, Appendable) -> Right Appendable
329 (Appendable, Comparable) -> Right CompAppend
330 (Comparable, Appendable) -> Right CompAppend
332 (CompAppend, CompAppend) -> Right CompAppend
333 (CompAppend, Comparable) -> Right CompAppend
334 (Comparable, CompAppend) -> Right CompAppend
335 (CompAppend, Appendable) -> Right CompAppend
336 (Appendable, CompAppend) -> Right CompAppend
339 Left $ doubleBad (errorSuper firstSuper) (errorSuper secondSuper)
342isPrimitiveFrom :: [String] -> Var.Canonical -> Bool
343isPrimitiveFrom prims var =
344 any (\p -> Var.isPrim p var) prims
347atomMatchesSuper :: Super -> Var.Canonical -> Bool
348atomMatchesSuper super name =
351 isPrimitiveFrom ["Int", "Float"] name
354 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
357 Var.isPrim "String" name || Var.isText name
360 Var.isPrim "String" name
363unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
364unifySuperStructure context super term =
365 do appStructure <- liftIO (collectApps (Structure term))
368 mismatch context (Just (badSuper super))
373 mismatch context (Just (badSuper super))
376 merge context (Structure term)
379 do merge context (Structure term)
380 unifyComparableRecursive (_orientation context) variable
383 do merge context (Structure term)
384 unifyComparableRecursive (_orientation context) variable
389 mismatch context (Just (badSuper super))
392 mismatch context (Just (badSuper super))
395 if length entries > 6 then
396 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
399 do merge context (Structure term)
400 mapM_ (unifyComparableRecursive (_orientation context)) entries
403 mismatch context (Just (badSuper super))
406unifyComparableRecursive :: Orientation -> Variable -> Unify ()
407unifyComparableRecursive orientation var =
410 do desc <- UF.descriptor var
413 { _content = Var Flex (Just Comparable) Nothing
419 guardedUnify orientation compVar var
428collectApps :: Content -> IO AppStructure
430 collectAppsHelp [] content
433collectAppsHelp :: [Variable] -> Content -> IO AppStructure
434collectAppsHelp args content =
435 case (content, args) of
436 (Structure (App1 func arg), _) ->
437 collectAppsHelp (args ++ [arg]) =<< getContent func
439 (Atom name, [arg]) | Var.isList name ->
442 (Atom name, _) | Var.isTuple name ->
449getContent :: Variable -> IO Content
451 _content <$> UF.descriptor variable
458unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
459unifyAtom context name otherContent =
464 Var Flex Nothing _ ->
465 merge context (Atom name)
467 Var Flex (Just super) _ ->
468 if atomMatchesSuper super name then
469 merge context (Atom name)
472 mismatch context (Just ((Error.flipReason (badSuper super))))
474 Var Rigid _ maybeName ->
475 mismatch context (Just (Error.flipReason (badRigid maybeName)))
478 if name == otherName then
479 merge context otherContent
483 if isIntFloat name otherName || isIntFloat otherName name then
489 subUnify context (_first context) realVar
492 mismatch context Nothing
495isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
496isIntFloat name otherName =
497 Var.isPrim "Int" name && Var.isPrim "Float" otherName
503unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
504unifyAlias context name args realVar otherContent =
509 Var Flex Nothing _ ->
510 merge context (Alias name args realVar)
513 subUnify context realVar (_second context)
516 subUnify context realVar (_second context)
519 Alias otherName otherArgs otherRealVar ->
520 if name == otherName then
521 do merge context otherContent
522 zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
525 subUnify context realVar otherRealVar
528 subUnify context realVar (_second context)
535unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
536unifyStructure context term otherContent =
541 Var Flex Nothing _ ->
542 merge context (Structure term)
544 Var Flex (Just super) _ ->
545 unifySuper (reorient context) super (Structure term)
547 Var Rigid _ maybeName ->
548 mismatch context (Just (Error.flipReason (badRigid maybeName)))
551 mismatch context Nothing
554 subUnify context (_first context) realVar
556 Structure otherTerm ->
557 case (term, otherTerm) of
558 (App1 func arg, App1 otherFunc otherArg) ->
559 do subUnify context func otherFunc
560 subUnify context arg otherArg
561 merge context otherContent
563 (Fun1 arg result, Fun1 otherArg otherResult) ->
564 do subUnify context arg otherArg
565 subUnify context result otherResult
566 merge context otherContent
568 (Effects1 names, Effects1 otherNames) ->
569 merge context (Structure (Effects1 (Set.union names otherNames)))
571 (EmptyRecord1, EmptyRecord1) ->
572 merge context otherContent
574 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
575 subUnify context ext (_second context)
577 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
578 subUnify context (_first context) ext
580 (Record1 fields extension, Record1 otherFields otherExtension) ->
581 do firstStructure <- gatherFields context fields extension
582 secondStructure <- gatherFields context otherFields otherExtension
583 unifyRecord context firstStructure secondStructure
586 mismatch context Nothing
593unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
594unifyRecord context firstStructure secondStructure =
595 do let (RecordStructure expFields expVar expStruct) = firstStructure
596 let (RecordStructure actFields actVar actStruct) = secondStructure
598 -- call after unifying extension, make sure record shape matches before
599 -- looking into whether the particular field types match.
600 let unifySharedFields otherFields ext =
601 do let sharedFields = Map.intersectionWith (,) expFields actFields
602 _ <- traverse (uncurry (subUnify context)) sharedFields
603 let allFields = Map.union (Map.map fst sharedFields) otherFields
604 merge context (Structure (Record1 allFields ext))
606 let uniqueExpFields = Map.difference expFields actFields
607 let uniqueActFields = Map.difference actFields expFields
609 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
610 (_, True, _, True) ->
611 do subUnify context expVar actVar
612 unifySharedFields Map.empty expVar
614 (Empty, _, _, False) ->
615 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
617 (_, False, Empty, _) ->
618 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
620 (_, False, _, True) ->
621 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
622 subUnify context subRecord actVar
623 unifySharedFields Map.empty subRecord
625 (_, True, _, False) ->
626 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
627 subUnify context expVar subRecord
628 unifySharedFields Map.empty subRecord
630 (Extension, False, Extension, False) ->
631 do let subFields = Map.union uniqueExpFields uniqueActFields
632 subExt <- fresh context (Var Flex Nothing Nothing)
633 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
634 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
635 subUnify context expVar expRecord
636 subUnify context actRecord actVar
637 unifySharedFields subFields subExt
641-- GATHER RECORD STRUCTURE
644data RecordStructure = RecordStructure
645 { _fields :: Map.Map String Variable
646 , _extVar :: Variable
647 , _extStruct :: ExtensionStructure
651data ExtensionStructure
656gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
657gatherFields context fields variable =
658 do desc <- liftIO (UF.descriptor variable)
659 case _content desc of
660 Structure (Record1 subFields subExt) ->
661 gatherFields context (Map.union fields subFields) subExt
663 Structure EmptyRecord1 ->
664 return (RecordStructure fields variable Empty)
667 -- TODO may be dropping useful alias info here
668 gatherFields context fields var
671 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