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)
132merge :: Context -> Content -> Unify ()
133merge (Context _ var1 (Descriptor _ rank1 _ _) var2 (Descriptor _ rank2 _ _)) content =
134 liftIO $ mergeHelp var1 var2 content (min rank1 rank2)
137mergeHelp :: Variable -> Variable -> Content -> Int -> IO ()
138mergeHelp var1 var2 newContent newRank =
140 Descriptor newContent newRank noMark Nothing
143fresh :: Context -> Content -> Unify Variable
144fresh (Context _ _ (Descriptor _ rank1 _ _) _ (Descriptor _ rank2 _ _)) content =
147 Descriptor content (min rank1 rank2) noMark Nothing
149 lift $ TS.register freshVariable
155-- ACTUALLY UNIFY THINGS
158guardedUnify :: Orientation -> Variable -> Variable -> Unify ()
159guardedUnify orientation left right =
160 do equivalent <- liftIO $ UF.equivalent left right
164 do leftDesc <- liftIO $ UF.descriptor left
165 rightDesc <- liftIO $ UF.descriptor right
166 actuallyUnify (Context orientation left leftDesc right rightDesc)
169subUnify :: Context -> Variable -> Variable -> Unify ()
170subUnify context var1 var2 =
171 guardedUnify (_orientation context) var1 var2
174actuallyUnify :: Context -> Unify ()
175actuallyUnify context@(Context _ _ (Descriptor firstContent _ _ _) _ (Descriptor secondContent _ _ _)) =
178 unifyFlex context secondContent
181 unifyFlexSuper context super firstContent secondContent
184 unifyRigid context Nothing name firstContent secondContent
186 RigidSuper super name ->
187 unifyRigid context (Just super) name firstContent secondContent
189 Alias name args realVar ->
190 unifyAlias context name args realVar secondContent
192 Structure flatType ->
193 unifyStructure context flatType firstContent secondContent
196 -- If there was an error, just pretend it is okay. This lets us avoid
197 -- "cascading" errors where one problem manifests as multiple message.
202-- UNIFY FLEXIBLE VARIABLES
205unifyFlex :: Context -> Content -> Unify ()
206unifyFlex context otherContent =
211 -- TODO see if wildcarding makes a noticable perf difference
214 merge context otherContent
217 merge context otherContent
220 merge context otherContent
223 merge context otherContent
226 merge context otherContent
229 merge context otherContent
233-- UNIFY RIGID VARIABLES
236unifyRigid :: Context -> Maybe Super -> Text -> Content -> Content -> Unify ()
237unifyRigid context maybeSuper name content otherContent =
240 merge context content
242 FlexSuper otherSuper _ ->
245 if combineRigidSupers super otherSuper then
246 merge context content
248 mismatch context $ Just $
249 Error.RigidSuperTooGeneric super name $ Error.SpecificSuper otherSuper
252 mismatch context $ Just $
253 Error.RigidVarTooGeneric name $ Error.SpecificSuper otherSuper
255 RigidVar otherName ->
256 mismatch context $ Just $
257 Error.RigidClash name otherName
259 RigidSuper _ otherName ->
260 mismatch context $ Just $
261 Error.RigidClash name otherName
263 Alias otherName _ _ ->
264 mismatch context $ Just $
265 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
266 Error.SpecificType otherName
268 Structure flatType ->
269 mismatch context $ Just $
270 maybe Error.RigidVarTooGeneric Error.RigidSuperTooGeneric maybeSuper name $
271 flatTypeToSpecificThing flatType
277flatTypeToSpecificThing :: FlatType -> Error.SpecificThing
278flatTypeToSpecificThing flatType =
281 Error.SpecificType name
284 Error.SpecificFunction
294-- UNIFY SUPER VARIABLES
297unifyFlexSuper :: Context -> Super -> Content -> Content -> Unify ()
298unifyFlexSuper context super content otherContent =
300 Structure flatType ->
301 unifyFlexSuperStructure context super flatType
304 mismatch context $ Just $
305 Error.RigidVarTooGeneric name (Error.SpecificSuper super)
307 RigidSuper otherSuper name ->
308 if combineRigidSupers otherSuper super then
309 merge context otherContent
311 mismatch context $ Just $
312 Error.RigidSuperTooGeneric otherSuper name (Error.SpecificSuper super)
315 merge context content
317 FlexSuper otherSuper _ ->
321 Number -> merge context content
322 Comparable -> merge context content
323 _ -> mismatch context Nothing -- Error.NumberAppendableClash
327 Comparable -> merge context otherContent
328 Number -> merge context otherContent
329 Appendable -> merge context (Type.unnamedFlexSuper CompAppend)
330 CompAppend -> merge context otherContent
334 Appendable -> merge context otherContent
335 Comparable -> merge context (Type.unnamedFlexSuper CompAppend)
336 CompAppend -> merge context otherContent
337 Number -> mismatch context Nothing -- Error.NumberAppendableClash
341 Comparable -> merge context content
342 Appendable -> merge context content
343 CompAppend -> merge context content
344 Number -> mismatch context Nothing -- Error.NumberAppendableClash
347 subUnify context (_first context) realVar
353combineRigidSupers :: Super -> Super -> Bool
354combineRigidSupers rigid flex =
356 || (rigid == Number && flex == Comparable)
357 || (rigid == CompAppend && (flex == Comparable || flex == Appendable))
360atomMatchesSuper :: Super -> Var.Canonical -> Bool
361atomMatchesSuper super name =
364 elem name [ Var.int, Var.float ]
367 elem name [ Var.string, Var.int, Var.float, Var.char]
376unifyFlexSuperStructure :: Context -> Super -> FlatType -> Unify ()
377unifyFlexSuperStructure context super flatType =
380 if atomMatchesSuper super name then
381 merge context (Structure flatType)
383 mismatch context $ Just $ Error.NotPartOfSuper super
385 App1 name [variable] | name == Var.list ->
388 mismatch context $ Just $ Error.NotPartOfSuper super
391 merge context (Structure flatType)
394 do comparableOccursCheck context
395 merge context (Structure flatType)
396 unifyComparableRecursive (_orientation context) variable
399 do comparableOccursCheck context
400 merge context (Structure flatType)
401 unifyComparableRecursive (_orientation context) variable
403 App1 name args | Var.isTuple name ->
406 mismatch context $ Just $ Error.NotPartOfSuper super
409 mismatch context $ Just $ Error.NotPartOfSuper super
412 if length args > 6 then
413 mismatch context (Just (Error.TooLongComparableTuple (length args)))
416 do comparableOccursCheck context
417 merge context (Structure flatType)
418 mapM_ (unifyComparableRecursive (_orientation context)) args
421 mismatch context $ Just $ Error.NotPartOfSuper super
424 mismatch context $ Just $ Error.NotPartOfSuper super
427-- TODO: is there some way to avoid doing this?
428-- Do type classes require occurs checks?
429comparableOccursCheck :: Context -> Unify ()
430comparableOccursCheck (Context _ _ _ var _) =
431 do hasOccurred <- liftIO $ Occurs.occurs var
433 then throwError Infinite
437unifyComparableRecursive :: Orientation -> Variable -> Unify ()
438unifyComparableRecursive orientation var =
439 do compVar <- liftIO $
440 do (Descriptor _ rank _ _) <- UF.descriptor var
441 UF.fresh $ Descriptor (Type.unnamedFlexSuper Comparable) rank noMark Nothing
443 lift $ TS.register compVar
445 guardedUnify orientation compVar var
448collectArgs :: Variable -> IO [Variable]
449collectArgs variable =
450 collectArgsHelp [] variable
453collectArgsHelp :: [Variable] -> Variable -> IO [Variable]
454collectArgsHelp revArgs variable =
455 do (Descriptor content _ _ _) <- UF.descriptor variable
457 Structure (Fun1 arg returnType) ->
458 collectArgsHelp (arg : revArgs) returnType
461 return $ variable : revArgs
468unifyAlias :: Context -> Var.Canonical -> [(Text, Variable)] -> Variable -> Content -> Unify ()
469unifyAlias context name args realVar otherContent =
472 merge context (Alias name args realVar)
475 subUnify context realVar (_second context)
478 subUnify context realVar (_second context)
481 subUnify context realVar (_second context)
483 Alias otherName otherArgs otherRealVar ->
484 if name == otherName then
485 do zipWithM_ (subUnify context) (map snd args) (map snd otherArgs)
486 merge context otherContent
489 subUnify context realVar otherRealVar
492 subUnify context realVar (_second context)
502unifyStructure :: Context -> FlatType -> Content -> Content -> Unify ()
503unifyStructure context flatType content otherContent =
506 merge context content
509 unifyFlexSuperStructure (reorient context) super flatType
512 mismatch context $ Just $ Error.RigidVarTooGeneric name (flatTypeToSpecificThing flatType)
514 RigidSuper super name ->
515 mismatch context $ Just $ Error.RigidSuperTooGeneric super name (flatTypeToSpecificThing flatType)
518 subUnify context (_first context) realVar
520 Structure otherTerm ->
521 case (flatType, otherTerm) of
522 (App1 name args, App1 otherName otherArgs) ->
523 if name == otherName then
524 do zipWithM_ (subUnify context) args otherArgs
525 merge context otherContent
527 else if isIntFloat name otherName || isIntFloat otherName name then
528 mismatch context (Just Error.IntFloat)
531 mismatch context Nothing
533 (Fun1 arg result, Fun1 otherArg otherResult) ->
534 do subUnify context arg otherArg
535 subUnify context result otherResult
536 merge context otherContent
538 (EmptyRecord1, EmptyRecord1) ->
539 merge context otherContent
541 (Record1 fields ext, EmptyRecord1) | Map.null fields ->
542 subUnify context ext (_second context)
544 (EmptyRecord1, Record1 fields ext) | Map.null fields ->
545 subUnify context (_first context) ext
547 (Record1 fields extension, Record1 otherFields otherExtension) ->
548 do firstStructure <- liftIO $ gatherFields context fields extension
549 secondStructure <- liftIO $ gatherFields context otherFields otherExtension
550 unifyRecord context firstStructure secondStructure
553 mismatch context Nothing
560isIntFloat :: Var.Canonical -> Var.Canonical -> Bool
561isIntFloat name otherName =
562 Var.isPrim "Int" name && Var.isPrim "Float" otherName
569unifyRecord :: Context -> RecordStructure -> RecordStructure -> Unify ()
570unifyRecord context firstStructure secondStructure =
571 do let (RecordStructure expFields expVar expStruct) = firstStructure
572 let (RecordStructure actFields actVar actStruct) = secondStructure
574 let sharedFields = Map.intersectionWith (,) expFields actFields
576 -- call after unifying extension, make sure record shape matches before
577 -- looking into whether the particular field types match.
578 let unifySharedFields otherFields ext =
579 do unifySharedFieldsHelp context sharedFields
580 let allFields = Map.union (Map.map fst sharedFields) otherFields
581 merge context (Structure (Record1 allFields ext))
583 let uniqueExpFields = Map.difference expFields actFields
584 let uniqueActFields = Map.difference actFields expFields
586 case (expStruct, Map.null uniqueExpFields, actStruct, Map.null uniqueActFields) of
587 (_, True, _, True) ->
588 do subUnify context expVar actVar
589 unifySharedFields Map.empty expVar
591 (Empty, _, _, False) ->
592 mismatch context $ Just $
593 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
595 (_, False, Empty, _) ->
596 mismatch context $ Just $
597 Error.MessyFields (Map.keys sharedFields) (Map.keys uniqueExpFields) (Map.keys uniqueActFields)
599 (_, False, _, True) ->
600 do subRecord <- fresh context (Structure (Record1 uniqueExpFields expVar))
601 subUnify context subRecord actVar
602 unifySharedFields Map.empty subRecord
604 (_, True, _, False) ->
605 do subRecord <- fresh context (Structure (Record1 uniqueActFields actVar))
606 subUnify context expVar subRecord
607 unifySharedFields Map.empty subRecord
609 (Extension, False, Extension, False) ->
610 do let subFields = Map.union uniqueExpFields uniqueActFields
611 subExt <- fresh context Type.unnamedFlexVar
612 expRecord <- fresh context (Structure (Record1 uniqueActFields subExt))
613 actRecord <- fresh context (Structure (Record1 uniqueExpFields subExt))
614 subUnify context expVar expRecord
615 subUnify context actRecord actVar
616 unifySharedFields subFields subExt
620unifySharedFieldsHelp :: Context -> Map.Map Text (Variable, Variable) -> Unify ()
621unifySharedFieldsHelp context sharedFields =
622 do maybeBadFields <- traverse (unifyField context) (Map.toList sharedFields)
623 case Maybe.catMaybes maybeBadFields of
628 mismatch context (Just (Error.BadFields (reverse badFields)))
631unifyField :: Context -> (Text, (Variable, Variable)) -> Unify (Maybe (Text, Maybe Error.Reason))
632unifyField context (field, (expected, actual)) =
633 do result <- lift $ runExceptT $ subUnify context expected actual
639 return $ Just $ (,) field $
652-- GATHER RECORD STRUCTURE
655data RecordStructure = RecordStructure
656 { _fields :: Map.Map Text Variable
657 , _extVar :: Variable
658 , _extStruct :: ExtensionStructure
662data ExtensionStructure
667gatherFields :: Context -> Map.Map Text Variable -> Variable -> IO RecordStructure
668gatherFields context fields variable =
669 do (Descriptor content _ _ _) <- UF.descriptor variable
671 Structure (Record1 subFields subExt) ->
672 gatherFields context (Map.union fields subFields) subExt
674 Structure EmptyRecord1 ->
675 return (RecordStructure fields variable Empty)
678 -- TODO may be dropping useful alias info here
679 gatherFields context fields var
682 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