blob: 1ec52112f3357ed5a78b0fc268cf2d9ce3748067 [file] [log] [blame] [edit]
-- |
-- Module : Minigent.TC.ConstraintGen
-- Copyright : (c) Data61 2018-2019
-- Commonwealth Science and Research Organisation (CSIRO)
-- ABN 41 687 119 230
-- License : BSD3
--
-- The constraint generator portion of the type inference.
--
-- May be used qualified or unqualified.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Minigent.TC.ConstraintGen (CG, runCG, cg, cgFunction) where
import Minigent.Syntax
import Minigent.Syntax.Utils
import qualified Minigent.Syntax.Utils.Row as Row
import Minigent.Fresh
import Minigent.Environment
import Control.Monad.Reader
import Control.Monad.Fail
import Control.Monad.State.Strict
import qualified Data.Map as M
import Data.Stream (Stream)
import Minigent.Syntax.PrettyPrint
import Debug.Trace
-- | A monad that is a combination of a state monad for the current type context,
-- a reader monad for the global environments, and fresh variables.
newtype CG a = CG (StateT (Context Type) (ReaderT GlobalEnvironments (FreshT VarName IO)) a)
deriving ( Monad, Applicative, Functor, MonadState (Context Type)
, MonadFresh VarName, MonadReader GlobalEnvironments
)
-- | Given an initial context and global environments, run a 'CG' computation.
runCG :: CG a -> Context Type -> GlobalEnvironments -> FreshT VarName IO a
runCG (CG x) ctx glb = runReaderT (evalStateT x ctx) glb
-- | The constraint generation relation for expressions as given in the language
-- definition.
--
-- @cg e tau@ returns @(e', c)@, where @e'@ is an annotated version of @e@ that contains
-- unknowns (unification variables) that, when substituted by a satisfying assignment to @c@,
-- produces a well-typed epxression.
cg :: Expr -> Type -> CG (Expr, Constraint)
cg e tau = case e of
(Var v) -> do
(rho, c) <- lookupVar v
withSig (Var v, c :&: rho :< tau)
(Sig e1 tau') -> do
(e1', c) <- cg e1 tau'
withSig (Sig e1' tau', c :&: tau' :< tau)
(Apply e1 e2) -> do
alpha <- UnifVar <$> fresh
(e1', c1) <- cg e1 (Function alpha tau)
(e2', c2) <- cg e2 alpha
withSig (Apply e1' e2', c1 :&: c2)
(Let x e1 e2) -> do
alpha <- UnifVar <$> fresh
(e1', c1) <- cg e1 alpha
modify (push (x, alpha))
(e2', c2) <- cg e2 tau
used <- topUsed <$> get
let c3 = if not used then Drop alpha else Sat
modify pop
withSig (Let x e1' e2', c1 :&: c2 :&: c3)
(Literal l) -> do
let c = case l of
BoolV _ -> PrimType Bool :< tau
UnitV -> PrimType Unit :< tau
IntV l -> l :<=: tau
withSig (e, c)
(If e1 e2 e3) -> do
(e1', c1) <- cg e1 (PrimType Bool)
g2 <- get
(e2', c2) <- cg e2 tau
g3 <- get
put g2
(e3', c3) <- cg e3 tau
g3' <- get
let (as, g4) = reconcile g3 g3'
c4 = conjunction (map Drop as)
put g4
withSig (If e1' e2' e3', c1 :&: c2 :&: c3 :&: c4)
(PrimOp Not [e]) -> do
(e', c) <- cg e (PrimType Bool)
withSig (PrimOp Not [e'], c :&: PrimType Bool :< tau)
(PrimOp o [e1,e2])
| o `elem` boolOps -> do
(e1', c1) <- cg e1 (PrimType Bool)
(e2', c2) <- cg e2 (PrimType Bool)
withSig (PrimOp o [e1', e2'], c1 :&: c2 :&: PrimType Bool :< tau)
| o `elem` compOps -> do
alpha <- UnifVar <$> fresh
(e1', c1) <- cg e1 alpha
(e2', c2) <- cg e2 alpha
withSig (PrimOp o [e1', e2'], 0 :<=: alpha :&: c1 :&: c2 :&: PrimType Bool :< tau)
| otherwise -> do
(e1', c1) <- cg e1 tau
(e2', c2) <- cg e2 tau
withSig (PrimOp o [e1', e2'], 0 :<=: tau :&: c1 :&: c2)
(TypeApp f ts) -> do
pt <- M.lookup f . types <$> ask
let (vs, cs, t) = case pt of
Just (Forall vs cs t) -> (vs, cs, t)
_ -> error "cg: TypeApp did not have a type in types"
as <- freshes (length vs - length ts)
let ts' = ts ++ map UnifVar as
subst = zip vs ts'
cs' = map (constraintTypes (traverseType (substTVs subst))) cs
withSig (TypeApp f ts', traverseType (substTVs subst) t :< tau :&: conjunction cs')
(Con n e) -> do
alpha <- UnifVar <$> fresh
row <- Row.incomplete [Entry n alpha False]
(e', c) <- cg e alpha
withSig (Con n e', Variant row :< tau :&: c)
(Case e1 k x e2 y e3) -> do
beta <- UnifVar <$> fresh
row <- Row.incomplete [Entry k beta False]
(e1', c1) <- cg e1 (Variant row)
g2 <- get
modify (push (x, beta))
(e2', c2) <- cg e2 tau
xUsed <- topUsed <$> get
let c6 = if xUsed then Sat else Drop beta
modify pop
g3 <- get
put g2
let row' = Row.take k row
modify (push (y, Variant row'))
(e3', c3) <- cg e3 tau
yUsed <- topUsed <$> get
let c7 = if yUsed then Sat else Drop (Variant row')
modify pop
g3' <- get
let (as, g4) = reconcile g3 g3'
c4 = conjunction (map Drop as)
put g4
withSig (Case e1' k x e2' y e3', c1 :&: c2 :&: c3 :&: c4 :&: c6 :&: c7)
(Esac e1 k x e2) -> do
beta <- UnifVar <$> fresh
row <- Row.incomplete [Entry k beta False]
(e1', c1) <- cg e1 (Variant row)
modify (push (x, beta))
(e2', c2) <- cg e2 tau
xUsed <- topUsed <$> get
let c4 = if xUsed then Sat else Drop beta
c3 = Exhausted (Variant (Row.take k row))
modify pop
withSig (Esac e1' k x e2', c1 :&: c2 :&: c3 :&: c4)
(LetBang ys x e1 e2) -> do
alpha <- UnifVar . (++"letba") <$> fresh
modify (alter ys (\(t,u) -> (Bang t, 0)))
(e1', c1) <- cg e1 alpha
bangRhos' <- factor ys <$> get
let c3 = conjunction (map Drop (unused bangRhos'))
modify (alter ys (\(Bang t, u) -> (t,0)))
modify (push (x, alpha))
(e2', c2) <- cg e2 tau
xUsed <- topUsed <$> get
let c5 = if xUsed then Sat else Drop alpha
modify pop
rhos' <- factor ys <$> get
let c4 = conjunction (map Drop (unused rhos'))
c6 = Escape alpha
withSig (LetBang ys x e1' e2', c1 :&: c2 :&: c3 :&: c4 :&: c5 :&: c6)
(Member e f) -> do
row <- Row.incomplete [Entry f tau False]
sigil <- fresh
recPar <- UnknownParameter <$> fresh
let alpha = Record recPar row (UnknownSigil sigil)
(e', c1) <- cg e alpha
let c2 = Drop (Record recPar (Row.take f row) (UnknownSigil sigil))
withSig (Member e' f, c1 :&: c2)
-- Remaining record, field name, extracted contents var, record extrating from, following expression
(Take x f y e1 e2) -> do
beta <- UnifVar <$> fresh
row <- Row.incomplete [Entry f beta False]
sigil <- fresh
recPar <- UnknownParameter <$> fresh
let alpha = Record recPar row (UnknownSigil sigil)
let c0 = UnboxedNoRecurse recPar (UnknownSigil sigil)
(e1', c1) <- cg e1 alpha
modify (push (y, beta))
modify (push (x, Record recPar (Row.take f row) (UnknownSigil sigil)))
(e2', c2) <- cg e2 tau
xUsed <- topUsed <$> get
let c3 = if xUsed then Sat else Drop (Record recPar (Row.take f row) (UnknownSigil sigil))
modify pop
yUsed <- topUsed <$> get
let c4 = if yUsed then Sat else Drop beta
modify pop
withSig (Take x f y e1' e2', c0 :&: c1 :&: c2 :&: c3 :&: c4)-- :&: c5)
(Put e1 f e2) -> do
beta <- UnifVar <$> fresh
sigil <- fresh
recPar <- UnknownParameter <$> fresh
row <- Row.incomplete [Entry f beta True]
let alpha = Record recPar row (UnknownSigil sigil)
(e1', c1) <- cg e1 alpha
(e2', c2) <- cg e2 beta
let c0 = UnboxedNoRecurse recPar (UnknownSigil sigil)
let c3 = Record recPar (Row.put f row) (UnknownSigil sigil) :< tau
withSig (Put e1' f e2', c0 :&: c1 :&: c2 :&: c3)
(Struct fs) -> do
(fs', ts, cs) <- cgStruct fs
withSig (Struct fs', conjunction cs :&: Record None (Row.fromList ts) Unboxed :< tau )
where
cgStruct :: [(FieldName, Expr)] -> CG ([(FieldName, Expr)], [Entry], [Constraint])
cgStruct [] = return ([], [], [])
cgStruct ((f,e):fs) = do
(fs', ts', cs') <- cgStruct fs
alpha <- UnifVar <$> fresh
(e', c) <- cg e alpha
return ((f,e'):fs', (Entry f alpha False):ts', c:cs')
withSig :: (Expr, Constraint) -> CG (Expr, Constraint)
withSig (e, c) = pure (Sig e tau, c)
-- | Looks up a variable and increases it's usage count, adding the
-- constraint that it is shareable if it's been used more than once
lookupVar :: VarName -> CG (Type, Constraint)
lookupVar v = do
(rho, used, ctx') <- use v <$> get
put ctx'
return (rho, if used then Share rho else Sat)
-- | Used for constraint generation for top-level functions.
-- Given a function name, argument name and a function body expression,
-- return an annotated function body along with the constraint that would make
-- it well typed. Also included in the first component of the return value
-- are the axioms (constraints placed by the user in the type signature)
-- about polymorphic type variables.
cgFunction :: FunName -> VarName -> Expr -> CG ([Constraint], Expr, Constraint)
cgFunction f x e = do
alpha <- UnifVar <$> fresh
beta <- UnifVar <$> fresh
let proposedType = Function alpha beta
modify (push (x,alpha))
(e', c) <- cg e beta
used <- topUsed <$> get
let c' = if used then Sat else Drop alpha
modify pop
envs <- ask
let (c'',axs) = case M.lookup f (types envs) of
Nothing -> (Sat, [])
Just (Forall vs cs tau) -> (proposedType :< tau, cs)
-- Inferred constraints for return type
-- && proposed function type is subtype of inferred function type
-- && Argument to function is used or droppable
pure (axs, e', c :&: c'' :&: c')