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
457 if isIntFloat name otherName || isIntFloat otherName name then
463 subUnify context (_first context) realVar
466 mismatch context Nothing
469isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
470isIntFloat name otherName =
471 Var.isPrim "Int" name && Var.isPrim "Float" otherName
477unifyAlias :: Context -> Var.Canonical -> [(String, Variable)] -> Variable -> Content -> Unify ()
478unifyAlias context name args realVar otherContent =
483 Var Flex Nothing _ ->
484 merge context (Alias name args realVar)
487 subUnify context realVar (_second context)
490 subUnify context realVar (_second context)
493 Alias otherName otherArgs otherRealVar ->
494 if name == otherName then
495 do merge context otherContent
496 zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
499 subUnify context realVar otherRealVar
502 subUnify context realVar (_second context)
509unifyStructure :: Context -> Term1 Variable -> Content -> Unify ()
510unifyStructure context term otherContent =
515 Var Flex Nothing _ ->
516 merge context (Structure term)
518 Var Flex (Just super) _ ->
519 unifySuper (reorient context) super (Structure term)
521 Var Rigid _ maybeName ->
522 rigidityError context maybeName
525 mismatch context Nothing
528 subUnify context (_first context) realVar
530 Structure otherTerm ->
531 case (term, otherTerm) of
532 (App1 func arg, App1 otherFunc otherArg) ->
533 do merge context otherContent
534 subUnify context func otherFunc
535 subUnify context arg otherArg
537 (Fun1 arg result, Fun1 otherArg otherResult) ->
538 do merge context otherContent
539 subUnify context arg otherArg
540 subUnify context result otherResult
542 (EmptyRecord1, EmptyRecord1) ->
543 merge context otherContent
545 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
546 subUnify context ext (_second context)
548 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
549 subUnify context (_first context) ext
551 (Record1 fields extension, Record1 otherFields otherExtension) ->
552 do firstStructure <- gatherFields context fields extension
553 secondStructure <- gatherFields context otherFields otherExtension
554 unifyRecord context firstStructure secondStructure
557 mismatch context Nothing
564unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
565unifyRecord context firstStructure secondStructure =
566 do let (RecordStructure expFields expVar expStruct) = firstStructure
567 let (RecordStructure actFields actVar actStruct) = secondStructure
569 -- call after unifying extension, make sure record shape matches before
570 -- looking into whether the particular field types match.
571 let unifySharedFields otherFields ext =
572 do let sharedFields = Map.intersectionWith (,) expFields actFields
573 _ <- traverse (uncurry (subUnify context)) sharedFields
574 let allFields = Map.union (Map.map fst sharedFields) otherFields
575 merge context (Structure (Record1 allFields ext))
577 let uniqueExpFields = Map.difference expFields actFields
578 let uniqueActFields = Map.difference actFields expFields
580 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
581 (_, True, _, True) ->
582 do subUnify context expVar actVar
583 unifySharedFields Map.empty expVar
585 (Empty, _, _, False) ->
586 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
588 (_, False, Empty, _) ->
589 mismatch context (Just (Error.MessyFields (Map.keys uniqueExpFields) (Map.keys uniqueActFields)))
591 (_, False, _, True) ->
592 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
593 subUnify context subRecord actVar
594 unifySharedFields Map.empty subRecord
596 (_, True, _, False) ->
597 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
598 subUnify context expVar subRecord
599 unifySharedFields Map.empty subRecord
601 (Extension, False, Extension, False) ->
602 do let subFields = Map.union uniqueExpFields uniqueActFields
603 subExt <- fresh context (Var Flex Nothing Nothing)
604 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
605 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
606 subUnify context expVar expRecord
607 subUnify context actRecord actVar
608 unifySharedFields subFields subExt
612-- GATHER RECORD STRUCTURE
615data RecordStructure = RecordStructure
616 { _fields :: Map.Map String Variable
617 , _extVar :: Variable
618 , _extStruct :: ExtensionStructure
622data ExtensionStructure
627gatherFields :: Context -> Map.Map String Variable -> Variable -> Unify RecordStructure
628gatherFields context fields variable =
629 do desc <- liftIO (UF.descriptor variable)
630 case _content desc of
631 Structure (Record1 subFields subExt) ->
632 gatherFields context (Map.union fields subFields) subExt
634 Structure EmptyRecord1 ->
635 return (RecordStructure fields variable Empty)
638 -- TODO may be dropping useful alias info here
639 gatherFields context fields var
642 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