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 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
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
39 Reason hasDecoder maybeReason ->
41 Error.MismatchInfo hint expectedSrcType actualSrcType hasDecoder maybeReason
44 Error.InfiniteType (Left hint) expectedSrcType
52 ExceptT Problem TS.Solver
57 { _orientation :: Orientation
59 , _firstDesc :: Descriptor
61 , _secondDesc :: Descriptor
70reorient :: Context -> Context
71reorient (Context orientation var1 desc1 var2 desc2) =
75 ExpectedActual -> ActualExpected
76 ActualExpected -> ExpectedActual
78 Context otherOrientation var2 desc2 var1 desc1
86 = Reason Bool (Maybe Error.Reason)
90mismatch :: Context -> Maybe Error.Reason -> Unify a
91mismatch context@(Context orientation var1 _ var2 _) maybeReason =
92 do anyInfinite <- liftIO $ liftM2 (||) (Occurs.occurs var1) (Occurs.occurs var2)
93 when anyInfinite $ throwError Infinite
95 args1 <- liftIO $ collectArgs [] var1
96 args2 <- liftIO $ collectArgs [] var2
98 hasDecoder <- liftIO $ or <$> traverse detectDecoder (args1 ++ args2)
100 if length args1 /= length args2
102 do lift $ zipWithM_ (\a1 a2 -> runExceptT $ subUnify context a1 a2) args1 args2
103 throwError $ Reason hasDecoder $ Just $
104 Error.MissingArgs (abs (length args2 - length args1))
106 throwError $ Reason hasDecoder $
112 fmap Error.flipReason maybeReason
118collectArgs :: [Variable] -> Variable -> IO [Variable]
119collectArgs revArgs variable =
120 do (Descriptor content _ _ _) <- UF.descriptor variable
122 Structure (Fun1 arg returnType) ->
123 collectArgs (arg : revArgs) returnType
126 return $ variable : revArgs
129detectDecoder :: Variable -> IO Bool
130detectDecoder variable =
131 do (Descriptor content _ _ _) <- UF.descriptor variable
133 Structure (App1 name [_]) | Var.decoder == name ->
144merge :: Context -> Content -> Unify ()
145merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
146 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
149mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
150mergeHelp var1 var2 newContent newRank =
152 Descriptor newContent newRank noMark Nothing
155fresh :: Context -> Content -> Unify Variable
156fresh (Context _ _ (Descriptor _ rank1 _ _) _ (Descriptor _ rank2 _ _)) content =
159 Descriptor content (min rank1 rank2) noMark Nothing
161 lift $ TS.register freshVariable
167-- ACTUALLY UNIFY THINGS
170guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
171guardedUnify orientation left right =
172 do equivalent <- liftIO $ UF.equivalent left right
176 do leftDesc <- liftIO $ UF.descriptor left
177 rightDesc <- liftIO $ UF.descriptor right
178 actuallyUnify (Context orientation left leftDesc right rightDesc)
181subUnify :: Context -> Variable -> Variable -> Unify ()
182subUnify context var1 var2 =
183 guardedUnify (_orientation context) var1 var2
186actuallyUnify :: Context -> Unify ()
187actuallyUnify context@(Context _ _ (Descriptor firstContent _ _ _) _ (Descriptor secondContent _ _ _)) =
190 unifyFlex context secondContent
193 unifyFlexSuper context super firstContent secondContent
196 unifyRigid context Nothing name firstContent secondContent
198 RigidSuper super name ->
199 unifyRigid context (Just super) name firstContent secondContent
201 Alias name args realVar ->
202 unifyAlias context name args realVar secondContent
204 Structure flatType ->
205 unifyStructure context flatType firstContent secondContent
208 -- If there was an error, just pretend it is okay. This lets us avoid
209 -- "cascading" errors where one problem manifests as multiple message.
214-- UNIFY FLEXIBLE VARIABLES
217unifyFlex :: Context -> Content -> Unify ()
218unifyFlex context otherContent =
223 -- TODO see if wildcarding makes a noticable perf difference
226 merge context otherContent
229 merge context otherContent
232 merge context otherContent
235 merge context otherContent
238 merge context otherContent
241 merge context otherContent
245-- UNIFY RIGID VARIABLES
248unifyRigid :: Context -> Maybe Super -> Text -> Content -> Content -> Unify ()
249unifyRigid context maybeSuper name content otherContent =
252 merge context content
254 FlexSuper otherSuper _ ->
257 if combineRigidSupers super otherSuper then
258 merge context content
260 mismatch context $ Just $
261 Error.RigidSuperTooGeneric super name $ Error.SpecificSuper otherSuper
264 mismatch context $ Just $
265 Error.RigidVarTooGeneric name $ Error.SpecificSuper otherSuper
267 RigidVar otherName ->
268 mismatch context $ Just $
269 Error.RigidClash name otherName
271 RigidSuper _ otherName ->
272 mismatch context $ Just $
273 Error.RigidClash name otherName
275 Alias otherName _ _ ->
276 mismatch context $ Just $
277 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
278 Error.SpecificType otherName
280 Structure flatType ->
281 mismatch context $ Just $
282 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
283 flatTypeToSpecificThing flatType
289flatTypeToSpecificThing :: FlatType -> Error.SpecificThing
290flatTypeToSpecificThing flatType =
293 Error.SpecificType name
296 Error.SpecificFunction
306-- UNIFY SUPER VARIABLES
309unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
310unifyFlexSuper context super content otherContent =
312 Structure flatType ->
313 unifyFlexSuperStructure context super flatType
316 mismatch context $ Just $
317 Error.RigidVarTooGeneric name (Error.SpecificSuper super)
319 RigidSuper otherSuper name ->
320 if combineRigidSupers otherSuper super then
321 merge context otherContent
323 mismatch context $ Just $
324 Error.RigidSuperTooGeneric otherSuper name (Error.SpecificSuper super)
327 merge context content
329 FlexSuper otherSuper _ ->
333 Number -> merge context content
334 Comparable -> merge context content
335 _ -> mismatch context Nothing -- Error.NumberAppendableClash
339 Comparable -> merge context otherContent
340 Number -> merge context otherContent
341 Appendable -> merge context (Type.unnamedFlexSuper CompAppend)
342 CompAppend -> merge context otherContent
346 Appendable -> merge context otherContent
347 Comparable -> merge context (Type.unnamedFlexSuper CompAppend)
348 CompAppend -> merge context otherContent
349 Number -> mismatch context Nothing -- Error.NumberAppendableClash
353 Comparable -> merge context content
354 Appendable -> merge context content
355 CompAppend -> merge context content
356 Number -> mismatch context Nothing -- Error.NumberAppendableClash
359 subUnify context (_first context) realVar
365combineRigidSupers :: Super -> Super -> Bool
366combineRigidSupers rigid flex =
368 || (rigid == Number && flex == Comparable)
369 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
372atomMatchesSuper :: Super -> Var.Canonical -> Bool
373atomMatchesSuper super name =
376 elem name [ Var.int, Var.float ]
379 elem name [ Var.string, Var.int, Var.float, Var.char]
388unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
389unifyFlexSuperStructure context super flatType =
392 if atomMatchesSuper super name then
393 merge context (Structure flatType)
395 mismatch context $ Just $ Error.NotPartOfSuper super
397 App1 name [variable] | name == Var.list ->
400 mismatch context $ Just $ Error.NotPartOfSuper super
403 merge context (Structure flatType)
406 do comparableOccursCheck context
407 merge context (Structure flatType)
408 unifyComparableRecursive (_orientation context) variable
411 do comparableOccursCheck context
412 merge context (Structure flatType)
413 unifyComparableRecursive (_orientation context) variable
415 App1 name args | Var.isTuple name ->
418 mismatch context $ Just $ Error.NotPartOfSuper super
421 mismatch context $ Just $ Error.NotPartOfSuper super
424 if length args > 6 then
425 mismatch context (Just (Error.TooLongComparableTuple (length args)))
428 do comparableOccursCheck context
429 merge context (Structure flatType)
430 mapM_ (unifyComparableRecursive (_orientation context)) args
433 mismatch context $ Just $ Error.NotPartOfSuper super
436 mismatch context $ Just $ Error.NotPartOfSuper super
439-- TODO: is there some way to avoid doing this?
440-- Do type classes require occurs checks?
441comparableOccursCheck :: Context -> Unify ()
442comparableOccursCheck (Context _ _ _ var _) =
443 do hasOccurred <- liftIO $ Occurs.occurs var
445 then throwError Infinite
449unifyComparableRecursive :: Orientation -> Variable -> Unify ()
450unifyComparableRecursive orientation var =
451 do compVar <- liftIO $
452 do (Descriptor _ rank _ _) <- UF.descriptor var
453 UF.fresh $ Descriptor (Type.unnamedFlexSuper Comparable) rank noMark Nothing
455 lift $ TS.register compVar
457 guardedUnify orientation compVar var
464unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
465unifyAlias context name args realVar otherContent =
468 merge context (Alias name args realVar)
471 subUnify context realVar (_second context)
474 subUnify context realVar (_second context)
477 subUnify context realVar (_second context)
479 Alias otherName otherArgs otherRealVar ->
480 if name == otherName then
481 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
482 merge context otherContent
485 subUnify context realVar otherRealVar
488 subUnify context realVar (_second context)
498unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
499unifyStructure context flatType content otherContent =
502 merge context content
505 unifyFlexSuperStructure (reorient context) super flatType
508 mismatch context $ Just $ Error.RigidVarTooGeneric name (flatTypeToSpecificThing flatType)
510 RigidSuper super name ->
511 mismatch context $ Just $ Error.RigidSuperTooGeneric super name (flatTypeToSpecificThing flatType)
514 subUnify context (_first context) realVar
516 Structure otherTerm ->
517 case (flatType, otherTerm) of
518 (App1 name args, App1 otherName otherArgs) ->
519 if name == otherName then
520 do zipWithM_ (subUnify context) args otherArgs
521 merge context otherContent
523 else if isIntFloat name otherName || isIntFloat otherName name then
524 mismatch context (Just Error.IntFloat)
527 mismatch context Nothing
529 (Fun1 arg result, Fun1 otherArg otherResult) ->
530 do subUnify context arg otherArg
531 subUnify context result otherResult
532 merge context otherContent
534 (EmptyRecord1, EmptyRecord1) ->
535 merge context otherContent
537 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
538 subUnify context ext (_second context)
540 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
541 subUnify context (_first context) ext
543 (Record1 fields extension, Record1 otherFields otherExtension) ->
544 do firstStructure <- liftIO $ gatherFields context fields extension
545 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
546 unifyRecord context firstStructure secondStructure
549 mismatch context Nothing
556isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
557isIntFloat name otherName =
558 Var.isPrim "Int" name && Var.isPrim "Float" otherName
565unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
566unifyRecord context firstStructure secondStructure =
567 do let (RecordStructure expFields expVar expStruct) = firstStructure
568 let (RecordStructure actFields actVar actStruct) = secondStructure
570 let sharedFields = Map.intersectionWith (,) expFields actFields
572 -- call after unifying extension, make sure record shape matches before
573 -- looking into whether the particular field types match.
574 let unifySharedFields otherFields ext =
575 do unifySharedFieldsHelp context sharedFields
576 let allFields = Map.union (Map.map fst sharedFields) otherFields
577 merge context (Structure (Record1 allFields ext))
579 let uniqueExpFields = Map.difference expFields actFields
580 let uniqueActFields = Map.difference actFields expFields
582 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
583 (_, True, _, True) ->
584 do subUnify context expVar actVar
585 unifySharedFields Map.empty expVar
587 (Empty, _, _, False) ->
588 mismatch context $ Just $
589 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
591 (_, False, Empty, _) ->
592 mismatch context $ Just $
593 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
595 (_, False, _, True) ->
596 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
597 subUnify context subRecord actVar
598 unifySharedFields Map.empty subRecord
600 (_, True, _, False) ->
601 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
602 subUnify context expVar subRecord
603 unifySharedFields Map.empty subRecord
605 (Extension, False, Extension, False) ->
606 do let subFields = Map.union uniqueExpFields uniqueActFields
607 subExt <- fresh context Type.unnamedFlexVar
608 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
609 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
610 subUnify context expVar expRecord
611 subUnify context actRecord actVar
612 unifySharedFields subFields subExt
616unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
617unifySharedFieldsHelp context sharedFields =
618 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
619 case Maybe.catMaybes maybeBadFields of
624 mismatch context (Just (Error.BadFields (reverse badFields)))
627unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
628unifyField context (field, (expected, actual)) =
629 do result <- lift $ runExceptT $ subUnify context expected actual
635 return $ Just $ (,) field $
645-- GATHER RECORD STRUCTURE
648data RecordStructure =
650 { _fields :: Map.Map Text Variable
651 , _extVar :: Variable
652 , _extStruct :: ExtensionStructure
656data ExtensionStructure
661gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
662gatherFields context fields variable =
663 do (Descriptor content _ _ _) <- UF.descriptor variable
665 Structure (Record1 subFields subExt) ->
666 gatherFields context (Map.union fields subFields) subExt
668 Structure EmptyRecord1 ->
669 return (RecordStructure fields variable Empty)
672 -- TODO may be dropping useful alias info here
673 gatherFields context fields var
676 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