elm/compiler/Types/Constrain.hs
2013-06-23 01:36:23 -07:00

344 lines
13 KiB
Haskell

module Types.Constrain (constrain) where
import Control.Arrow (second)
import Control.Monad (liftM,mapM,zipWithM,foldM)
import Control.Monad.State (evalState)
import Data.Char (isDigit)
import Data.List (foldl',sort,group,isPrefixOf,intercalate,isSuffixOf)
import qualified Data.Map as Map
import qualified Data.Set as Set
import SourceSyntax.Everything
import Unique
import Types.Types
import qualified Types.Substitutions as Subs
import System.IO.Unsafe
beta = VarT `liftM` guid
unionA = Map.unionWith (++)
unionsA = Map.unionsWith (++)
getAliases :: [(String,ImportMethod)] -> [(String,Scheme)] -> [(String,Scheme)]
getAliases imports hints = concatMap aliasesFrom imports
where
-- Get the names of values that are now in scope after a particular
-- import statement.
aliasesFrom :: (String,ImportMethod) -> [(String,Scheme)]
aliasesFrom (name,method) =
let values = concatMap (getValue name) hints
prefixed name = map (\(n,t) -> (name ++ "." ++ n, t)) values
in case method of
As alias -> prefixed alias
Hiding vs -> prefixed name ++ filter (\(n,t) -> n `notElem` vs) values
Importing vs -> prefixed name ++ filter (\(n,t) -> n `elem` vs) values
-- Take a module name and a type annotation. Return the unprefxed
-- type annotation: getValue "List" ("List.map",t) == [("map",t)]
getValue :: String -> (String,Scheme) -> [(String,Scheme)]
getValue inModule (name,tipe) =
case inModule `isPrefixOf` name of
True -> [ (drop (length inModule + 1) name, tipe) ]
False -> []
findAmbiguous hints assumptions continue =
let potentialDups = map head . filter (\g -> length g > 1) . group . sort $
filter (elem '.') hints
dups = filter (\k -> Map.member k assumptions) potentialDups
in case dups of
n:_ -> return . Left $ "Error: Ambiguous occurrence of '" ++ n ++
"' could refer to " ++
intercalate ", " (filter (isSuffixOf n) hints)
_ -> continue
mergeSchemes :: [Map.Map String Scheme]
-> Unique (TVarMap, Constraints, Map.Map String Scheme)
mergeSchemes schmss = do (ass,css,sss) <- unzip3 `liftM` mapM split kvs
return (Map.unions ass, concat css, Map.unions sss)
where
kvs = Map.toList $ Map.unionsWith (++) (map (Map.map (:[])) schmss)
split (k,vs) =
let ps = zipWith (\s v -> (s++k,v)) (map (flip replicate '_') [0..]) vs
eq t u = L (Just $ msg ++ k) NoSpan (VarT t :=: VarT u)
msg = "the definition of "
in do xs <- mapM (\_ -> guid) vs
return ( Map.fromList $ zip (map fst ps) (map (:[]) xs)
, case xs of
t:ts -> zipWith eq xs ts
[] -> []
, Map.fromList ps )
--constrain :: Map.Map String Scheme
-- -> Module
-- -> Unique (Either String Constraints)
constrain typeHints (Module _ _ imports stmts) = do
(ass,css,schemess) <- unzip3 `liftM` mapM stmtGen stmts
aliasHints <- getAliases imports `liftM` typeHints
(as', cs', schemes) <- mergeSchemes schemess
let constraints = concat (cs':css)
as = unionsA (as':ass)
allHints = Map.union schemes (Map.fromList aliasHints)
insert as n = do v <- guid; return $ Map.insertWith' (\_ x -> x) n [v] as
assumptions <- foldM insert as (Map.keys schemes)
findAmbiguous (map fst aliasHints) assumptions $ do
let f k s vs = map (\v -> L (Just k) NoSpan $ v :<<: s) vs
cs = concat . Map.elems $ Map.intersectionWithKey f allHints assumptions
escapees = Map.keys $ Map.difference assumptions allHints
msg = "Warning! Type-checker could not find variables:\n" ++ intercalate ", " escapees
return $ case escapees of
[] -> Right (schemess, constraints ++ cs)
_ -> unsafePerformIO (putStrLn msg) `seq` Right (schemess, constraints ++ cs)
--_ -> Left ("Undefined variable(s): " ++ intercalate ", " escapees)
type TVarMap = Map.Map String [X]
type Constraints = [Located Constraint]
loc e span = L (Just $ show e) span
gen :: LExpr t v -> Unique (TVarMap, Constraints, Type)
gen (L _ span expr) =
let loc' = L (Just $ show expr) span in
case expr of
Var x ->
do b <- guid
return (Map.singleton x [b], [], VarT b)
App e1 e2 ->
do (a1,c1,t1) <- gen e1
(a2,c2,t2) <- gen e2
b <- beta
return ( unionA a1 a2
, c1 ++ c2 ++ [loc' $ t1 :=: (LambdaT t2 b)]
, b )
Lambda x e ->
do (a,c,t) <- gen e
b <- beta
v <- guid
return ( Map.delete x a
, (++) c . map (\x -> loc' $ VarT x :=: b) $
Map.findWithDefault [v] x a
, LambdaT b t )
Let defs e ->
do (as,cs,t) <- gen e
(ass, schemes) <- liftM unzip (mapM defScheme defs)
let assumptions = unionsA (as:ass)
getName d = case d of FnDef f _ _ -> f
OpDef op _ _ _ -> op
TypeAnnotation n _ -> n
names = map getName defs
genCs name s = do
v <- guid
let vs = Map.findWithDefault [v] name assumptions
return $ map (\x -> loc name span $ x :<<: s) vs
cs' <- zipWithM genCs names schemes
return ( foldr Map.delete assumptions names
, concat cs' ++ cs
, t )
Case e cases ->
do (as,cs,t) <- gen e
(ass,css,ts) <- liftM unzip3 $ mapM (caseGen t) cases
b <- beta
return ( unionsA $ as:ass
, let cases' = map snd cases
locs = zipWith merge cases' (tail cases')
cs' = zipWith (\t loc -> loc (b :=: t)) ts locs
in concat $ cs' : cs : css
, b)
ExplicitList es ->
do (ass,css,ts) <- unzip3 `liftM` mapM gen es
t <- beta
let cs = zipWith (\t' (L a b _) -> L a b (t :=: t')) ts es
return ( unionsA ass, cs ++ concat css, listOf t )
Data name es ->
gen $ foldl' (\f x -> merge f x $ App f x) (loc' $ Var name) es
Binop op e1 e2 ->
gen $ loc' (App (loc' $ App (loc' $ Var op) e1) e2)
Access e label ->
do (as,cs,rtype) <- gen e
t <- beta
rtype' <- beta
let fs = Map.singleton label [t]
c = (loc' (RecordT fs rtype' :=: rtype))
return (as, c:cs, t)
Remove e x ->
do (as,cs,rtype) <- gen e
t <- beta
rtype' <- beta
let c = (loc' (RecordT (Map.singleton x [t]) rtype' :=: rtype))
return (as, c:cs, rtype')
Insert e x v ->
do (eas,ecs,etype) <- gen e
(vas,vcs,vtype) <- gen v
return ( unionA eas vas
, ecs ++ vcs
, RecordT (Map.singleton x [vtype]) etype )
Modify record fields ->
do (ras,rcs,rtype) <- gen record
(ass,css,newTs) <- unzip3 `liftM` mapM gen (map snd fields)
oldTs <- mapM (\_ -> beta) fields
rtype' <- beta
let rT ts = RecordT (recordT (zip (map fst fields) ts)) rtype'
c = [ loc' (rtype :=: rT oldTs) ]
return ( unionsA (ras:ass), concat (c : rcs : css), rT newTs )
Record fields ->
let insert label tipe = Map.insertWith (++) label [tipe]
getScheme (f,args,e) = do
(as, _, (label, Forall _ cs tipe)) <- defGenHelp f args e
return (as, cs, insert label tipe)
in do (ass, css, fs) <- unzip3 `liftM` mapM getScheme fields
return ( unionsA ass
, concat css
, RecordT (foldr ($) Map.empty fs) EmptyRecord )
Range e1@(L w1 s1 _) e2@(L w2 s2 _) ->
do (a1,c1,t1) <- gen e1
(a2,c2,t2) <- gen e2
return ( unionsA [a1,a2]
, c1 ++ c2 ++ [ L w1 s1 (t1 :=: int), L w1 s2 (t2 :=: int) ]
, listOf int )
MultiIf ps -> do (ass,css,t:ts) <- unzip3 `liftM` mapM genPair ps
let cs = map (loc' . (t :=:)) ts
return (unionsA ass, concat (cs:css), t)
where genPair (b@(L t s _),e) = do
(a1,c1,t1) <- gen b
(a2,c2,t2) <- gen e
return ( unionsA [a1,a2]
, c1 ++ c2 ++ [ L t s (t1 :=: bool) ]
, t2 )
Markdown _ -> primitive element
Literal lit -> literalGen loc' lit
literalGen :: (Constraint -> Located Constraint)
-> Literal
-> Unique (TVarMap, Constraints, Type)
literalGen loc lit =
case lit of
IntNum _ -> do t <- beta
return (Map.empty, [loc $ t :<: number], t)
FloatNum _ -> primitive float
Boolean _ -> primitive bool
Chr _ -> primitive char
Str _ -> primitive string
primitive :: Type -> Unique (TVarMap, Constraints, Type)
primitive t = return (Map.empty, [], t)
caseGen :: Type
-> (Pattern, LExpr t v)
-> Unique (TVarMap, Constraints, Type)
caseGen tipe (p, ce@(L _ span e)) = do
(as ,cs ,t) <- gen ce
(as',cs',_) <- patternGen (loc p span) tipe as p
return ( as', cs ++ cs', t )
patternGen :: (Constraint -> Located Constraint)
-> Type -- Type of e in `case e of ...`
-> TVarMap
-> Pattern
-> Unique (TVarMap, Constraints, Type)
patternGen loc tipe as pattern =
case pattern of
PAnything -> do b <- beta ; return ( as, [], b )
PLiteral lit -> literalGen loc lit
PVar v -> do
b <- beta
let cs = map (loc . (b :=:) . VarT) (Map.findWithDefault [] v as)
return ( Map.delete v as, loc (b :=: tipe) : cs, b )
PAlias v p -> do
b <- beta
let cs = map (loc . (b :=:) . VarT) (Map.findWithDefault [] v as)
(as', cs', tipe') <- patternGen loc b as p
return (Map.delete v as',
cs' ++ [ loc (b :=: tipe), loc (b :=: tipe') ] ++ cs,
b)
PData name ps -> do
constr <- guid
output <- beta
let step (as,cs,tipe) p = do b <- beta
(as',cs',t) <- patternGen loc b as p
return (as', cs ++ cs', t ==> tipe)
(as',cs, t) <- foldM step (as,[],tipe) (reverse ps)
return ( Map.insert name [constr] as'
, loc (VarT constr :=: t) : cs
, output )
PRecord fs ->
do pairs <- mapM (\f -> do b <- beta; return (f,b)) fs
b <- beta
let t = RecordT (Map.fromList $ map (second (:[])) pairs) b
mkCs (name,tipe) = map (loc . (tipe :=:) . VarT)
(Map.findWithDefault [] name as)
return ( foldr Map.delete as fs
, loc (t :=: tipe) : concatMap mkCs pairs
, t )
defScheme :: Def t v -> Unique (Map.Map String [X], Scheme)
defScheme def = do (as,cs,hint) <- defGen def
return ( as, snd hint )
defGen def = case def of
FnDef f args e -> defGenHelp f args e
OpDef op a1 a2 e -> defGenHelp op [a1,a2] e
TypeAnnotation name tipe -> do
schm <- Subs.generalize [] =<< Subs.superize name tipe
return (Map.empty, [], (name, schm))
defGenHelp name args e = do
argDict <- mapM (\a -> liftM ((,) a) guid) args
(as,cs,t) <- gen e
let as' = foldr Map.delete as args
tipe = foldr (==>) t $ map (VarT . snd) argDict
genCs (arg,x) = do
v <- guid
let as' = Map.findWithDefault [v] arg as
return $ map (\y -> loc arg NoSpan $ VarT x :=: VarT y) as'
cs' <- concat `liftM` mapM genCs argDict
scheme <- Subs.generalize (concat $ Map.elems as') $
Forall (map snd argDict) (cs' ++ cs) tipe
return ( as', [], (name, scheme) )
stmtGen :: Declaration t v
-> Unique (TVarMap, Constraints, Map.Map String Scheme)
stmtGen stmt =
case stmt of
Definition def -> do (as,cs,hint) <- defGen def
return ( as, cs, uncurry Map.singleton hint )
Datatype name xs tcs ->
let toScheme ts = Forall xs [] (foldr (==>) (ADT name $ map VarT xs) ts)
in return (Map.empty, [], Map.fromList (map (second toScheme) tcs))
ExportEvent js elm tipe ->
do x <- guid
return ( Map.singleton elm [x]
, [ loc elm NoSpan $ VarT x :=: tipe ]
, Map.empty )
ImportEvent js e@(L txt span base) elm tipe ->
do (as,cs,t) <- gen e
return ( as
, L txt span (signalOf t :=: tipe) : cs
, Map.singleton elm (Forall [] [] tipe) )
TypeAlias _ _ _ -> return (Map.empty, [], Map.empty)