on master help sign in
Get type inference working in the basic case. Begin working on

printing errors in a prettier way.

commitb5887f41c963aa3ea4689f20c7d3eb29fd163ae7 authorEvan Czaplicki <evan@Evans-MacBook-Air.local> dateWed 10 Jul 2013, 14:31 (13 years ago) parentf87e316
5 files changed+87−37
compiler/SourceSyntax/PrettyPrint.hs+3 compiler/Type/Constrain/Expression.hs+6−3 compiler/Type/Solve.hs+32−19 compiler/Type/State.hs+40−11 compiler/Type/Unify.hs+6−4
compiler/SourceSyntax/PrettyPrint.hs+3
@@ -10,6 +10,9 @@ class Pretty a where
1010instance Pretty a => Pretty (Located a) where
1111 pretty (L _ _ e) = pretty e
1212
13+instance Pretty () where
14+ pretty () = empty
15+
1316commaCat docs = cat (punctuate comma docs)
1417
1518parensIf bool doc = if bool then parens doc else doc
compiler/Type/Constrain/Expression.hs+6−3
@@ -35,10 +35,13 @@ test str =
3535 var <- flexibleVar
3636 constraint <- constrain env expression (VarN var)
3737 prettyNames constraint
38− print (pretty constraint)
39− print (P.text "Solving for:" <+> pretty var)
40− (env,_,_,_) <- execStateT (solve constraint) TS.initialState
38+ --print (pretty constraint)
39+ --print (P.text "Solving for:" <+> pretty var)
40+ (env,_,_,errors) <- execStateT (solve constraint) TS.initialState
4141 mapM_ (\(n,t) -> print $ P.text n <+> P.text ":" <+> pretty t) $ Map.toList env
42+ if null errors then return () else do
43+ putStrLn "\n"
44+ mapM_ print errors
4245{-- todo: remove testing code --}
4346
4447constrain :: Env.Environment -> LExpr a b -> Type -> IO TypeConstraint
compiler/Type/Solve.hs+32−19
@@ -1,4 +1,4 @@
1−
1+{-# OPTIONS_GHC -XMultiWayIf #-}
22module Type.Solve where
33
44import Control.Monad
@@ -11,6 +11,7 @@ import Type.Type
1111import Type.Unify
1212import qualified Type.Environment as Env
1313import qualified Type.State as TS
14+import qualified Text.PrettyPrint as P
1415
1516
1617-- | Every variable has rank less than or equal to the maxRank of the pool.
@@ -47,11 +48,16 @@ generalize youngPool = do
4748 if rank desc < youngRank
4849 then TS.register var >> return ()
4950 else let flex' = if flex desc == Flexible then Rigid else flex desc
50− in liftIO $ UF.setDescriptor var (desc { rank = noRank, flex = flex' })
51+ in do liftIO $ UF.setDescriptor var (desc { rank = noRank, flex = flex' })
52+ TS.debug $ print var
5153
5254 Traversable.traverse (mapM registerIfNotRedundant) rankDict
5355 Traversable.traverse (mapM registerIfHigherRank) rankDict
5456
57+ TS.debug $ print youngMark
58+ TS.debug $ print visitedMark
59+-- TS.debug $ mapM print youngVars
60+
5561 return ()
5662
5763
@@ -63,9 +69,8 @@ adjustRank :: Int -> Int -> Int -> Variable -> StateT TS.SolverState IO Int
6369adjustRank youngMark visitedMark groupRank variable =
6470 let adjust = adjustRank youngMark visitedMark groupRank in
6571 do desc <- liftIO $ UF.descriptor variable
66− case mark desc == youngMark of
67− True -> do
68− rank' <- case structure desc of
72+ if | mark desc == youngMark -> do
73+ rank' <- case structure desc of
6974 Nothing -> return groupRank
7075 Just term -> case term of
7176 App1 a b -> max `liftM` adjust a `ap` adjust b
@@ -75,31 +80,37 @@ adjustRank youngMark visitedMark groupRank variable =
7580 Record1 fields extension -> do
7681 ranks <- mapM adjust (concat (Map.elems fields))
7782 max (maximum ranks) `liftM` adjust extension
78− liftIO $ UF.setDescriptor variable (desc { mark = visitedMark, rank = rank' })
79− return rank'
83+ liftIO $ UF.setDescriptor variable (desc { mark = visitedMark, rank = rank' })
84+ return rank'
85+
86+ | mark desc /= visitedMark -> do
87+ let rank' = min groupRank (rank desc)
88+ liftIO $ UF.setDescriptor variable (desc { mark = visitedMark, rank = rank' })
89+ return rank'
90+
91+ | otherwise -> return (rank desc)
8092
81− False -> do
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' })
85− return rank'
8693
8794
88−solve :: TypeConstraint -> StateT TS.SolverState IO ()
95+solve :: TypeConstraint -> StateT TS.SolverState IO (Map.Map String Variable)
8996solve constraint =
9097 case constraint of
91− CTrue -> return ()
98+ CTrue -> TS.getEnv
9299
93100 CEqual term1 term2 -> do
94101 t1 <- TS.flatten term1
95102 t2 <- TS.flatten term2
96103 unify t1 t2
104+ TS.getEnv
97105
98− CAnd cs -> mapM_ solve cs
106+ CAnd cs -> mapM solve cs >> TS.getEnv
99107
100108 CLet [Scheme [] fqs constraint' _] CTrue -> do
101− mapM_ TS.introduce fqs
109+ oldEnv <- TS.getEnv
110+ mapM TS.introduce fqs
102111 solve constraint'
112+ TS.modifyEnv (\_ -> oldEnv)
113+ return oldEnv
103114
104115 CLet schemes constraint' -> do
105116 headers <- mapM solveScheme schemes
@@ -111,6 +122,7 @@ solve constraint =
111122 freshCopy <- TS.makeInstance ((Map.!) env name)
112123 t <- TS.flatten term
113124 unify freshCopy t
125+ TS.getEnv
114126
115127solveScheme :: TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
116128solveScheme scheme =
@@ -147,10 +159,11 @@ allDistinct vars = do
147159 let check var = do
148160 desc <- liftIO $ UF.descriptor var
149161 case structure desc of
150− Just _ -> TS.addError "Cannot generalize something that is not a type variable."
162+ Just _ ->
163+ TS.addError "Cannot generalize something that is not a type variable" () var
151164 Nothing -> do
152165 if mark desc == seen
153− then TS.addError "Duplicate variable during generalization"
166+ then TS.addError "Duplicate variable during generalization" var ()
154167 else return ()
155168 liftIO $ UF.setDescriptor var (desc { mark = seen })
156169 mapM_ check vars
@@ -161,4 +174,4 @@ isGeneric var = do
161174 desc <- liftIO $ UF.descriptor var
162175 if rank desc == noRank
163176 then return ()
164− else TS.addError "Cannot generalize. Variable must have not have a rank."
177+ else TS.addError "Cannot generalize. Variable must have not have a rank." () var
compiler/Type/State.hs+40−11
@@ -3,15 +3,17 @@ module Type.State where
33
44import Type.Type
55import qualified Data.Map as Map
6+import qualified Data.List as List
67import qualified Type.Environment as Env
78import qualified Data.UnionFind.IO as UF
89import Control.Monad.State
910import Control.Applicative ((<$>),(<*>), Applicative)
1011import qualified Data.Traversable as Traversable
11−
12−-- todo: remove later
12+import Text.PrettyPrint as P
1313import SourceSyntax.PrettyPrint
1414
15+debug e = {-- liftIO e --} return ()
16+
1517-- Pool
1618-- Holds a bunch of variables
1719-- The rank of each variable is less than or equal to the pool's "maxRank"
@@ -25,14 +27,23 @@ data Pool = Pool {
2527emptyPool = Pool { maxRank = 0, inhabitants = [] }
2628
2729-- Keeps track of the environment, type variable pool, and a list of errors
28−type SolverState = (Map.Map String Variable, Pool, Int, [String])
30+type SolverState = (Map.Map String Variable, Pool, Int, [P.Doc])
2931
3032-- The mark must never be equal to noMark!
3133initialState = (Map.empty, emptyPool, noMark + 1, [])
3234
3335modifyEnv f = modify $ \(env, pool, mark, errors) -> (f env, pool, mark, errors)
3436modifyPool f = modify $ \(env, pool, mark, errors) -> (env, f pool, mark, errors)
35−addError err = modify $ \(env, pool, mark, errors) -> (env, pool, mark, err:errors)
37+addError message t1 t2 = modify $ \(env, pool, mark, errors) -> (env, pool, mark, err:errors)
38+ where
39+ msg = P.fsep . map P.text $ words message
40+ width = maximum . map length . lines $ render msg
41+ err = P.vcat [ P.text $ "Type error on line ???"
42+ , P.text (List.replicate width '-')
43+ , msg <> P.text "\n"
44+ , P.text " Expected Type:" <+> pretty t1
45+ , P.text " Actual Type:" <+> pretty t2 <> P.text "\n"
46+ ]
3647
3748switchToPool pool = modifyPool (\_ -> pool)
3849
@@ -87,23 +98,30 @@ flatten term =
8798
8899makeInstance :: Variable -> StateT SolverState IO Variable
89100makeInstance var = do
101+-- liftIO $ putStrLn ""
102+-- liftIO $ print $ pretty var
90103 alreadyCopied <- uniqueMark
91104 freshVar <- makeCopy alreadyCopied var
92105 restore alreadyCopied var
106+-- liftIO $ print $ pretty freshVar
93107 return freshVar
94108
95109makeCopy :: Int -> Variable -> StateT SolverState IO Variable
96110makeCopy alreadyCopied variable = do
111+-- liftIO $ putStr "Copying: "
97112 desc <- liftIO $ UF.descriptor variable
98113 if | mark desc == alreadyCopied ->
114+ do debug $ putStrLn "Already Copied"
99115 case copy desc of
100116 Just v -> return v
101117 Nothing -> error "This should be impossible."
102118
103− | mark desc /= noRank || flex desc == Constant ->
104− return variable
119+ | rank desc /= noRank || flex desc == Constant ->
120+ do debug $ putStr "did not make a copy: " >> print (mark desc, noRank, flex desc)
121+ return variable
105122
106123 | otherwise -> do
124+ debug $ print "no problems!"
107125 pool <- getPool
108126 newVar <- liftIO $ UF.fresh $ Descriptor {
109127 structure = Nothing,
@@ -118,12 +136,17 @@ makeCopy alreadyCopied variable = do
118136
119137 register newVar
120138
121− -- Link the original variable to the new variable
122− -- Need to do this before recursively copying to
123− -- avoid looping on cyclic terms.
139+ -- Link the original variable to the new variable. This lets us
140+ -- avoid making multiple copies of the variable we are instantiating.
141+ --
142+ -- Need to do this before recursively copying the structure of
143+ -- the variable to avoid looping on cyclic terms.
124144 liftIO $ UF.modifyDescriptor variable $ \desc ->
125145 desc { mark = alreadyCopied, copy = Just newVar }
126146
147+ -- Now we recursively copy the structure of the variable.
148+ -- We have already marked the variable as copied, so we
149+ -- will not repeat this work or crawl this variable again.
127150 case structure desc of
128151 Nothing -> return newVar
129152 Just term -> do
@@ -134,13 +157,19 @@ makeCopy alreadyCopied variable = do
134157
135158restore :: Int -> Variable -> StateT SolverState IO Variable
136159restore alreadyCopied variable = do
160+ debug $ putStr "Restoring: "
137161 desc <- liftIO $ UF.descriptor variable
138− if mark desc /= alreadyCopied then return variable else do
162+ if mark desc /= alreadyCopied
163+ then do
164+ debug $ putStrLn "not copied, no need to do anything"
165+ return variable
166+ else do
167+ debug $ putStrLn "restoring"
139168 restoredStructure <-
140169 case structure desc of
141170 Nothing -> return Nothing
142171 Just term -> Just <$> traverseTerm (restore alreadyCopied) term
143− liftIO $ UF.modifyDescriptor variable $ \desc ->
172+ debug $ UF.modifyDescriptor variable $ \desc ->
144173 desc { mark = noMark, rank = noRank, structure = restoredStructure }
145174 return variable
146175
compiler/Type/Unify.hs+6−4
@@ -5,6 +5,8 @@ import qualified Data.UnionFind.IO as UF
55import qualified Type.State as TS
66import Control.Arrow (first,second)
77import Control.Monad.State
8+import SourceSyntax.PrettyPrint
9+import qualified Text.PrettyPrint as P
810
911unify :: Variable -> Variable -> StateT TS.SolverState IO ()
1012unify variable1 variable2 = do
@@ -60,8 +62,8 @@ actuallyUnify variable1 variable2 = do
6062 (Just (Var1 v), _) -> unify v variable2
6163 (_, Just (Var1 v)) -> unify v variable1
6264
63− (Nothing, _) -> TS.addError "Cannot unify rigid type variable."
64− (_, Nothing) -> TS.addError "Cannot unify rigid type variable."
65+ (Nothing, _) -> TS.addError "The following types are not equal" variable1 variable2
66+ (_, Nothing) -> TS.addError "The following types are not equal" variable1 variable2
6567
6668 (Just type1, Just type2) ->
6769 case (type1,type2) of
@@ -78,6 +80,6 @@ actuallyUnify variable1 variable2 = do
7880 return ()
7981
8082 (Record1 fields1 ext1, Record1 fields2 ext2) ->
81− TS.addError "did not write record unification yet"
83+ TS.addError "did not write record unification yet" variable1 variable2
8284
83− _ -> TS.addError "Could not unify types"
85+ _ -> TS.addError "could not unify types" variable1 variable2