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 mismatch context (Just Error.DoubleRigid)
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 (Just (superReason super))
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
300superReason :: Super -> Error.Reason
316isPrimitiveFrom :: [String] -> Var.Canonical -> Bool
317isPrimitiveFrom prims var =
318 any (\p -> Var.isPrim p var) prims
321atomMatchesSuper :: Super -> Var.Canonical -> Bool
322atomMatchesSuper super name =
325 isPrimitiveFrom ["Int", "Float"] name
328 isPrimitiveFrom ["Int", "Float", "Char", "String"] name
331 Var.isPrim "String" name || Var.isText name
334 Var.isPrim "String" name
337unifySuperStructure :: Context -> Super -> Term1 Variable -> Unify ()
338unifySuperStructure context super term =
339 do appStructure <- liftIO (collectApps (Structure term))
342 mismatch context (Just (superReason super))
347 mismatch context (Just Error.NotNumber)
350 merge context (Structure term)
353 do merge context (Structure term)
354 unifyComparableRecursive (_orientation context) variable
357 do merge context (Structure term)
358 unifyComparableRecursive (_orientation context) variable
363 mismatch context (Just Error.NotNumber)
366 mismatch context (Just Error.NotAppendable)
369 if length entries > 6 then
370 mismatch context (Just (Error.TooLongComparableTuple (length entries)))
373 do merge context (Structure term)
374 mapM_ (unifyComparableRecursive (_orientation context)) entries
377 mismatch context (Just Error.NotCompAppend)
380unifyComparableRecursive :: Orientation -> Variable -> Unify ()
381unifyComparableRecursive orientation var =
384 do desc <- UF.descriptor var
387 { _content = Var Flex (Just Comparable) Nothing
393 guardedUnify orientation compVar var
402collectApps :: Content -> IO AppStructure
404 collectAppsHelp [] content
407collectAppsHelp :: [Variable] -> Content -> IO AppStructure
408collectAppsHelp args content =
409 case (content, args) of
410 (Structure (App1 func arg), _) ->
411 collectAppsHelp (args ++ [arg]) =<< getContent func
413 (Atom name, [arg]) | Var.isList name ->
416 (Atom name, _) | Var.isTuple name ->
423getContent :: Variable -> IO Content
425 _content <$> UF.descriptor variable
432unifyAtom :: Context -> Var.Canonical -> Content -> Unify ()
433unifyAtom context name otherContent =
438 Var Flex Nothing _ ->
439 merge context (Atom name)
441 Var Flex (Just super) _ ->
442 if atomMatchesSuper super name then
443 merge context (Atom name)
446 mismatch context (Just (superReason super))
448 Var Rigid _ maybeName ->
449 rigidityError context maybeName
452 if name == otherName then
453 merge context otherContent
456 mismatch context Nothing
459 subUnify context (_first context) realVar
462 mismatch context Nothing
468unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
469unifyAlias context name args realVar otherContent =
474 Var Flex Nothing _ ->
475 merge context (Alias name args realVar)
478 subUnify context realVar (_second context)
481 subUnify context realVar (_second context)
484 Alias otherName otherArgs otherRealVar ->
485 if name == otherName then
486 do merge context otherContent
487 zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
490 subUnify context realVar otherRealVar
493 subUnify context realVar (_second context)
500unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
501unifyStructure context term otherContent =
506 Var Flex Nothing _ ->
507 merge context (Structure term)
509 Var Flex (Just super) _ ->
510 unifySuper (reorient context) super (Structure term)
512 Var Rigid _ maybeName ->
513 rigidityError context maybeName
516 mismatch context Nothing
519 subUnify context (_first context) realVar
521 Structure otherTerm ->
522 case (term, otherTerm) of
523 (App1 func arg, App1 otherFunc otherArg) ->
524 do merge context otherContent
525 subUnify context func otherFunc
526 subUnify context arg otherArg
528 (Fun1 arg result, Fun1 otherArg otherResult) ->
529 do merge context otherContent
530 subUnify context arg otherArg
531 subUnify context result otherResult
533 (EmptyRecord1, EmptyRecord1) ->
534 merge context otherContent
536 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
537 subUnify context ext (_second context)
539 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
540 subUnify context (_first context) ext
542 (Record1 fields extension, Record1 otherFields otherExtension) ->
543 do firstStructure <- gatherFields context fields extension
544 secondStructure <- gatherFields context otherFields otherExtension
545 unifyRecord context firstStructure secondStructure
548 mismatch context Nothing
555unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
556unifyRecord context firstStructure secondStructure =
557 do let (RecordStructure expFields expVar expStruct) = firstStructure
558 let (RecordStructure actFields actVar actStruct) = secondStructure
560 -- call after unifying extension, make sure record shape matches before
561 -- looking into whether the particular field types match.
562 let unifySharedFields otherFields ext =
563 do let sharedFields = Map.intersectionWith (,) expFields actFields
564 _ <- traverse (uncurry (subUnify context)) sharedFields
565 let allFields = Map.union (Map.map fst sharedFields) otherFields
566 merge context (Structure (Record1 allFields ext))
568 let uniqueExpFields = Map.difference expFields actFields
569 let uniqueActFields = Map.difference actFields expFields
571 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
572 (_, True, _, True) ->
573 do subUnify context expVar actVar
574 unifySharedFields Map.empty expVar
576 (Empty, _, _, False) ->
577 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
579 (_, False, Empty, _) ->
580 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
582 (_, False, _, True) ->
583 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
584 subUnify context subRecord actVar
585 unifySharedFields Map.empty subRecord
587 (_, True, _, False) ->
588 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
589 subUnify context expVar subRecord
590 unifySharedFields Map.empty subRecord
592 (Extension, False, Extension, False) ->
593 do let subFields = Map.union uniqueExpFields uniqueActFields
594 subExt <- fresh context (Var Flex Nothing Nothing)
595 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
596 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
597 subUnify context expVar expRecord
598 subUnify context actRecord actVar
599 unifySharedFields subFields subExt
603-- GATHER RECORD STRUCTURE
606data RecordStructure = RecordStructure
607 { _fields :: Map.Map String Variable
608 , _extVar :: Variable
609 , _extStruct :: ExtensionStructure
613data ExtensionStructure
618gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
619gatherFields context fields variable =
620 do desc <- liftIO (UF.descriptor variable)
621 case _content desc of
622 Structure (Record1 subFields subExt) ->
623 gatherFields context (Map.union fields subFields) subExt
625 Structure EmptyRecord1 ->
626 return (RecordStructure fields variable Empty)
629 -- TODO may be dropping useful alias info here
630 gatherFields context fields var
633 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