1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Type.Unify (unify) where
5import Control.Monad (liftM2, when, zipWithM_)
6import Control.Monad.Except (ExceptT, lift, liftIO, throwError, runExceptT)
7import qualified Data.Map as Map
8import qualified Data.Maybe as Maybe
11import AST.Type (Super(..))
12import qualified AST.Variable as Var
13import qualified Reporting.Region as R
14import qualified Reporting.Error.Type as Error
15import qualified Type.Occurs as Occurs
16import qualified Type.State as TS
17import Type.Type as Type
18import qualified Type.UnionFind as UF
22-- KICK OFF UNIFICATION
25unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
26unify hint region expected actual =
27 {-# SCC elm_compiler_type_unify #-}
28 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
34 do expectedSrcType <- liftIO $ Type.toSrcType expected
35 actualSrcType <- liftIO $ Type.toSrcType actual
36 liftIO $ mergeHelp expected actual (Error "?") noRank
41 Error.MismatchInfo hint expectedSrcType actualSrcType Nothing
45 Error.MismatchInfo hint expectedSrcType actualSrcType (Just reason)
48 Error.InfiniteType (Left hint) expectedSrcType
56 ExceptT Problem TS.Solver
61 { _orientation :: Orientation
63 , _firstDesc :: Descriptor
65 , _secondDesc :: Descriptor
69data Orientation = ExpectedActual | ActualExpected
72reorient :: Context -> Context
73reorient (Context orientation var1 desc1 var2 desc2) =
77 ExpectedActual -> ActualExpected
78 ActualExpected -> ExpectedActual
80 Context otherOrientation var2 desc2 var1 desc1
89 | Special Error.Reason
93mismatch :: Context -> Maybe Error.Reason -> Unify a
94mismatch context@(Context orientation var1 _ var2 _) maybeReason =
95 do anyInfinite <- liftIO $ liftM2 (||) (Occurs.occurs var1) (Occurs.occurs var2)
96 when anyInfinite $ throwError Infinite
98 args1 <- liftIO $ collectArgs var1
99 args2 <- liftIO $ collectArgs var2
101 let numArgs1 = length args1
102 let numArgs2 = length args2
104 if numArgs1 == numArgs2
106 throwError $ reasonToProblem orientation maybeReason
108 do lift $ zipWithM_ (\a1 a2 -> runExceptT $ subUnify context a1 a2) args1 args2
109 throwError $ reasonToProblem orientation $ Just $
110 Error.MissingArgs $ abs (numArgs2 - numArgs1)
113reasonToProblem :: Orientation -> Maybe Error.Reason -> Problem
114reasonToProblem orientation maybeReason =
125 Special (Error.flipReason reason)
128badRigid :: Maybe Text -> Error.Reason
130 Error.BadVar (Just (Error.Rigid maybeName)) Nothing
133badSuper :: Super -> Error.Reason
135 Error.BadVar (Just (errorSuper super)) Nothing
138badSupers :: Super -> Super -> Error.Reason
139badSupers super1 super2 =
140 Error.BadVar (Just (errorSuper super1)) (Just (errorSuper super2))
143doubleBad :: Error.VarType -> Error.VarType -> Error.Reason
145 Error.BadVar (Just vt1) (Just vt2)
148errorSuper :: Super -> Error.VarType
168merge :: Context -> Content -> Unify ()
169merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
170 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
173mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
174mergeHelp var1 var2 newContent newRank =
176 Descriptor newContent newRank noMark Nothing
179fresh :: Context -> Content -> Unify Variable
180fresh (Context _ _ (Descriptor _ rank1 _ _) _ (Descriptor _ rank2 _ _)) content =
183 Descriptor content (min rank1 rank2) noMark Nothing
184 lift (TS.register freshVariable)
188-- ACTUALLY UNIFY THINGS
191guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
192guardedUnify orientation left right =
193 do equivalent <- liftIO $ UF.equivalent left right
197 do leftDesc <- liftIO $ UF.descriptor left
198 rightDesc <- liftIO $ UF.descriptor right
199 actuallyUnify (Context orientation left leftDesc right rightDesc)
202subUnify :: Context -> Variable -> Variable -> Unify ()
203subUnify context var1 var2 =
204 guardedUnify (_orientation context) var1 var2
207actuallyUnify :: Context -> Unify ()
208actuallyUnify context@(Context _ _ (Descriptor firstContent _ _ _) _ (Descriptor secondContent _ _ _)) =
211 -- If there was an error, just pretend it is okay. This lets us avoid
212 -- "cascading" errors where one problem manifests as multiple message.
215 Var Flex Nothing _ ->
216 unifyFlex context secondContent
218 Var Flex (Just super) _ ->
219 unifyFlexSuper context super firstContent secondContent
221 Var Rigid maybeSuper maybeName ->
222 unifyRigid context maybeSuper maybeName firstContent secondContent
224 Alias name args realVar ->
225 unifyAlias context name args realVar secondContent
228 unifyStructure context term firstContent secondContent
232-- UNIFY FLEXIBLE VARIABLES
235unifyFlex :: Context -> Content -> Unify ()
236unifyFlex context otherContent =
242 merge context otherContent
245 merge context otherContent
248 merge context otherContent
251 merge context otherContent
255-- UNIFY RIGID VARIABLES
258unifyRigid :: Context -> Maybe Super -> Maybe Text -> Content -> Content -> Unify ()
259unifyRigid context maybeSuper maybeName content otherContent =
264 Var Flex otherMaybeSuper _ ->
265 case (maybeSuper, otherMaybeSuper) of
267 merge context content
270 mismatch context (Just (badRigid maybeName))
272 (Just super, Just otherSuper) ->
273 if combineRigidSupers super otherSuper then
274 merge context content
276 mismatch context (Just (badRigid maybeName))
278 Var Rigid _ otherMaybeName ->
279 mismatch context $ Just $
280 doubleBad (Error.Rigid maybeName) (Error.Rigid otherMaybeName)
283 mismatch context (Just (badRigid maybeName))
286 mismatch context (Just (badRigid maybeName))
290-- UNIFY SUPER VARIABLES
293unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
294unifyFlexSuper context super content otherContent =
297 unifyFlexSuperStructure context super term
299 Var Rigid Nothing maybeName ->
300 mismatch context (Just (doubleBad (errorSuper super) (Error.Rigid maybeName)))
302 Var Rigid (Just otherSuper) maybeName ->
303 if combineRigidSupers otherSuper super then
304 merge context otherContent
306 mismatch context $ Just $ badRigid maybeName
308 Var Flex Nothing _ ->
309 merge context content
311 Var Flex (Just otherSuper) _ ->
315 Number -> merge context content
316 Comparable -> merge context content
317 _ -> mismatch context $ Just $ badSupers super otherSuper
321 Comparable -> merge context otherContent
322 Number -> merge context otherContent
323 Appendable -> merge context (Var Flex (Just CompAppend) Nothing)
324 CompAppend -> merge context otherContent
328 Appendable -> merge context otherContent
329 Comparable -> merge context (Var Flex (Just CompAppend) Nothing)
330 CompAppend -> merge context otherContent
331 Number -> mismatch context $ Just $ badSupers super otherSuper
335 Comparable -> merge context content
336 Appendable -> merge context content
337 CompAppend -> merge context content
338 Number -> mismatch context $ Just $ badSupers super otherSuper
341 subUnify context (_first context) realVar
347combineRigidSupers :: Super -> Super -> Bool
348combineRigidSupers rigid flex =
350 || (rigid == Number && flex == Comparable)
351 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
354atomMatchesSuper :: Super -> Var.Canonical -> Bool
355atomMatchesSuper super name =
358 elem name [ Var.int, Var.float ]
361 elem name [ Var.string, Var.int, Var.float, Var.char]
370unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
371unifyFlexSuperStructure context super term =
374 if atomMatchesSuper super name then
375 merge context (Structure term)
377 mismatch context (Just (badSuper super))
379 App1 name [variable] | name == Var.list ->
382 mismatch context (Just (badSuper super))
385 merge context (Structure term)
388 do comparableOccursCheck context
389 merge context (Structure term)
390 unifyComparableRecursive (_orientation context) variable
393 do comparableOccursCheck context
394 merge context (Structure term)
395 unifyComparableRecursive (_orientation context) variable
397 App1 name args | Var.isTuple name ->
400 mismatch context (Just (badSuper super))
403 mismatch context (Just (badSuper super))
406 if length args > 6 then
407 mismatch context (Just (Error.TooLongComparableTuple (length args)))
410 do comparableOccursCheck context
411 merge context (Structure term)
412 mapM_ (unifyComparableRecursive (_orientation context)) args
415 mismatch context (Just (badSuper super))
418 mismatch context (Just (badSuper super))
421-- TODO: is there some way to avoid doing this?
422-- Do type classes require occurs checks?
423comparableOccursCheck :: Context -> Unify ()
424comparableOccursCheck (Context _ _ _ var _) =
425 do hasOccurred <- liftIO $ Occurs.occurs var
427 then throwError Infinite
431unifyComparableRecursive :: Orientation -> Variable -> Unify ()
432unifyComparableRecursive orientation var =
433 do compVar <- liftIO $
434 do (Descriptor _ rank _ _) <- UF.descriptor var
435 UF.fresh $ Descriptor (Var Flex (Just Comparable) Nothing) rank noMark Nothing
437 guardedUnify orientation compVar var
440collectArgs :: Variable -> IO [Variable]
441collectArgs variable =
442 collectArgsHelp [] variable
445collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
446collectArgsHelp revArgs variable =
447 do (Descriptor content _ _ _) <- UF.descriptor variable
449 Structure (Fun1 arg returnType) ->
450 collectArgsHelp (arg : revArgs) returnType
453 return $ variable : revArgs
460unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
461unifyAlias context name args realVar otherContent =
466 Var Flex Nothing _ ->
467 merge context (Alias name args realVar)
470 subUnify context realVar (_second context)
472 Alias otherName otherArgs otherRealVar ->
473 if name == otherName then
474 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
475 merge context otherContent
478 subUnify context realVar otherRealVar
481 subUnify context realVar (_second context)
488unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
489unifyStructure context term content otherContent =
494 Var Flex Nothing _ ->
495 merge context content
497 Var Flex (Just super) _ ->
498 unifyFlexSuperStructure (reorient context) super term
500 Var Rigid _ maybeName ->
501 mismatch context (Just (Error.flipReason (badRigid maybeName)))
504 subUnify context (_first context) realVar
506 Structure otherTerm ->
507 case (term, otherTerm) of
508 (App1 name args, App1 otherName otherArgs) ->
509 if name == otherName then
510 do zipWithM_ (subUnify context) args otherArgs
511 merge context otherContent
513 else if isIntFloat name otherName || isIntFloat otherName name then
514 mismatch context (Just Error.IntFloat)
517 mismatch context Nothing
519 (Fun1 arg result, Fun1 otherArg otherResult) ->
520 do subUnify context arg otherArg
521 subUnify context result otherResult
522 merge context otherContent
524 (EmptyRecord1, EmptyRecord1) ->
525 merge context otherContent
527 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
528 subUnify context ext (_second context)
530 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
531 subUnify context (_first context) ext
533 (Record1 fields extension, Record1 otherFields otherExtension) ->
534 do firstStructure <- liftIO $ gatherFields context fields extension
535 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
536 unifyRecord context firstStructure secondStructure
539 mismatch context Nothing
542isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
543isIntFloat name otherName =
544 Var.isPrim "Int" name && Var.isPrim "Float" otherName
551unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
552unifyRecord context firstStructure secondStructure =
553 do let (RecordStructure expFields expVar expStruct) = firstStructure
554 let (RecordStructure actFields actVar actStruct) = secondStructure
556 let sharedFields = Map.intersectionWith (,) expFields actFields
558 -- call after unifying extension, make sure record shape matches before
559 -- looking into whether the particular field types match.
560 let unifySharedFields otherFields ext =
561 do unifySharedFieldsHelp context sharedFields
562 let allFields = Map.union (Map.map fst sharedFields) otherFields
563 merge context (Structure (Record1 allFields ext))
565 let uniqueExpFields = Map.difference expFields actFields
566 let uniqueActFields = Map.difference actFields expFields
568 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
569 (_, True, _, True) ->
570 do subUnify context expVar actVar
571 unifySharedFields Map.empty expVar
573 (Empty, _, _, False) ->
574 mismatch context $ Just $
575 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
577 (_, False, Empty, _) ->
578 mismatch context $ Just $
579 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
581 (_, False, _, True) ->
582 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
583 subUnify context subRecord actVar
584 unifySharedFields Map.empty subRecord
586 (_, True, _, False) ->
587 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
588 subUnify context expVar subRecord
589 unifySharedFields Map.empty subRecord
591 (Extension, False, Extension, False) ->
592 do let subFields = Map.union uniqueExpFields uniqueActFields
593 subExt <- fresh context (Var Flex Nothing Nothing)
594 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
595 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
596 subUnify context expVar expRecord
597 subUnify context actRecord actVar
598 unifySharedFields subFields subExt
602unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
603unifySharedFieldsHelp context sharedFields =
604 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
605 case Maybe.catMaybes maybeBadFields of
610 mismatch context (Just (Error.BadFields (reverse badFields)))
613unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
614unifyField context (field, (expected, actual)) =
615 do result <- lift $ runExceptT $ subUnify context expected actual
621 return $ Just $ (,) field $
634-- GATHER RECORD STRUCTURE
637data RecordStructure = RecordStructure
638 { _fields :: Map.Map Text Variable
639 , _extVar :: Variable
640 , _extStruct :: ExtensionStructure
644data ExtensionStructure
649gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
650gatherFields context fields variable =
651 do (Descriptor content _ _ _) <- UF.descriptor variable
653 Structure (Record1 subFields subExt) ->
654 gatherFields context (Map.union fields subFields) subExt
656 Structure EmptyRecord1 ->
657 return (RecordStructure fields variable Empty)
660 -- TODO may be dropping useful alias info here
661 gatherFields context fields var
664 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