on master help sign in
Get the solver working on basic programs. It outputs pretty types for the

variables in the program. Need to test further and start doing some benchmarking.

commit497d478d26993908503fd42f9406ed6ed4c8c08a authorEvan Czaplicki <evan@Evans-MacBook-Air.local> dateTue 9 Jul 2013, 21:52 (13 years ago) parentca62ee6
5 files changed+237−99
compiler/Type/Constrain/Expression.hs+6−3 compiler/Type/Solve.hs+106−76 compiler/Type/State.hs+110−14 compiler/Type/Type.hs+10−3 compiler/Type/Unify.hs+5−3
compiler/Type/Constrain/Expression.hs+6−3
@@ -21,8 +21,11 @@ import qualified Type.Constrain.Pattern as Pattern
2121
2222{-- Testing section --}
2323import SourceSyntax.PrettyPrint
24+import Text.PrettyPrint as P
2425import Parse.Expression
2526import Parse.Helpers (iParse)
27+import Type.Solve (solve)
28+import qualified Type.State as TS
2629
2730test str =
2831 case iParse expr "" str of
@@ -33,8 +36,9 @@ test str =
3336 constraint <- constrain env expression (VarN var)
3437 prettyNames constraint
3538 print (pretty constraint)
36− print (pretty var)
37− return ()
39+ print (P.text "Solving for:" <+> pretty var)
40+ (env,_,_,_) <- execStateT (solve constraint) TS.initialState
41+ mapM_ (\(n,t) -> print $ P.text n <+> P.text ":" <+> pretty t) $ Map.toList env
3842{-- todo: remove testing code --}
3943
4044constrain :: Env.Environment -> LExpr a b -> Type -> IO TypeConstraint
@@ -152,7 +156,6 @@ constrain env (L _ _ expr) tipe =
152156 v <- flexibleVar -- needs an ex
153157 c <- constrain env body (VarN v)
154158 return ((name, v), c)
155−
156159
157160 Markdown _ ->
158161 return $ tipe === Env.get env Env.builtin "Element"
compiler/Type/Solve.hs+106−76
@@ -4,7 +4,6 @@ module Type.Solve where
44import Control.Monad
55import Control.Monad.State
66import qualified Data.UnionFind.IO as UF
7−import qualified Data.Array.IO as Array
87import qualified Data.Map as Map
98import qualified Data.Traversable as Traversable
109import qualified Data.Maybe as Maybe
@@ -13,73 +12,78 @@ import Type.Unify
1312import qualified Type.Environment as Env
1413import qualified Type.State as TS
1514
16−register = undefined
17−
18−generalize :: TS.Pool -> TS.Pool -> IO [Variable]
19−generalize oldPool youngPool = do
20− let young = 0
21− visited = 1
22− youngRank = TS.maxRank youngPool
23−
24− array' <- Array.newArray (0, youngRank) []
25− let array = array' :: Array.IOArray Int [Variable]
26−
27− -- Insert all of the youngPool variables into the array.
28− -- They are placed into a list at the index corresponding
29− -- to their rank.
30− forM (TS.inhabitants youngPool) $ \var -> do
31− desc <- UF.descriptor var
32− vars <- Array.readArray array (rank desc)
33− Array.writeArray array (rank desc) (var : vars)
34−
35− -- get the ranks right for each entry
36− forM [0 .. youngRank] $ \i -> do
37− vars <- Array.readArray array i
38− mapM (traverse young visited i) vars
39−
40− -- do not need to work with variables that have become redundant
41− vars <- Array.readArray array youngRank
42− forM vars $ \var -> do
43− isRedundant <- UF.redundant var
44− if isRedundant then do
45− desc <- UF.descriptor var
46− if rank desc < youngRank
47− then register oldPool var
48− else let flex' = if flex desc == Flexible then Rigid else flex desc
49− in UF.setDescriptor var (desc { rank = noRank, flex = flex' })
50− else return ()
51−
52− return vars
53−
54−traverse :: Int -> Int -> Int -> Variable -> IO Int
55−traverse young visited k variable =
56− let f = traverse young visited k in
57− do desc <- UF.descriptor variable
58− case mark desc == young of
15+
16+-- | Every variable has rank less than or equal to the maxRank of the pool.
17+-- This sorts variables into the young and old pools accordingly.
18+generalize :: TS.Pool -> StateT TS.SolverState IO ()
19+generalize youngPool = do
20+ let youngRank = TS.maxRank youngPool
21+ insert dict var = do
22+ desc <- liftIO $ UF.descriptor var
23+ return $ Map.insertWith (++) (rank desc) [var] dict
24+
25+ -- Sort the youngPool variables by rank.
26+ rankDict <- foldM insert Map.empty (TS.inhabitants youngPool)
27+
28+ -- get the ranks right for each entry.
29+ -- start at low ranks so that we only have to pass
30+ -- over the information once.
31+ youngMark <- TS.uniqueMark
32+ visitedMark <- TS.uniqueMark
33+ Traversable.traverse (mapM (adjustRank youngMark visitedMark youngRank)) rankDict
34+
35+ -- Move variables out of the young pool if they do not have a young rank.
36+ -- We should not generalize things we cannot use.
37+ let youngVars = (Map.!) rankDict youngRank
38+
39+ registerIfNotRedundant var = do
40+ isRedundant <- liftIO $ UF.redundant var
41+ if isRedundant then return var else TS.register var
42+
43+ registerIfHigherRank var = do
44+ isRedundant <- liftIO $ UF.redundant var
45+ if isRedundant then return () else do
46+ desc <- liftIO $ UF.descriptor var
47+ if rank desc < youngRank
48+ then TS.register var >> return ()
49+ else let flex' = if flex desc == Flexible then Rigid else flex desc
50+ in liftIO $ UF.setDescriptor var (desc { rank = noRank, flex = flex' })
51+
52+ Traversable.traverse (mapM registerIfNotRedundant) rankDict
53+ Traversable.traverse (mapM registerIfHigherRank) rankDict
54+
55+ return ()
56+
57+
58+-- adjust the ranks of variables such that ranks never increase as you
59+-- move deeper into a variable. This mean the rank actually represents the
60+-- deepest variable in the whole type, and we can ignore things at a lower
61+-- rank than the current constraints.
62+adjustRank :: Int -> Int -> Int -> Variable -> StateT TS.SolverState IO Int
63+adjustRank youngMark visitedMark groupRank variable =
64+ let adjust = adjustRank youngMark visitedMark groupRank in
65+ do desc <- liftIO $ UF.descriptor variable
66+ case mark desc == youngMark of
5967 True -> do
6068 rank' <- case structure desc of
61− Nothing -> return k
69+ Nothing -> return groupRank
6270 Just term -> case term of
63− App1 a b -> max `liftM` f a `ap` f b
64− Fun1 a b -> max `liftM` f a `ap` f b
65− Var1 x -> f x
71+ App1 a b -> max `liftM` adjust a `ap` adjust b
72+ Fun1 a b -> max `liftM` adjust a `ap` adjust b
73+ Var1 x -> adjust x
6674 EmptyRecord1 -> return outermostRank
6775 Record1 fields extension -> do
68− ranks <- mapM f (concat (Map.elems fields))
69− max (maximum ranks) `liftM` f extension
70− UF.setDescriptor variable (desc { mark = visited, rank = rank' })
76+ ranks <- mapM adjust (concat (Map.elems fields))
77+ max (maximum ranks) `liftM` adjust extension
78+ liftIO $ UF.setDescriptor variable (desc { mark = visitedMark, rank = rank' })
7179 return rank'
7280
7381 False -> do
74− if mark desc /= visited then do
75− let rank' = min k (rank desc)
76− UF.setDescriptor variable (desc { mark = visited, rank = rank' })
82+ if mark desc == visitedMark then return (rank desc) else do
83+ let rank' = min groupRank (rank desc)
84+ liftIO $ UF.setDescriptor variable (desc { mark = visitedMark, rank = rank' })
7785 return rank'
78− else return (rank desc)
7986
80−addTo = undefined
81−newPool = undefined
82−introduce = undefined
8387
8488solve :: TypeConstraint -> StateT TS.SolverState IO ()
8589solve constraint =
@@ -94,41 +98,67 @@ solve constraint =
9498 CAnd cs -> mapM_ solve cs
9599
96100 CLet [Scheme [] fqs constraint' _] CTrue -> do
97− mapM_ introduce fqs
101+ mapM_ TS.introduce fqs
98102 solve constraint'
99103
100104 CLet schemes constraint' -> do
101− mapM solveScheme schemes
105+ headers <- mapM solveScheme schemes
106+ TS.modifyEnv $ \env -> Map.unions (headers ++ [env])
102107 solve constraint'
103108
104109 CInstance name term -> do
105− let instance' = undefined
106− inst = undefined --instance' pool (Env.get env value name)
110+ env <- TS.getEnv
111+ freshCopy <- TS.makeInstance ((Map.!) env name)
107112 t <- TS.flatten term
108− unify inst t
113+ unify freshCopy t
109114
110−solveScheme :: TypeScheme -> StateT TS.SolverState IO ()
115+solveScheme :: TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
111116solveScheme scheme =
112117 case scheme of
113118 Scheme [] [] constraint header -> do
114119 solve constraint
115120 Traversable.traverse TS.flatten header
116− return ()
117121
118122 Scheme rigidQuantifiers flexibleQuantifiers constraint header -> do
119123 let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
120− globalPool <- TS.getPool
121− localPool <- TS.newPool
122− TS.modifyPool (\_ -> localPool)
124+ currentPool <- TS.getPool
125+
126+ -- fill in a new pool when working on this scheme's constraints
127+ emptyPool <- TS.nextRankPool
128+ TS.switchToPool emptyPool
123129 mapM TS.introduce quantifiers
124130 header' <- Traversable.traverse TS.flatten header
125131 solve constraint
126− -- distinct variables
127− -- generalize
128− -- generic variables
129− TS.modifyPool (\_ -> globalPool)
130−
131−isGeneric var =
132− do desc <- UF.descriptor var
133− undefined
134132
133+ allDistinct rigidQuantifiers
134+ localPool <- TS.getPool
135+ TS.switchToPool currentPool
136+ generalize localPool
137+ mapM isGeneric rigidQuantifiers
138+ return header'
139+
140+
141+-- Checks that all of the given variables belong to distinct equivalence classes.
142+-- Also checks that their structure is Nothing, so they represent a variable, not
143+-- a more complex term.
144+allDistinct :: [Variable] -> StateT TS.SolverState IO ()
145+allDistinct vars = do
146+ seen <- TS.uniqueMark
147+ let check var = do
148+ desc <- liftIO $ UF.descriptor var
149+ case structure desc of
150+ Just _ -> TS.addError "Cannot generalize something that is not a type variable."
151+ Nothing -> do
152+ if mark desc == seen
153+ then TS.addError "Duplicate variable during generalization"
154+ else return ()
155+ liftIO $ UF.setDescriptor var (desc { mark = seen })
156+ mapM_ check vars
157+
158+-- Check that a variable has rank == noRank, meaning that it can be generalized.
159+isGeneric :: Variable -> StateT TS.SolverState IO ()
160+isGeneric var = do
161+ desc <- liftIO $ UF.descriptor var
162+ if rank desc == noRank
163+ then return ()
164+ else TS.addError "Cannot generalize. Variable must have not have a rank."
compiler/Type/State.hs+110−14
@@ -1,9 +1,16 @@
1+{-# OPTIONS_GHC -XMultiWayIf #-}
12module Type.State where
23
34import Type.Type
5+import qualified Data.Map as Map
46import qualified Type.Environment as Env
57import qualified Data.UnionFind.IO as UF
68import Control.Monad.State
9+import Control.Applicative ((<$>),(<*>), Applicative)
10+import qualified Data.Traversable as Traversable
11+
12+-- todo: remove later
13+import SourceSyntax.PrettyPrint
714
815-- Pool
916-- Holds a bunch of variables
@@ -13,23 +20,41 @@ import Control.Monad.State
1320data Pool = Pool {
1421 maxRank :: Int,
1522 inhabitants :: [Variable]
16−}
23+} deriving Show
24+
25+emptyPool = Pool { maxRank = 0, inhabitants = [] }
1726
1827-- Keeps track of the environment, type variable pool, and a list of errors
19−type SolverState = (Env.Environment, Pool, [String])
28+type SolverState = (Map.Map String Variable, Pool, Int, [String])
29+
30+-- The mark must never be equal to noMark!
31+initialState = (Map.empty, emptyPool, noMark + 1, [])
2032
21−modifyEnv f = modify $ \(env, pool, errors) -> (f env, pool, errors)
22−modifyPool f = modify $ \(env, pool, errors) -> (env, f pool, errors)
23−addError err = modify $ \(env, pool, errors) -> (env, pool, err:errors)
33+modifyEnv f = modify $ \(env, pool, mark, errors) -> (f env, pool, mark, errors)
34+modifyPool f = modify $ \(env, pool, mark, errors) -> (env, f pool, mark, errors)
35+addError err = modify $ \(env, pool, mark, errors) -> (env, pool, mark, err:errors)
36+
37+switchToPool pool = modifyPool (\_ -> pool)
2438
2539getPool :: StateT SolverState IO Pool
2640getPool = do
27− (_, pool, _) <- get
41+ (_, pool, _, _) <- get
2842 return pool
2943
30−newPool :: StateT SolverState IO Pool
31−newPool = do
32− (_, pool, _) <- get
44+getEnv :: StateT SolverState IO (Map.Map String Variable)
45+getEnv = do
46+ (env, _, _, _) <- get
47+ return env
48+
49+uniqueMark :: StateT SolverState IO Int
50+uniqueMark = do
51+ (env, pool, mark, errs) <- get
52+ put (env, pool, mark+1, errs)
53+ return mark
54+
55+nextRankPool :: StateT SolverState IO Pool
56+nextRankPool = do
57+ pool <- getPool
3358 return $ Pool { maxRank = maxRank pool + 1, inhabitants = [] }
3459
3560register :: Variable -> StateT SolverState IO Variable
@@ -39,7 +64,7 @@ register variable = do
3964
4065introduce :: Variable -> StateT SolverState IO Variable
4166introduce variable = do
42− (_, pool, _) <- get
67+ pool <- getPool
4368 liftIO $ UF.modifyDescriptor variable (\desc -> desc { rank = maxRank pool })
4469 register variable
4570
@@ -48,13 +73,84 @@ flatten term =
4873 case term of
4974 VarN v -> return v
5075 TermN t -> do
51− flatStructure <- undefined -- chop t
52− (_, pool, _) <- get
76+ flatStructure <- traverseTerm flatten t
77+ pool <- getPool
5378 var <- liftIO . UF.fresh $ Descriptor {
5479 structure = Just flatStructure,
5580 rank = maxRank pool,
5681 flex = Flexible,
5782 name = Nothing,
58− mark = 0
83+ copy = Nothing,
84+ mark = noMark
5985 }
60− register var
86+ register var
87+
88+makeInstance :: Variable -> StateT SolverState IO Variable
89+makeInstance var = do
90+ alreadyCopied <- uniqueMark
91+ freshVar <- makeCopy alreadyCopied var
92+ restore alreadyCopied var
93+ return freshVar
94+
95+makeCopy :: Int -> Variable -> StateT SolverState IO Variable
96+makeCopy alreadyCopied variable = do
97+ desc <- liftIO $ UF.descriptor variable
98+ if | mark desc == alreadyCopied ->
99+ case copy desc of
100+ Just v -> return v
101+ Nothing -> error "This should be impossible."
102+
103+ | mark desc /= noRank || flex desc == Constant ->
104+ return variable
105+
106+ | otherwise -> do
107+ pool <- getPool
108+ newVar <- liftIO $ UF.fresh $ Descriptor {
109+ structure = Nothing,
110+ rank = maxRank pool,
111+ mark = noMark,
112+ flex = Flexible,
113+ copy = Nothing,
114+ name = case flex desc of
115+ Rigid -> Nothing
116+ _ -> name desc
117+ }
118+
119+ register newVar
120+
121+ -- Link the original variable to the new variable
122+ -- Need to do this before recursively copying to
123+ -- avoid looping on cyclic terms.
124+ liftIO $ UF.modifyDescriptor variable $ \desc ->
125+ desc { mark = alreadyCopied, copy = Just newVar }
126+
127+ case structure desc of
128+ Nothing -> return newVar
129+ Just term -> do
130+ newTerm <- traverseTerm (makeCopy alreadyCopied) term
131+ liftIO $ UF.modifyDescriptor newVar $ \desc ->
132+ desc { structure = Just newTerm }
133+ return newVar
134+
135+restore :: Int -> Variable -> StateT SolverState IO Variable
136+restore alreadyCopied variable = do
137+ desc <- liftIO $ UF.descriptor variable
138+ if mark desc /= alreadyCopied then return variable else do
139+ restoredStructure <-
140+ case structure desc of
141+ Nothing -> return Nothing
142+ Just term -> Just <$> traverseTerm (restore alreadyCopied) term
143+ liftIO $ UF.modifyDescriptor variable $ \desc ->
144+ desc { mark = noMark, rank = noRank, structure = restoredStructure }
145+ return variable
146+
147+traverseTerm :: (Monad f, Applicative f) => (a -> f b) -> Term1 a -> f (Term1 b)
148+traverseTerm f term =
149+ case term of
150+ App1 a b -> App1 <$> f a <*> f b
151+ Fun1 a b -> Fun1 <$> f a <*> f b
152+ Var1 x -> Var1 <$> f x
153+ EmptyRecord1 -> return EmptyRecord1
154+ Record1 fields ext ->
155+ Record1 <$> Traversable.traverse (mapM f) fields <*> f ext
156+
compiler/Type/Type.hs+10−3
@@ -50,12 +50,16 @@ data Descriptor = Descriptor {
5050 rank :: Int,
5151 flex :: Flex,
5252 name :: Maybe TypeName,
53+ copy :: Maybe Variable,
5354 mark :: Int
5455} deriving Show
5556
5657noRank = -1
5758outermostRank = 0 :: Int
5859
60+noMark = 0
61+initialMark = 1
62+
5963data Flex = Rigid | Flexible | Constant
6064 deriving (Show, Eq)
6165
@@ -85,7 +89,8 @@ namedVar name = UF.fresh $ Descriptor {
8589 rank = noRank,
8690 flex = Constant,
8791 name = Just name,
88− mark = 0
92+ copy = Nothing,
93+ mark = noMark
8994 }
9095
9196flexibleVar = UF.fresh $ Descriptor {
@@ -93,7 +98,8 @@ flexibleVar = UF.fresh $ Descriptor {
9398 rank = noRank,
9499 flex = Flexible,
95100 name = Nothing,
96− mark = 0
101+ copy = Nothing,
102+ mark = noMark
97103 }
98104
99105rigidVar = UF.fresh $ Descriptor {
@@ -101,7 +107,8 @@ rigidVar = UF.fresh $ Descriptor {
101107 rank = noRank,
102108 flex = Rigid,
103109 name = Nothing,
104− mark = 0
110+ copy = Nothing,
111+ mark = noMark
105112 }
106113
107114-- ex qs constraint == exists qs. constraint
compiler/Type/Unify.hs+5−3
@@ -37,17 +37,19 @@ actuallyUnify variable1 variable2 = do
3737 merge1 :: StateT TS.SolverState IO ()
3838 merge1 = liftIO $ do
3939 UF.union variable2 variable1
40− UF.setDescriptor variable1 (desc1 { flex = flex', name = name', rank = rank', mark = undefined })
40+ UF.setDescriptor variable1 (desc1 {
41+ flex = flex', name = name', rank = rank', copy = Nothing, mark = noMark })
4142
4243 merge2 :: StateT TS.SolverState IO ()
4344 merge2 = liftIO $ do
4445 UF.union variable1 variable2
45− UF.setDescriptor variable2 (desc2 { flex = flex', name = name', rank = rank', mark = undefined })
46+ UF.setDescriptor variable2 (desc2 {
47+ flex = flex', name = name', rank = rank', copy = Nothing, mark = noMark })
4648
4749 fresh :: Maybe (Term1 Variable) -> StateT TS.SolverState IO Variable
4850 fresh structure = do
4951 var <- liftIO . UF.fresh $ Descriptor {
50− structure = structure, rank = rank', flex = flex', name = name', mark = undefined
52+ structure = structure, rank = rank', flex = flex', name = name', copy = Nothing, mark = noMark
5153 }
5254 TS.register var
5355