1module Type.Unify (unify) where
3import Control.Applicative ((<|>))
4import Control.Monad.Except (ExceptT, throwError, runExceptT)
5import Control.Monad.State as State
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.UnionFind.IO as UF
9import Text.PrettyPrint (render)
11import qualified AST.Type as AstType
12import qualified AST.Variable as Var
13import qualified Reporting.Region as R
14import qualified Reporting.Error.Type as Error
15import qualified Type.State as TS
16import Type.Type as Type
17import Type.PrettyPrint
18import Elm.Utils ((|>))
26 -> StateT TS.SolverState IO ()
27unify hint region variable1 variable2 =
28 do result <- runExceptT (unifyHelp variable1 variable2)
33 Left (RawMismatch (leftType, rightType, note)) ->
34 TS.addError region $ Error.Mismatch $
35 Error.MismatchInfo hint leftType rightType note
38-- ACTUALLY UNIFY STUFF
41 ExceptT RawMismatch (StateT TS.SolverState IO)
45 RawMismatch (AstType.Canonical, AstType.Canonical, Maybe String)
49 :: UF.Point Descriptor
50 -> UF.Point Descriptor
53typeError leftType rightType note =
54 do leftSrcType <- liftIO (toSrcType leftType)
55 rightSrcType <- liftIO (toSrcType rightType)
57 UF.setDescriptor leftType (Type.descriptor Error)
58 UF.setDescriptor rightType (Type.descriptor Error)
59 throwError (RawMismatch (leftSrcType, rightSrcType, note))
62unifyHelp :: Variable -> Variable -> Unify ()
63unifyHelp variable1 variable2 =
64 do equivalent <- liftIO $ UF.equivalent variable1 variable2
67 else actuallyUnify variable1 variable2
70actuallyUnify :: Variable -> Variable -> Unify ()
71actuallyUnify variable1 variable2 = do
72 desc1 <- liftIO $ UF.descriptor variable1
73 desc2 <- liftIO $ UF.descriptor variable2
74 let (name', flex', rank', alias') = combinedDescriptors desc1 desc2
79 if rank desc1 < rank desc2
80 then UF.union variable2 variable1
81 else UF.union variable1 variable2
82 UF.modifyDescriptor variable1 $ \desc ->
83 desc { structure = structure desc1
92 if rank desc1 < rank desc2
93 then UF.union variable2 variable1
94 else UF.union variable1 variable2
95 UF.modifyDescriptor variable2 $ \desc ->
96 desc { structure = structure desc2
103 if rank desc1 < rank desc2 then merge1 else merge2
105 fresh :: Maybe (Term1 Variable) -> Unify Variable
107 do v <- liftIO . UF.fresh $ Descriptor
108 { structure = structure
119 do liftIO $ UF.modifyDescriptor v $ \desc -> desc { flex = Flexible }
120 unifyHelp variable1 variable2
122 unifyNumber svar (Var.Canonical home name) =
124 Var.BuiltIn | name `elem` ["Int","Float"] ->
127 Var.Local | List.isPrefixOf "number" name ->
130 Var.TopLevel _ | List.isPrefixOf "number" name ->
134 typeError variable1 variable2 Nothing
136 comparableError maybeHint =
137 typeError variable1 variable2 $ Just $
138 "Note: comparable types include Int, Float, Char, String, lists, and tuples."
139 ++ maybe "" ("\n"++) maybeHint
142 typeError variable1 variable2 $ Just $
143 "Note: appendable types include only Strings, Lists, and Text."
145 unifyComparable v (Var.Canonical home name) =
147 Var.BuiltIn | name `elem` ["Int","Float","Char","String"] ->
149 Var.Local | List.isPrefixOf "comparable" name ->
152 comparableError Nothing
154 unifyComparableStructure varSuper varFlex =
155 do struct <- liftIO $ collectApps varFlex
158 comparableError Nothing
161 do flexAndUnify varSuper
162 unifyHelp v =<< liftIO (variable $ Is Comparable)
165 if length vs > 6 then
166 comparableError $ Just $
167 "Furthermore, it cannot be a tuple with more than 6 elements."
169 do flexAndUnify varSuper
170 cmpVars <- liftIO $ forM [1..length vs] $ \_ -> variable (Is Comparable)
171 zipWithM_ unifyHelp vs cmpVars
173 unifyAppendable varSuper varFlex =
174 do struct <- liftIO $ collectApps varFlex
176 List _ -> flexAndUnify varSuper
180 typeError variable1 variable2 $ Just $
181 rigidErrorMessage (render (pretty Never var))
184 case (flex desc1, flex desc2, name desc1, name desc2) of
185 (Is super1, Is super2, _, _)
186 | super1 == super2 -> merge
187 (Is Number, Is Comparable, _, _) -> merge1
188 (Is Comparable, Is Number, _, _) -> merge2
190 (Is Number, _, _, Just name) -> unifyNumber variable1 name
191 (_, Is Number, Just name, _) -> unifyNumber variable2 name
193 (Is Comparable, _, _, Just name) -> unifyComparable variable1 name
194 (_, Is Comparable, Just name, _) -> unifyComparable variable2 name
195 (Is Comparable, _, _, _) -> unifyComparableStructure variable1 variable2
196 (_, Is Comparable, _, _) -> unifyComparableStructure variable2 variable1
198 (Is Appendable, _, _, Just name)
199 | Var.isText name || Var.isPrim "String" name -> flexAndUnify variable1
200 (_, Is Appendable, Just name, _)
201 | Var.isText name || Var.isPrim "String" name -> flexAndUnify variable2
202 (Is Appendable, _, _, _) -> unifyAppendable variable1 variable2
203 (_, Is Appendable, _, _) -> unifyAppendable variable2 variable1
205 (Rigid, _, _, _) -> rigidError variable1
206 (_, Rigid, _, _) -> rigidError variable2
207 _ -> typeError variable1 variable2 Nothing
209 case (structure desc1, structure desc2) of
211 | flex desc1 == Flexible && flex desc1 == Flexible ->
215 | flex desc1 == Flexible -> merge2
216 | flex desc1 == Error -> return ()
219 | flex desc2 == Flexible -> merge1
220 | flex desc2 == Error -> return ()
222 (Just (Var1 v), _) ->
223 unifyHelp v variable2
225 (_, Just (Var1 v)) ->
226 unifyHelp variable1 v
234 (Just type1, Just type2) ->
235 case (type1,type2) of
236 (App1 term1 term2, App1 term1' term2') ->
238 unifyHelp term1 term1'
239 unifyHelp term2 term2'
240 (Fun1 term1 term2, Fun1 term1' term2') ->
242 unifyHelp term1 term1'
243 unifyHelp term2 term2'
245 (EmptyRecord1, EmptyRecord1) ->
248 (Record1 fields ext, EmptyRecord1) | Map.null fields -> unifyHelp ext variable2
249 (EmptyRecord1, Record1 fields ext) | Map.null fields -> unifyHelp variable1 ext
251 (Record1 _ _, Record1 _ _) ->
252 recordUnify fresh variable1 variable2
254 _ -> typeError variable1 variable2 Nothing
259rigidErrorMessage :: String -> String
260rigidErrorMessage var =
261 "Could not unify user provided type variable `" ++ var ++ "`. The most likely cases are:\n\n"
262 ++ " 1. The type you wrote down is too general. It says any type can go through\n"
263 ++ " but in fact only a certain type can.\n"
264 ++ " 2. A type variable is probably being shared between two type annotations.\n"
265 ++ " They are treated as different things in the current implementation, so\n"
266 ++ " Try commenting out some type annotations and see what happens."
272 :: (Maybe (Term1 Variable) -> Unify Variable)
276recordUnify fresh variable1 variable2 =
277 do (ExpandedRecord fields1 ext1) <- liftIO (gatherFields variable1)
278 (ExpandedRecord fields2 ext2) <- liftIO (gatherFields variable2)
280 unifyOverlappingFields fields1 fields2
282 let freshRecord fields ext =
283 fresh (Just (Record1 fields ext))
285 let uniqueFields1 = diffFields fields1 fields2
286 let uniqueFields2 = diffFields fields2 fields1
288 let addFieldMismatchError missingFields =
289 typeError variable1 variable2 $ Just $
290 fieldMismatchError missingFields
293 (Empty _, Empty _) ->
294 case Map.null uniqueFields1 && Map.null uniqueFields2 of
296 False -> typeError variable1 variable2 Nothing
298 (Empty var1, Extension var2) ->
299 case (Map.null uniqueFields1, Map.null uniqueFields2) of
300 (_, False) -> addFieldMismatchError uniqueFields2
301 (True, True) -> unifyHelp var1 var2
303 do subRecord <- freshRecord uniqueFields1 var1
304 unifyHelp subRecord var2
306 (Extension var1, Empty var2) ->
307 case (Map.null uniqueFields1, Map.null uniqueFields2) of
308 (False, _) -> addFieldMismatchError uniqueFields1
309 (True, True) -> unifyHelp var1 var2
311 do subRecord <- freshRecord uniqueFields2 var2
312 unifyHelp var1 subRecord
314 (Extension var1, Extension var2) ->
315 case (Map.null uniqueFields1, Map.null uniqueFields2) of
320 do subRecord <- freshRecord uniqueFields2 var2
321 unifyHelp var1 subRecord
324 do subRecord <- freshRecord uniqueFields1 var1
325 unifyHelp subRecord var2
328 do record1' <- freshRecord uniqueFields1 =<< fresh Nothing
329 record2' <- freshRecord uniqueFields2 =<< fresh Nothing
330 unifyHelp record1' var2
331 unifyHelp var1 record2'
334unifyOverlappingFields
335 :: Map.Map String [Variable]
336 -> Map.Map String [Variable]
338unifyOverlappingFields fields1 fields2 =
339 Map.intersectionWith (zipWith unifyHelp) fields1 fields2
345diffFields :: Map.Map String [a] -> Map.Map String [a] -> Map.Map String [a]
346diffFields fields1 fields2 =
347 let eat (_:xs) (_:ys) = eat xs ys
350 Map.union (Map.intersectionWith eat fields1 fields2) fields1
351 |> Map.filter (not . null)
354data ExpandedRecord = ExpandedRecord
355 { _fields :: Map.Map String [Variable]
356 , _extension :: Extension
359data Extension = Empty Variable | Extension Variable
362gatherFields :: Variable -> IO ExpandedRecord
364 do desc <- UF.descriptor var
365 case structure desc of
366 (Just (Record1 fields ext)) ->
367 do (ExpandedRecord deeperFields rootExt) <- gatherFields ext
368 return (ExpandedRecord (Map.unionWith (++) fields deeperFields) rootExt)
370 (Just EmptyRecord1) ->
371 return (ExpandedRecord Map.empty (Empty var))
374 return (ExpandedRecord Map.empty (Extension var))
377-- assumes that one of the dicts has stuff in it
378fieldMismatchError :: Map.Map String a -> String
379fieldMismatchError missingFields =
380 case Map.keys missingFields of
383 "Looks like a record is missing the field `" ++ key ++ "`"
385 "Looks like a record is missing fields "
386 ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
392 -> (Maybe Var.Canonical, Flex, Int, Alias Variable)
393combinedDescriptors desc1 desc2 =
394 (name', flex', rank', alias')
398 min (rank desc1) (rank desc2)
400 alias' :: Alias Variable
402 alias desc1 <|> alias desc2
404 name' :: Maybe Var.Canonical
406 case (name desc1, name desc2) of
407 (Just name1, Just name2) ->
408 case (flex desc1, flex desc2) of
409 (_, Flexible) -> Just name1
410 (Flexible, _) -> Just name2
411 (Is Number, Is _) -> Just name1
412 (Is _, Is Number) -> Just name2
413 (Is _, Is _) -> Just name1
415 (Just name1, _) -> Just name1
416 (_, Just name2) -> Just name2
421 case (flex desc1, flex desc2) of
424 (Is Number, Is _) -> Is Number
425 (Is _, Is Number) -> Is Number
426 (Is super, Is _) -> Is super
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