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.Strict as Map
8import qualified Data.Maybe as Maybe
11import qualified AST.Module.Name as ModuleName
12import AST.Type (Super(..))
13import qualified Elm.Name as N
14import qualified Reporting.Region as R
15import qualified Reporting.Error.Type as Error
16import qualified Type.Occurs as Occurs
17import qualified Type.State as TS
18import Type.Type as Type
19import qualified Type.UnionFind as UF
26unify :: Error.Hint -> R.Region -> Variable -> Variable -> TS.Solver ()
27unify hint region expected actual =
28 {-# SCC elm_compiler_type_unify #-}
29 do result <- runExceptT (guardedUnify ExpectedActual expected actual)
35 do expectedSrcType <- liftIO $ Type.toSrcType expected
36 actualSrcType <- liftIO $ Type.toSrcType actual
37 liftIO $ mergeHelp expected actual (Error "?") noRank
40 Reason hasDecoder maybeReason ->
42 Error.MismatchInfo hint expectedSrcType actualSrcType hasDecoder maybeReason
45 Error.InfiniteType (Left hint) expectedSrcType
53 ExceptT Problem TS.Solver
58 { _orientation :: Orientation
60 , _firstDesc :: Descriptor
62 , _secondDesc :: Descriptor
71reorient :: Context -> Context
72reorient (Context orientation var1 desc1 var2 desc2) =
76 ExpectedActual -> ActualExpected
77 ActualExpected -> ExpectedActual
79 Context otherOrientation var2 desc2 var1 desc1
87 = Reason Bool (Maybe Error.Reason)
91mismatch :: Context -> Maybe Error.Reason -> Unify a
92mismatch context@(Context orientation var1 _ var2 _) maybeReason =
93 do anyInfinite <- liftIO $ liftM2 (||) (Occurs.occurs var1) (Occurs.occurs var2)
94 when anyInfinite $ throwError Infinite
96 args1 <- liftIO $ collectArgs [] var1
97 args2 <- liftIO $ collectArgs [] var2
99 hasDecoder <- liftIO $ or <$> traverse detectDecoder (args1 ++ args2)
101 if length args1 /= length args2
103 do lift $ zipWithM_ (\a1 a2 -> runExceptT $ subUnify context a1 a2) args1 args2
104 throwError $ Reason hasDecoder $ Just $
105 Error.MissingArgs (abs (length args2 - length args1))
107 throwError $ Reason hasDecoder $
113 fmap Error.flipReason maybeReason
119collectArgs :: [Variable] -> Variable -> IO [Variable]
120collectArgs revArgs variable =
121 do (Descriptor content _ _ _) <- UF.descriptor variable
123 Structure (Fun1 arg returnType) ->
124 collectArgs (arg : revArgs) returnType
127 return $ variable : revArgs
130detectDecoder :: Variable -> IO Bool
131detectDecoder variable =
132 do (Descriptor content _ _ _) <- UF.descriptor variable
134 Structure (App1 home name [_]) ->
135 return (home == ModuleName.jsonDecode && name == "Decoder")
145merge :: Context -> Content -> Unify ()
146merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
147 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
150mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
151mergeHelp var1 var2 newContent newRank =
153 Descriptor newContent newRank noMark Nothing
156fresh :: Context -> Content -> Unify Variable
157fresh (Context _ _ (Descriptor _ rank1 _ _) _ (Descriptor _ rank2 _ _)) content =
160 Descriptor content (min rank1 rank2) noMark Nothing
162 lift $ TS.register freshVariable
168-- ACTUALLY UNIFY THINGS
171guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
172guardedUnify orientation left right =
173 do equivalent <- liftIO $ UF.equivalent left right
177 do leftDesc <- liftIO $ UF.descriptor left
178 rightDesc <- liftIO $ UF.descriptor right
179 actuallyUnify (Context orientation left leftDesc right rightDesc)
182subUnify :: Context -> Variable -> Variable -> Unify ()
183subUnify context var1 var2 =
184 guardedUnify (_orientation context) var1 var2
187actuallyUnify :: Context -> Unify ()
188actuallyUnify context@(Context _ _ (Descriptor firstContent _ _ _) _ (Descriptor secondContent _ _ _)) =
191 unifyFlex context secondContent
194 unifyFlexSuper context super firstContent secondContent
197 unifyRigid context Nothing name firstContent secondContent
199 RigidSuper super name ->
200 unifyRigid context (Just super) name firstContent secondContent
202 Alias home name args realVar ->
203 unifyAlias context home name args realVar secondContent
205 Structure flatType ->
206 unifyStructure context flatType firstContent secondContent
209 -- If there was an error, just pretend it is okay. This lets us avoid
210 -- "cascading" errors where one problem manifests as multiple message.
215-- UNIFY FLEXIBLE VARIABLES
218unifyFlex :: Context -> Content -> Unify ()
219unifyFlex context otherContent =
224 -- TODO see if wildcarding makes a noticable perf difference
227 merge context otherContent
230 merge context otherContent
233 merge context otherContent
236 merge context otherContent
239 merge context otherContent
242 merge context otherContent
246-- UNIFY RIGID VARIABLES
249unifyRigid :: Context -> Maybe Super -> Text -> Content -> Content -> Unify ()
250unifyRigid context maybeSuper name content otherContent =
253 merge context content
255 FlexSuper otherSuper _ ->
258 if combineRigidSupers super otherSuper then
259 merge context content
261 mismatch context $ Just $
262 Error.RigidSuperTooGeneric super name $ Error.SpecificSuper otherSuper
265 mismatch context $ Just $
266 Error.RigidVarTooGeneric name $ Error.SpecificSuper otherSuper
268 RigidVar otherName ->
269 mismatch context $ Just $
270 Error.RigidClash name otherName
272 RigidSuper _ otherName ->
273 mismatch context $ Just $
274 Error.RigidClash name otherName
276 Alias _ otherName _ _ ->
277 mismatch context $ Just $
278 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
279 Error.SpecificType otherName
281 Structure flatType ->
282 mismatch context $ Just $
283 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
284 flatTypeToSpecificThing flatType
290flatTypeToSpecificThing :: FlatType -> Error.SpecificThing
291flatTypeToSpecificThing flatType =
294 Error.SpecificType name
297 Error.SpecificFunction
313-- UNIFY SUPER VARIABLES
316unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
317unifyFlexSuper context super content otherContent =
319 Structure flatType ->
320 unifyFlexSuperStructure context super flatType
323 mismatch context $ Just $
324 Error.RigidVarTooGeneric name (Error.SpecificSuper super)
326 RigidSuper otherSuper name ->
327 if combineRigidSupers otherSuper super then
328 merge context otherContent
330 mismatch context $ Just $
331 Error.RigidSuperTooGeneric otherSuper name (Error.SpecificSuper super)
334 merge context content
336 FlexSuper otherSuper _ ->
340 Number -> merge context content
341 Comparable -> merge context content
342 _ -> mismatch context Nothing -- Error.NumberAppendableClash
346 Comparable -> merge context otherContent
347 Number -> merge context otherContent
348 Appendable -> merge context (Type.unnamedFlexSuper CompAppend)
349 CompAppend -> merge context otherContent
353 Appendable -> merge context otherContent
354 Comparable -> merge context (Type.unnamedFlexSuper CompAppend)
355 CompAppend -> merge context otherContent
356 Number -> mismatch context Nothing -- Error.NumberAppendableClash
360 Comparable -> merge context content
361 Appendable -> merge context content
362 CompAppend -> merge context content
363 Number -> mismatch context Nothing -- Error.NumberAppendableClash
365 Alias _ _ _ realVar ->
366 subUnify context (_first context) realVar
372combineRigidSupers :: Super -> Super -> Bool
373combineRigidSupers rigid flex =
375 || (rigid == Number && flex == Comparable)
376 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
379atomMatchesSuper :: Super -> ModuleName.Canonical -> N.Name -> Bool
380atomMatchesSuper super home name =
381 home == ModuleName.basics
401unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
402unifyFlexSuperStructure context super flatType =
405 if atomMatchesSuper super home name then
406 merge context (Structure flatType)
408 mismatch context $ Just $ Error.NotPartOfSuper super
410 App1 home name [variable] | home == ModuleName.list && name == N.list ->
413 mismatch context $ Just $ Error.NotPartOfSuper super
416 merge context (Structure flatType)
419 do comparableOccursCheck context
420 merge context (Structure flatType)
421 unifyComparableRecursive (_orientation context) variable
424 do comparableOccursCheck context
425 merge context (Structure flatType)
426 unifyComparableRecursive (_orientation context) variable
431 mismatch context $ Just $ Error.NotPartOfSuper super
434 mismatch context $ Just $ Error.NotPartOfSuper super
437 do comparableOccursCheck context
438 merge context (Structure flatType)
439 let orientation = _orientation context
440 unifyComparableRecursive orientation a
441 unifyComparableRecursive orientation b
447 unifyComparableRecursive orientation c
450 mismatch context $ Just $ Error.NotPartOfSuper super
453 mismatch context $ Just $ Error.NotPartOfSuper super
456-- TODO: is there some way to avoid doing this?
457-- Do type classes require occurs checks?
458comparableOccursCheck :: Context -> Unify ()
459comparableOccursCheck (Context _ _ _ var _) =
460 do hasOccurred <- liftIO $ Occurs.occurs var
462 then throwError Infinite
466unifyComparableRecursive :: Orientation -> Variable -> Unify ()
467unifyComparableRecursive orientation var =
468 do compVar <- liftIO $
469 do (Descriptor _ rank _ _) <- UF.descriptor var
470 UF.fresh $ Descriptor (Type.unnamedFlexSuper Comparable) rank noMark Nothing
472 lift $ TS.register compVar
474 guardedUnify orientation compVar var
481unifyAlias :: Context -> ModuleName.Canonical -> N.Name -> [(Text, Variable)] -> Variable -> Content -> Unify ()
482unifyAlias context home name args realVar otherContent =
485 merge context (Alias home name args realVar)
488 subUnify context realVar (_second context)
491 subUnify context realVar (_second context)
494 subUnify context realVar (_second context)
496 Alias otherHome otherName otherArgs otherRealVar ->
497 if name == otherName && home == otherHome then
498 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
499 merge context otherContent
502 subUnify context realVar otherRealVar
505 subUnify context realVar (_second context)
515unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
516unifyStructure context flatType content otherContent =
519 merge context content
522 unifyFlexSuperStructure (reorient context) super flatType
525 mismatch context $ Just $ Error.RigidVarTooGeneric name (flatTypeToSpecificThing flatType)
527 RigidSuper super name ->
528 mismatch context $ Just $ Error.RigidSuperTooGeneric super name (flatTypeToSpecificThing flatType)
530 Alias _ _ _ realVar ->
531 subUnify context (_first context) realVar
533 Structure otherTerm ->
534 case (flatType, otherTerm) of
535 (App1 home name args, App1 otherHome otherName otherArgs) ->
536 if home == otherHome && name == otherName then
537 do zipWithM_ (subUnify context) args otherArgs
538 merge context otherContent
540 else if isIntFloat home name otherHome otherName then
541 mismatch context (Just Error.IntFloat)
544 mismatch context Nothing
546 (Fun1 arg result, Fun1 otherArg otherResult) ->
547 do subUnify context arg otherArg
548 subUnify context result otherResult
549 merge context otherContent
551 (EmptyRecord1, EmptyRecord1) ->
552 merge context otherContent
554 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
555 subUnify context ext (_second context)
557 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
558 subUnify context (_first context) ext
560 (Record1 fields extension, Record1 otherFields otherExtension) ->
561 do firstStructure <- liftIO $ gatherFields context fields extension
562 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
563 unifyRecord context firstStructure secondStructure
566 mismatch context Nothing
573isIntFloat :: ModuleName.Canonical -> N.Name -> ModuleName.Canonical -> N.Name -> Bool
574isIntFloat home name otherHome otherName =
575 otherHome == ModuleName.basics
576 && home == ModuleName.basics
578 (name == N.int && otherName == N.float)
580 (name == N.float && otherName == N.int)
588unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
589unifyRecord context firstStructure secondStructure =
590 do let (RecordStructure expFields expVar expStruct) = firstStructure
591 let (RecordStructure actFields actVar actStruct) = secondStructure
593 let sharedFields = Map.intersectionWith (,) expFields actFields
595 -- call after unifying extension, make sure record shape matches before
596 -- looking into whether the particular field types match.
597 let unifySharedFields otherFields ext =
598 do unifySharedFieldsHelp context sharedFields
599 let allFields = Map.union (Map.map fst sharedFields) otherFields
600 merge context (Structure (Record1 allFields ext))
602 let uniqueExpFields = Map.difference expFields actFields
603 let uniqueActFields = Map.difference actFields expFields
605 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
606 (_, True, _, True) ->
607 do subUnify context expVar actVar
608 unifySharedFields Map.empty expVar
610 (Empty, _, _, False) ->
611 mismatch context $ Just $
612 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
614 (_, False, Empty, _) ->
615 mismatch context $ Just $
616 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
618 (_, False, _, True) ->
619 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
620 subUnify context subRecord actVar
621 unifySharedFields Map.empty subRecord
623 (_, True, _, False) ->
624 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
625 subUnify context expVar subRecord
626 unifySharedFields Map.empty subRecord
628 (Extension, False, Extension, False) ->
629 do let subFields = Map.union uniqueExpFields uniqueActFields
630 subExt <- fresh context Type.unnamedFlexVar
631 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
632 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
633 subUnify context expVar expRecord
634 subUnify context actRecord actVar
635 unifySharedFields subFields subExt
639unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
640unifySharedFieldsHelp context sharedFields =
641 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
642 case Maybe.catMaybes maybeBadFields of
647 mismatch context (Just (Error.BadFields (reverse badFields)))
650unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
651unifyField context (field, (expected, actual)) =
652 do result <- lift $ runExceptT $ subUnify context expected actual
658 return $ Just $ (,) field $
668-- GATHER RECORD STRUCTURE
671data RecordStructure =
673 { _fields :: Map.Map Text Variable
674 , _extVar :: Variable
675 , _extStruct :: ExtensionStructure
679data ExtensionStructure
684gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
685gatherFields context fields variable =
686 do (Descriptor content _ _ _) <- UF.descriptor variable
688 Structure (Record1 subFields subExt) ->
689 gatherFields context (Map.union fields subFields) subExt
691 Structure EmptyRecord1 ->
692 return (RecordStructure fields variable Empty)
695 -- TODO may be dropping useful alias info here
696 gatherFields context fields var
699 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