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 subExpected subActual maybeReason) ->
30 do expectedSrcType <- Type.toSrcType expected
31 actualSrcType <- Type.toSrcType actual
32 mergeHelp subExpected subActual 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 Variable Variable (Maybe Error.Reason)
78mismatch :: Context -> Maybe Error.Reason -> Unify a
79mismatch (Context orientation first _ second _) maybeReason =
89 throwError (Mismatch expected actual maybeReason)
92rigidityError :: Context -> Maybe String -> Unify ()
93rigidityError context maybeName =
94 mismatch context (Just (Error.Rigid maybeName))
101merge :: Context -> Content -> Unify ()
102merge (Context _ first _ second _) content =
103 liftIO $ mergeHelp first second content
106mergeHelp :: Variable -> Variable -> Content -> IO ()
107mergeHelp first second content =
108 UF.union' first second $ \desc1 desc2 ->
112 , _rank = min (_rank desc1) (_rank desc2)
118fresh :: Context -> Content -> Unify Variable
119fresh (Context _ _ desc1 _ desc2) content =
124 , _rank = min (_rank desc1) (_rank desc2)
128 lift (TS.register freshVariable)
132-- ACTUALLY UNIFY THINGS
135guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
136guardedUnify orientation left right =
137 do equivalent <- liftIO $ UF.equivalent left right
141 do leftDesc <- liftIO $ UF.descriptor left
142 rightDesc <- liftIO $ UF.descriptor right
143 actuallyUnify (Context orientation left leftDesc right rightDesc)
146subUnify :: Context -> Variable -> Variable -> Unify ()
147subUnify context var1 var2 =
148 guardedUnify (_orientation context) var1 var2
151actuallyUnify :: Context -> Unify ()
152actuallyUnify context@(Context _ _ firstDesc _ secondDesc) =
154 secondContent = _content secondDesc
156 case _content firstDesc of
158 -- If there was an error, just pretend it is okay. This lets us avoid
159 -- "cascading" errors where one problem manifests as multiple message.
162 Var Flex Nothing _ ->
163 unifyFlex context secondContent
165 Var Flex (Just super) _ ->
166 unifySuper context super secondContent
168 Var Rigid maybeSuper maybeName ->
169 unifyRigid context maybeSuper maybeName secondContent
172 unifyAtom context name secondContent
174 Alias name args realVar ->
175 unifyAlias context name args realVar secondContent
178 unifyStructure context term secondContent
182-- UNIFY FLEXIBLE VARIABLES
185unifyFlex :: Context -> Content -> Unify ()
186unifyFlex context otherContent =
192 merge context otherContent
195 merge context otherContent
198 merge context otherContent
201 merge context otherContent
204 merge context otherContent
208-- UNIFY RIGID VARIABLES
211unifyRigid :: Context -> Maybe Super -> Maybe String -> Content -> Unify ()
212unifyRigid context maybeSuper maybeName otherContent =
217 Var Flex otherMaybeSuper _ ->
218 if maybeSuper == otherMaybeSuper then
219 merge context (Var Rigid maybeSuper maybeName)
221 rigidityError context maybeName
224 rigidityError context maybeName
227 rigidityError context maybeName
230 rigidityError context maybeName
233 rigidityError context maybeName
236-- UNIFY SUPER VARIABLES
239unifySuper :: Context -> Super -> Content -> Unify ()
240unifySuper context super otherContent =
243 unifySuperStructure context super term
246 if atomMatchesSuper super name then
247 merge context otherContent
249 mismatch context Nothing
251 Var Rigid Nothing maybeName ->
252 rigidityError context maybeName
254 Var Rigid (Just otherSuper) maybeName ->
255 if super == otherSuper then
256 merge context otherContent
258 rigidityError context maybeName
260 Var Flex Nothing _ ->
261 merge context (Var Flex (Just super) Nothing)
263 Var Flex (Just otherSuper) _ ->
264 case combineSupers super otherSuper of
266 mismatch context Nothing
269 merge context (Var Flex (Just newSuper) Nothing)
272 subUnify context (_first context) realVar
278combineSupers :: Super -> Super -> Maybe Super
279combineSupers firstSuper secondSuper =
280 case (firstSuper, secondSuper) of
281 (Number , Number ) -> Just Number
282 (Comparable, Number ) -> Just Number
283 (Number , Comparable) -> Just Number
285 (Comparable, Comparable) -> Just Comparable
286 (Appendable, Appendable) -> Just Appendable
288 (Appendable, Comparable) -> Just CompAppend
289 (Comparable, Appendable) -> Just CompAppend
291 (CompAppend, CompAppend) -> Just CompAppend
292 (CompAppend, Comparable) -> Just CompAppend
293 (Comparable, CompAppend) -> Just CompAppend
294 (CompAppend, Appendable) -> Just CompAppend
295 (Appendable, CompAppend) -> Just CompAppend
300isPrimitiveFrom :: [String] -> Var.Canonical -> Bool
301isPrimitiveFrom prims var =
302 any (\p -> Var.isPrim p var) prims
305atomMatchesSuper :: Super -> Var.Canonical -> Bool
306atomMatchesSuper super name =
309 isPrimitiveFrom ["Int", "Float"] name
312 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
315 Var.isPrim "String" name || Var.isText name
318 Var.isPrim "String" name
321unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
322unifySuperStructure context super term =
323 do appStructure <- liftIO (collectApps (Structure term))
326 mismatch context Nothing
331 mismatch context Nothing
334 merge context (Structure term)
337 do merge context (Structure term)
338 unifyComparableRecursive (_orientation context) variable
341 do merge context (Structure term)
342 unifyComparableRecursive (_orientation context) variable
347 mismatch context Nothing
350 mismatch context Nothing
353 if length entries > 6 then
354 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
357 do merge context (Structure term)
358 mapM_ (unifyComparableRecursive (_orientation context)) entries
361 mismatch context Nothing
364unifyComparableRecursive :: Orientation -> Variable -> Unify ()
365unifyComparableRecursive orientation var =
368 do desc <- UF.descriptor var
371 { _content = Var Flex (Just Comparable) Nothing
377 guardedUnify orientation compVar var
386collectApps :: Content -> IO AppStructure
388 collectAppsHelp [] content
391collectAppsHelp :: [Variable] -> Content -> IO AppStructure
392collectAppsHelp args content =
393 case (content, args) of
394 (Structure (App1 func arg), _) ->
395 collectAppsHelp (args ++ [arg]) =<< getContent func
397 (Atom name, [arg]) | Var.isList name ->
400 (Atom name, _) | Var.isTuple name ->
407getContent :: Variable -> IO Content
409 _content <$> UF.descriptor variable
416unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
417unifyAtom context name otherContent =
422 Var Flex Nothing _ ->
423 merge context (Atom name)
425 Var Flex (Just super) _ ->
426 if atomMatchesSuper super name then
427 merge context (Atom name)
430 mismatch context Nothing
432 Var Rigid _ maybeName ->
433 rigidityError context maybeName
436 if name == otherName then
437 merge context otherContent
440 mismatch context Nothing
443 subUnify context (_first context) realVar
446 mismatch context Nothing
452unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
453unifyAlias context name args realVar otherContent =
458 Var Flex Nothing _ ->
459 merge context (Alias name args realVar)
462 subUnify context realVar (_second context)
465 subUnify context realVar (_second context)
468 Alias otherName otherArgs otherRealVar ->
469 if name == otherName then
470 do merge context otherContent
471 zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
474 subUnify context realVar otherRealVar
477 subUnify context realVar (_second context)
484unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
485unifyStructure context term otherContent =
490 Var Flex Nothing _ ->
491 merge context (Structure term)
493 Var Flex (Just super) _ ->
494 unifySuper (reorient context) super (Structure term)
496 Var Rigid _ maybeName ->
497 rigidityError context maybeName
500 mismatch context Nothing
503 subUnify context (_first context) realVar
505 Structure otherTerm ->
506 case (term, otherTerm) of
507 (App1 func arg, App1 otherFunc otherArg) ->
508 do merge context otherContent
509 subUnify context func otherFunc
510 subUnify context arg otherArg
512 (Fun1 arg result, Fun1 otherArg otherResult) ->
513 do merge context otherContent
514 subUnify context arg otherArg
515 subUnify context result otherResult
517 (EmptyRecord1, EmptyRecord1) ->
518 merge context otherContent
520 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
521 subUnify context ext (_second context)
523 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
524 subUnify context (_first context) ext
526 (Record1 fields extension, Record1 otherFields otherExtension) ->
527 do firstStructure <- gatherFields context fields extension
528 secondStructure <- gatherFields context otherFields otherExtension
529 unifyRecord context firstStructure secondStructure
532 mismatch context Nothing
539unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
540unifyRecord context firstStructure secondStructure =
541 do let (RecordStructure expFields expVar expStruct) = firstStructure
542 let (RecordStructure actFields actVar actStruct) = secondStructure
544 -- call after unifying extension, make sure record shape matches before
545 -- looking into whether the particular field types match.
546 let unifySharedFields otherFields ext =
547 do let sharedFields = Map.intersectionWith (,) expFields actFields
548 _ <- traverse (uncurry (subUnify context)) sharedFields
549 let allFields = Map.union (Map.map fst sharedFields) otherFields
550 merge context (Structure (Record1 allFields ext))
552 let uniqueExpFields = Map.difference expFields actFields
553 let uniqueActFields = Map.difference actFields expFields
555 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
556 (_, True, _, True) ->
557 do subUnify context expVar actVar
558 unifySharedFields Map.empty expVar
560 (Empty, _, _, False) ->
561 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
563 (_, False, Empty, _) ->
564 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
566 (_, False, _, True) ->
567 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
568 subUnify context subRecord actVar
569 unifySharedFields Map.empty subRecord
571 (_, True, _, False) ->
572 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
573 subUnify context expVar subRecord
574 unifySharedFields Map.empty subRecord
576 (Extension, False, Extension, False) ->
577 do let subFields = Map.union uniqueExpFields uniqueActFields
578 subExt <- fresh context (Var Flex Nothing Nothing)
579 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
580 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
581 subUnify context expVar expRecord
582 subUnify context actRecord actVar
583 unifySharedFields subFields subExt
587-- GATHER RECORD STRUCTURE
590data RecordStructure = RecordStructure
591 { _fields :: Map.Map String Variable
592 , _extVar :: Variable
593 , _extStruct :: ExtensionStructure
597data ExtensionStructure
602gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
603gatherFields context fields variable =
604 do desc <- liftIO (UF.descriptor variable)
605 case _content desc of
606 Structure (Record1 subFields subExt) ->
607 gatherFields context (Map.union fields subFields) subExt
609 Structure (EmptyRecord1) ->
610 return (RecordStructure fields variable Empty)
612 Structure (App1 _ _) ->
613 mismatch context (Just Error.NotRecord)
615 Structure (Fun1 _ _) ->
616 mismatch context (Just Error.NotRecord)
619 mismatch context (Just Error.NotRecord)
622 return (RecordStructure fields variable Extension)
625 mismatch context (Just Error.NotRecord)
627 -- TODO may be dropping useful alias info here
629 gatherFields context fields var
632 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