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
10import qualified Data.Vector as Vector
11import qualified Data.Vector.Mutable as MVector
13import qualified AST.Module.Name as ModuleName
14import qualified Reporting.Annotation as A
15import qualified Reporting.Error.Type as Error
16import qualified Type.Occurs as Occurs
17import qualified Type.State as TS
18import Type.Constraint (Constraint(..), Scheme(..))
19import Type.Type as Type
21import qualified Type.UnionFind as UF
28solve :: Constraint -> ExceptT [A.Located Error.Error] IO TS.State
30 {-# SCC elm_compiler_type_solve #-}
31 do state <- liftIO (TS.run (actuallySolve constraint))
32 case TS.sError state of
39actuallySolve :: Constraint -> TS.Solver ()
40actuallySolve constraint =
48 CEqual hint region term1 term2 ->
49 do t1 <- TS.flatten term1
50 t2 <- TS.flatten term2
51 unify hint region t1 t2
54 mapM_ actuallySolve cs
56 CLet [Scheme [] fqs constraint' _] CTrue ->
57 do oldEnv <- TS.getEnv
59 actuallySolve constraint'
60 TS.modifyEnv (\_ -> oldEnv)
62 CLet schemes constraint' ->
63 do oldEnv <- TS.getEnv
64 headers <- Map.unions <$> mapM solveScheme schemes
65 TS.modifyEnv $ \env -> Map.union headers env
66 actuallySolve constraint'
67 mapM_ occurs $ Map.toList headers
68 TS.modifyEnv (\_ -> oldEnv)
70 CInstance region name term ->
73 case Map.lookup name env of
78 if ModuleName.isKernel name then
82 error ("Could not find `" ++ Text.unpack name ++ "` when solving type constraints.")
85 unify (Error.Instance name) region freshCopy t
88solveScheme :: Scheme -> TS.Solver TS.Env
91 flatten (A.A region term) =
92 A.A region <$> TS.flatten term
95 Scheme [] [] constraint header ->
96 do actuallySolve constraint
97 traverse flatten header
99 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
100 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
101 oldPool <- TS.getPool
103 -- fill in a new pool when working on this scheme's constraints
104 freshPool <- TS.nextRankPool
105 TS.switchToPool freshPool
106 TS.introduce quantifiers
107 header' <- traverse flatten header
108 actuallySolve constraint
110 youngPool <- TS.getPool
111 TS.switchToPool oldPool
113 mapM_ isGeneric rigidQuantifiers
121-- Check that a variable has rank == noRank, meaning that it can be generalized.
122isGeneric :: Variable -> TS.Solver ()
124 do (Descriptor _ rank _ _) <- liftIO $ UF.descriptor var
127 else crash "Unable to generalize a type variable. It is not unranked."
133 "It looks like something went wrong with the type inference algorithm.\n\n"
135 ++ "Please create a minimal example that triggers this problem and report it to\n"
136 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
143occurs :: (Text.Text, A.Located Variable) -> TS.Solver ()
144occurs (name, A.A region variable) =
145 {-# SCC elm_compiler_type_occurs #-}
146 do hasOccurred <- liftIO $ Occurs.occurs variable
152 do overallType <- liftIO $ Type.toSrcType variable
153 (Descriptor _ rank mark copy) <- liftIO $ UF.descriptor variable
154 liftIO $ UF.setDescriptor variable (Descriptor (Error "∞") rank mark copy)
155 TS.addError region (Error.InfiniteType (Right name) overallType)
162{-| Every variable has rank less than or equal to the maxRank of the pool.
163This sorts variables into the young and old pools accordingly.
165generalize :: TS.Pool -> TS.Solver ()
166generalize pool@(TS.Pool youngRank _) =
167 {-# SCC elm_compiler_type_generalize #-}
168 do youngMark <- TS.uniqueMark
170 rankTable <- liftIO $ poolToRankTable youngMark pool
172 -- get the ranks right for each entry.
173 -- start at low ranks so that we only have to pass
174 -- over the information once.
175 visitedMark <- TS.uniqueMark
176 let adjust poolRank vars =
177 F.traverse_ (adjustRank youngMark visitedMark poolRank) vars
178 liftIO $ Vector.imapM_ adjust rankTable
180 -- For variables that have rank lowerer than youngRank, register them in
181 -- the old pool if they are not redundant.
182 Vector.forM_ (Vector.unsafeInit rankTable) $ \vars ->
184 do isRedundant <- liftIO $ UF.redundant var
185 if isRedundant then return var else TS.register var
187 -- For variables with rank youngRank
188 -- If rank < youngRank: register in oldPool
189 -- otherwise generalize
190 forM_ (Vector.unsafeLast rankTable) $ \var ->
191 {-# SCC elm_compiler_type_register #-}
192 do isRedundant <- liftIO $ UF.redundant var
196 (Descriptor content rank mark copy) <- liftIO $ UF.descriptor var
199 TS.register var >> return ()
201 liftIO $ UF.setDescriptor var $ Descriptor (rigidify content) noRank mark copy
204poolToRankTable :: Mark -> TS.Pool -> IO (Vector.Vector [Variable])
205poolToRankTable youngMark (TS.Pool youngRank youngInhabitants) =
206 do mutableTable <- MVector.replicate (youngRank + 1) []
208 -- Sort the youngPool variables by rank.
209 forM_ youngInhabitants $ \var ->
210 do (Descriptor content rank _ copy) <- UF.descriptor var
211 UF.setDescriptor var (Descriptor content rank youngMark copy)
212 MVector.modify mutableTable (var:) rank
214 Vector.unsafeFreeze mutableTable
217rigidify :: Content -> Content
220 Var Flex maybeSuper maybeName ->
221 Var Rigid maybeSuper maybeName
231-- Adjust variable ranks such that ranks never increase as you move deeper.
232-- This way the outermost rank is representative of the entire structure.
234adjustRank :: Mark -> Mark -> Int -> Variable -> IO Int
235adjustRank youngMark visitedMark groupRank var =
236 {-# SCC elm_compiler_type_adjust #-}
237 do (Descriptor content rank mark copy) <- UF.descriptor var
238 if mark == youngMark then
239 do -- Set the variable as marked first because it may be cyclic.
240 UF.setDescriptor var $ Descriptor content rank visitedMark copy
241 maxRank <- adjustRankContent youngMark visitedMark groupRank content
242 UF.setDescriptor var $ Descriptor content maxRank visitedMark copy
245 else if mark == visitedMark then
249 do let minRank = min groupRank rank
250 -- TODO how can minRank ever be groupRank?
251 UF.setDescriptor var $ Descriptor content minRank visitedMark copy
255adjustRankContent :: Mark -> Mark -> Int -> Content -> IO Int
256adjustRankContent youngMark visitedMark groupRank content =
258 go = adjustRank youngMark visitedMark groupRank
268 -- THEORY: anything in the realVar would be outermostRank
269 foldM (\rank (_, argVar) -> max rank <$> go argVar) outermostRank args
271 Structure flatType ->
274 foldM (\rank arg -> max rank <$> go arg) outermostRank args
277 max <$> go arg <*> go result
280 -- THEORY: an empty record never needs to get generalized
283 Record1 fields extension ->
284 do extRank <- go extension
285 foldM (\rank field -> max rank <$> go field) extRank 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