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 maybeName ->
75 Var Rigid maybeSuper maybeName
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)
151solve :: TypeConstraint -> ExceptT [A.Located Error.Error] IO TS.SolverState
154 liftIO (execStateT (actuallySolve constraint) TS.initialState)
155 case TS.sError state of
162actuallySolve :: TypeConstraint -> TS.Solver ()
163actuallySolve constraint =
171 CEqual hint region term1 term2 ->
172 do t1 <- TS.flatten term1
173 t2 <- TS.flatten term2
174 unify hint region t1 t2
177 mapM_ actuallySolve cs
179 CLet [Scheme [] fqs constraint' _] CTrue ->
180 do oldEnv <- TS.getEnv
181 mapM TS.introduce fqs
182 actuallySolve constraint'
183 TS.modifyEnv (\_ -> oldEnv)
185 CLet schemes constraint' ->
186 do oldEnv <- TS.getEnv
187 headers <- Map.unions <$> mapM solveScheme schemes
188 TS.modifyEnv $ \env -> Map.union headers env
189 actuallySolve constraint'
190 mapM occurs $ Map.toList headers
191 TS.modifyEnv (\_ -> oldEnv)
193 CInstance region name term ->
196 case Map.lookup name env of
201 if List.isPrefixOf "Native." name then
202 liftIO (mkVar Nothing)
205 error ("Could not find `" ++ name ++ "` when solving type constraints.")
208 unify (Error.Instance name) region freshCopy t
211solveScheme :: TypeScheme -> TS.Solver (Map.Map String (A.Located Variable))
214 flatten (A.A region term) =
215 A.A region <$> TS.flatten term
218 Scheme [] [] constraint header ->
219 do actuallySolve constraint
220 T.traverse flatten header
222 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
223 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
224 oldPool <- TS.getPool
226 -- fill in a new pool when working on this scheme's constraints
227 freshPool <- TS.nextRankPool
228 TS.switchToPool freshPool
229 mapM TS.introduce quantifiers
230 header' <- T.traverse flatten header
231 actuallySolve constraint
233 youngPool <- TS.getPool
234 TS.switchToPool oldPool
236 mapM isGeneric rigidQuantifiers
243-- Check that a variable has rank == noRank, meaning that it can be generalized.
244isGeneric :: Variable -> TS.Solver ()
246 do desc <- liftIO $ UF.descriptor var
247 if _rank desc == noRank
249 else crash "Unable to generalize a type variable. It is not unranked."
255 "It looks like something went wrong with the type inference algorithm.\n\n"
257 ++ "Please create a minimal example that triggers this problem and report it to\n"
258 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
265occurs :: (String, A.Located Variable) -> TS.Solver ()
266occurs (name, A.A region variable) =
267 do isInfinite <- liftIO $ occursHelp [] variable
273 do overallType <- liftIO (Type.toSrcType variable)
274 TS.addError region (Error.InfiniteType name overallType)
277occursHelp :: [Variable] -> Variable -> IO Bool
279 if elem var seen then
280 do infiniteDescriptor <- UF.descriptor var
281 UF.setDescriptor var (infiniteDescriptor { _content = Error })
285 do desc <- UF.descriptor var
286 case _content desc of
297 -- TODO is it okay to only check args?
298 or <$> mapM (occursHelp (var:seen) . snd) args
302 go = occursHelp (var:seen)
306 (||) <$> go a <*> go b
309 (||) <$> go a <*> go b
314 Record1 fields ext ->
315 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