1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Type.Solve (solve) where
6import Control.Monad.Except (ExceptT, liftIO, throwError)
7import qualified Data.Foldable as F
8import qualified Data.Map as Map
9import qualified Data.Text as Text
11import qualified AST.Module.Name as ModuleName
12import qualified Reporting.Annotation as A
13import qualified Reporting.Error.Type as Error
14import qualified Type.State as TS
15import Type.Type as Type
17import qualified Type.UnionFind as UF
21{-| Every variable has rank less than or equal to the maxRank of the pool.
22This sorts variables into the young and old pools accordingly.
24generalize :: TS.Pool -> TS.Solver ()
25generalize (TS.Pool youngRank youngInhabitants) =
26 {-# SCC elm_compiler_type_generalize #-}
27 do youngMark <- TS.uniqueMark
29 {-# SCC elm_compiler_type_insert #-}
30 do descriptor <- UF.descriptor var
31 UF.modifyDescriptor var (\desc -> desc { _mark = youngMark })
32 return $ Map.insertWith (++) (_rank descriptor) [var] dict
34 -- Sort the youngPool variables by rank.
35 rankDict <- liftIO $ foldM insert Map.empty youngInhabitants
37 -- get the ranks right for each entry.
38 -- start at low ranks so that we only have to pass
39 -- over the information once.
40 visitedMark <- TS.uniqueMark
41 liftIO $ forM_ (Map.toList rankDict) $ \(poolRank, vars) ->
42 forM_ vars (adjustRank youngMark visitedMark poolRank)
44 -- For variables that have rank lowerer than youngRank, register them in
45 -- the old pool if they are not redundant.
46 let registerIfNotRedundant var =
47 do isRedundant <- liftIO $ UF.redundant var
48 if isRedundant then return var else TS.register var
50 let rankDict' = Map.delete youngRank rankDict
51 F.traverse_ (F.traverse_ registerIfNotRedundant) rankDict'
53 -- For variables with rank youngRank
54 -- If rank < youngRank: register in oldPool
55 -- otherwise generalize
56 let registerIfLowerRank var =
57 {-# SCC elm_compiler_type_register #-}
58 do isRedundant <- liftIO $ UF.redundant var
62 desc <- liftIO $ UF.descriptor var
63 case _rank desc < youngRank of
65 TS.register var >> return ()
67 liftIO $ UF.setDescriptor var $ desc
69 , _content = rigidify (_content desc)
72 mapM_ registerIfLowerRank (Map.findWithDefault [] youngRank rankDict)
75rigidify :: Content -> Content
78 Var Flex maybeSuper maybeName ->
79 Var Rigid maybeSuper maybeName
85-- adjust the ranks of variables such that ranks never increase as you
86-- move deeper into a variable.
87adjustRank :: Int -> Int -> Int -> Variable -> IO Int
88adjustRank youngMark visitedMark groupRank var =
89 {-# SCC elm_compiler_type_adjust #-}
90 do descriptor <- UF.descriptor var
91 adjustRankHelp youngMark visitedMark groupRank var descriptor
94adjustRankHelp :: Int -> Int -> Int -> Variable -> Descriptor -> IO Int
95adjustRankHelp youngMark visitedMark groupRank var descriptor@(Descriptor content rank mark _) =
96 if mark == youngMark then
98 do -- Set the variable as marked first because it may be cyclic.
99 UF.modifyDescriptor var $ \desc -> desc { _mark = visitedMark }
100 maxRank <- adjustRankContent youngMark visitedMark groupRank content
101 UF.modifyDescriptor var $ \desc -> desc { _rank = maxRank }
104 else if mark /= visitedMark then
106 do let minRank = min groupRank rank
107 UF.setDescriptor var (descriptor { _mark = visitedMark, _rank = minRank })
115adjustRankContent :: Int -> Int -> Int -> Content -> IO Int
116adjustRankContent youngMark visitedMark groupRank content =
118 go = adjustRank youngMark visitedMark groupRank
130 Alias _ args realVar ->
131 -- TODO do you have to crawl the args?
132 do realRank <- go realVar
133 foldM (\rank (_, argVar) -> max rank <$> go argVar) realRank args
135 Structure (App1 func arg) ->
136 max <$> go func <*> go arg
138 Structure (Fun1 arg result) ->
139 max <$> go arg <*> go result
141 Structure EmptyRecord1 ->
144 Structure (Record1 fields extension) ->
145 do extRank <- go extension
146 foldM (\rank field -> max rank <$> go field) extRank fields
153solve :: Constraint -> ExceptT [A.Located Error.Error] IO TS.State
155 {-# SCC elm_compiler_type_solve #-}
156 do state <- liftIO (TS.run (actuallySolve constraint))
157 case TS.sError state of
164actuallySolve :: Constraint -> TS.Solver ()
165actuallySolve constraint =
173 CEqual hint region term1 term2 ->
174 do t1 <- TS.flatten term1
175 t2 <- TS.flatten term2
176 unify hint region t1 t2
179 mapM_ actuallySolve cs
181 CLet [Scheme [] fqs constraint' _] CTrue ->
182 do oldEnv <- TS.getEnv
183 mapM_ TS.introduce fqs
184 actuallySolve constraint'
185 TS.modifyEnv (\_ -> oldEnv)
187 CLet schemes constraint' ->
188 do oldEnv <- TS.getEnv
189 headers <- Map.unions <$> mapM solveScheme schemes
190 TS.modifyEnv $ \env -> Map.union headers env
191 actuallySolve constraint'
192 mapM_ occurs $ Map.toList headers
193 TS.modifyEnv (\_ -> oldEnv)
195 CInstance region name term ->
198 case Map.lookup name env of
203 if ModuleName.isKernel name then
204 liftIO (mkVar Nothing)
207 error ("Could not find `" ++ Text.unpack name ++ "` when solving type constraints.")
210 unify (Error.Instance name) region freshCopy t
213solveScheme :: Scheme -> TS.Solver TS.Env
216 flatten (A.A region term) =
217 A.A region <$> TS.flatten term
220 Scheme [] [] constraint header ->
221 do actuallySolve constraint
222 traverse flatten header
224 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
225 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
226 oldPool <- TS.getPool
228 -- fill in a new pool when working on this scheme's constraints
229 freshPool <- TS.nextRankPool
230 TS.switchToPool freshPool
231 mapM_ TS.introduce quantifiers
232 header' <- traverse flatten header
233 actuallySolve constraint
235 youngPool <- TS.getPool
236 TS.switchToPool oldPool
238 mapM_ isGeneric rigidQuantifiers
246-- Check that a variable has rank == noRank, meaning that it can be generalized.
247isGeneric :: Variable -> TS.Solver ()
249 do desc <- liftIO $ UF.descriptor var
250 if _rank desc == noRank
252 else crash "Unable to generalize a type variable. It is not unranked."
258 "It looks like something went wrong with the type inference algorithm.\n\n"
260 ++ "Please create a minimal example that triggers this problem and report it to\n"
261 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
268occurs :: (Text.Text, A.Located Variable) -> TS.Solver ()
269occurs (name, A.A region variable) =
270 {-# SCC elm_compiler_type_occurs #-}
271 do hasOccurred <- liftIO $ occursHelp [] variable
277 do overallType <- liftIO (Type.toSrcType variable)
278 TS.addError region (Error.InfiniteType name overallType)
281occursHelp :: [Variable] -> Variable -> IO Bool
283 if elem var seen then
284 do infiniteDescriptor <- UF.descriptor var
285 UF.setDescriptor var (infiniteDescriptor { _content = Error })
289 do desc <- UF.descriptor var
290 case _content desc of
301 -- TODO is it okay to only check args?
302 or <$> mapM (occursHelp (var:seen) . snd) args
306 go = occursHelp (var:seen)
310 (||) <$> go a <*> go b
313 (||) <$> go a <*> go b
318 Record1 fields ext ->
319 or <$> mapM go (ext : Map.elems fields)
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