1module Type.Solve (solve) where
3import Control.Applicative ((<$>), (<*>), (<|>))
5import Control.Monad.State (StateT, 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
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 -> StateT TS.SolverState IO ()
26 do youngMark <- TS.uniqueMark
27 let youngRank = TS.maxRank youngPool
29 do descriptor <- liftIO $ UF.descriptor var
30 liftIO $ UF.modifyDescriptor var (\desc -> desc { mark = youngMark })
31 return $ Map.insertWith (++) (rank descriptor) [var] dict
33 -- Sort the youngPool variables by rank.
34 rankDict <- foldM insert Map.empty (TS.inhabitants youngPool)
36 -- get the ranks right for each entry.
37 -- start at low ranks so that we only have to pass
38 -- over the information once.
39 visitedMark <- TS.uniqueMark
40 forM (Map.toList rankDict) $ \(poolRank, vars) ->
41 mapM (adjustRank youngMark visitedMark poolRank) vars
43 -- For variables that have rank lowerer than youngRank, register them in
44 -- the old pool if they are not redundant.
45 let registerIfNotRedundant var =
46 do isRedundant <- liftIO $ UF.redundant var
47 if isRedundant then return var else TS.register var
49 let rankDict' = Map.delete youngRank rankDict
50 T.traverse (mapM registerIfNotRedundant) rankDict'
52 -- For variables with rank youngRank
53 -- If rank < youngRank: register in oldPool
54 -- otherwise generalize
55 let registerIfLowerRank var = do
56 isRedundant <- liftIO $ UF.redundant var
60 desc <- liftIO $ UF.descriptor var
61 case rank desc < youngRank of
62 True -> TS.register var >> return ()
64 let flex' = case flex desc of { Flexible -> Rigid ; other -> other }
65 liftIO $ UF.setDescriptor var (desc { rank = noRank, flex = flex' })
67 mapM_ registerIfLowerRank (Map.findWithDefault [] youngRank rankDict)
70-- adjust the ranks of variables such that ranks never increase as you
71-- move deeper into a variable.
72adjustRank :: Int -> Int -> Int -> Variable -> StateT TS.SolverState IO Int
73adjustRank youngMark visitedMark groupRank var =
74 do descriptor <- liftIO $ UF.descriptor var
76 () | mark descriptor == youngMark ->
77 do -- Set the variable as marked first because it may be cyclic.
78 liftIO $ UF.modifyDescriptor var $ \desc ->
79 desc { mark = visitedMark }
80 rank' <- maybe (return groupRank) adjustTerm (structure descriptor)
81 liftIO $ UF.modifyDescriptor var $ \desc ->
85 | mark descriptor /= visitedMark ->
86 do let rank' = min groupRank (rank descriptor)
87 liftIO $ UF.setDescriptor var (descriptor { mark = visitedMark, rank = rank' })
91 return (rank descriptor)
94 adjustRank youngMark visitedMark groupRank
99 max `liftM` adjust a `ap` adjust b
102 max `liftM` adjust a `ap` adjust b
110 Record1 fields extension ->
111 do ranks <- mapM adjust (concat (Map.elems fields))
112 rnk <- adjust extension
113 return . maximum $ rnk : ranks
118solve :: TypeConstraint -> ExceptT [A.Located Error.Error] IO TS.SolverState
121 liftIO (execStateT (actuallySolve constraint) TS.initialState)
122 case TS.sError state of
129actuallySolve :: TypeConstraint -> StateT TS.SolverState IO ()
130actuallySolve constraint =
138 CEqual hint region term1 term2 ->
139 do t1 <- TS.flatten term1
140 t2 <- TS.flatten term2
141 unify hint region t1 t2
144 mapM_ actuallySolve cs
146 CLet [Scheme [] fqs constraint' _] CTrue ->
147 do oldEnv <- TS.getEnv
148 mapM TS.introduce fqs
149 actuallySolve constraint'
150 TS.modifyEnv (\_ -> oldEnv)
152 CLet schemes constraint' ->
153 do oldEnv <- TS.getEnv
154 headers <- Map.unions `fmap` mapM solveScheme schemes
155 TS.modifyEnv $ \env -> Map.union headers env
156 actuallySolve constraint'
157 mapM occurs $ Map.toList headers
158 TS.modifyEnv (\_ -> oldEnv)
160 CInstance region name term ->
163 case Map.lookup name env of
168 if List.isPrefixOf "Native." name
169 then liftIO (variable Flexible)
170 else error ("Could not find `" ++ name ++ "` when solving type constraints.")
173 unify (Error.Instance name) region freshCopy t
178 -> StateT TS.SolverState IO (Map.Map String (A.Located Variable))
181 flatten (A.A region term) =
182 A.A region <$> TS.flatten term
185 Scheme [] [] constraint header ->
186 do actuallySolve constraint
187 T.traverse flatten header
189 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
190 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
191 oldPool <- TS.getPool
193 -- fill in a new pool when working on this scheme's constraints
194 freshPool <- TS.nextRankPool
195 TS.switchToPool freshPool
196 mapM TS.introduce quantifiers
197 header' <- T.traverse flatten header
198 actuallySolve constraint
200 allDistinct rigidQuantifiers
201 youngPool <- TS.getPool
202 TS.switchToPool oldPool
204 mapM isGeneric rigidQuantifiers
210-- Checks that all of the given variables belong to distinct equivalence classes.
211-- Also checks that their structure is Nothing, so they represent a variable, not
212-- a more complex term.
213allDistinct :: [Variable] -> StateT TS.SolverState IO ()
215 do seen <- TS.uniqueMark
217 do desc <- liftIO $ UF.descriptor var
218 case structure desc of
220 crash "Cannot generalize something that is not a type variable."
224 then crash "Duplicate variable during generalization."
225 else liftIO (UF.setDescriptor var (desc { mark = seen }))
228-- Check that a variable has rank == noRank, meaning that it can be generalized.
229isGeneric :: Variable -> StateT TS.SolverState IO ()
231 do desc <- liftIO $ UF.descriptor var
232 if rank desc == noRank
234 else crash "Unable to generalize a type variable. It is not unranked."
240 "It looks like something went wrong with the type inference algorithm.\n"
242 ++ "Please create a minimal example that triggers this problem and report it to\n"
243 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
246occurs :: (String, A.Located Variable) -> StateT TS.SolverState IO ()
247occurs (name, A.A region variable) =
248 do maybePair <- liftIO $ occursHelp [] variable
255 info = Error.InfiniteTypeInfo name t1 t2
257 TS.addError region (Error.InfiniteType info)
263 -> IO (Maybe (Type.Canonical, Type.Canonical))
265 if elem var seen then
266 do desc <- UF.descriptor var
267 UF.setDescriptor var (desc { structure = Nothing })
268 var' <- UF.fresh desc
269 srcVar <- toSrcType var
270 srcVar' <- toSrcType var'
271 return (Just (srcVar, srcVar'))
273 do desc <- UF.descriptor var
274 case structure desc of
279 let go = occursHelp (var:seen)
283 (<|>) <$> go a <*> go b
286 (<|>) <$> go a <*> go b
294 Record1 fields ext ->
295 F.asum <$> mapM go (ext : concat (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