1-----------------------------------------------------------------------------
3-- Module : PureScript.TypeChecker.Types
4-- Copyright : (c) Phil Freeman 2013
7-- Maintainer : Phil Freeman <paf31@cantab.net>
8-- Stability : experimental
13-----------------------------------------------------------------------------
15module PureScript.TypeChecker.Types (
23import Data.Data (Data(..))
24import Data.Generics (everywhere, everywhereM, everything, mkT, mkM, mkQ, extM, extQ)
26import PureScript.Values
27import PureScript.Types
28import PureScript.Kinds
29import PureScript.TypeChecker.Monad
30import PureScript.TypeChecker.Kinds
31import PureScript.TypeChecker.Synonyms
33import Control.Monad.State
34import Control.Monad.Error
36import Control.Applicative
37import Control.Arrow (Kleisli(..), (***), (&&&))
38import qualified Control.Category as C
40import qualified Data.Map as M
43 = TypeConstraint Int Type
44 | RowConstraint Int Row deriving Show
46newtype TypeSolution = TypeSolution { runTypeSolution :: (Int -> Type, Int -> Row) }
48emptyTypeSolution :: TypeSolution
49emptyTypeSolution = TypeSolution (TUnknown, RUnknown)
51typeOf :: String -> Value -> Check PolyType
54 (cs, n) <- typeConstraints (M.singleton name me) val
55 solution <- solveTypeConstraints ((TypeConstraint me (TUnknown n)) : cs) emptyTypeSolution
56 return $ varIfUnknown $ fst (runTypeSolution solution) n
58varIfUnknown :: Type -> PolyType
61 (ty', m) = flip runState M.empty $ everywhereM (flip extM g $ mkM f) ty
62 -- TODO : check no nested unknowns
64 PolyType (sort $ nub $ M.elems m) ty'
66 f :: Type -> State (M.Map Int String) Type
71 let name = "t" ++ show (M.size m)
72 put $ M.insert n name m
74 Just name -> return $ TypeVar name
76 g :: Row -> State (M.Map Int String) Row
81 let name = "r" ++ show (M.size m)
82 put $ M.insert n name m
84 Just name -> return $ RowVar name
87replaceVarsWithUnknowns :: [String] -> Type -> Check Type
88replaceVarsWithUnknowns idents = flip evalStateT M.empty . everywhereM (flip extM f $ mkM g)
90 f :: Type -> StateT (M.Map String Int) Check Type
91 f (TypeVar var) | var `elem` idents = do
94 case M.lookup var m of
96 put (M.insert var n m)
98 Just u -> return $ TUnknown u
100 g :: Row -> StateT (M.Map String Int) Check Row
101 g (RowVar var) | var `elem` idents = do
104 case M.lookup var m of
106 put (M.insert var n m)
108 Just u -> return $ RUnknown u
111replaceType :: (Data d) => Int -> Type -> d -> d
112replaceType n t = everywhere (mkT go)
114 go (TUnknown m) | m == n = t
117replaceRow :: (Data d) => Int -> Row -> d -> d
118replaceRow n r = everywhere (mkT go)
120 go (RUnknown m) | m == n = r
123typeOccursCheck :: Int -> Type -> Check ()
124typeOccursCheck u (TUnknown _) = return ()
125typeOccursCheck u t = when (occursCheck u t) $ throwError $ "Occurs check failed: " ++ show u ++ " = " ++ show t
127rowOccursCheck :: Int -> Row -> Check ()
128rowOccursCheck u (RUnknown _) = return ()
129rowOccursCheck u r = when (occursCheck u r) $ throwError $ "Occurs check failed: " ++ show u ++ " = " ++ show r
131occursCheck :: (Data d) => Int -> d -> Bool
132occursCheck u = everything (||) $ flip extQ g $ mkQ False f
134 f (TUnknown u') | u' == u = True
136 g (RUnknown u') | u' == u = True
139typeConstraints :: M.Map String Int -> Value -> Check ([TypeConstraint], Int)
140typeConstraints _ (NumericLiteral _) = do
142 return ([TypeConstraint me Number], me)
143typeConstraints _ (StringLiteral _) = do
145 return ([TypeConstraint me String], me)
146typeConstraints _ (BooleanLiteral _) = do
148 return ([TypeConstraint me Boolean], me)
149typeConstraints m (ArrayLiteral vals) = do
150 all <- mapM (typeConstraints m) vals
151 let (cs, ns) = (concat . map fst &&& map snd) all
153 return (cs ++ map (TypeConstraint me . Array . TUnknown) ns, me)
154typeConstraints m (Unary op val) = do
155 (cs, n1) <- typeConstraints m val
157 return (cs ++ unaryOperatorConstraints op n1 me, me)
158typeConstraints m (Binary op left right) = do
159 (cs1, n1) <- typeConstraints m left
160 (cs2, n2) <- typeConstraints m right
162 return (cs1 ++ cs2 ++ binaryOperatorConstraints op n1 n2 me, me)
163typeConstraints m (ObjectLiteral ps) = do
164 all <- mapM (typeConstraints m . snd) ps
165 let (cs, ns) = (concat . map fst &&& map snd) all
167 let tys = zipWith (\(name, _) u -> (name, TUnknown u)) ps ns
168 return ((TypeConstraint me (Object (typesToRow tys))) : cs, me)
170 typesToRow [] = REmpty
171 typesToRow ((name, ty):tys) = RCons name ty (typesToRow tys)
172typeConstraints m (Indexer index val) = do
173 (cs1, n1) <- typeConstraints m index
174 (cs2, n2) <- typeConstraints m val
176 return ((TypeConstraint n1 Number) : (TypeConstraint n2 (Array (TUnknown me))) : cs1 ++ cs2, me)
177typeConstraints m (Accessor prop val) = do
178 (cs, n1) <- typeConstraints m val
181 return ((TypeConstraint n1 (Object (RCons prop (TUnknown me) (RUnknown rest)))) : cs, me)
182typeConstraints m (Abs args ret) = do
183 ns <- replicateM (length args) fresh
184 let m' = m `M.union` M.fromList (zipWith (,) args ns)
185 (cs, n') <- typeConstraints m' ret
187 return ((TypeConstraint me (Function (map TUnknown ns) (TUnknown n'))) : cs, me)
188typeConstraints m (App f xs) = do
189 (cs1, n1) <- typeConstraints m f
190 all <- mapM (typeConstraints m) xs
191 let (cs2, ns) = (concat . map fst &&& map snd) all
193 return ((TypeConstraint n1 (Function (map TUnknown ns) (TUnknown me))) : cs1 ++ cs2, me)
194typeConstraints m (Var var) =
195 case M.lookup var m of
198 case M.lookup var (names env) of
199 Nothing -> throwError $ var ++ " is undefined"
200 Just (PolyType idents ty, _) -> do
202 replaced <- replaceVarsWithUnknowns idents ty
203 return ([TypeConstraint me replaced], me)
206 return ([TypeConstraint u (TUnknown me)], me)
207typeConstraints m (Block ss) = do
209 (cs, allCodePathsReturn, _) <- typeConstraintsForBlock m M.empty ret ss
210 guardWith "Block is missing a return statement" allCodePathsReturn
212typeConstraints m (Constructor c) = do
214 case M.lookup c (dataConstructors env) of
215 Nothing -> throwError $ "Constructor " ++ c ++ " is undefined"
216 Just (PolyType idents ty) -> do
218 replaced <- replaceVarsWithUnknowns idents ty
219 return ([TypeConstraint me replaced], me)
220typeConstraints m (Case val binders) = do
221 (cs1, n1) <- typeConstraints m val
223 cs2 <- typeConstraintsForBinders m n1 ret binders
224 return (cs1 ++ cs2, ret)
225typeConstraints m (TypedValue val poly@(PolyType idents ty)) = do
227 guardWith ("Expected type of kind *, was " ++ show kind) $ kind == Star
228 desugared <- replaceAllTypeSynonyms ty
229 (cs, n1) <- typeConstraints m val
230 return (TypeConstraint n1 desugared : cs, n1)
232replaceAllTypeSynonyms :: Type -> Check Type
233replaceAllTypeSynonyms ty = do
235 go (M.toList $ typeSynonyms env) ty
238 go ((name, (args, repl)):rest) ty = do
239 ty' <- either throwError return (substituteTypeSynonym name args repl ty)
242unaryOperatorConstraints :: UnaryOperator -> Int -> Int -> [TypeConstraint]
243unaryOperatorConstraints Negate val result = [TypeConstraint val Number, TypeConstraint result Number]
244unaryOperatorConstraints Not val result = [TypeConstraint val Boolean, TypeConstraint result Boolean]
245unaryOperatorConstraints BitwiseNot val result = [TypeConstraint val Number, TypeConstraint result Number]
247binaryOperatorConstraints :: BinaryOperator -> Int -> Int -> Int -> [TypeConstraint]
248binaryOperatorConstraints Add = symBinOpConstraints Number
249binaryOperatorConstraints Subtract = symBinOpConstraints Number
250binaryOperatorConstraints Multiply = symBinOpConstraints Number
251binaryOperatorConstraints Divide = symBinOpConstraints Number
252binaryOperatorConstraints Modulus = symBinOpConstraints Number
253binaryOperatorConstraints LessThan = asymBinOpConstraints Number Boolean
254binaryOperatorConstraints LessThanOrEqualTo = asymBinOpConstraints Number Boolean
255binaryOperatorConstraints GreaterThan = asymBinOpConstraints Number Boolean
256binaryOperatorConstraints GreaterThanOrEqualTo = asymBinOpConstraints Number Boolean
257binaryOperatorConstraints BitwiseAnd = symBinOpConstraints Number
258binaryOperatorConstraints BitwiseOr = symBinOpConstraints Number
259binaryOperatorConstraints BitwiseXor = symBinOpConstraints Number
260binaryOperatorConstraints ShiftLeft = symBinOpConstraints Number
261binaryOperatorConstraints ShiftRight = symBinOpConstraints Number
262binaryOperatorConstraints ZeroFillShiftRight = symBinOpConstraints Number
263binaryOperatorConstraints EqualTo = equalityBinOpConstraints
264binaryOperatorConstraints NotEqualTo = equalityBinOpConstraints
265binaryOperatorConstraints And = symBinOpConstraints Boolean
266binaryOperatorConstraints Or = symBinOpConstraints Boolean
267binaryOperatorConstraints Concat = symBinOpConstraints String
269equalityBinOpConstraints :: Int -> Int -> Int -> [TypeConstraint]
270equalityBinOpConstraints left right result = [TypeConstraint left (TUnknown right), TypeConstraint result Boolean]
272symBinOpConstraints :: Type -> Int -> Int -> Int -> [TypeConstraint]
273symBinOpConstraints ty left right result = asymBinOpConstraints ty ty left right result
275asymBinOpConstraints :: Type -> Type -> Int -> Int -> Int -> [TypeConstraint]
276asymBinOpConstraints ty res left right result = [TypeConstraint left ty, TypeConstraint right ty, TypeConstraint result res]
278typeConstraintsForBinder :: Int -> Binder -> Check ([TypeConstraint], M.Map String Int)
279typeConstraintsForBinder val (StringBinder _) = constantBinder val String
280typeConstraintsForBinder val (NumberBinder _) = constantBinder val Number
281typeConstraintsForBinder val (BooleanBinder _) = constantBinder val Boolean
282typeConstraintsForBinder val (VarBinder name) = do
284 return ([TypeConstraint me (TUnknown val)], M.singleton name me)
285typeConstraintsForBinder val (NullaryBinder ctor) = do
287 case M.lookup ctor (dataConstructors env) of
288 Just (PolyType args ret) -> do
289 ret' <- replaceVarsWithUnknowns args ret
290 return ([TypeConstraint val ret'], M.empty)
291 _ -> throwError $ "Constructor " ++ ctor ++ " is not defined"
292typeConstraintsForBinder val (UnaryBinder ctor binder) = do
294 case M.lookup ctor (dataConstructors env) of
295 Just (PolyType idents f@(Function [_] _)) -> do
297 (Function [ty] ret) <- replaceVarsWithUnknowns idents f
298 (cs, m1) <- typeConstraintsForBinder obj binder
299 return ((TypeConstraint val ret) : (TypeConstraint obj ty) : cs, m1)
300 Just _ -> throwError $ ctor ++ " is not a unary constructor"
301 _ -> throwError $ "Constructor " ++ ctor ++ " is not defined"
302typeConstraintsForBinder val (ObjectBinder props) = do
305 (cs, m1) <- typeConstraintsForProperties row (RUnknown rest) props
306 return ((TypeConstraint val (Object (RUnknown row))) : cs, m1)
308 typeConstraintsForProperties :: Int -> Row -> [(String, Binder)] -> Check ([TypeConstraint], M.Map String Int)
309 typeConstraintsForProperties nrow row [] = return ([RowConstraint nrow row], M.empty)
310 typeConstraintsForProperties nrow row ((name, binder):binders) = do
312 (cs1, m1) <- typeConstraintsForBinder propTy binder
313 (cs2, m2) <- typeConstraintsForProperties nrow (RCons name (TUnknown propTy) row) binders
314 return (cs1 ++ cs2, M.union m1 m2)
315typeConstraintsForBinder val (ArrayBinder binders rest) = do
317 all <- mapM (typeConstraintsForBinder el) binders
318 let (cs1, m1) = (concat . map fst &&& M.unions . map snd) all
319 let arrayConstraint = TypeConstraint val (Array (TUnknown el))
321 Nothing -> return (arrayConstraint : cs1, m1)
323 (cs2, m2) <- typeConstraintsForBinder val binder
324 return (arrayConstraint : cs1 ++ cs2, M.union m1 m2)
326constantBinder :: Int -> Type -> Check ([TypeConstraint], M.Map String Int)
327constantBinder val ty = return ([TypeConstraint val ty], M.empty)
329typeConstraintsForBinders :: M.Map String Int -> Int -> Int -> [(Binder, Value)] -> Check [TypeConstraint]
330typeConstraintsForBinders _ _ _ [] = return []
331typeConstraintsForBinders m nval ret ((binder, val):bs) = do
332 (cs1, m1) <- typeConstraintsForBinder nval binder
333 (cs2, n2) <- typeConstraints (M.union m m1) val
334 cs3 <- typeConstraintsForBinders m nval ret bs
335 return ((TypeConstraint n2 (TUnknown ret)) : cs1 ++ cs2 ++ cs3)
337typeConstraintsForStatement :: M.Map String Int -> M.Map String Int -> Int -> Statement -> Check ([TypeConstraint], Bool, M.Map String Int)
338typeConstraintsForStatement m mass ret (VariableIntroduction name val) = do
339 case M.lookup name (m `M.union` mass) of
341 (cs1, n1) <- typeConstraints m val
342 return $ (cs1, False, M.insert name n1 mass)
343 Just ty -> throwError $ "Variable with name " ++ name ++ " already exists."
344typeConstraintsForStatement m mass ret (Assignment tgt val) = do
345 (cs1, n1) <- typeConstraints m val
346 cs2 <- typeConstraintsForAssignmentTarget m mass n1 tgt
347 return (cs1 ++ cs2, False, mass)
348typeConstraintsForStatement m mass ret (While val inner) = do
349 (cs1, n1) <- typeConstraints m val
350 (cs2, allCodePathsReturn, _) <- typeConstraintsForBlock m mass ret inner
351 return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2, allCodePathsReturn, mass)
352typeConstraintsForStatement m mass ret (IfThenElse val thens Nothing) = do
353 (cs1, n1) <- typeConstraints m val
354 (cs2, allCodePathsReturn, _) <- typeConstraintsForBlock m mass ret thens
355 return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2, allCodePathsReturn, mass)
356typeConstraintsForStatement m mass ret (IfThenElse val thens (Just elses)) = do
357 (cs1, n1) <- typeConstraints m val
358 (cs2, allCodePathsReturn1, _) <- typeConstraintsForBlock m mass ret thens
359 (cs3, allCodePathsReturn2, _) <- typeConstraintsForBlock m mass ret elses
360 return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2 ++ cs3, allCodePathsReturn1 && allCodePathsReturn2, mass)
361typeConstraintsForStatement m mass ret (For (init, cond, cont) inner) = do
362 (cs1, b1, mass1) <- typeConstraintsForStatement m mass ret init
363 guardWith "Cannot return from inside for" $ not b1
364 (cs2, n1) <- typeConstraints (m `M.union` mass1) cond
365 (cs3, b2, mass2) <- typeConstraintsForStatement (m `M.union` mass1) mass1 ret cont
366 guardWith "Cannot return from inside for" $ not b2
367 (cs4, allCodePathsReturn, _) <- typeConstraintsForBlock (m `M.union` mass2) mass2 ret inner
368 return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2 ++ cs3 ++ cs4, allCodePathsReturn, mass)
369typeConstraintsForStatement m mass ret (Return val) = do
370 (cs1, n1) <- typeConstraints (m `M.union` mass) val
371 return ((TypeConstraint n1 (TUnknown ret)) : cs1, True, mass)
373typeConstraintsForAssignmentTarget :: M.Map String Int -> M.Map String Int -> Int -> AssignmentTarget -> Check [TypeConstraint]
374typeConstraintsForAssignmentTarget m mass nval (AssignVariable var) =
375 case M.lookup var mass of
376 Nothing -> throwError $ "No local variable with name " ++ var
378 return [TypeConstraint nval (TUnknown ty)]
379typeConstraintsForAssignmentTarget m mass nval (AssignArrayIndex index tgt) = do
381 (cs1, n1) <- typeConstraints (m `M.union` mass) index
382 cs2 <- typeConstraintsForAssignmentTarget m mass arr tgt
383 return $ (TypeConstraint arr (Array (TUnknown nval))) : (TypeConstraint n1 Number) : cs1 ++ cs2
384typeConstraintsForAssignmentTarget m mass nval (AssignObjectProperty prop tgt) = do
387 cs <- typeConstraintsForAssignmentTarget m mass obj tgt
388 return $ (TypeConstraint obj (Object (RCons prop (TUnknown nval) $ RUnknown row))) : cs
390typeConstraintsForBlock :: M.Map String Int -> M.Map String Int -> Int -> [Statement] -> Check ([TypeConstraint], Bool, M.Map String Int)
391typeConstraintsForBlock _ mass _ [] = return ([], False, mass)
392typeConstraintsForBlock m mass ret (s:ss) = do
393 (cs1, b1, mass1) <- typeConstraintsForStatement (m `M.union` mass) mass ret s
395 (True, []) -> return (cs1, True, mass1)
396 (True, _) -> throwError "Unreachable code"
398 (cs2, b2, mass2) <- typeConstraintsForBlock m mass1 ret ss
399 return (cs1 ++ cs2, b2, mass2)
401solveTypeConstraints :: [TypeConstraint] -> TypeSolution -> Check TypeSolution
402solveTypeConstraints [] s = return s
403solveTypeConstraints all@(TypeConstraint n t:cs) s = do
405 let s' = let (f, g) = runTypeSolution s
406 in TypeSolution (replaceType n t . f, replaceType n t . g)
407 cs' <- fmap concat $ mapM (substituteTypeInConstraint n t) cs
408 solveTypeConstraints cs' s'
409solveTypeConstraints (RowConstraint n r:cs) s = do
411 let s' = let (f, g) = runTypeSolution s
412 in TypeSolution $ (replaceRow n r . f, replaceRow n r . g)
413 cs' <- fmap concat $ mapM (substituteRowInConstraint n r) cs
414 solveTypeConstraints cs' s'
416substituteTypeInConstraint :: Int -> Type -> TypeConstraint -> Check [TypeConstraint]
417substituteTypeInConstraint n s (TypeConstraint m t)
418 | n == m = unifyTypes s t
419 | otherwise = return [TypeConstraint m (replaceType n s t)]
420substituteTypeInConstraint n s (RowConstraint m r)
421 = return [RowConstraint m (replaceType n s r)]
423substituteRowInConstraint :: Int -> Row -> TypeConstraint -> Check [TypeConstraint]
424substituteRowInConstraint n r (TypeConstraint m t)
425 = return [TypeConstraint m (replaceRow n r t)]
426substituteRowInConstraint n r (RowConstraint m r1)
427 | m == n = unifyRows r r1
428 | otherwise = return [RowConstraint m (replaceRow n r r1)]
430unifyTypes :: Type -> Type -> Check [TypeConstraint]
431unifyTypes (TUnknown u1) (TUnknown u2) | u1 == u2 = return []
432unifyTypes (TUnknown u) t = do
434 return [TypeConstraint u t]
435unifyTypes t (TUnknown u) = do
437 return [TypeConstraint u t]
438unifyTypes Number Number = return []
439unifyTypes String String = return []
440unifyTypes Boolean Boolean = return []
441unifyTypes (Array s) (Array t) = unifyTypes s t
442unifyTypes (Object row1) (Object row2) = unifyRows row1 row2
443unifyTypes (Function args1 ret1) (Function args2 ret2) = do
444 guardWith "Function applied to incorrect number of args" $ length args1 == length args2
445 cs1 <- fmap concat $ zipWithM unifyTypes args1 args2
446 cs2 <- unifyTypes ret1 ret2
448unifyTypes (TypeVar v1) (TypeVar v2) | v1 == v2 = return []
449unifyTypes (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = return []
450unifyTypes (TypeVar v1) (TypeVar v2) = return []
451unifyTypes (TypeApp t1 t2) (TypeApp t3 t4) = do
452 cs1 <- unifyTypes t1 t3
453 cs2 <- unifyTypes t2 t4
455unifyTypes t1 t2 = throwError $ "Cannot unify " ++ show t1 ++ " with " ++ show t2 ++ "."
457unifyRows :: Row -> Row -> Check [TypeConstraint]
460 (s1, r1') = toList r1
461 (s2, r2') = toList r2
462 int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
463 sd1 = [ (name, t1) | (name, t1) <- s1, not (elem name (map fst s2)) ]
464 sd2 = [ (name, t2) | (name, t2) <- s2, not (elem name (map fst s1)) ]
465 in rethrow (\e -> "Cannot unify " ++ show r1 ++ " with " ++ show r2 ++ ": " ++ e) $ do
466 cs1 <- fmap concat $ mapM (uncurry unifyTypes) int
467 cs2 <- unifyRows' sd1 r1' sd2 r2'
470 unifyRows' :: [(String, Type)] -> Row -> [(String, Type)] -> Row -> Check [TypeConstraint]
471 unifyRows' [] (RUnknown u) sd r = do
473 return [RowConstraint u (fromList (sd, r))]
474 unifyRows' ((name, ty):row) r others (RUnknown u) = do
476 cs <- unifyRows' row r others (RUnknown u')
477 return [ RowConstraint u (RCons name ty (RUnknown u')) ]
478 unifyRows' [] REmpty [] REmpty = return []
479 unifyRows' sd1 r1 sd2 r2 = throwError $ "Cannot unify " ++ show (fromList (sd1, r1)) ++ " with " ++ show (fromList (sd2, r2)) ++ "."
480 toList :: Row -> ([(String, Type)], Row)
481 toList (RCons name ty row) = let (tys, rest) = toList row
482 in ((name, ty):tys, rest)
484 fromList :: ([(String, Type)], Row) -> Row
486 fromList ((name, t):ts, r) = RCons name t (fromList (ts, r))
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