1module Type.Solve (solve) where
3import Control.Applicative ((<|>))
5import Control.Monad.State (execStateT, liftIO)
6import Control.Monad.Except (ExceptT, throwError)
7import qualified Data.Foldable as F
8import qualified Data.List as List
9import qualified Data.Map as Map
10import qualified Data.Traversable as T
11import qualified Data.UnionFind.IO as UF
13import qualified AST.Type as Type
14import qualified Reporting.Annotation as A
15import qualified Reporting.Error.Type as Error
16import qualified Type.State as TS
17import Type.Type as Type
22{-| Every variable has rank less than or equal to the maxRank of the pool.
23This sorts variables into the young and old pools accordingly.
25generalize :: TS.Pool -> TS.Solver ()
27 do youngMark <- TS.uniqueMark
28 let youngRank = TS.maxRank youngPool
30 do descriptor <- liftIO $ UF.descriptor var
31 liftIO $ UF.modifyDescriptor var (\desc -> desc { _mark = youngMark })
32 return $ Map.insertWith (++) (_rank descriptor) [var] dict
34 -- Sort the youngPool variables by rank.
35 rankDict <- foldM insert Map.empty (TS.inhabitants youngPool)
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 forM (Map.toList rankDict) $ \(poolRank, vars) ->
42 mapM (adjustRank youngMark visitedMark poolRank) vars
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 T.traverse (mapM registerIfNotRedundant) rankDict'
53 -- For variables with rank youngRank
54 -- If rank < youngRank: register in oldPool
55 -- otherwise generalize
56 let registerIfLowerRank var = do
57 isRedundant <- liftIO $ UF.redundant var
61 desc <- liftIO $ UF.descriptor var
62 case _rank desc < youngRank of
64 TS.register var >> return ()
66 liftIO $ UF.setDescriptor var $ desc
68 , _content = rigidify (_content desc)
71 mapM_ registerIfLowerRank (Map.findWithDefault [] youngRank rankDict)
74rigidify :: Content -> Content
77 Var Flex maybeSuper _ ->
78 Var Rigid maybeSuper Nothing
84-- adjust the ranks of variables such that ranks never increase as you
85-- move deeper into a variable.
86adjustRank :: Int -> Int -> Int -> Variable -> TS.Solver Int
87adjustRank youngMark visitedMark groupRank var =
88 do descriptor <- liftIO $ UF.descriptor var
89 adjustRankHelp youngMark visitedMark groupRank var descriptor
92adjustRankHelp :: Int -> Int -> Int -> Variable -> Descriptor -> TS.Solver Int
93adjustRankHelp youngMark visitedMark groupRank var descriptor =
94 if _mark descriptor == youngMark then
96 do -- Set the variable as marked first because it may be cyclic.
97 liftIO $ UF.modifyDescriptor var $ \desc ->
98 desc { _mark = visitedMark }
101 adjustRankContent youngMark visitedMark groupRank (_content descriptor)
103 liftIO $ UF.modifyDescriptor var $ \desc ->
104 desc { _rank = maxRank }
108 else if _mark descriptor /= visitedMark then
110 do let minRank = min groupRank (_rank descriptor)
111 liftIO $ UF.setDescriptor var (descriptor { _mark = visitedMark, _rank = minRank })
116 return (_rank descriptor)
119adjustRankContent :: Int -> Int -> Int -> Content -> TS.Solver Int
120adjustRankContent youngMark visitedMark groupRank content =
122 go = adjustRank youngMark visitedMark groupRank
134 Alias _ args realVar ->
135 maximum <$> mapM go (realVar : map snd args)
137 Structure (App1 func arg) ->
138 max <$> go func <*> go arg
140 Structure (Fun1 arg result) ->
141 max <$> go arg <*> go result
143 Structure EmptyRecord1 ->
146 Structure (Record1 fields extension) ->
147 maximum <$> mapM go (extension : Map.elems fields)
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 maybeInfinite <- liftIO $ occursHelp [] variable
300 case maybeInfinite of
305 do overallType <- liftIO (Type.toSrcType variable)
306 TS.addError region (Error.InfiniteType name overallType infiniteType)
309occursHelp :: [Variable] -> Variable -> IO (Maybe Type.Canonical)
311 if elem var seen then
312 do infiniteDescriptor <- UF.descriptor var
313 structureVar <- UF.fresh infiniteDescriptor
314 UF.setDescriptor var (infiniteDescriptor { _content = Error })
315 srcVar <- Type.toSrcType structureVar
319 do desc <- UF.descriptor var
320 case _content desc of
331 -- TODO is it okay to only check args?
332 F.asum <$> mapM (occursHelp (var:seen) . snd) args
336 go = occursHelp (var:seen)
340 (<|>) <$> go a <*> go b
343 (<|>) <$> go a <*> go b
348 Record1 fields ext ->
349 F.asum <$> 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