1module Type.Solve (solve) where
4import Control.Monad.State
5import qualified Data.List as List
6import qualified Data.Map as Map
7import qualified Data.Traversable as Traversable
8import qualified Data.UnionFind.IO as UF
11import qualified Type.ExtraChecks as Check
12import qualified Type.Hint as Hint
13import qualified Type.State as TS
14import qualified AST.Annotation as A
17-- | Every variable has rank less than or equal to the maxRank of the pool.
18-- This sorts variables into the young and old pools accordingly.
19generalize :: TS.Pool -> StateT TS.SolverState IO ()
21 do youngMark <- TS.uniqueMark
22 let youngRank = TS.maxRank youngPool
24 descriptor <- liftIO $ UF.descriptor var
25 liftIO $ UF.modifyDescriptor var (\desc -> desc { mark = youngMark })
26 return $ Map.insertWith (++) (rank descriptor) [var] dict
28 -- Sort the youngPool variables by rank.
29 rankDict <- foldM insert Map.empty (TS.inhabitants youngPool)
31 -- get the ranks right for each entry.
32 -- start at low ranks so that we only have to pass
33 -- over the information once.
34 visitedMark <- TS.uniqueMark
35 forM (Map.toList rankDict) $ \(poolRank, vars) ->
36 mapM (adjustRank youngMark visitedMark poolRank) vars
38 -- For variables that have rank lowerer than youngRank, register them in
39 -- the old pool if they are not redundant.
40 let registerIfNotRedundant var = do
41 isRedundant <- liftIO $ UF.redundant var
42 if isRedundant then return var else TS.register var
44 let rankDict' = Map.delete youngRank rankDict
45 Traversable.traverse (mapM registerIfNotRedundant) rankDict'
47 -- For variables with rank youngRank
48 -- If rank < youngRank: register in oldPool
49 -- otherwise generalize
50 let registerIfLowerRank var = do
51 isRedundant <- liftIO $ UF.redundant var
55 desc <- liftIO $ UF.descriptor var
56 case rank desc < youngRank of
57 True -> TS.register var >> return ()
59 let flex' = case flex desc of { Flexible -> Rigid ; other -> other }
60 liftIO $ UF.setDescriptor var (desc { rank = noRank, flex = flex' })
62 mapM_ registerIfLowerRank (Map.findWithDefault [] youngRank rankDict)
65-- adjust the ranks of variables such that ranks never increase as you
66-- move deeper into a variable.
67adjustRank :: Int -> Int -> Int -> Variable -> StateT TS.SolverState IO Int
68adjustRank youngMark visitedMark groupRank var =
69 do descriptor <- liftIO $ UF.descriptor var
71 () | mark descriptor == youngMark ->
72 do -- Set the variable as marked first because it may be cyclic.
73 liftIO $ UF.modifyDescriptor var $ \desc ->
74 desc { mark = visitedMark }
75 rank' <- maybe (return groupRank) adjustTerm (structure descriptor)
76 liftIO $ UF.modifyDescriptor var $ \desc ->
80 | mark descriptor /= visitedMark ->
81 do let rank' = min groupRank (rank descriptor)
82 liftIO $ UF.setDescriptor var (descriptor { mark = visitedMark, rank = rank' })
86 return (rank descriptor)
88 adjust = adjustRank youngMark visitedMark groupRank
92 App1 a b -> max `liftM` adjust a `ap` adjust b
93 Fun1 a b -> max `liftM` adjust a `ap` adjust b
95 EmptyRecord1 -> return outermostRank
96 Record1 fields extension ->
97 do ranks <- mapM adjust (concat (Map.elems fields))
98 rnk <- adjust extension
99 return . maximum $ rnk : ranks
102solve :: TypeConstraint -> StateT TS.SolverState IO ()
103solve (A.A region constraint) =
111 CEqual term1 term2 ->
112 do t1 <- TS.flatten term1
113 t2 <- TS.flatten term2
119 CLet [Scheme [] fqs constraint' _] (A.A _ CTrue) ->
120 do oldEnv <- TS.getEnv
121 mapM TS.introduce fqs
123 TS.modifyEnv (\_ -> oldEnv)
125 CLet schemes constraint' ->
126 do oldEnv <- TS.getEnv
127 headers <- Map.unions `fmap` mapM (solveScheme region) schemes
128 TS.modifyEnv $ \env -> Map.union headers env
130 mapM Check.occurs $ Map.toList headers
131 TS.modifyEnv (\_ -> oldEnv)
133 CInstance name term ->
136 case Map.lookup name env of
137 Just tipe -> TS.makeInstance tipe
139 | List.isPrefixOf "Native." name -> liftIO (variable Flexible)
141 error ("Could not find '" ++ name ++ "' when solving type constraints.")
144 unify region freshCopy t
147solveScheme :: A.Region -> TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
148solveScheme region scheme =
150 Scheme [] [] constraint header ->
152 Traversable.traverse TS.flatten header
154 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
155 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
156 oldPool <- TS.getPool
158 -- fill in a new pool when working on this scheme's constraints
159 freshPool <- TS.nextRankPool
160 TS.switchToPool freshPool
161 mapM TS.introduce quantifiers
162 header' <- Traversable.traverse TS.flatten header
165 allDistinct region rigidQuantifiers
166 youngPool <- TS.getPool
167 TS.switchToPool oldPool
169 mapM (isGeneric region) rigidQuantifiers
176 -> UF.Point Descriptor
177 -> UF.Point Descriptor
178 -> StateT TS.SolverState IO ()
179addHint region hint t1 t2 =
180 do msg <- liftIO (Hint.create region hint t1 t2)
184-- Checks that all of the given variables belong to distinct equivalence classes.
185-- Also checks that their structure is Nothing, so they represent a variable, not
186-- a more complex term.
187allDistinct :: A.Region -> [Variable] -> StateT TS.SolverState IO ()
188allDistinct region vars =
189 do seen <- TS.uniqueMark
191 do desc <- liftIO $ UF.descriptor var
192 case structure desc of
194 let msg = "Cannot generalize something that is not a type variable."
196 addHint region (Just msg) var var
199 do when (mark desc == seen) $
200 do let msg = "Duplicate variable during generalization."
201 addHint region (Just msg) var var
202 liftIO $ UF.setDescriptor var (desc { mark = seen })
205-- Check that a variable has rank == noRank, meaning that it can be generalized.
206isGeneric :: A.Region -> Variable -> StateT TS.SolverState IO ()
207isGeneric region var =
208 do desc <- liftIO $ UF.descriptor var
209 if rank desc == noRank
212 let msg = "Unable to generalize a type variable. It is not unranked."
213 in addHint region (Just msg) var var
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