1502 lines
60 KiB
Agda
1502 lines
60 KiB
Agda
module Lib.Elab
|
||
|
||
import Prelude
|
||
import Lib.Common
|
||
import Lib.Parser.Impl
|
||
import Lib.Prettier
|
||
import Data.String
|
||
import Data.SnocList
|
||
import Data.IORef
|
||
import Data.SortedMap
|
||
import Lib.Eval
|
||
import Lib.Util
|
||
import Lib.TopContext
|
||
-- FIXME Def is shadowing...
|
||
import Lib.Syntax
|
||
import Lib.Types
|
||
|
||
vprint : Context -> Val -> M String
|
||
vprint ctx v = do
|
||
tm <- quote (length' ctx.env) v
|
||
pure $ render 90 $ pprint (names ctx) tm
|
||
|
||
-- collectDecl collects multiple Def for one function into one
|
||
collectDecl : List Decl -> List Decl
|
||
collectDecl Nil = Nil
|
||
collectDecl ((Def fc nm cl) :: rest@(Def _ nm' cl' :: xs)) =
|
||
if nm == nm' then collectDecl (Def fc nm (cl ++ cl') :: xs)
|
||
else (Def fc nm cl :: collectDecl rest)
|
||
collectDecl (x :: xs) = x :: collectDecl xs
|
||
|
||
rpprint : List String → Tm → String
|
||
rpprint names tm = render 90 $ pprint names tm
|
||
|
||
showCtx : Context -> M String
|
||
showCtx ctx =
|
||
unlines ∘ reverse <$> go (names ctx) 0 (reverse $ zip ctx.env ctx.types) Nil
|
||
where
|
||
isVar : Int -> Val -> Bool
|
||
isVar k (VVar _ k' Lin) = k == k'
|
||
isVar _ _ = False
|
||
|
||
go : List String -> Int -> List (Val × String × Val) -> List String -> M (List String)
|
||
go _ _ Nil acc = pure acc
|
||
go names k ((v, n, ty) :: xs) acc = if isVar k v
|
||
-- TODO - use Doc and add <+/> as appropriate to printing
|
||
then do
|
||
tty <- quote ctx.lvl ty
|
||
go names (1 + k) xs (" \{n} : \{rpprint names tty}" :: acc)
|
||
else do
|
||
tm <- quote ctx.lvl v
|
||
tty <- quote ctx.lvl ty
|
||
go names (1 + k) xs (" \{n} = \{rpprint names tm} : \{rpprint names tty}" :: acc)
|
||
|
||
dumpCtx : Context -> M String
|
||
dumpCtx ctx = do
|
||
let names = (map fst ctx.types)
|
||
-- I want to know which ones are defines. I should skip the `=` bit if they match, I'll need indices in here too.
|
||
env <- for (zip ctx.env ctx.types) $ \case
|
||
(v, n, ty) => do
|
||
sty <- vprint ctx ty
|
||
sv <- vprint ctx v
|
||
pure " \{n} : \{sty} = \{sv}"
|
||
let msg = unlines (reverse env) -- ++ " -----------\n" ++ " goal \{rpprint names ty'}"
|
||
pure msg
|
||
|
||
-- return Bnd and type for name
|
||
lookupName : Context -> String -> Maybe (Tm × Val)
|
||
lookupName ctx name = go 0 ctx.types
|
||
where
|
||
go : Int -> List (String × Val) -> Maybe (Tm × Val)
|
||
go ix Nil = Nothing
|
||
-- FIXME - we should stuff a Binder of some sort into "types"
|
||
go ix ((nm, ty) :: xs) = if nm == name then Just (Bnd emptyFC ix, ty) else go (1 + ix) xs
|
||
|
||
lookupDef : Context -> String -> Maybe Val
|
||
lookupDef ctx name = go 0 ctx.types ctx.env
|
||
where
|
||
go : Int -> List (String × Val) -> List Val -> Maybe Val
|
||
go ix ((nm, ty) :: xs) (v :: vs) = if nm == name then Just v else go (1 + ix) xs vs
|
||
go ix _ _ = Nothing
|
||
|
||
forceMeta : Val -> M Val
|
||
forceMeta (VMeta fc ix sp) = do
|
||
meta <- lookupMeta ix
|
||
case meta of
|
||
(Solved _ k t) => vappSpine t sp >>= forceMeta
|
||
_ => pure (VMeta fc ix sp)
|
||
forceMeta x = pure x
|
||
|
||
record UnifyResult where
|
||
constructor MkResult
|
||
-- wild guess here - lhs is a VVar
|
||
constraints : List (Int × Val)
|
||
|
||
instance Semigroup UnifyResult where
|
||
(MkResult cs) <+> (MkResult cs') = MkResult (cs ++ cs')
|
||
|
||
instance Monoid UnifyResult where
|
||
neutral = MkResult Nil
|
||
|
||
data UnifyMode = UNormal | UPattern
|
||
|
||
check : Context -> Raw -> Val -> M Tm
|
||
|
||
unifyCatch : FC -> Context -> Val -> Val -> M Unit
|
||
|
||
-- Check that the arguments are not explicit and the type constructor in codomain matches
|
||
-- Later we will build a table of codomain type, and maybe make the user tag the candidates
|
||
-- like Idris does with %hint
|
||
isCandidate : Val -> Tm -> Bool
|
||
isCandidate ty (Pi fc nm Explicit rig t u) = False
|
||
isCandidate ty (Pi fc nm icit rig t u) = isCandidate ty u
|
||
isCandidate (VRef _ nm _) (Ref fc nm') = nm == nm'
|
||
isCandidate ty (App fc t u) = isCandidate ty t
|
||
isCandidate _ _ = False
|
||
|
||
findMatches : Context -> Val -> List TopEntry -> M (List String)
|
||
findMatches ctx ty Nil = pure Nil
|
||
findMatches ctx ty ((MkEntry _ name type def) :: xs) = do
|
||
let (True) = isCandidate ty type | False => findMatches ctx ty xs
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
catchError (do
|
||
-- TODO sort out the FC here
|
||
let fc = getFC ty
|
||
debug $ \ _ => "TRY \{show name} : \{rpprint Nil type} for \{show ty}"
|
||
-- This check is solving metas, so we save mc below in case we want this solution
|
||
let (QN ns nm) = name
|
||
let (cod, tele) = splitTele type
|
||
modifyIORef top.metaCtx $ \ mc => MC mc.metas mc.next CheckFirst
|
||
tm <- check ctx (RVar fc nm) ty
|
||
debug $ \ _ => "Found \{rpprint Nil tm} for \{show ty}"
|
||
writeIORef top.metaCtx mc
|
||
(_::_ nm) <$> findMatches ctx ty xs)
|
||
(\ err => do
|
||
debug $ \ _ => "No match \{show ty} \{rpprint Nil type} \{showError "" err}"
|
||
writeIORef top.metaCtx mc
|
||
findMatches ctx ty xs)
|
||
|
||
contextMatches : Context -> Val -> M (List (Tm × Val))
|
||
contextMatches ctx ty = go (zip ctx.env ctx.types)
|
||
where
|
||
go : List (Val × String × Val) -> M (List (Tm × Val))
|
||
go Nil = pure Nil
|
||
go ((tm, nm, vty) :: xs) = do
|
||
type <- quote ctx.lvl vty
|
||
let (True) = isCandidate ty type | False => go xs
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
catchError(do
|
||
debug $ \ _ => "TRY context \{nm} : \{rpprint (names ctx) type} for \{show ty}"
|
||
unifyCatch (getFC ty) ctx ty vty
|
||
mc' <- readIORef top.metaCtx
|
||
writeIORef top.metaCtx mc
|
||
tm <- quote ctx.lvl tm
|
||
(_::_ (tm, vty)) <$> go xs)
|
||
(\ err => do
|
||
debug $ \ _ => "No match \{show ty} \{rpprint (names ctx) type} \{showError "" err}"
|
||
writeIORef top.metaCtx mc
|
||
go xs)
|
||
|
||
getArity : Tm -> Int
|
||
getArity (Pi x str icit rig t u) = 1 + getArity u
|
||
-- Ref or App (of type constructor) are valid
|
||
getArity _ = 0
|
||
|
||
-- Makes the arg for `solve` when we solve an auto
|
||
makeSpine : Int -> List BD -> SnocList Val
|
||
makeSpine k Nil = Lin
|
||
makeSpine k (Defined :: xs) = makeSpine (k - 1) xs
|
||
makeSpine k (Bound :: xs) = makeSpine (k - 1) xs :< VVar emptyFC (k - 1) Lin
|
||
|
||
solve : Env -> QName -> SnocList Val -> Val -> M Unit
|
||
|
||
trySolveAuto : MetaEntry -> M Bool
|
||
trySolveAuto (Unsolved fc k ctx ty AutoSolve _) = do
|
||
debug $ \ _ => "TRYAUTO solving \{show k} : \{show ty}"
|
||
-- fill in solved metas in type
|
||
x <- quote ctx.lvl ty
|
||
ty <- eval ctx.env CBN x
|
||
debug $ \ _ => "AUTO ---> \{show ty}"
|
||
-- we want the context here too.
|
||
top <- get
|
||
Nil <- contextMatches ctx ty
|
||
| ((tm, vty) :: Nil) => do
|
||
unifyCatch (getFC ty) ctx ty vty
|
||
val <- eval ctx.env CBN tm
|
||
debug $ \ _ => "SOLUTION \{rpprint Nil tm} evaled to \{show val}"
|
||
let sp = makeSpine ctx.lvl ctx.bds
|
||
solve ctx.env k sp val
|
||
pure True
|
||
| res => do
|
||
debug $ \ _ => "FAILED to solve \{show ty}, matches: \{render 90 $ commaSep $ map (pprint Nil ∘ fst) res}"
|
||
pure False
|
||
let te = listValues top.defs
|
||
let rest = map {List} (\ x => listValues x.modDefs) $
|
||
mapMaybe (flip lookupMap' top.modules) top.imported
|
||
|
||
(nm :: Nil) <- findMatches ctx ty $ join (te :: rest)
|
||
| res => do
|
||
debug $ \ _ => "FAILED to solve \{show ty}, matches: \{show res}"
|
||
pure False
|
||
tm <- check ctx (RVar fc nm) ty
|
||
val <- eval ctx.env CBN tm
|
||
debug $ \ _ => "SOLUTION \{rpprint Nil tm} evaled to \{show val}"
|
||
let sp = makeSpine ctx.lvl ctx.bds
|
||
solve ctx.env k sp val
|
||
pure True
|
||
trySolveAuto _ = pure False
|
||
|
||
solveAutos : M Unit
|
||
solveAutos = do
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
res <- run $ filter isAuto (listValues mc.metas)
|
||
if res then solveAutos else pure MkUnit
|
||
where
|
||
isAuto : MetaEntry -> Bool
|
||
isAuto (Unsolved fc k ctx x AutoSolve xs) = True
|
||
isAuto _ = False
|
||
|
||
run : List MetaEntry -> M Bool
|
||
run Nil = pure False
|
||
run (e :: es) = do
|
||
res <- trySolveAuto e
|
||
if res then pure True else run es
|
||
|
||
-- We need to switch to SortedMap here
|
||
|
||
updateMeta : QName -> (MetaEntry -> M MetaEntry) -> M Unit
|
||
updateMeta ix f = do
|
||
top <- get
|
||
mc <- readIORef {M} top.metaCtx
|
||
let (Just me) = lookupMap' ix mc.metas | Nothing => pure MkUnit
|
||
me <- f me
|
||
writeIORef top.metaCtx $ MC (updateMap ix me mc.metas) mc.next mc.mcmode
|
||
|
||
checkAutos : QName -> List MetaEntry -> M Unit
|
||
checkAutos ix Nil = pure MkUnit
|
||
checkAutos ix (entry@(Unsolved fc k ctx ty AutoSolve _) :: rest) = do
|
||
ty' <- quote ctx.lvl ty
|
||
when (usesMeta ty') $ \ _ => ignore $ trySolveAuto entry
|
||
checkAutos ix rest
|
||
where
|
||
usesMeta : Tm -> Bool
|
||
usesMeta (App _ (Meta _ k) u) = if k == ix then True else usesMeta u
|
||
usesMeta (App _ _ u) = usesMeta u
|
||
usesMeta _ = False
|
||
checkAutos ix (_ :: rest) = checkAutos ix rest
|
||
|
||
addConstraint : Env -> QName -> SnocList Val -> Val -> M Unit
|
||
addConstraint env ix sp tm = do
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
let (CheckAll) = mc.mcmode | _ => pure MkUnit
|
||
updateMeta ix $ \case
|
||
(Unsolved pos k a b c cons) => do
|
||
debug $ \ _ => "Add constraint m\{show ix} \{show sp} =?= \{show tm}"
|
||
pure (Unsolved pos k a b c (MkMc (getFC tm) env sp tm :: cons))
|
||
(Solved _ k tm) => error' "Meta \{show k} already solved [addConstraint]"
|
||
(OutOfScope) => error' "Meta \{show ix} out of scope"
|
||
mc <- readIORef top.metaCtx
|
||
checkAutos ix (listValues mc.metas)
|
||
pure MkUnit
|
||
|
||
-- return renaming, the position is the new VVar
|
||
invert : Int -> SnocList Val -> M (List Int)
|
||
invert lvl sp = go sp Nil
|
||
where
|
||
go : SnocList Val -> List Int -> M (List Int)
|
||
go Lin acc = pure $ reverse acc
|
||
go (xs :< VVar fc k Lin) acc = do
|
||
if elem k acc
|
||
then do
|
||
debug $ \ _ => "\{show k} \{show acc}"
|
||
-- when does this happen?
|
||
error fc "non-linear pattern: \{show sp}"
|
||
else go xs (k :: acc)
|
||
go (xs :< v) _ = error emptyFC "non-variable in pattern \{show v}"
|
||
|
||
rename : QName -> List Int -> Int -> Val -> M Tm
|
||
|
||
renameSpine : QName -> List Int -> Int -> Tm -> SnocList Val -> M Tm
|
||
renameSpine meta ren lvl tm Lin = pure tm
|
||
renameSpine meta ren lvl tm (xs :< x) = do
|
||
xtm <- rename meta ren lvl x
|
||
xs' <- renameSpine meta ren lvl tm xs
|
||
pure $ App emptyFC xs' xtm
|
||
|
||
rename meta ren lvl (VVar fc k sp) = case findIndex' (_==_ k) ren of
|
||
Nothing => error fc "scope/skolem thinger VVar \{show k} ren \{show ren}"
|
||
Just x => renameSpine meta ren lvl (Bnd fc x) sp
|
||
rename meta ren lvl (VRef fc nm sp) = renameSpine meta ren lvl (Ref fc nm) sp
|
||
rename meta ren lvl (VMeta fc ix sp) = do
|
||
-- So sometimes we have an unsolved meta in here which reference vars out of scope.
|
||
debug $ \ _ => "rename Meta \{show ix} spine \{show sp}"
|
||
if ix == meta
|
||
-- REVIEW is this the right fc?
|
||
then error fc "meta occurs check"
|
||
else do
|
||
meta' <- lookupMeta ix
|
||
case meta' of
|
||
Solved fc _ val => do
|
||
debug $ \ _ => "rename: \{show ix} is solved"
|
||
val' <- vappSpine val sp
|
||
rename meta ren lvl val'
|
||
_ => do
|
||
debug $ \ _ => "rename: \{show ix} is unsolved"
|
||
catchError (renameSpine meta ren lvl (Meta fc ix) sp) (\err => throwError $ Postpone fc ix (errorMsg err))
|
||
rename meta ren lvl (VLam fc n icit rig t) = do
|
||
tapp <- t $$ VVar fc lvl Lin
|
||
scope <- rename meta (lvl :: ren) (1 + lvl) tapp
|
||
pure (Lam fc n icit rig scope)
|
||
rename meta ren lvl (VPi fc n icit rig ty tm) = do
|
||
ty' <- rename meta ren lvl ty
|
||
tmapp <- tm $$ VVar emptyFC lvl Lin
|
||
scope' <- rename meta (lvl :: ren) (1 + lvl) tmapp
|
||
pure (Pi fc n icit rig ty' scope')
|
||
rename meta ren lvl (VU fc) = pure (UU fc)
|
||
rename meta ren lvl (VErased fc) = pure (Erased fc)
|
||
-- for now, we don't do solutions with case in them.
|
||
rename meta ren lvl (VCase fc sc alts) = error fc "Case in solution"
|
||
rename meta ren lvl (VLit fc lit) = pure (Lit fc lit)
|
||
rename meta ren lvl (VLet fc name val body) = do
|
||
val' <- rename meta ren lvl val
|
||
body' <- rename meta (lvl :: ren) (1 + lvl) body
|
||
pure $ Let fc name val' body'
|
||
-- these probably shouldn't show up in solutions...
|
||
rename meta ren lvl (VLetRec fc name ty val body) = do
|
||
ty' <- rename meta ren lvl ty
|
||
val' <- rename meta (lvl :: ren) (1 + lvl) val
|
||
body' <- rename meta (lvl :: ren) (1 + lvl) body
|
||
pure $ LetRec fc name ty' val' body'
|
||
|
||
lams : Nat -> List String -> Tm -> Tm
|
||
lams Z _ tm = tm
|
||
-- REVIEW do we want a better FC, icity?, rig?
|
||
lams (S k) Nil tm = Lam emptyFC "arg_\{show k}" Explicit Many (lams k Nil tm)
|
||
lams (S k) (x :: xs) tm = Lam emptyFC x Explicit Many (lams k xs tm)
|
||
|
||
unify : Env -> UnifyMode -> Val -> Val -> M UnifyResult
|
||
|
||
.boundNames : Context -> List String
|
||
ctx.boundNames = map snd $ filter (\x => fst x == Bound) $ zip ctx.bds (map fst ctx.types)
|
||
|
||
maybeCheck : M Unit -> M Unit
|
||
maybeCheck action = do
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
case mc.mcmode of
|
||
CheckAll => action
|
||
CheckFirst => do
|
||
modifyIORef top.metaCtx $ \ mc => MC mc.metas mc.next NoCheck
|
||
action
|
||
modifyIORef top.metaCtx $ \ mc => MC mc.metas mc.next CheckFirst
|
||
NoCheck => pure MkUnit
|
||
|
||
solve env m sp t = do
|
||
meta@(Unsolved metaFC ix ctx_ ty kind cons) <- lookupMeta m
|
||
| _ => error (getFC t) "Meta \{show m} already solved! [solve]"
|
||
debug $ \ _ => "SOLVE \{show m} \{show kind} lvl \{show $ length' env} sp \{show sp} is \{show t}"
|
||
let size = length $ filter (\x => x == Bound) ctx_.bds
|
||
debug $ \ _ => "\{show m} size is \{show size} sps \{show $ snoclen sp}"
|
||
let (True) = snoclen sp == size
|
||
| _ => do
|
||
let l = length' env
|
||
debug $ \ _ => "meta \{show m} (\{show ix}) applied to \{show $ snoclen sp} args instead of \{show size}"
|
||
debug $ \ _ => "CONSTRAINT m\{show ix} \{show sp} =?= \{show t}"
|
||
addConstraint env m sp t
|
||
let l = length' env
|
||
debug $ \ _ => "meta \{show meta}"
|
||
ren <- invert l sp
|
||
-- force unlet
|
||
hack <- quote l t
|
||
t <- eval env CBN hack
|
||
catchError (do
|
||
tm <- rename m ren l t
|
||
|
||
let tm = lams (snoclen sp) (reverse ctx_.boundNames) tm
|
||
top <- get
|
||
soln <- eval Nil CBN tm
|
||
|
||
updateMeta m $ \case
|
||
(Unsolved pos k _ _ _ cons) => pure $ Solved pos k soln
|
||
(Solved _ k x) => error' "Meta \{show ix} already solved! [solve2]"
|
||
OutOfScope => error' "Meta \{show ix} out of scope"
|
||
maybeCheck $ for_ cons $ \case
|
||
MkMc fc env sp rhs => do
|
||
val <- vappSpine soln sp
|
||
debug $ \ _ => "discharge l=\{show $ length' env} \{(show val)} =?= \{(show rhs)}"
|
||
unify env UNormal val rhs
|
||
mc <- readIORef top.metaCtx
|
||
-- stack ...
|
||
-- checkAutos ix mc.metas
|
||
pure MkUnit
|
||
)
|
||
|
||
(\case
|
||
Postpone fc ix msg => do
|
||
-- let someone else solve this and then check again.
|
||
debug $ \ _ => "CONSTRAINT2 m\{show ix} \{show sp} =?= \{show t}"
|
||
addConstraint env m sp t
|
||
-- I get legit errors after stuffing in solution
|
||
-- report for now, tests aren't hitting this branch
|
||
err => throwError err
|
||
-- debug $ \ _ => "CONSTRAINT3 m\{show ix} \{show sp} =?= \{show t}"
|
||
-- debug $ \ _ => "because \{showError "" err}"
|
||
-- addConstraint env m sp t
|
||
)
|
||
|
||
unifySpine : Env -> UnifyMode -> Bool -> SnocList Val -> SnocList Val -> M UnifyResult
|
||
unifySpine env mode False _ _ = error emptyFC "unify failed at head" -- unreachable now
|
||
unifySpine env mode True Lin Lin = pure (MkResult Nil)
|
||
unifySpine env mode True (xs :< x) (ys :< y) =
|
||
-- I had idiom brackets here, technically fairly easy to desugar, but not adding at this time
|
||
_<+>_ <$> unify env mode x y <*> unifySpine env mode True xs ys
|
||
unifySpine env mode True _ _ = error emptyFC "meta spine length mismatch"
|
||
|
||
unify env mode t u = do
|
||
debug $ \ _ => "Unify lvl \{show $ length env}"
|
||
debug $ \ _ => " \{show t}"
|
||
debug $ \ _ => " =?= \{show u}"
|
||
t' <- forceMeta t >>= unlet env
|
||
u' <- forceMeta u >>= unlet env
|
||
debug $ \ _ => "forced \{show t'}"
|
||
debug $ \ _ => " =?= \{show u'}"
|
||
debug $ \ _ => "env \{show env}"
|
||
-- debug $ \ _ => "types \{show $ ctx.types}"
|
||
let l = length' env
|
||
-- On the LHS, variable matching is yields constraints/substitutions
|
||
-- We want this to happen before VRefs are expanded, and mixing mode
|
||
-- into the case tree explodes it further.
|
||
case mode of
|
||
UPattern => unifyPattern t' u'
|
||
UNormal => unifyMeta t' u'
|
||
|
||
-- The case tree is still big here. It's hard for idris to sort
|
||
-- What we really want is what I wrote - handle meta, handle lam, handle var, etc
|
||
|
||
where
|
||
-- The case tree here was huge, so I split it into stages
|
||
-- newt will have similar issues because it doesn't emit a default case
|
||
|
||
unifyRest : Val -> Val -> M UnifyResult
|
||
unifyRest (VPi fc _ _ _ a b) (VPi fc' _ _ _ a' b') = do
|
||
let fresh = VVar fc (length' env) Lin
|
||
xb <- b $$ fresh
|
||
xb' <- b' $$ fresh
|
||
_<+>_ <$> unify env mode a a' <*> unify (fresh :: env) mode xb xb'
|
||
|
||
unifyRest (VU _) (VU _) = pure neutral
|
||
-- REVIEW I'd like to quote this back, but we have l that aren't in the environment.
|
||
unifyRest t' u' = error (getFC t') "unify failed \{show t'} =?= \{show u'} \n env is \{show env}"
|
||
|
||
unifyRef : Val -> Val -> M UnifyResult
|
||
unifyRef t'@(VRef fc k sp) u'@(VRef fc' k' sp') =
|
||
-- unifySpine is a problem for cmp (S x) (S y) =?= cmp x y
|
||
do
|
||
-- catchError(unifySpine env mode (k == k') sp sp') $ \ err => do
|
||
Nothing <- tryEval env t'
|
||
| Just v => do
|
||
debug $ \ _ => "tryEval \{show t'} to \{show v}"
|
||
unify env mode v u'
|
||
Nothing <- tryEval env u'
|
||
| Just v => unify env mode t' v
|
||
if k == k'
|
||
then unifySpine env mode (k == k') sp sp'
|
||
else error fc "vref mismatch \{show t'} \{show u'}"
|
||
|
||
-- Lennart.newt cursed type references itself
|
||
-- We _could_ look up the ref, eval against Nil and vappSpine...
|
||
unifyRef t u@(VRef fc' k' sp') = do
|
||
debug $ \ _ => "expand \{show t} =?= %ref \{show k'}"
|
||
top <- get
|
||
case lookup k' top of
|
||
Just (MkEntry _ name ty (Fn tm)) => do
|
||
vtm <- eval Nil CBN tm
|
||
appvtm <- vappSpine vtm sp'
|
||
unify env mode t appvtm
|
||
_ => error fc' "unify failed \{show t} =?= \{show u} (no Fn :: Nil)\n env is \{show env}"
|
||
|
||
unifyRef t@(VRef fc k sp) u = do
|
||
debug $ \ _ => "expand %ref \{show k} \{show sp} =?= \{show u}"
|
||
top <- get
|
||
case lookup k top of
|
||
Just (MkEntry _ name ty (Fn tm)) => do
|
||
vtm <- eval Nil CBN tm
|
||
tmsp <- vappSpine vtm sp
|
||
unify env mode tmsp u
|
||
_ => error fc "unify failed \{show t} (no Fn :: Nil) =?= \{show u}\n env is \{show env}"
|
||
unifyRef t u = unifyRest t u
|
||
|
||
unifyVar : Val -> Val -> M UnifyResult
|
||
unifyVar t'@(VVar fc k sp) u'@(VVar fc' k' sp') =
|
||
if k == k' then unifySpine env mode (k == k') sp sp'
|
||
else error fc "Failed to unify \{show t'} and \{show u'}"
|
||
|
||
unifyVar t'@(VVar fc k Lin) u = do
|
||
vu <- tryEval env u
|
||
case vu of
|
||
Just v => unify env mode t' v
|
||
Nothing => error fc "Failed to unify \{show t'} and \{show u}"
|
||
|
||
unifyVar t u'@(VVar fc k Lin) = do
|
||
vt <- tryEval env t
|
||
case vt of
|
||
Just v => unify env mode v u'
|
||
Nothing => error fc "Failed to unify \{show t} and \{show u'}"
|
||
unifyVar t u = unifyRef t u
|
||
|
||
unifyLam : Val -> Val -> M UnifyResult
|
||
unifyLam (VLam fc _ _ _ t) (VLam _ _ _ _ t') = do
|
||
let fresh = VVar fc (length' env) Lin
|
||
vappt <- t $$ fresh
|
||
vappt' <- t' $$ fresh
|
||
unify (fresh :: env) mode vappt vappt'
|
||
unifyLam t (VLam fc' _ _ _ t') = do
|
||
debug $ \ _ => "ETA \{show t}"
|
||
let fresh = VVar fc' (length' env) Lin
|
||
vappt <- vapp t fresh
|
||
vappt' <- t' $$ fresh
|
||
unify (fresh :: env) mode vappt vappt'
|
||
unifyLam (VLam fc _ _ _ t) t' = do
|
||
debug $ \ _ => "ETA' \{show t'}"
|
||
let fresh = VVar fc (length' env) Lin
|
||
appt <- t $$ fresh
|
||
vappt' <- vapp t' fresh
|
||
unify (fresh :: env) mode appt vappt'
|
||
unifyLam t u = unifyVar t u
|
||
|
||
unifyMeta : Val -> Val -> M UnifyResult
|
||
-- flex/flex
|
||
unifyMeta (VMeta fc k sp) (VMeta fc' k' sp') =
|
||
if k == k' then unifySpine env mode (k == k') sp sp'
|
||
-- TODO, might want to try the other way, too.
|
||
else if snoclen sp < snoclen sp'
|
||
then solve env k' sp' (VMeta fc k sp) >> pure neutral
|
||
else solve env k sp (VMeta fc' k' sp') >> pure neutral
|
||
unifyMeta t (VMeta fc' i' sp') = solve env i' sp' t >> pure neutral
|
||
unifyMeta (VMeta fc i sp) t' = solve env i sp t' >> pure neutral
|
||
unifyMeta t v = unifyLam t v
|
||
|
||
unifyPattern : Val -> Val -> M UnifyResult
|
||
unifyPattern t'@(VVar fc k sp) u'@(VVar fc' k' sp') =
|
||
if k == k' then unifySpine env mode (k == k') sp sp'
|
||
else case (sp, sp') of
|
||
(Lin,Lin) => if k < k'
|
||
then pure $ MkResult ((k, u') :: Nil)
|
||
else pure $ MkResult ((k', t') :: Nil)
|
||
_ => error fc "Failed to unify \{show t'} and \{show u'}"
|
||
|
||
unifyPattern (VVar fc k Lin) u = pure $ MkResult((k, u) :: Nil)
|
||
unifyPattern t (VVar fc k Lin) = pure $ MkResult ((k, t) :: Nil)
|
||
unifyPattern t u = unifyMeta t u
|
||
|
||
unifyCatch fc ctx ty' ty = do
|
||
res <- catchError (unify ctx.env UNormal ty' ty) $ \err => do
|
||
let names = map fst ctx.types
|
||
debug $ \ _ => "fail \{show ty'} \{show ty}"
|
||
a <- quote ctx.lvl ty'
|
||
b <- quote ctx.lvl ty
|
||
let msg = "unification failure: \{errorMsg err}\n failed to unify \{rpprint names a}\n with \{rpprint names b}\n "
|
||
throwError (E fc msg)
|
||
case res of
|
||
MkResult Nil => pure MkUnit
|
||
cs => do
|
||
-- probably want a unification mode that throws instead of returning constraints
|
||
-- TODO need a normalizeHoles, maybe on quote?
|
||
debug $ \ _ => "fail with constraints \{show cs.constraints}"
|
||
a <- quote ctx.lvl ty'
|
||
b <- quote ctx.lvl ty
|
||
let names = map fst ctx.types
|
||
let msg = "unification failure\n failed to unify \{rpprint names a}\n with \{rpprint names b}"
|
||
let msg = msg ++ "\nconstraints \{show cs.constraints}"
|
||
throwError (E fc msg)
|
||
-- error fc "Unification yields constraints \{show cs.constraints}"
|
||
|
||
freshMeta : Context -> FC -> Val -> MetaKind -> M Tm
|
||
freshMeta ctx fc ty kind = do
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
debug $ \ _ => "fresh meta \{show mc.next} : \{show ty} (\{show kind})"
|
||
-- need the ns here
|
||
-- we were fudging this for v1
|
||
let qn = QN top.ns "$m\{show mc.next}"
|
||
let newmeta = Unsolved fc qn ctx ty kind Nil
|
||
writeIORef top.metaCtx $ MC (updateMap qn newmeta mc.metas) (1 + mc.next) mc.mcmode
|
||
-- infinite loop - keeps trying Ord a => Ord (a \x a)
|
||
-- when (kind == AutoSolve) $ \ _ => ignore $ trySolveAuto newmeta
|
||
pure $ applyBDs 0 (Meta fc qn) ctx.bds
|
||
where
|
||
-- hope I got the right order here :)
|
||
applyBDs : Int -> Tm -> List BD -> Tm
|
||
applyBDs k t Nil = t
|
||
-- review the order here
|
||
applyBDs k t (Bound :: xs) = App emptyFC (applyBDs (1 + k) t xs) (Bnd emptyFC k)
|
||
applyBDs k t (Defined :: xs) = applyBDs (1 + k) t xs
|
||
|
||
insert : (ctx : Context) -> Tm -> Val -> M (Tm × Val)
|
||
insert ctx tm ty = do
|
||
ty' <- forceMeta ty
|
||
case ty' of
|
||
VPi fc x Auto rig a b => do
|
||
m <- freshMeta ctx (getFC tm) a AutoSolve
|
||
debug $ \ _ => "INSERT Auto \{rpprint (names ctx) m} : \{show a}"
|
||
debug $ \ _ => "TM \{rpprint (names ctx) tm}"
|
||
mv <- eval ctx.env CBN m
|
||
bapp <- b $$ mv
|
||
insert ctx (App (getFC tm) tm m) bapp
|
||
VPi fc x Implicit rig a b => do
|
||
m <- freshMeta ctx (getFC tm) a Normal
|
||
debug $ \ _ => "INSERT \{rpprint (names ctx) m} : \{show a}"
|
||
debug $ \ _ => "TM \{rpprint (names ctx) tm}"
|
||
mv <- eval ctx.env CBN m
|
||
bapp <- b $$ mv
|
||
insert ctx (App (getFC tm) tm m) bapp
|
||
va => pure (tm, va)
|
||
|
||
primType : FC -> QName -> M Val
|
||
primType fc nm = do
|
||
top <- get
|
||
case lookup nm top of
|
||
Just (MkEntry _ name ty (PrimTCon _)) => pure $ VRef fc name Lin
|
||
_ => error fc "Primitive type \{show nm} not in scope"
|
||
|
||
infer : Context -> Raw -> M (Tm × Val)
|
||
|
||
data Bind = MkBind String Icit Val
|
||
|
||
instance Show Bind where
|
||
show (MkBind str icit x) = "\{str} \{show icit}"
|
||
|
||
---------------- Case builder
|
||
|
||
record Problem where
|
||
constructor MkProb
|
||
clauses : List Clause
|
||
-- I think a pi-type representing the pattern args -> goal, so we're checking
|
||
-- We might pull out the pattern abstraction to a separate step and drop pats from clauses.
|
||
ty : Val
|
||
|
||
-- Might have to move this if Check reaches back in...
|
||
-- this is kinda sketchy, we can't use it twice at the same depth with the same name.
|
||
fresh : {{ctx : Context}} -> String -> String
|
||
fresh {{ctx}} base = base ++ "$" ++ show (length' ctx.env)
|
||
|
||
-- The result is a casetree, but it's in Tm
|
||
|
||
buildTree : Context -> Problem -> M Tm
|
||
|
||
-- Updates a clause for INTRO
|
||
introClause : String -> Icit -> Clause -> M Clause
|
||
introClause nm icit (MkClause fc cons (pat :: pats) expr) =
|
||
if icit == getIcit pat then pure $ MkClause fc ((nm, pat) :: cons) pats expr
|
||
else if icit == Implicit then pure $ MkClause fc ((nm, PatWild fc Implicit) :: cons) (pat :: pats) expr
|
||
else if icit == Auto then pure $ MkClause fc ((nm, PatWild fc Auto) :: cons) (pat :: pats) expr
|
||
else error fc "Explicit arg and \{show $ getIcit pat} pattern \{show nm} \{show pat}"
|
||
-- handle implicts at end?
|
||
introClause nm Implicit (MkClause fc cons Nil expr) = pure $ MkClause fc ((nm, PatWild fc Implicit) :: cons) Nil expr
|
||
introClause nm Auto (MkClause fc cons Nil expr) = pure $ MkClause fc ((nm, PatWild fc Auto) :: cons) Nil expr
|
||
introClause nm icit (MkClause fc cons Nil expr) = error fc "Clause size doesn't match"
|
||
|
||
-- A split candidate looks like x /? Con ...
|
||
-- we need a type here. I pulled if off of the
|
||
-- pi-type, but do we need metas solved or dependents split?
|
||
-- this may dot into a dependent.
|
||
findSplit : List Constraint -> Maybe Constraint
|
||
findSplit Nil = Nothing
|
||
-- FIXME look up type, ensure it's a constructor
|
||
findSplit (x@(nm, PatCon _ _ _ _ _) :: xs) = Just x
|
||
findSplit (x@(nm, PatLit _ val) :: xs) = Just x
|
||
findSplit (x :: xs) = findSplit xs
|
||
|
||
-- ***************
|
||
-- right, I think we rewrite the names in the context before running raw and we're good to go?
|
||
-- We have stuff like S k /? x, but I think we can back up to whatever the scrutinee variable was?
|
||
|
||
-- we could pass into build case and use it for (x /? y)
|
||
|
||
-- TODO, we may need to filter these against the type to rule out
|
||
-- impossible cases
|
||
getConstructors : Context -> FC -> Val -> M (List (QName × Int × Tm))
|
||
getConstructors ctx scfc (VRef fc nm _) = do
|
||
names <- lookupTCon nm
|
||
traverse lookupDCon names
|
||
where
|
||
lookupTCon : QName -> M (List QName)
|
||
lookupTCon str = do
|
||
top <- get
|
||
case lookup nm top of
|
||
(Just (MkEntry _ name type (TCon _ names))) => pure names
|
||
_ => error scfc "Not a type constructor \{show nm}"
|
||
lookupDCon : QName -> M (QName × Int × Tm)
|
||
lookupDCon nm = do
|
||
top <- get
|
||
case lookup nm top of
|
||
(Just (MkEntry _ name type (DCon k str))) => pure (name, k, type)
|
||
Just _ => error fc "Internal Error: \{show nm} is not a DCon"
|
||
Nothing => error fc "Internal Error: DCon \{show nm} not found"
|
||
getConstructors ctx scfc tm = do
|
||
tms <- vprint ctx tm
|
||
error scfc "Can't split - not VRef: \{tms}"
|
||
|
||
-- Extend environment with fresh variables from a pi-type
|
||
-- the pi-type here is the telescope of a constructor
|
||
-- return context, remaining type, and list of names
|
||
extendPi : Context -> Val -> SnocList Bind -> SnocList Val -> M (Context × Val × List Bind × SnocList Val)
|
||
extendPi ctx (VPi x str icit rig a b) nms sc = do
|
||
let nm = fresh str -- "pat"
|
||
let ctx' = extend ctx nm a
|
||
let v = VVar emptyFC (length' ctx.env) Lin
|
||
tyb <- b $$ v
|
||
extendPi ctx' tyb (nms :< MkBind nm icit a) (sc :< VVar x (length' ctx.env) Lin)
|
||
extendPi ctx ty nms sc = pure (ctx, ty, nms <>> Nil, sc)
|
||
|
||
-- turn vars into lets for forced values.
|
||
-- substitute inside values
|
||
-- FIXME we're not going under closures at the moment.
|
||
substVal : Int -> Val -> Val -> Val
|
||
substVal k v tm = go tm
|
||
where
|
||
go : Val -> Val
|
||
go (VVar fc j sp) = if j == k then v else (VVar fc j (map go sp))
|
||
go (VLet fc nm a b) = VLet fc nm (go a) b
|
||
go (VPi fc nm icit rig a b) = VPi fc nm icit rig (go a) b
|
||
go (VMeta fc ix sp) = VMeta fc ix (map go sp)
|
||
go (VRef fc nm sp) = VRef fc nm (map go sp)
|
||
go tm = tm
|
||
-- FIXME - do I need a Val closure like idris?
|
||
-- or env in unify...
|
||
-- or quote back
|
||
-- go (VLam fc nm sc) = VLam fc nm sc
|
||
-- go (VCase x sc xs) = ?rhs_2
|
||
-- go (VU x) = ?rhs_7
|
||
-- go (VLit x y) = ?rhs_8
|
||
|
||
-- need to turn k into a ground value
|
||
|
||
-- TODO rework this to do something better. We've got constraints, and
|
||
-- and may need to do proper unification if it's already defined to a value
|
||
-- below we're handling the case if it's defined to another var, but not
|
||
-- checking for loops.
|
||
updateContext : Context -> List (Int × Val) -> M Context
|
||
updateContext ctx Nil = pure ctx
|
||
updateContext ctx ((k, val) :: cs) =
|
||
let ix = cast $ lvl2ix (length' ctx.env) k in
|
||
case getAt ix ctx.env of
|
||
(Just (VVar _ k' Lin)) =>
|
||
if k' /= k
|
||
then updateContext ctx ((k',val) :: cs)
|
||
else
|
||
let ctx' = MkCtx ctx.lvl (map (substVal k val) ctx.env) ctx.types (replaceV ix Defined ctx.bds) ctx.ctxFC
|
||
in updateContext ctx' cs
|
||
(Just val') => do
|
||
-- This is fine for Z =?= Z but for other stuff, we probably have to match
|
||
info (getFC val) "need to unify \{show val} and \{show val'} or something"
|
||
updateContext ctx cs
|
||
Nothing => error (getFC val) "INTERNAL ERROR: bad index in updateContext"
|
||
|
||
--
|
||
-- updateContext ({env $= replace ix val, bds $= replaceV ix Defined } ctx) cs
|
||
where
|
||
replace : Nat -> Val -> List Val -> List Val
|
||
replace k x Nil = Nil
|
||
replace Z x (y :: xs) = x :: xs
|
||
replace (S k) x (y :: xs) = y :: replace k x xs
|
||
|
||
replaceV : ∀ a. Nat -> a -> List a -> List a
|
||
replaceV k x Nil = Nil
|
||
replaceV Z x (y :: xs) = x :: xs
|
||
replaceV (S k) x (y :: xs) = y :: replaceV k x xs
|
||
|
||
checkCase : Context → Problem → String → Val → (QName × Int × Tm) → M Bool
|
||
checkCase ctx prob scnm scty (dcName, arity, ty) = do
|
||
vty <- eval Nil CBN ty
|
||
(ctx', ty', vars, sc) <- extendPi ctx (vty) Lin Lin
|
||
(Just res) <- catchError (Just <$> unify ctx'.env UPattern ty' scty)
|
||
(\err => do
|
||
debug $ \ _ => "SKIP \{show dcName} because unify error \{errorMsg err}"
|
||
pure Nothing)
|
||
| _ => pure False
|
||
|
||
(Right res) <- tryError (unify ctx'.env UPattern ty' scty)
|
||
| Left err => do
|
||
debug $ \ _ => "SKIP \{show dcName} because unify error \{errorMsg err}"
|
||
pure False
|
||
|
||
case lookupDef ctx scnm of
|
||
Just val@(VRef fc nm sp) => pure $ nm == dcName
|
||
_ => pure True
|
||
|
||
-- ok, so this is a single constructor, CaseAlt
|
||
-- return Nothing if dcon doesn't unify with scrut
|
||
buildCase : Context -> Problem -> String -> Val -> (QName × Int × Tm) -> M (Maybe CaseAlt)
|
||
buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
||
debug $ \ _ => "CASE \{scnm} match \{show dcName} ty \{rpprint (names ctx) ty}"
|
||
vty <- eval Nil CBN ty
|
||
(ctx', ty', vars, sc) <- extendPi ctx (vty) Lin Lin
|
||
|
||
-- TODO I think we need to figure out what is dotted, maybe
|
||
-- the environment manipulation below is sufficient bookkeeping
|
||
-- or maybe it's a bad approach.
|
||
|
||
-- At some point, I'll take a break and review more papers and code,
|
||
-- now that I know some of the questions I'm trying to answer.
|
||
|
||
-- We get unification constraints from matching the data constructors
|
||
-- codomain with the scrutinee type
|
||
debug $ \ _ => "unify dcon cod with scrut\n \{show ty'}\n \{show scty}"
|
||
Just res <- catchError(Just <$> unify ctx'.env UPattern ty' scty)
|
||
(\err => do
|
||
debug $ \ _ => "SKIP \{show dcName} because unify error \{errorMsg err}"
|
||
pure Nothing)
|
||
| _ => pure Nothing
|
||
|
||
-- if the value is already constrained to a different constructor, return Nothing
|
||
debug $ \ _ => "scrut \{scnm} constrained to \{show $ lookupDef ctx scnm}"
|
||
let (VRef _ sctynm _) = scty | _ => error (getFC scty) "case split on non-inductive \{show scty}"
|
||
|
||
case lookupDef ctx scnm of
|
||
Just val@(VRef fc nm sp) =>
|
||
if nm /= dcName
|
||
then do
|
||
debug $ \ _ => "SKIP \{show dcName} because \{scnm} forced to \{show val}"
|
||
pure Nothing
|
||
else do
|
||
debug $ \ _ => "case \{show dcName} dotted \{show val}"
|
||
when (length vars /= snoclen sp) $ \ _ => error emptyFC "\{show $ length vars} vars /= \{show $ snoclen sp}"
|
||
|
||
-- TODO - I think we need to define the context vars to sp via updateContext
|
||
|
||
let lvl = length' ctx'.env - length' vars
|
||
let scons = constrainSpine lvl (sp <>> Nil) -- REVIEW is this the right order?
|
||
ctx' <- updateContext ctx' scons
|
||
|
||
debug $ \ _ => "(dcon \{show dcName} ty \{show ty'} scty \{show scty}"
|
||
debug $ \ _ => "(dcon \{show dcName}) (vars \{show vars}) clauses were"
|
||
for prob.clauses $ (\x => debug $ \ _ => " \{show x}")
|
||
clauses <- mapMaybe id <$> traverse (rewriteClause sctynm vars) prob.clauses
|
||
debug $ \ _ => "and now:"
|
||
for clauses $ (\x => debug $ \ _ => " \{show x}")
|
||
when (length' clauses == 0) $ \ _ => error ctx.ctxFC "Missing case for \{show dcName} splitting \{scnm}"
|
||
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
||
pure $ Just $ CaseCons dcName (map getName vars) tm
|
||
|
||
_ => do
|
||
(Right res) <- tryError (unify ctx'.env UPattern ty' scty)
|
||
| Left err => do
|
||
debug $ \ _ => "SKIP \{show dcName} because unify error \{errorMsg err}"
|
||
pure Nothing
|
||
|
||
-- Constrain the scrutinee's variable to be dcon applied to args
|
||
let (Just x) = findIndex' ((_==_ scnm) ∘ fst) ctx'.types
|
||
| Nothing => error ctx.ctxFC "\{scnm} not is scope?"
|
||
let lvl = lvl2ix (length' ctx'.env) x
|
||
let scon = (lvl, VRef ctx.ctxFC dcName sc) -- (DCon arity dcName)
|
||
|
||
debug $ \ _ => "scty \{show scty}"
|
||
debug $ \ _ => "UNIFY results \{show res.constraints}"
|
||
debug $ \ _ => "before types: \{show ctx'.types}"
|
||
debug $ \ _ => "before env: \{show ctx'.env}"
|
||
debug $ \ _ => "SC CONSTRAINT: \{show scon}"
|
||
|
||
-- push the constraints into the environment by turning bind into define
|
||
-- Is this kosher? It might be a problem if we've already got metas over
|
||
-- this stuff, because it intends to ignore defines.
|
||
ctx' <- updateContext ctx' (scon :: res.constraints)
|
||
|
||
debug $ \ _ => "context types: \{show ctx'.types}"
|
||
debug $ \ _ => "context env: \{show ctx'.env}"
|
||
|
||
-- What if all of the pattern vars are defined to meta
|
||
|
||
debug $ \ _ => "(dcon \{show dcName} ty \{show ty'} scty \{show scty}"
|
||
debug $ \ _ => "(dcon \{show dcName}) (vars \{show vars}) clauses were"
|
||
for prob.clauses $ (\x => debug $ \ _ => " \{show x}")
|
||
clauses <- mapMaybe id <$> traverse (rewriteClause sctynm vars) prob.clauses
|
||
debug $ \ _ => "and now:"
|
||
for clauses $ (\x => debug $ \ _ => " \{show x}")
|
||
when (length' clauses == 0) $ \ _ => error ctx.ctxFC "Missing case for \{show dcName} splitting \{scnm}"
|
||
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
||
pure $ Just $ CaseCons dcName (map getName vars) tm
|
||
where
|
||
constrainSpine : Int -> List Val -> List (Int × Val)
|
||
constrainSpine lvl Nil = Nil
|
||
constrainSpine lvl (v :: vs) = (lvl, v) :: constrainSpine (1 + lvl) vs
|
||
|
||
getName : Bind -> String
|
||
getName (MkBind nm _ _) = nm
|
||
|
||
-- for each clause in prob, find nm on LHS of some constraint, and
|
||
-- "replace" with the constructor and vars.
|
||
--
|
||
-- This will be:
|
||
-- x /? y can probably just leave this
|
||
-- x /? D a b c split into three x /? a, y /? b, z /? c
|
||
-- x /? E a b drop this clause
|
||
-- We get a list of clauses back (a Problem) and then solve that
|
||
-- If they all fail, we have a coverage issue. (Assuming the constructor is valid)
|
||
|
||
makeConstr : FC -> List Bind -> List Pattern -> M (List (String × Pattern))
|
||
makeConstr fc Nil Nil = pure $ Nil
|
||
makeConstr fc Nil (pat :: pats) = error (getFC pat) "too many patterns"
|
||
makeConstr fc ((MkBind nm Implicit x) :: xs) Nil = do
|
||
rest <- makeConstr fc xs Nil
|
||
pure $ (nm, PatWild emptyFC Implicit) :: rest
|
||
makeConstr fc ((MkBind nm Auto x) :: xs) Nil = do
|
||
rest <- makeConstr fc xs Nil
|
||
pure $ (nm, PatWild emptyFC Auto) :: rest
|
||
makeConstr fc ((MkBind nm Explicit x) :: xs) Nil = error fc "not enough patterns"
|
||
makeConstr fc ((MkBind nm Explicit x) :: xs) (pat :: pats) =
|
||
if getIcit pat == Explicit
|
||
then do
|
||
rest <- makeConstr fc xs pats
|
||
pure $ (nm, pat) :: rest
|
||
else error ctx.ctxFC "mismatch between Explicit and \{show $ getIcit pat}"
|
||
makeConstr fc ((MkBind nm icit x) :: xs) (pat :: pats) =
|
||
if getIcit pat /= icit -- Implicit/Explicit Implicit/Auto, etc
|
||
then do
|
||
rest <- makeConstr fc xs (pat :: pats)
|
||
pure $ (nm, PatWild (getFC pat) icit) :: rest
|
||
else do
|
||
rest <- makeConstr fc xs pats
|
||
pure $ (nm, pat) :: rest
|
||
|
||
-- replace constraint with constraints on parameters, or nothing if non-matching clause
|
||
rewriteConstraint : QName -> List Bind -> List Constraint -> List Constraint -> M (Maybe (List Constraint))
|
||
rewriteConstraint sctynm vars Nil acc = pure $ Just acc
|
||
rewriteConstraint sctynm vars (c@(nm, y) :: xs) acc =
|
||
if nm == scnm
|
||
then case y of
|
||
PatVar _ _ s => pure $ Just $ c :: (xs ++ acc)
|
||
PatWild _ _ => pure $ Just $ c :: (xs ++ acc)
|
||
-- FIXME why don't we hit this (when user puts 'x' for Just 'x')
|
||
PatLit fc lit => error fc "Literal \{show lit} in constructor split"
|
||
PatCon fc icit nm ys as => if nm == dcName
|
||
then case as of
|
||
Nothing => do
|
||
rest <- makeConstr fc vars ys
|
||
pure $ Just $ rest ++ xs ++ acc
|
||
-- putting this in constraints causes it to be renamed scnm -> nm' when we check body.
|
||
Just nm' => do
|
||
rest <- makeConstr fc vars ys
|
||
pure $ Just $ (scnm, (PatVar fc icit nm')) :: rest ++ xs ++ acc
|
||
else do
|
||
-- TODO can we check this when we make the PatCon?
|
||
top <- get
|
||
case lookup nm top of
|
||
(Just (MkEntry _ name type (DCon k tcname))) =>
|
||
if (tcname /= sctynm)
|
||
then error fc "Constructor is \{show tcname} expected \{show sctynm}"
|
||
else pure Nothing
|
||
Just _ => error fc "Internal Error: \{show nm} is not a DCon"
|
||
Nothing => error fc "Internal Error: DCon \{show nm} not found"
|
||
else rewriteConstraint sctynm vars xs (c :: acc)
|
||
|
||
rewriteClause : QName -> List Bind -> Clause -> M (Maybe Clause)
|
||
rewriteClause sctynm vars (MkClause fc cons pats expr) = do
|
||
Just cons <- rewriteConstraint sctynm vars cons Nil | _ => pure Nothing
|
||
pure $ Just $ MkClause fc cons pats expr
|
||
|
||
splitArgs : Raw -> List (Raw × Icit) -> (Raw × List (Raw × Icit))
|
||
splitArgs (RApp fc t u icit) args = splitArgs t ((u, icit) :: args)
|
||
splitArgs tm args = (tm, args)
|
||
|
||
mkPat : (Raw × Icit) -> M Pattern
|
||
mkPat (RAs fc as tm, icit) = do
|
||
pat <- mkPat (tm, icit)
|
||
case pat of
|
||
(PatCon fc icit nm args Nothing) => pure $ PatCon fc icit nm args (Just as)
|
||
(PatCon fc icit nm args _) => error fc "Double as pattern \{show tm}"
|
||
t => error fc "Can't put as on non-constructor \{show tm}"
|
||
mkPat (tm, icit) = do
|
||
top <- get
|
||
case splitArgs tm Nil of
|
||
((RVar fc nm), b) => case lookupRaw nm top of
|
||
(Just (MkEntry _ name type (DCon k str))) => do
|
||
-- TODO check arity, also figure out why we need reverse
|
||
bpat <- traverse (mkPat) b
|
||
pure $ PatCon fc icit name bpat Nothing
|
||
-- This fires when a global is shadowed by a pattern var
|
||
-- Just _ => error (getFC tm) "\{show nm} is not a data constructor"
|
||
_ => case b of
|
||
Nil => pure $ PatVar fc icit nm
|
||
_ => error (getFC tm) "patvar applied to args"
|
||
((RImplicit fc), Nil) => pure $ PatWild fc icit
|
||
((RImplicit fc), _) => error fc "implicit pat can't be applied to arguments"
|
||
((RLit fc lit), Nil) => pure $ PatLit fc lit
|
||
((RLit fc y), b) => error fc "lit cannot be applied to arguments"
|
||
(a,b) => error (getFC a) "expected pat var or constructor, got \{show a}"
|
||
|
||
makeClause : (Raw × Raw) -> M Clause
|
||
makeClause (lhs, rhs) = do
|
||
let (nm, args) = splitArgs lhs Nil
|
||
pats <- traverse mkPat args
|
||
pure $ MkClause (getFC lhs) Nil pats rhs
|
||
|
||
-- we'll want both check and infer, we're augmenting a context
|
||
-- so probably a continuation:
|
||
-- Context -> List Decl -> (Context -> M a) -> M a
|
||
checkWhere : Context -> List Decl -> Raw -> Val -> M Tm
|
||
checkWhere ctx decls body ty = do
|
||
-- we're going to be very proscriptive here
|
||
let (TypeSig sigFC (name :: Nil) rawtype :: decls) = decls
|
||
| x :: _ => error (getFC x) "expected type signature"
|
||
| _ => check ctx body ty
|
||
funTy <- check ctx rawtype (VU sigFC)
|
||
debug $ \ _ => "where clause \{name} : \{rpprint (names ctx) funTy}"
|
||
let (Def defFC name' clauses :: decls') = decls
|
||
| x :: _ => error (getFC x) "expected function definition"
|
||
| _ => error sigFC "expected function definition after this signature"
|
||
unless (name == name') $ \ _ => error defFC "Expected def for \{name}"
|
||
-- REVIEW is this right, cribbed from my top level code
|
||
top <- get
|
||
clauses' <- traverse makeClause clauses
|
||
vty <- eval ctx.env CBN funTy
|
||
debug $ \ _ => "\{name} vty is \{show vty}"
|
||
let ctx' = extend ctx name vty
|
||
|
||
-- if I lift, I need to namespace it, add args, and add args when
|
||
-- calling locally
|
||
-- context could hold a Name -> Val (not Tm because levels) to help with that
|
||
-- e.g. "go" -> (VApp ... (VApp (VRef "ns.go") ...)
|
||
-- But I'll attempt letrec first
|
||
tm <- buildTree (withPos ctx' defFC) (MkProb clauses' vty)
|
||
vtm <- eval ctx'.env CBN tm
|
||
-- Should we run the rest with the definition in place?
|
||
-- I'm wondering if switching from bind to define will mess with metas
|
||
-- let ctx' = define ctx name vtm vty
|
||
ty' <- checkWhere ctx' decls' body ty
|
||
pure $ LetRec sigFC name funTy tm ty'
|
||
|
||
-- checks the body after we're done with a case tree branch
|
||
checkDone : Context -> List (String × Pattern) -> Raw -> Val -> M Tm
|
||
checkDone ctx Nil body ty = do
|
||
debug $ \ _ => "DONE-> check body \{show body} at \{show ty}"
|
||
-- TODO better dump context function
|
||
debugM $ showCtx ctx
|
||
-- Hack to try to get Combinatory working
|
||
-- we're trying to subst in solutions here.
|
||
env' <- for ctx.env $ \ val => do
|
||
ty <- quote (length' ctx.env) val
|
||
-- This is not getting vars under lambdas
|
||
eval ctx.env CBV ty
|
||
types' <- for ctx.types $ \case
|
||
(nm,ty) => do
|
||
nty <- quote (length' env') ty
|
||
ty' <- eval env' CBV nty
|
||
pure (nm, ty')
|
||
let ctx = MkCtx ctx.lvl env' types' ctx.bds ctx.ctxFC
|
||
debug $ \ _ => "AFTER"
|
||
debugM $ showCtx ctx
|
||
-- I'm running an eval here to run case statements that are
|
||
-- unblocked by lets in the env. (Tree.newt:cmp)
|
||
-- The case eval code only works in the Tm -> Val case at the moment.
|
||
-- we don't have anything like `vapp` for case
|
||
ty <- quote (length' ctx.env) ty
|
||
ty <- eval ctx.env CBN ty
|
||
|
||
debug $ \ _ => "check at \{show ty}"
|
||
got <- check ctx body ty
|
||
debug $ \ _ => "DONE<- got \{rpprint (names ctx) got}"
|
||
pure got
|
||
checkDone ctx ((x, PatWild _ _) :: xs) body ty = checkDone ctx xs body ty
|
||
checkDone ctx ((nm, (PatVar _ _ nm')) :: xs) body ty =
|
||
let ctx = MkCtx ctx.lvl ctx.env (rename ctx.types) ctx.bds ctx.ctxFC in
|
||
checkDone ctx xs body ty
|
||
where
|
||
rename : List (String × Val) -> List (String × Val)
|
||
rename Nil = Nil
|
||
rename ((name, ty) :: xs) =
|
||
if name == nm
|
||
then (nm', ty) :: xs
|
||
else (name, ty) :: rename xs
|
||
|
||
checkDone ctx ((x, pat) :: xs) body ty = error (getFC pat) "stray constraint \{x} /? \{show pat}"
|
||
|
||
-- need to run constructors, then run default
|
||
|
||
-- wild/var can come before 'x' so we need a list first
|
||
getLits : String -> List Clause -> List Literal
|
||
getLits nm Nil = Nil
|
||
getLits nm ((MkClause fc cons pats expr) :: cs) = case find ((_==_ nm) ∘ fst) cons of
|
||
Just (_, (PatLit _ lit)) => lit :: getLits nm cs
|
||
_ => getLits nm cs
|
||
|
||
-- collect constructors that are matched on
|
||
matchedConstructors : String → List Clause → List QName
|
||
matchedConstructors nm Nil = Nil
|
||
matchedConstructors nm ((MkClause fc cons pats expr) :: cs) = case find ((_==_ nm) ∘ fst) cons of
|
||
Just (_, (PatCon _ _ dcon _ _)) => dcon :: matchedConstructors nm cs
|
||
_ => matchedConstructors nm cs
|
||
|
||
-- then build a lit case for each of those
|
||
|
||
buildLitCase : Context -> Problem -> FC -> String -> Val -> Literal -> M CaseAlt
|
||
buildLitCase ctx prob fc scnm scty lit = do
|
||
|
||
-- Constrain the scrutinee's variable to be lit value
|
||
let (Just ix) = findIndex' ((_==_ scnm) ∘ fst) ctx.types
|
||
| Nothing => error ctx.ctxFC "\{scnm} not is scope?"
|
||
let lvl = lvl2ix (length' ctx.env) ix
|
||
let scon = (lvl, VLit fc lit)
|
||
ctx' <- updateContext ctx (scon :: Nil)
|
||
let clauses = mapMaybe rewriteClause prob.clauses
|
||
|
||
when (length' clauses == 0) $ \ _ => error ctx.ctxFC "Missing case for \{show lit} splitting \{scnm}"
|
||
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
||
pure $ CaseLit lit tm
|
||
|
||
where
|
||
-- FIXME - thread in M for errors
|
||
-- replace constraint with constraints on parameters, or nothing if non-matching clause
|
||
rewriteConstraint : List Constraint -> List Constraint -> Maybe (List Constraint)
|
||
rewriteConstraint Nil acc = Just acc
|
||
rewriteConstraint (c@(nm, y) :: xs) acc =
|
||
if nm == scnm
|
||
then case y of
|
||
PatVar _ _ s => Just $ c :: (xs ++ acc)
|
||
PatWild _ _ => Just $ c :: (xs ++ acc)
|
||
PatLit fc lit' => if lit' == lit then Just $ (xs ++ acc) else Nothing
|
||
PatCon _ _ str ys as => Nothing -- error matching lit against constructor
|
||
else rewriteConstraint xs (c :: acc)
|
||
|
||
rewriteClause : Clause -> Maybe Clause
|
||
rewriteClause (MkClause fc cons pats expr) = do
|
||
cons <- rewriteConstraint cons Nil
|
||
pure $ MkClause fc cons pats expr
|
||
|
||
buildDefault : Context → Problem → FC → String → M CaseAlt
|
||
buildDefault ctx prob fc scnm = do
|
||
let defclauses = filter isDefault prob.clauses
|
||
when (length' defclauses == 0) $ \ _ => error fc "no default for literal slot on \{show scnm}"
|
||
CaseDefault <$> buildTree ctx (MkProb defclauses prob.ty)
|
||
where
|
||
isDefault : Clause -> Bool
|
||
isDefault cl = case find ((_==_ scnm) ∘ fst) cl.cons of
|
||
Just (_, (PatVar _ _ _)) => True
|
||
Just (_, (PatWild _ _)) => True
|
||
Nothing => True
|
||
_ => False
|
||
|
||
buildLitCases : Context -> Problem -> FC -> String -> Val -> M (List CaseAlt)
|
||
buildLitCases ctx prob fc scnm scty = do
|
||
let lits = nub $ getLits scnm prob.clauses
|
||
alts <- traverse (buildLitCase ctx prob fc scnm scty) lits
|
||
-- TODO build default case
|
||
-- run getLits
|
||
-- buildLitCase for each
|
||
|
||
let defclauses = filter isDefault prob.clauses
|
||
when (length' defclauses == 0) $ \ _ => error fc "no default for literal slot on \{show scnm}"
|
||
tm <- buildTree ctx (MkProb defclauses prob.ty)
|
||
|
||
pure $ alts ++ ( CaseDefault tm :: Nil)
|
||
where
|
||
isDefault : Clause -> Bool
|
||
isDefault cl = case find ((_==_ scnm) ∘ fst) cl.cons of
|
||
Just (_, (PatVar _ _ _)) => True
|
||
Just (_, (PatWild _ _)) => True
|
||
Nothing => True
|
||
_ => False
|
||
|
||
-- TODO - figure out if these need to be in Prelude or have a special namespace
|
||
-- If we lookupRaw "String", we could get different answers in different contexts.
|
||
-- maybe Hardwire this one
|
||
stringType intType charType : QName
|
||
stringType = QN ("Prim" :: Nil) "String"
|
||
intType = QN ("Prim" :: Nil) "Int"
|
||
charType = QN ("Prim" :: Nil) "Char"
|
||
|
||
litTyName : Literal -> QName
|
||
litTyName (LString str) = stringType
|
||
litTyName (LInt i) = intType
|
||
litTyName (LChar c) = charType
|
||
|
||
renameContext : String -> String -> Context -> Context
|
||
renameContext from to ctx = MkCtx ctx.lvl ctx.env (go ctx.types) ctx.bds ctx.ctxFC
|
||
where
|
||
go : List (String × Val) -> List (String × Val)
|
||
go Nil = Nil
|
||
go ((a,b) :: types) = if a == from then (to,b) :: types else (a,b) :: go types
|
||
|
||
-- This process is similar to extendPi, but we need to stop
|
||
-- if one clause is short on patterns.
|
||
buildTree ctx (MkProb Nil ty) = error emptyFC "no clauses"
|
||
buildTree ctx prob@(MkProb ((MkClause fc cons (x :: xs) expr) :: cs) (VPi _ str icit rig a b)) = do
|
||
let l = length' ctx.env
|
||
let nm = fresh str -- "pat"
|
||
let ctx' = extend ctx nm a
|
||
-- type of the rest
|
||
clauses <- traverse (introClause nm icit) prob.clauses
|
||
-- REVIEW fc from a pat?
|
||
vb <- b $$ VVar fc l Lin
|
||
Lam fc nm icit rig <$> buildTree ctx' (MkProb clauses vb)
|
||
buildTree ctx prob@(MkProb ((MkClause fc cons pats@(x :: xs) expr) :: cs) ty) =
|
||
error fc "Extra pattern variables \{show pats}"
|
||
-- need to find some name we can split in (x :: xs)
|
||
-- so LHS of constraint is name (or VVar - if we do Val)
|
||
-- then run the split
|
||
-- some of this is copied into check
|
||
buildTree ctx prob@(MkProb ((MkClause fc constraints Nil expr) :: cs) ty) = do
|
||
debug $ \ _ => "buildTree \{show constraints} \{show expr}"
|
||
let (Just (scnm, pat)) = findSplit constraints
|
||
| _ => do
|
||
debug $ \ _ => "checkDone \{show constraints}"
|
||
checkDone ctx constraints expr ty
|
||
|
||
debug $ \ _ => "SPLIT on \{scnm} because \{show pat} \{show $ getFC pat}"
|
||
let (Just (sctm, scty)) = lookupName ctx scnm
|
||
| _ => error fc "Internal Error: can't find \{scnm} in environment"
|
||
|
||
-- REVIEW We probably need to know this is a VRef before we decide to split on this slot..
|
||
scty' <- unlet ctx.env scty >>= forceType ctx.env
|
||
-- TODO attempting to pick up Autos that could have been solved immediately
|
||
-- If we try on creation, we're looping at the moment, because of the possibility
|
||
-- of Ord a -> Ord b -> Ord (a \x b). Need to cut earlier when solving or switch to
|
||
-- Idris method...
|
||
scty' <- case scty' of
|
||
(VMeta fc1 ix sp) => do
|
||
meta <- lookupMeta ix
|
||
case meta of
|
||
(Solved _ k t) => forceType ctx.env scty'
|
||
(Unsolved _ k xs _ _ _) => do
|
||
top <- get
|
||
mc <- readIORef top.metaCtx
|
||
-- TODO - only hit the relevant ones
|
||
solveAutos
|
||
forceType ctx.env scty'
|
||
OutOfScope => pure scty'
|
||
|
||
_ => pure scty'
|
||
|
||
case pat of
|
||
PatCon fc _ _ _ as => do
|
||
-- expand vars that may be solved, eval top level functions
|
||
debug $ \ _ => "EXP \{show scty} -> \{show scty'}"
|
||
-- this is per the paper, but it would be nice to coalesce
|
||
-- default cases
|
||
cons <- getConstructors ctx (getFC pat) scty'
|
||
let matched = matchedConstructors scnm prob.clauses
|
||
let (hit,miss) = partition (flip elem matched ∘ fst) cons
|
||
-- need to check miss is possible
|
||
miss' <- filterM (checkCase ctx prob scnm scty') miss
|
||
|
||
debug $ \ _ => "CONS \{show $ map fst cons} matched \{show matched} miss \{show miss} miss' \{show miss'}"
|
||
|
||
-- process constructors with matches
|
||
alts <- traverse (buildCase ctx prob scnm scty') hit
|
||
debug $ \ _ => "GOTALTS \{show alts}"
|
||
let alts' = mapMaybe id alts
|
||
when (length' alts' == 0) $ \ _ => error (fc) "no alts for \{show scty'}"
|
||
-- build a default case for missed constructors
|
||
case miss' of
|
||
Nil => pure $ Case fc sctm (mapMaybe id alts)
|
||
_ => do
|
||
-- ctx prob fc scnm
|
||
default <- buildDefault ctx prob fc scnm
|
||
pure $ Case fc sctm (snoc alts' default)
|
||
|
||
PatLit fc v => do
|
||
let tyname = litTyName v
|
||
case scty' of
|
||
(VRef fc1 nm sp) => when (nm /= tyname) $ \ _ => error fc "expected \{show scty} and got \{show tyname}"
|
||
_ => error fc "expected \{show scty} and got \{show tyname}"
|
||
-- need to run through all of the PatLits in this slot and then find a fallback
|
||
-- walk the list of patterns, stop if we hit a PatVar / PatWild, fail if we don't
|
||
alts <- buildLitCases ctx prob fc scnm scty
|
||
pure $ Case fc sctm alts
|
||
pat => error (getFC pat) "Internal error - tried to split on \{show pat}"
|
||
|
||
showDef : Context -> List String -> Int -> Val -> M String
|
||
showDef ctx names n v@(VVar _ n' Lin) = if n == n'
|
||
then pure ""
|
||
else do
|
||
-- REVIEW - was using names, is it ok to pull from the context?
|
||
vv <- vprint ctx v
|
||
pure "= \{vv}"
|
||
showDef ctx names n v = do
|
||
vv <- vprint ctx v
|
||
pure "= \{vv}"
|
||
|
||
-- desugar do
|
||
undo : FC -> List DoStmt -> M Raw
|
||
undo prev Nil = error prev "do block must end in expression"
|
||
undo prev ((DoExpr fc tm) :: Nil) = pure tm
|
||
-- TODO decide if we want to use >> or just >>=
|
||
undo prev ((DoExpr fc tm) :: xs) = do
|
||
xs' <- undo fc xs
|
||
-- output is bigger, not sure if it helps inference or not
|
||
-- pure $ RApp fc (RApp fc (RVar fc "_>>_") tm Explicit) xs' Explicit
|
||
pure $ RApp fc (RApp fc (RVar fc "_>>=_") tm Explicit) (RLam fc (BI fc "_" Explicit Many) xs') Explicit
|
||
undo prev ((DoLet fc nm tm) :: xs) = RLet fc nm (RImplicit fc) tm <$> undo fc xs
|
||
undo prev ((DoArrow fc left@(RVar fc' nm) right Nil) :: xs) = do
|
||
top <- get
|
||
case lookupRaw nm top of
|
||
Just _ => do
|
||
let nm = "$sc"
|
||
xs' <- undo fc xs
|
||
rest <- pure $ RCase fc (RVar fc nm) (MkAlt left xs' :: Nil)
|
||
pure $ RApp fc (RApp fc (RVar fc "_>>=_") right Explicit)
|
||
(RLam fc (BI fc nm Explicit Many) rest) Explicit
|
||
Nothing => do
|
||
xs' <- undo fc xs
|
||
pure $ RApp fc (RApp fc (RVar fc "_>>=_") right Explicit)
|
||
(RLam fc (BI fc' nm Explicit Many) xs') Explicit
|
||
undo prev ((DoArrow fc left right alts) :: xs) = do
|
||
let nm = "$sc"
|
||
xs' <- undo fc xs
|
||
rest <- pure $ RCase fc (RVar fc nm) (MkAlt left xs' :: alts)
|
||
pure $ RApp fc (RApp fc (RVar fc "_>>=_") right Explicit)
|
||
(RLam fc (BI fc nm Explicit Many) rest) Explicit
|
||
|
||
check ctx tm ty = do
|
||
ty' <- forceType ctx.env ty
|
||
case (tm, ty') of
|
||
(RWhere fc decls body, ty) => checkWhere ctx (collectDecl decls) body ty
|
||
(RIf fc a b c, ty) =>
|
||
let tm' = RCase fc a ( MkAlt (RVar (getFC b) "True") b :: MkAlt (RVar (getFC c) "False") c :: Nil) in
|
||
check ctx tm' ty
|
||
(RDo fc stmts, ty) => do
|
||
stmts' <- undo fc stmts
|
||
check ctx stmts' ty
|
||
(RCase fc rsc alts, ty) => do
|
||
(sc, scty) <- infer ctx rsc
|
||
scty <- forceMeta scty
|
||
debug $ \ _ => "SCTM \{rpprint (names ctx) sc}"
|
||
debug $ \ _ => "SCTY \{show scty}"
|
||
|
||
let scnm = fresh "sc"
|
||
top <- get
|
||
clauses <- for alts $ \case
|
||
(MkAlt pat rawRHS) => do
|
||
pat' <- mkPat (pat, Explicit)
|
||
pure $ MkClause (getFC pat) ((scnm, pat') :: Nil) Nil rawRHS
|
||
-- buildCase expects scrutinee to be a name in the context, so we need to let it.
|
||
-- if it's a Bnd and not shadowed we could skip the let, but that's messy.
|
||
let ctx' = withPos (extend ctx scnm scty) (getFC tm)
|
||
tree <- buildTree ctx' $ MkProb clauses ty
|
||
pure $ Let fc scnm sc tree
|
||
|
||
-- rendered in ProcessDecl
|
||
(RHole fc, ty) => freshMeta ctx fc ty User
|
||
|
||
(t@(RLam fc (BI _ nm icit _) tm), ty@(VPi fc' nm' icit' rig a b)) => do
|
||
debug $ \ _ => "icits \{nm} \{show icit} \{nm'} \{show icit'}"
|
||
if icit == icit' then do
|
||
let var = VVar fc (length' ctx.env) Lin
|
||
let ctx' = extend ctx nm a
|
||
bapp <- b $$ var
|
||
tm' <- check ctx' tm bapp
|
||
pure $ Lam fc nm icit rig tm'
|
||
else if icit' /= Explicit then do
|
||
let var = VVar fc (length' ctx.env) Lin
|
||
ty' <- b $$ var
|
||
-- use nm' here if we want them automatically in scope
|
||
sc <- check (extend ctx nm' a) t ty'
|
||
pure $ Lam fc nm' icit rig sc
|
||
else
|
||
error fc "Icity issue checking \{show t} at \{show ty}"
|
||
(t@(RLam _ (BI fc nm icit quant) tm), ty) => do
|
||
pty <- prvalCtx ty
|
||
error fc "Expected pi type, got \{pty}"
|
||
|
||
(RLet fc nm ty v sc, rty) => do
|
||
ty' <- check ctx ty (VU emptyFC)
|
||
vty <- eval ctx.env CBN ty'
|
||
v' <- check ctx v vty
|
||
vv <- eval ctx.env CBN v'
|
||
let ctx' = define ctx nm vv vty
|
||
sc' <- check ctx' sc rty
|
||
pure $ Let fc nm v' sc'
|
||
|
||
(RImplicit fc, ty) => freshMeta ctx fc ty Normal
|
||
|
||
(tm, ty@(VPi fc nm' Implicit rig a b)) => do
|
||
let names = map fst ctx.types
|
||
debug $ \ _ => "XXX edge case add implicit lambda {\{nm'} : \{show a}} to \{show tm} "
|
||
let var = VVar fc (length' ctx.env) Lin
|
||
ty' <- b $$ var
|
||
debugM $ do
|
||
pty' <- prvalCtx {{(extend ctx nm' a)}} ty'
|
||
pure "XXX ty' is \{pty'}"
|
||
sc <- check (extend ctx nm' a) tm ty'
|
||
pure $ Lam (getFC tm) nm' Implicit rig sc
|
||
|
||
(tm, ty@(VPi fc nm' Auto rig a b)) => do
|
||
let names = map fst ctx.types
|
||
debug $ \ _ => "XXX edge case add auto lambda {\{nm'} : \{show a}} to \{show tm} "
|
||
let var = VVar fc (length' ctx.env) Lin
|
||
ty' <- b $$ var
|
||
debugM $ do
|
||
pty' <- prvalCtx {{(extend ctx nm' a)}} ty'
|
||
pure "XXX ty' is \{pty'}"
|
||
sc <- check (extend ctx nm' a) tm ty'
|
||
pure $ Lam (getFC tm) nm' Auto rig sc
|
||
|
||
(tm,ty) => do
|
||
(tm', ty') <- infer ctx tm
|
||
(tm', ty') <- insert ctx tm' ty'
|
||
|
||
let names = map fst ctx.types
|
||
debug $ \ _ => "INFER \{show tm} to (\{rpprint names tm'} : \{show ty'}) expect \{show ty}"
|
||
unifyCatch (getFC tm) ctx ty' ty
|
||
pure tm'
|
||
|
||
infer ctx (RVar fc nm) = go 0 ctx.types
|
||
where
|
||
go : Int -> List (String × Val) -> M (Tm × Val)
|
||
go i Nil = do
|
||
top <- get
|
||
case lookupRaw nm top of
|
||
Just (MkEntry _ name ty def) => do
|
||
debug $ \ _ => "lookup \{show name} as \{show def}"
|
||
vty <- eval Nil CBN ty
|
||
pure (Ref fc name, vty)
|
||
Nothing => error fc "\{show nm} not in scope"
|
||
go i ((x, ty) :: xs) = if x == nm then pure $ (Bnd fc i, ty)
|
||
else go (i + 1) xs
|
||
|
||
infer ctx (RApp fc t u icit) = do
|
||
-- If the app is explicit, add any necessary metas
|
||
(icit, t, tty) <- case the Icit icit of
|
||
Explicit => do
|
||
(t, tty) <- infer ctx t
|
||
(t, tty) <- insert ctx t tty
|
||
pure (Explicit, t, tty)
|
||
Implicit => do
|
||
(t, tty) <- infer ctx t
|
||
pure (Implicit, t, tty)
|
||
Auto => do
|
||
(t, tty) <- infer ctx t
|
||
pure (Auto, t, tty)
|
||
|
||
(a,b) <- do
|
||
tty' <- forceMeta tty
|
||
case tty' of
|
||
(VPi fc' str icit' rig a b) => if icit' == icit then pure (a,b)
|
||
else error fc "IcitMismatch \{show icit} \{show icit'}"
|
||
|
||
-- If it's not a VPi, try to unify it with a VPi
|
||
-- TODO test case to cover this.
|
||
tty => do
|
||
debug $ \ _ => "unify PI for \{show tty}"
|
||
a <- freshMeta ctx fc (VU emptyFC) Normal >>= eval ctx.env CBN
|
||
b <- MkClosure ctx.env <$> freshMeta (extend ctx ":ins" a) fc (VU emptyFC) Normal
|
||
-- FIXME - I had to guess Many here. What are the side effects?
|
||
unifyCatch fc ctx tty (VPi fc ":ins" icit Many a b)
|
||
pure (a,b)
|
||
|
||
u <- check ctx u a
|
||
u' <- eval ctx.env CBN u
|
||
bappu <- b $$ u'
|
||
pure (App fc t u, bappu)
|
||
|
||
infer ctx (RU fc) = pure (UU fc, VU fc) -- YOLO
|
||
infer ctx (RPi _ (BI fc nm icit quant) ty ty2) = do
|
||
ty' <- check ctx ty (VU fc)
|
||
vty' <- eval ctx.env CBN ty'
|
||
ty2' <- check (extend ctx nm vty') ty2 (VU fc)
|
||
pure (Pi fc nm icit quant ty' ty2', (VU fc))
|
||
infer ctx (RLet fc nm ty v sc) = do
|
||
ty' <- check ctx ty (VU emptyFC)
|
||
vty <- eval ctx.env CBN ty'
|
||
v' <- check ctx v vty
|
||
vv <- eval ctx.env CBN v'
|
||
let ctx' = define ctx nm vv vty
|
||
(sc',scty) <- infer ctx' sc
|
||
pure $ (Let fc nm v' sc', scty)
|
||
|
||
infer ctx (RAnn fc tm rty) = do
|
||
ty <- check ctx rty (VU fc)
|
||
vty <- eval ctx.env CBN ty
|
||
tm <- check ctx tm vty
|
||
pure (tm, vty)
|
||
|
||
infer ctx (RLam _ (BI fc nm icit quant) tm) = do
|
||
a <- freshMeta ctx fc (VU emptyFC) Normal >>= eval ctx.env CBN
|
||
let ctx' = extend ctx nm a
|
||
(tm', b) <- infer ctx' tm
|
||
debug $ \ _ => "make lam for \{show nm} scope \{rpprint (names ctx) tm'} : \{show b}"
|
||
tyb <- quote (1 + ctx.lvl) b
|
||
pure $ (Lam fc nm icit quant tm', VPi fc nm icit quant a (MkClosure ctx.env tyb))
|
||
|
||
infer ctx (RImplicit fc) = do
|
||
ty <- freshMeta ctx fc (VU emptyFC) Normal
|
||
vty <- eval ctx.env CBN ty
|
||
tm <- freshMeta ctx fc vty Normal
|
||
pure (tm, vty)
|
||
|
||
infer ctx (RLit fc (LString str)) = do
|
||
ty <- primType fc stringType
|
||
pure (Lit fc (LString str), ty)
|
||
infer ctx (RLit fc (LInt i)) = do
|
||
ty <- primType fc intType
|
||
pure (Lit fc (LInt i), ty)
|
||
infer ctx (RLit fc (LChar c)) = do
|
||
ty <- primType fc charType
|
||
pure (Lit fc (LChar c), ty)
|
||
infer ctx (RAs fc _ _) = error fc "@ can only be used in patterns"
|
||
infer ctx tm = error (getFC tm) "Implement infer \{show tm}"
|