1module Type.Solve (solve) where
4import Control.Monad.State (execStateT, liftIO)
5import Control.Monad.Except (ExceptT, throwError)
6import qualified Data.List as List
7import qualified Data.Map as Map
8import qualified Data.Traversable as T
9import qualified Data.UnionFind.IO as UF
11import qualified Reporting.Annotation as A
12import qualified Reporting.Error.Type as Error
13import qualified Type.State as TS
14import Type.Type as Type
19{-| Every variable has rank less than or equal to the maxRank of the pool.
20This sorts variables into the young and old pools accordingly.
22generalize :: TS.Pool -> TS.Solver ()
24 do youngMark <- TS.uniqueMark
25 let youngRank = TS.maxRank youngPool
27 do descriptor <- liftIO $ UF.descriptor var
28 liftIO $ UF.modifyDescriptor var (\desc -> desc { _mark = youngMark })
29 return $ Map.insertWith (++) (_rank descriptor) [var] dict
31 -- Sort the youngPool variables by rank.
32 rankDict <- foldM insert Map.empty (TS.inhabitants youngPool)
34 -- get the ranks right for each entry.
35 -- start at low ranks so that we only have to pass
36 -- over the information once.
37 visitedMark <- TS.uniqueMark
38 forM (Map.toList rankDict) $ \(poolRank, vars) ->
39 mapM (adjustRank youngMark visitedMark poolRank) vars
41 -- For variables that have rank lowerer than youngRank, register them in
42 -- the old pool if they are not redundant.
43 let registerIfNotRedundant var =
44 do isRedundant <- liftIO $ UF.redundant var
45 if isRedundant then return var else TS.register var
47 let rankDict' = Map.delete youngRank rankDict
48 T.traverse (mapM registerIfNotRedundant) rankDict'
50 -- For variables with rank youngRank
51 -- If rank < youngRank: register in oldPool
52 -- otherwise generalize
53 let registerIfLowerRank var = do
54 isRedundant <- liftIO $ UF.redundant var
58 desc <- liftIO $ UF.descriptor var
59 case _rank desc < youngRank of
61 TS.register var >> return ()
63 liftIO $ UF.setDescriptor var $ desc
65 , _content = rigidify (_content desc)
68 mapM_ registerIfLowerRank (Map.findWithDefault [] youngRank rankDict)
71rigidify :: Content -> Content
74 Var Flex maybeSuper _ ->
75 Var Rigid maybeSuper Nothing
81-- adjust the ranks of variables such that ranks never increase as you
82-- move deeper into a variable.
83adjustRank :: Int -> Int -> Int -> Variable -> TS.Solver Int
84adjustRank youngMark visitedMark groupRank var =
85 do descriptor <- liftIO $ UF.descriptor var
86 adjustRankHelp youngMark visitedMark groupRank var descriptor
89adjustRankHelp :: Int -> Int -> Int -> Variable -> Descriptor -> TS.Solver Int
90adjustRankHelp youngMark visitedMark groupRank var descriptor =
91 if _mark descriptor == youngMark then
93 do -- Set the variable as marked first because it may be cyclic.
94 liftIO $ UF.modifyDescriptor var $ \desc ->
95 desc { _mark = visitedMark }
98 adjustRankContent youngMark visitedMark groupRank (_content descriptor)
100 liftIO $ UF.modifyDescriptor var $ \desc ->
101 desc { _rank = maxRank }
105 else if _mark descriptor /= visitedMark then
107 do let minRank = min groupRank (_rank descriptor)
108 liftIO $ UF.setDescriptor var (descriptor { _mark = visitedMark, _rank = minRank })
113 return (_rank descriptor)
116adjustRankContent :: Int -> Int -> Int -> Content -> TS.Solver Int
117adjustRankContent youngMark visitedMark groupRank content =
119 go = adjustRank youngMark visitedMark groupRank
131 Alias _ args realVar ->
132 maximum <$> mapM go (realVar : map snd args)
134 Structure (App1 func arg) ->
135 max <$> go func <*> go arg
137 Structure (Fun1 arg result) ->
138 max <$> go arg <*> go result
140 Structure EmptyRecord1 ->
143 Structure (Record1 fields extension) ->
144 maximum <$> mapM go (extension : Map.elems fields)
146 Structure (Effects1 _) ->
154solve :: TypeConstraint -> ExceptT [A.Located Error.Error] IO TS.SolverState
157 liftIO (execStateT (actuallySolve constraint) TS.initialState)
158 case TS.sError state of
165actuallySolve :: TypeConstraint -> TS.Solver ()
166actuallySolve constraint =
174 CEqual hint region term1 term2 ->
175 do t1 <- TS.flatten term1
176 t2 <- TS.flatten term2
177 unify hint region t1 t2
180 mapM_ actuallySolve cs
182 CLet [Scheme [] fqs constraint' _] CTrue ->
183 do oldEnv <- TS.getEnv
184 mapM TS.introduce fqs
185 actuallySolve constraint'
186 TS.modifyEnv (\_ -> oldEnv)
188 CLet schemes constraint' ->
189 do oldEnv <- TS.getEnv
190 headers <- Map.unions <$> mapM solveScheme schemes
191 TS.modifyEnv $ \env -> Map.union headers env
192 actuallySolve constraint'
193 mapM occurs $ Map.toList headers
194 TS.modifyEnv (\_ -> oldEnv)
196 CInstance region name term ->
199 case Map.lookup name env of
204 if List.isPrefixOf "Native." name then
205 liftIO (mkVar Nothing)
208 error ("Could not find `" ++ name ++ "` when solving type constraints.")
211 unify (Error.Instance name) region freshCopy t
214solveScheme :: TypeScheme -> TS.Solver (Map.Map String (A.Located Variable))
217 flatten (A.A region term) =
218 A.A region <$> TS.flatten term
221 Scheme [] [] constraint header ->
222 do actuallySolve constraint
223 T.traverse flatten header
225 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
226 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
227 oldPool <- TS.getPool
229 -- fill in a new pool when working on this scheme's constraints
230 freshPool <- TS.nextRankPool
231 TS.switchToPool freshPool
232 mapM TS.introduce quantifiers
233 header' <- T.traverse flatten header
234 actuallySolve constraint
236 allDistinct rigidQuantifiers
237 youngPool <- TS.getPool
238 TS.switchToPool oldPool
240 mapM isGeneric rigidQuantifiers
246-- Checks that all of the given variables belong to distinct equivalence classes.
247-- Also checks that their structure is Nothing, so they represent a variable, not
248-- a more complex term.
249allDistinct :: [Variable] -> TS.Solver ()
251 do seenMark <- TS.uniqueMark
253 do desc <- liftIO $ UF.descriptor var
254 case _content desc of
256 crash "Can only generalize type variables, not structures."
259 crash "Can only generalize type variables, not structures."
262 crash "Can only generalize type variables, not aliases."
265 crash "Can only generalize type variables, not error types."
268 if _mark desc == seenMark then
269 crash "Duplicate variable during generalization."
272 liftIO (UF.setDescriptor var (desc { _mark = seenMark }))
275-- Check that a variable has rank == noRank, meaning that it can be generalized.
276isGeneric :: Variable -> TS.Solver ()
278 do desc <- liftIO $ UF.descriptor var
279 if _rank desc == noRank
281 else crash "Unable to generalize a type variable. It is not unranked."
287 "It looks like something went wrong with the type inference algorithm.\n\n"
289 ++ "Please create a minimal example that triggers this problem and report it to\n"
290 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
297occurs :: (String, A.Located Variable) -> TS.Solver ()
298occurs (name, A.A region variable) =
299 do isInfinite <- liftIO $ occursHelp [] variable
305 do overallType <- liftIO (Type.toSrcType variable)
306 TS.addError region (Error.InfiniteType name overallType)
309occursHelp :: [Variable] -> Variable -> IO Bool
311 if elem var seen then
312 do infiniteDescriptor <- UF.descriptor var
313 UF.setDescriptor var (infiniteDescriptor { _content = Error })
317 do desc <- UF.descriptor var
318 case _content desc of
329 -- TODO is it okay to only check args?
330 or <$> mapM (occursHelp (var:seen) . snd) args
334 go = occursHelp (var:seen)
338 (||) <$> go a <*> go b
341 (||) <$> go a <*> go b
346 Record1 fields ext ->
347 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