MiniAgda-0.2025.7.23: src/ScopeChecker.hs
-- NOTE: insertion of polarity variables disabled here, must be done
-- in TypeChecker
{-# LANGUAGE TupleSections, DeriveFunctor, GeneralizedNewtypeDeriving,
FlexibleContexts, FlexibleInstances, UndecidableInstances,
MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module ScopeChecker (scopeCheck) where
import Prelude hiding (mapM, null)
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Control.Monad hiding (mapM)
import Control.Monad.Identity (Identity, runIdentity)
import Control.Monad.Reader (ReaderT, runReaderT, MonadReader, ask, asks, local)
import Control.Monad.State (StateT, execStateT, runStateT, get, gets, modify, put)
import Control.Monad.Except (ExceptT, runExceptT, MonadError, catchError)
import Control.Monad.Trans (lift)
import Data.List (sort, (\\))
import qualified Data.List as List
import Data.Maybe
import Data.Traversable (mapM)
-- import Debug.Trace
import Polarity(Pol(..))
import qualified Polarity as A
import Abstract
( Sized, Co, ConK(..), PrePost(..), MVar, Override(..), Measure(..), adjustTopDecsM
, Arity, polarity, LensPol(..)
)
import qualified Abstract as A
import qualified Concrete as C
import TraceError
import Util
-- * Scope checker.
-- - check that all identifiers are in scope and global identifiers are only used once
-- - replaces Ident with Con, Def, Let or Var
-- - replaces IdentP with ConP or VarP in patterns
-- - replaces Unknown by a new Meta-Variable
-- - check pattern length is equal in each clause
-- - group mutual declarations
-- | Entry point for scope checker.
scopeCheck :: [C.Declaration] -> Either TraceError ([A.Declaration],SCState)
scopeCheck dl = runScopeCheck initCtx initSt (scopeCheckDecls dl)
-- * Local identifiers.
-- ** local environment of scope checker
data SCCxt = SCCxt
{ stack :: Stack -- ^ Local names in scope.
-- We keep a stack of these to disallow shadowing on the same level.
, defaultPolarity :: Pol -- ^ Replacement for @Default@ polarity.
, constraintAllowed :: Bool -- ^ Is a constraint @|m| < |m'|@ legal now, since we just parsed a quantifier?
}
type Stack = [Context]
initCtx :: SCCxt
initCtx = SCCxt
{ stack = [[]] -- one empty context to begin with
, defaultPolarity = A.Rec -- POL VARS DISABLED!!
, constraintAllowed = False
}
-- ** A lens for @constraintAllowed@
class LensConstraintAllowed a where
mapConstraintAllowed :: (Bool -> Bool) -> a -> a
setConstraintAllowed :: Bool -> a -> a
setConstraintAllowed b = mapConstraintAllowed (const b)
instance LensConstraintAllowed SCCxt where
mapConstraintAllowed f sc = sc { constraintAllowed = f (constraintAllowed sc) }
instance (LensConstraintAllowed r, MonadReader r m) => LensConstraintAllowed (m a) where
mapConstraintAllowed f = local (mapConstraintAllowed f)
-- ** Managing the stack of local contexts.
newLevel :: ScopeCheck a -> ScopeCheck a
newLevel = local $ \ cxt -> cxt { stack = [] : stack cxt }
thisLevel :: SCCxt -> Context
thisLevel cxt = head (stack cxt)
instance Push Local SCCxt where
push nx sc = sc { stack = push nx (stack sc) }
-- ** translating concrete names to abstract names
type Local = (C.Name,A.Name)
type Context = [Local]
emptyCtx :: Context
emptyCtx = []
newLocal :: Push Local b => C.Name -> b -> (A.Name, b)
newLocal n cxt = (x, push (n, x) cxt)
where x = A.fresh $ C.theName n
lookupLocal :: C.Name -> ScopeCheck (Maybe A.Name)
lookupLocal n = retrieve n <$> asks stack
lookupGlobal :: C.QName -> ScopeCheck (Maybe DefI)
lookupGlobal n = lookupSig n <$> getSig
addContext :: Context -> SCCxt -> SCCxt
addContext delta sc = sc { stack = delta : stack sc }
-- * Global identifiers.
-- | Kind of identifier.
data IKind
= DataK
| ConK ConK
| FunK Bool -- ^ @False@ = inside body, @True@ = outside body
| ProjK -- ^ a record projection
| LetK
-- | Global identifier.
data DefI = DefI { ikind :: IKind, aname :: A.QName }
-- | Scope check signature.
type Sig = [(C.QName,DefI)]
emptySig :: Sig
emptySig = []
lookupSigU :: C.Name -> Sig -> Maybe DefI
lookupSigU n = lookupSig (C.QName n)
lookupSig :: C.QName -> Sig -> Maybe DefI
lookupSig n [] = Nothing
lookupSig n ((x,k):xs) = if (x == n) then Just k else lookupSig n xs
-- ** State of scope checker.
data SCState = SCState
{ signature :: Sig
, nextMeta :: MVar
, nextPolVar :: MVar
}
initSt :: SCState
initSt = SCState emptySig 0 0
-- * The scope checking monad.
-- | Scope checking monad.
--
-- Reader monad for local environment of variables (used in expresssions and patterns).
-- State monad (hidden) for global signature.
-- Error monad for reporting scope violations.
newtype ScopeCheck a = ScopeCheck { unScopeCheck ::
ReaderT SCCxt (StateT SCState (ExceptT TraceError Identity)) a }
deriving (Functor, Applicative, Monad,
MonadReader SCCxt, MonadError TraceError)
runScopeCheck
:: SCCxt -- ^ Local variable mapping.
-> SCState -- ^ Global identifier mapping.
-> ScopeCheck a -- ^ The computation.
-> Either TraceError (a, SCState)
runScopeCheck ctx st (ScopeCheck sc) = runIdentity $ runExceptT $
runStateT (runReaderT sc ctx) st
-- ** Local state.
-- | Add a local identifier.
-- (Not tail recursive, since it also returns the generate id.)
addBind' :: Show e => e -> C.Name -> (A.Name -> ScopeCheck a) -> ScopeCheck (A.Name, a)
addBind' e n k = do
ctx <- ask
case retrieve n (thisLevel ctx) of
Just _ -> errorAlreadyInContext e n
Nothing -> do
let (x, ctx') = newLocal n ctx -- addCtx' n ctx
a <- local (const ctx') $ k x
return (x, a)
addBind :: Show e => e -> C.Name -> ScopeCheck a -> ScopeCheck (A.Name, a)
addBind e n k = addBind' e n $ const k
addBinds :: Show e => e -> [C.Name] -> ScopeCheck a -> ScopeCheck ([A.Name], a)
addBinds e ns k = foldr step start ns where
start = do
a <- k
return ([], a)
step n k = do
(x, (xs, a)) <- addBind e n k
return (x:xs, a)
-- | Add local variable without checking shadowing.
addLocal :: C.Name -> (A.Name -> ScopeCheck a) -> ScopeCheck a
addLocal n k = do
ctx <- ask
let (x, ctx') = newLocal n ctx
local (const ctx') $ k x
addTel :: C.Telescope -> A.Telescope -> ScopeCheck a -> ScopeCheck a
addTel ctel atel = local (addContext nxs)
where nxs = reverse $ zipTels ctel atel
zipTels :: C.Telescope -> A.Telescope -> [(C.Name,A.Name)]
zipTels ctel atel = zip ns xs
where ns = collectTelescopeNames ctel
xs = map A.boundName $ A.telescope atel
-- ** Global state.
getSig :: ScopeCheck Sig
getSig = ScopeCheck $ gets signature
-- | Add a global identifier.
addName :: IKind -> C.Name -> ScopeCheck A.Name
addName k n = do
sig <- getSig
when (isJust (lookupSig (C.QName n) sig)) $
errorAlreadyInSignature "shadowing of global definitions forbidden" n
let x = A.fresh $ C.theName n
addANameU k n x
return x
-- addNameU :: IKind -> C.Name -> ScopeCheck A.Name
-- addNameU k n = A.unqual <$> addName k (C.QName n)
-- | Add an already translated global identifier.
addAName :: IKind -> C.QName -> A.QName -> ScopeCheck ()
addAName k n x = ScopeCheck $ modify $ \ st ->
st { signature = (n, DefI k x) : signature st }
addANameU :: IKind -> C.Name -> A.Name -> ScopeCheck ()
addANameU ki n x = addAName ki (C.QName n) (A.QName x)
-- | Add or reuse an unqualified name.
overloadName :: IKind -> C.Name -> ScopeCheck A.Name
overloadName k n = do
sig <- getSig
case lookupSigU n sig of
Nothing -> do
let x = A.fresh $ C.theName n
addANameU k n x
return x
Just (DefI k' (A.QName x)) -> return x
{- UNUSED
addDecl :: C.Declaration -> ScopeCheck A.Name
addDecl (C.DataDecl n _ _ _ _ _ _) = addName DataK n
addDecl (C.RecordDecl n _ _ _ _) = addName DataK n
-}
{- UNUSED
addFunDecl :: Bool -> C.Declaration -> ScopeCheck A.Name
addFunDecl b (C.FunDecl _ ts _) = addTypeSig (FunK b) ts
-}
addTypeSig :: IKind -> C.TypeSig -> A.TypeSig -> ScopeCheck ()
addTypeSig kind (C.TypeSig n _) (A.TypeSig x _) = addANameU kind n x
{- UNUSED
-- | Add a global identifier. Fail if already in signature.
addGlobal :: Show d => d -> IKind -> C.Name -> ScopeCheck A.Name
addGlobal d k n = enterShow n $ do
sig <- getSig
case lookupSig n sig of
Just _ -> errorAlreadyInSignature d n
Nothing -> addName k n
-}
-- | Create a meta variable.
nextMVar :: (MVar -> ScopeCheck a) -> ScopeCheck a
nextMVar f = ScopeCheck $ do
st <- get
put $ st { nextMeta = nextMeta st + 1 }
unScopeCheck $ f (nextMeta st)
-- | Create a polarity meta variable.
nextPVar :: (MVar -> ScopeCheck a) -> ScopeCheck a
nextPVar f = ScopeCheck $ do
st <- get
put $ st { nextPolVar = nextPolVar st + 1 }
unScopeCheck $ f (nextPolVar st)
-- ** Additional services of scope monad.
-- | Default polarity is context-sensitive.
setDefaultPolarity :: Pol -> ScopeCheck a -> ScopeCheck a
setDefaultPolarity p = local (\ sccxt -> sccxt { defaultPolarity = p })
{-
insertingPolVars :: Bool -> ScopeCheck a -> ScopeCheck a
insertingPolVars b = local (\ sccxt -> sccxt { insertPolVars = b })
-}
-- | Insert polarity variables for omitted polarities.
generalizeDec :: A.Dec -> ScopeCheck A.Dec
generalizeDec dec@A.Hidden = return dec
generalizeDec dec@A.Dec{} =
if (polarity dec == Default) then do
p0 <- asks defaultPolarity
case p0 of
PVar{} -> nextPVar $ \ i ->
return $ setPol (PVar i) dec
_ -> return $ setPol p0 dec
else return dec
generalizeTBind :: C.TBind -> ScopeCheck C.TBind
generalizeTBind tb@C.TMeasure{} = return tb
generalizeTBind tb = do
dec' <- generalizeDec (C.boundDec tb)
return $ tb { C.boundDec = dec' }
-- | Insert polarity variables in telescope.
generalizeTel :: C.Telescope -> ScopeCheck C.Telescope
generalizeTel = mapM generalizeTBind
-- * Scope checking concrete syntax.
----------------------------------------------------------------------
scopeCheckDecls :: [C.Declaration] -> ScopeCheck [A.Declaration]
scopeCheckDecls = mapM scopeCheckDeclaration
scopeCheckDeclaration :: C.Declaration -> ScopeCheck A.Declaration
scopeCheckDeclaration (C.OverrideDecl Check ds) = ScopeCheck $ do
st <- get
as <- unScopeCheck $ scopeCheckDecls ds -- declarations need to scope check
put st -- but then forget their effect: restore old state
return $ A.OverrideDecl Check as
scopeCheckDeclaration (C.OverrideDecl Fail ds) = ScopeCheck $ do
st <- get
as <- unScopeCheck $ scopeCheckDecls ds
`catchError` (const $ return []) --on error discard block
put st
return $ A.OverrideDecl Fail as
{-
scopeCheckDeclaration (C.OverrideDecl Fail ds) = do
st <- get
(as,st') <- (do as <- scopeCheckDecls ds
st' <- get
return (as,st'))
`catchError` (const $ return ([],st)) --on error discard block
put st'
return $ A.OverrideDecl Fail as
-}
scopeCheckDeclaration (C.OverrideDecl override ds) = do -- TrustMe,Impredicative
as <- scopeCheckDecls ds
return $ A.OverrideDecl override as
scopeCheckDeclaration (C.RecordDecl n tel t c fields) =
scopeCheckRecordDecl n tel t c fields
scopeCheckDeclaration d@(C.DataDecl{}) =
scopeCheckDataDecl d -- >>= return . (:[])
scopeCheckDeclaration d@(C.FunDecl co _ _) =
scopeCheckFunDecls co [d] -- >>= return . (:[])
scopeCheckDeclaration (C.LetDecl eval letdef@C.LetDef{ C.letDefDec = dec, C.letDefName = n }) = do
unless (dec == A.defaultDec) $
throwErrorMsg $ "polarity annotation not supported in global let definition of " ++ show n
(tel, mt, e) <- scopeCheckLetDef letdef
x <- addName LetK n
return $ A.LetDecl eval x tel mt e
scopeCheckDeclaration d@(C.PatternDecl n ns p) = do
let errorHead = "invalid pattern declaration\n" ++ C.prettyDecl d ++ "\n"
-- check pattern
(p, delta) <- runStateT (scopeCheckPattern p) emptyCtx
p <- local (addContext delta) $ scopeCheckDotPattern p
-- ensure that pattern variables are the declared variables
unless (sort ns == sort (map fst delta)) $ do
let usedNames = map fst delta
unusedNames = ns \\ usedNames
undeclaredNames = usedNames \\ ns
when (not (null unusedNames)) $ throwErrorMsg $
errorHead ++ "unsed variables in pattern: "
++ Util.showList " " show unusedNames
when (not (null undeclaredNames)) $ throwErrorMsg $
errorHead ++ "undeclared variables in pattern: "
++ Util.showList " " show undeclaredNames
-- when (n `elem` ns) $ throwErrorMsg $ errorHead ++ "pattern"
x <- addName (ConK DefPat) n
let xs = map (fromJust . flip lookup delta) ns
return (A.PatternDecl x xs p)
-- we support
-- - mutual (co)funs
-- - mutual (co)data
scopeCheckDeclaration (C.MutualDecl []) = throwErrorMsg "empty mutual block"
scopeCheckDeclaration (C.MutualDecl l@(C.DataDecl{}:xl)) =
scopeCheckMutual l
scopeCheckDeclaration (C.MutualDecl l@(C.FunDecl co _ _:xl)) =
scopeCheckFunDecls co l -- >>= return . (:[])
scopeCheckDeclaration (C.MutualDecl _) = throwErrorMsg "mutual combination not supported"
scopeCheckLetDef :: C.LetDef -> ScopeCheck (A.Telescope, Maybe (A.Type), A.Expr)
scopeCheckLetDef (C.LetDef dec n tel mt e) = setDefaultPolarity A.Rec $ do
tel <- generalizeTel tel
(tel, (mt, e)) <- scopeCheckTele tel $ do
(,) <$> mapM scopeCheckExprN mt -- allow shadowing after : in type
<*> scopeCheckExprN e -- allow shadowing after =
return (tel, mt, e)
{- scopeCheck Mutual block
first check signatures
then bodies
-}
scopeCheckMutual :: [C.Declaration] -> ScopeCheck A.Declaration
scopeCheckMutual ds0 = do
-- flatten nested mutual blocks and override decls
ds <- mutualFlattenDecls ds0
-- extract, check, and add type signatures
let ktsigs = map mutualGetTypeSig ds
(mmm, tsigs') <- unzip <$> mapM checkAndAddTypeSig ktsigs
-- funs have been added with internal names
-- check that all functions are unmeasured or have a same length measure
let (ns, mll) = unzip $ compressMaybes mmm
let measured = null mll || isJust (head mll)
let ok = null mll || all ((head mll)==) (tail mll)
when (not ok) $ throwErrorMsg $ "in a mutual function block, either all functions must be without measure or have a measure of the same length"
{-
-- switch to internal fun ids
let funNames = [ n | (FunK _ , A.TypeSig n _) <- ktsigs ] -- internal fun names
{- SAME W/O COMPR
let funNames = map (\ (_, C.TypeSig n _) -> n) $ filter aux ktsigs where
aux (FunK _, _) = True
aux _ = False
-}
mapM_ (addName (FunK False)) funNames -- TODO
-}
-- check bodies of declarations
ds' <- mapM (setDefaultPolarity A.Rec . checkBody) (zip tsigs' ds)
-- switch back to external fun ids
let funNames = [ x | A.FunDecl _ (A.Fun _ x _ _) <- ds' ] -- external fun names
zipWithM_ (addANameU (LetK)) ns funNames
-- zipWithM_ (addAName (FunK True)) ns funNames
return $ A.MutualDecl measured ds'
scopeCheckTele :: C.Telescope -> ScopeCheck a -> ScopeCheck (A.Telescope, a)
scopeCheckTele [] cont = (A.emptyTel,) <$> cont
scopeCheckTele (tb : tel) cont = do
(tbs, (A.Telescope tel, a)) <- scopeCheckTBind tb $ scopeCheckTele tel cont
return (A.Telescope $ tbs ++ tel, a)
scopeCheckTBind :: C.TBind -> ScopeCheck a -> ScopeCheck ([A.TBind], a)
scopeCheckTBind tb cont = do
let contYes = setConstraintAllowed True cont
contNo = setConstraintAllowed False cont
case tb of
C.TBind dec [] t -> do -- non-dependent function type
t <- scopeCheckExprN t
([A.noBind $ A.Domain t A.defaultKind dec],) <$> contNo
C.TBind dec ns t -> do
t <- scopeCheckExprN t
(xs, a) <- addBinds tb ns $ contYes
return (map (\ x -> A.TBind x (A.Domain t A.defaultKind dec)) xs, a)
C.TBounded dec n ltle e -> do
e <- scopeCheckExprN e
(x, a) <- addBind tb n $ contYes
return ([A.TBind x (A.Domain (A.Below ltle e) A.defaultKind dec)], a)
C.TMeasure mu -> do
mu <- scopeCheckMeasure mu
([A.TMeasure mu],) <$> cont
-- C.TMeasure mu -> throwErrorMsg $ "measure not allowed in telescope"
C.TBound beta -> do
unlessM (asks constraintAllowed) $
errorConstraintNotAllowed beta
beta <- scopeCheckBound beta
([A.TBound beta],) <$> cont
checkBody :: (A.TypeSig, C.Declaration) -> ScopeCheck A.Declaration
checkBody (A.TypeSig x tt, C.DataDecl n sz co tel _ cs fields) =
checkDataBody tt n x sz co tel cs fields
checkBody (ts@(A.TypeSig n t), d@(C.FunDecl co tsig cls)) = do
(ar,cls') <- scopeCheckFunClauses d
let n' = A.mkExtName n
return $ A.FunDecl co $ A.Fun ts n' ar cls'
mutualFlattenDecls :: [C.Declaration] -> ScopeCheck [C.Declaration]
mutualFlattenDecls ds = mapM mutualFlattenDecl ds >>= return . concat
mutualFlattenDecl :: C.Declaration -> ScopeCheck [C.Declaration]
mutualFlattenDecl (C.MutualDecl ds) = mutualFlattenDecls ds
mutualFlattenDecl (C.OverrideDecl Fail _) = throwErrorMsg $ "fail declaration not supported in mutual block"
mutualFlattenDecl (C.OverrideDecl o ds) = do
ds' <- mutualFlattenDecls ds
return $ map (\ d -> C.OverrideDecl o [d]) ds'
mutualFlattenDecl (C.LetDecl{}) = throwErrorMsg $ "let in mutual block not supported"
mutualFlattenDecl d = return $ [d]
-- extract type sigs of a mutual block in order, error on nested mutual
mutualGetTypeSig :: C.Declaration -> (IKind, C.TypeSig)
mutualGetTypeSig (C.DataDecl n sz co tel t cs fields) =
(DataK, C.TypeSig n (C.teleToType tel t))
mutualGetTypeSig (C.FunDecl co tsig cls) =
(FunK False, tsig) -- fun id for use inside defining body
mutualGetTypeSig (C.LetDecl ev (C.LetDef dec n tel Nothing e)) =
error $ "let declaration of " ++ show n ++ ": type required in mutual block"
mutualGetTypeSig (C.LetDecl ev (C.LetDef dec n tel (Just t) e)) =
(LetK, C.TypeSig n (C.teleToType tel t))
{- mutualGetTypeSig (C.LetDecl ev tsig e) =
(LetK, tsig) -}
mutualGetTypeSig (C.OverrideDecl _ [d]) =
mutualGetTypeSig d
scopeCheckRecordDecl :: C.Name -> C.Telescope -> C.Type -> C.Constructor -> [C.Name] ->
ScopeCheck A.Declaration
scopeCheckRecordDecl n tel t c cfields = enterShow n $ do
setDefaultPolarity A.Param $ do
tel <- generalizeTel tel
-- STALE COMMENT: we do not infer at all: -- do not infer polarities in index arguments
(A.TypeSig x tt') <- scopeCheckTypeSig (C.TypeSig n $ C.teleToType tel t)
addANameU DataK n x
let names = collectTelescopeNames tel
target = C.App (C.ident n) (map C.ident names) -- R pars
(tel',t') = A.typeToTele' (length names) tt'
c' <- scopeCheckConstructor n x (zipTels tel tel') A.CoInd target c
let delta = contextFromConstructors c c'
afields <- addFields ProjK delta cfields
return $ A.RecordDecl x tel' t' c' afields
contextFromConstructors :: C.Constructor -> A.Constructor -> Context
contextFromConstructors (C.Constructor _ ctel0 mct) (A.Constructor _ _ at) = delta
where ctel = maybe [] (fst . C.typeToTele) mct
(atel, _) = A.typeToTele at
delta = zipTels (ctel0 ++ ctel) atel
scopeCheckField :: Context -> C.Name -> ScopeCheck A.Name
scopeCheckField delta n =
case lookup n delta of
Nothing -> errorNotAField n
Just x -> return $ x
addFields :: IKind -> Context -> [C.Name] -> ScopeCheck [A.Name]
addFields kind delta cfields = do
afields <- mapM (scopeCheckField delta) cfields
mapM_ (uncurry $ addANameU kind) $ zip cfields afields
return afields
scopeCheckDataDecl :: C.Declaration -> ScopeCheck A.Declaration
scopeCheckDataDecl decl@(C.DataDecl n sz co tel0 t cs fields) = enterShow n $ do
setDefaultPolarity A.Param $ do
tel <- generalizeTel tel0
-- STALE: -- do not infer polarities in index arguments
(A.TypeSig x tt') <- scopeCheckTypeSig (C.TypeSig n $ C.teleToType tel t)
addANameU DataK n x
checkDataBody tt' n x sz co tel cs fields
-- precondition: name already added to signature
checkDataBody :: A.Type -> C.Name -> A.Name -> Sized -> Co -> C.Telescope -> [C.Constructor] -> [C.Name] -> ScopeCheck A.Declaration
checkDataBody tt' n x sz co tel cs fields = do
let cnames = collectTelescopeNames tel -- parameters
target = C.App (C.ident n) $ map C.ident cnames -- D pars
(tel',t') = A.typeToTele' (length cnames) tt'
cs' <- mapM (scopeCheckConstructor n x (zipTels tel tel') co target) cs
{- NO LONGER INFER DESTRUCTORS
-- traceM ("constructors: " ++ show cs')
-- when (t' == A.Sort A.Set && length cs' == 1) $ do
-- when (length cs' == 1) $ do -- TOO STRICT, DOES NOT TREAT Vec right
let cis = A.analyzeConstructors co n tel' cs'
flip mapM_ cis $ \ ci -> when (A.cEtaExp ci) $ do
-- Add destructor names
let fields = A.cFields ci -- A.classifyFields co n (A.typePart c)
-- TODO Check for recursive occurrence!
-- when (A.etaExpandable fields) $
let destrNames = A.destructorNames fields
--when (not (null (destrNames))) $
-- traceM ("fields: " ++ show fields)
-- traceM ("destructors: " ++ show destrNames)
mapM_ (addName (FunK True)) $ destrNames -- destructors are also upped
{-
let (ctel,_) = A.typeToTele (A.typePart (head cs'))
let destrNames = map (\(_,x,_) -> x) ctel
when (all (/= "") destrNames) $
mapM_ (addName (FunK True)) destrNames -- destructors are also upped
-}
-}
-- add declared destructor names
let delta = concat $ map (uncurry contextFromConstructors) $ zip cs cs'
-- fields <- addFields (LetK) delta fields
-- 2012-01-26 register as projections
fields <- addFields ProjK delta fields
let pos = map (A.polarity . A.decor . A.boundDom) $ A.telescope tel'
return $ A.DataDecl x sz co pos tel' t' cs' fields
-- check whether all declarations in mutual block are (co)funs
checkFunMutual :: Co -> [C.Declaration] -> ScopeCheck ()
checkFunMutual co [] = return ()
checkFunMutual co (C.FunDecl co' _ _:xl) | co == co' = checkFunMutual co xl
checkFunMutual _ _ = throwErrorMsg "mutual combination not supported"
scopeCheckFunDecls :: Co -> [C.Declaration] -> ScopeCheck A.Declaration
scopeCheckFunDecls co l = do
-- check for uniformity of mutual block (all funs/all cofuns)
checkFunMutual co l
-- check signatures and look for measures
r <- mapM (\ (C.FunDecl _ tysig _) -> scopeCheckFunSig tysig) l
let (ml:mll, tsl') = unzip r
let ok = all (ml==) mll
when (not ok) $ throwErrorMsg $ "in a mutual function block, either all functions must be without measure or have a measure of the same length"
-- add names as internal ids and check bodies
let nxs = zipWith (\ (C.FunDecl _ (C.TypeSig n _) _) (A.TypeSig x _) -> (n,x)) l tsl'
--let addFuns b = mapM (uncurry $ addAName $ FunK b) nxs
-- let addFuns b = mapM (\ (n,x) -> addAName (FunK b) n x) nxs
-- addFuns False
mapM_ (uncurry $ addANameU $ FunK False) nxs
arcll' <- mapM (setDefaultPolarity A.Rec . scopeCheckFunClauses) l
-- add names as external ids
--addFuns True
let nxs' = map (mapPair id A.mkExtName) nxs
mapM_ (uncurry $ addANameU (LetK)) nxs'
-- mapM (uncurry $ addAName (FunK True)) nxs'
return $ A.MutualFunDecl (isJust ml) co $
zipWith3 (\ ts (_, x') (ar, cls) -> A.Fun ts x' ar cls) tsl' nxs' arcll'
-- | Does not add name to signature.
scopeCheckFunSig :: C.TypeSig -> ScopeCheck (Maybe Int, A.TypeSig)
scopeCheckFunSig d@(C.TypeSig n t) = checkInSig d n $ \ x -> do
(ml, t') <- scopeCheckFunType t
return (ml, A.TypeSig x t')
-- scope check type of mutual function, return length of measure (if present)
-- a fun type is a telescope followed by (maybe) a measure and a type expression
scopeCheckFunType :: C.Expr -> ScopeCheck (Maybe Int, A.Expr)
scopeCheckFunType t =
case t of
-- found a measure: continue normal scope checking
C.Quant A.Pi [C.TMeasure mu] e1 -> do
mu' <- scopeCheckMeasure mu
e1' <- scopeCheckExprN e1
return (Just $ length (measure mu'), A.pi (A.TMeasure mu') e1')
-- bounds are allowed here, since we check a function type
C.Quant A.Pi [C.TBound beta] e1 -> do
beta' <- scopeCheckBound beta
(ml, e1') <- scopeCheckFunType e1
return (ml, A.pi (A.TBound beta') e1')
C.Quant A.Pi tel e -> do
tel <- generalizeTel tel
(tel, (ml, e)) <- setDefaultPolarity A.Rec $ setConstraintAllowed False $
scopeCheckTele tel $ setConstraintAllowed True $ scopeCheckFunType e
ml' <- findMeasure tel
ml <- case (ml,ml') of
(Nothing,ml') -> return ml'
(ml, Nothing) -> return ml
(Just{}, Just{}) -> errorOnlyOneMeasure
return (ml, A.teleToType tel e)
t -> (Nothing,) <$> scopeCheckExpr t -- no measure found
findMeasure :: A.Telescope -> ScopeCheck (Maybe Int)
findMeasure (A.Telescope tel) =
case [ mu | A.TMeasure mu <- tel ] of
[] -> return Nothing
[Measure mu] -> return $ Just $ length mu
_ -> errorOnlyOneMeasure
-- | Check whether concrete name is already in signature.
-- If yes, fail. If no, create abstract name and continue.
checkInSig :: Show d => d -> C.Name -> (A.Name -> ScopeCheck a) -> ScopeCheck a
checkInSig d n k = enterShow n $ do
sig <- getSig
case lookupSig (C.QName n) sig of
Just _ -> errorAlreadyInSignature d n
Nothing -> k (A.fresh $ C.theName n)
-- checkInSigU :: Show d => d -> C.Name -> (A.Name -> ScopeCheck a) -> ScopeCheck a
-- checkInSigU d n k = checkInSig d (C.QName n) (k . A.unqual)
scopeCheckFunClauses :: C.Declaration -> ScopeCheck (Arity, [A.Clause])
scopeCheckFunClauses (C.FunDecl _ (C.TypeSig n _) cl) = enterShow n $ do
cl <- mapM (scopeCheckClause (Just n)) cl
let m = if null cl then 0 else
List.foldl1 min $ map (length . A.clPatterns) cl
return (A.Arity m Nothing, cl)
{-
let b = checkPatternLength cl
case b of
Just m -> return $ (A.Arity m Nothing, cl)
Nothing -> throwErrorMsg $ " pattern length differs"
-}
-- | Check the type of a signature and generate abstract name.
-- Does not add abstract name to signature.
scopeCheckTypeSig :: C.TypeSig -> ScopeCheck A.TypeSig
scopeCheckTypeSig d@(C.TypeSig n t) = checkInSig d n $ \ x -> do
t' <- scopeCheckExpr t
return $ A.TypeSig x t'
-- | Results:
--
-- @Nothing@ Not a function declaration.
--
-- @Just (n, Nothing)@ Unmeasured function.
--
-- @Just (n, Just m)@ Function with measure of length m
checkAndAddTypeSig :: (IKind, C.TypeSig) -> ScopeCheck (Maybe (C.Name, Maybe Int), A.TypeSig)
checkAndAddTypeSig (kind, ts@(C.TypeSig n _)) = do
(mm, ts'@(A.TypeSig x _)) <-
case kind of
FunK _ -> mapPair (Just . (n,)) id <$> scopeCheckFunSig ts
{-
do
(mi, ts) <- scopeCheckFunSig ts
return (Just mi, ts)
-}
_ -> (Nothing,) <$> scopeCheckTypeSig ts
addANameU kind n x -- or: addTypeSig kind ts ts'
return (mm, ts')
collectTelescopeNames :: C.Telescope -> [C.Name]
collectTelescopeNames = concat . map C.boundNames
-- | Check whether concrete name is already in signature.
-- If yes, fail. If no, create abstract name and continue.
checkConsInSig :: Show decl => decl -> C.Name -> A.Name -> IKind -> C.Name -> (A.QName -> ScopeCheck a) -> ScopeCheck a
checkConsInSig decl d dx ki n cont = enterShow n $ do
-- first check whether the datatype has this constructor already
ifJustM (lookupSig (C.Qual d n) <$> getSig) (const $ errorAlreadyInSignature decl n) $ do
-- then check the overloaded name and possibly add it
x <- overloadName ki n
-- the qualified name is added in the continuation
cont $ A.Qual dx x
-- | @cxt@ is the data telescope.
scopeCheckConstructor :: C.Name -> A.Name -> Context -> Co -> C.Type -> C.Constructor -> ScopeCheck A.Constructor
scopeCheckConstructor d dx cxt co t0 a@(C.Constructor n tel mt) = do
let ki = ConK $ A.coToConK co
checkConsInSig a d dx ki n $ \ x -> do
let finish t mcxt = local (addContext $ maybe cxt id mcxt) $ do
t <- setDefaultPolarity A.Param $ scopeCheckExpr $ C.teleToType tel t
t <- adjustTopDecsM defaultToParam t
addAName ki (C.Qual d n) x
let dummyDom = A.Domain A.Irr A.NoKind $ A.Dec Param
mtel = fmap (map (\ (n,x) -> A.TBind x dummyDom)) mcxt
ps = [] -- patterns computed during type checking
return $ A.Constructor x (fmap ((,ps) . A.Telescope) mtel) t
case mt of
-- no target given, then add the data tel to the scope
Nothing -> finish t0 Nothing
-- target given, then the target binds the parameter names
Just t -> do
-- get the final target
let (_, target) = C.typeToTele t
fallback = finish t Nothing
continue d' es = do
-- unless (d == d') $ errorWrongTarget n d d'
if (d /= d') then fallback else do
-- get the parameters of target
let (pars, inds) = splitAt (length cxt) es
unless (length pars == length cxt) $ errorNotEnoughParameters n target
-- if parameters are just data parameters, do it old style
if and (zipWith isTelPar cxt pars) then fallback else do
-- scopeCheck the parameters as patterns
finish t . Just =<< parameterVariables pars
case target of
C.Ident (C.QName d') -> continue d' []
C.App (C.Ident (C.QName d')) es -> continue d' es
_ -> fallback -- errorTargetMustBeAppliedName n target
{- OLD CODE
scopeCheckConstructor :: C.Telescope -> A.Telescope -> Co -> C.Type -> C.Constructor -> ScopeCheck A.Constructor
scopeCheckConstructor ctel atel co t0 a@(C.Constructor n tel mt) = addTel ctel atel $ checkInSig a n $ \ x -> do
let t = maybe t0 id mt
t <- setDefaultPolarity A.Param $ scopeCheckExpr $ C.teleToType tel t
t <- adjustTopDecsM defaultToParam t
addAName (ConK $ A.coToConK co) n x
return $ A.TypeSig x t
-}
where isTelPar (c,_) (C.Ident (C.QName x)) = c == x
isTelPar _ _ = False
defaultToParam dec = case (A.polarity dec) of
A.Default -> return $ setPol A.Param dec
A.Param -> return dec
A.Const -> return dec
A.PVar{} -> return dec
_ -> throwErrorMsg $ "illegal polarity " ++ show (polarity dec) ++ " in type of constructor " ++ show a
-- | Allow shadowing of previous locals.
-- Always if we enter a subexpression which is not the body
-- of a binder.
scopeCheckExprN :: C.Expr -> ScopeCheck A.Expr
scopeCheckExprN = newLevel . scopeCheckExpr
scopeCheckExpr :: C.Expr -> ScopeCheck A.Expr
scopeCheckExpr e = setConstraintAllowed False $ scopeCheckExpr' e
scopeCheckExpr' :: C.Expr -> ScopeCheck A.Expr
scopeCheckExpr' e =
case e of
-- replace underscore by next meta-variable
C.Unknown -> nextMVar (return . A.Meta)
C.Set e -> A.Sort . A.Set <$> scopeCheckExprN e
C.CoSet e -> A.Sort . A.CoSet <$> scopeCheckExprN e
C.Size -> return $ A.Sort (A.SortC A.Size)
C.Succ e1 -> A.Succ <$> scopeCheckExprN e1
C.Zero -> return A.Zero
C.Infty -> return A.Infty
C.Plus e1 e2 -> do
e1 <- scopeCheckExprN e1
e2 <- scopeCheckExprN e2
return $ A.Plus [e1, e2]
C.Pair e1 e2 -> A.Pair <$> scopeCheckExprN e1 <*> scopeCheckExprN e2
C.Sing e1 et -> A.Sing <$> scopeCheckExprN e1 <*> scopeCheckExprN et
C.App C.Max el -> do
el' <- mapM scopeCheckExprN el
when (length el' < 2) $ throwErrorMsg "max expects at least 2 arguments"
return $ A.Max el'
C.App e1 el -> foldl A.App <$> scopeCheckExprN e1 <*> mapM scopeCheckExprN el
C.Case e mt cl -> do
e' <- scopeCheckExprN e
mt' <- mapM scopeCheckExprN mt
cl' <- mapM (scopeCheckClause Nothing) cl
return $ A.Case e' mt' cl'
-- measure & bound
-- measures can only appear in fun sigs!
C.Quant pisig [C.TMeasure mu] e1 -> do
throwErrorMsg $ "measure not allowed in expression " ++ show e
-- measure bound mu < mu'
C.Quant A.Pi [C.TBound beta] e1 -> do
unlessM (asks constraintAllowed) $ errorConstraintNotAllowed beta
beta' <- scopeCheckBound beta
e1' <- scopeCheckExpr' e1
return $ A.pi (A.TBound beta') e1'
C.Quant A.Sigma [C.TBound beta] e1 -> throwErrorMsg $
"measure bound not allowed in expression " ++ show e
C.Quant pisig tel e -> do
tel <- generalizeTel tel
pol <- asks defaultPolarity
(A.Telescope tel, e) <- setDefaultPolarity A.Rec $ setConstraintAllowed False $ scopeCheckTele tel $
setDefaultPolarity pol $ scopeCheckExpr' e
return $ quant pisig tel e where
-- quant A.Sigma [tb] = A.Quant A.Sigma tb
quant A.Sigma tel e = foldr (A.Quant A.Sigma) e tel
quant A.Pi tel e = A.teleToType (A.Telescope tel) e
C.Lam n e1 -> do
(n, e1') <- addBind e n $ scopeCheckExpr e1
return $ A.Lam A.defaultDec n e1' -- dec. in Lam is ignored in t.c.
C.LLet letdef e2 -> do
let dec = C.letDefDec letdef
(tel, mt, e1) <- scopeCheckLetDef letdef
(x, e2) <- addBind e (C.letDefName letdef) $ scopeCheckExpr e2
return $ A.LLet (A.TBind x $ A.Domain mt A.defaultKind dec) tel e1 e2
C.Record rs -> do
let fields = map fst rs
if (hasDuplicate fields) then (errorDuplicateField e) else do
rs <- mapM scopeCheckRecordLine rs
return $ A.Record A.AnonRec rs
C.Proj n -> A.Proj Post <$> scopeCheckProj n
C.Ident n@C.Qual{} -> scopeCheckGlobalVar n
C.Ident n@C.QName{} -> do
res <- lookupLocal (C.name n)
case res of
Just x -> return $ A.Var x
Nothing -> scopeCheckGlobalVar n
_ -> throwErrorMsg $ "NYI: scopeCheckExpr " ++ show e
scopeCheckGlobalVar :: C.QName -> ScopeCheck A.Expr
scopeCheckGlobalVar n = do
res <- lookupGlobal n
case res of
Just (DefI k x) -> case k of
(ConK co) -> return $ A.con co x
LetK -> return $ A.letdef (A.unqual x)
-- references to recursive functions are coded differently
-- outside the mutual block
FunK True -> return $ A.fun x -- A.letdef x -- A.mkExtRef x
FunK False -> return $ A.fun x
DataK -> return $ A.dat x
ProjK -> return $ A.Proj A.Pre (A.unqual x) -- errorProjectionUsedAsExpression n
Nothing -> errorIdentifierUndefined n
scopeCheckLocalVar :: C.Name -> ScopeCheck A.Name
scopeCheckLocalVar n = maybe (errorIdentifierUndefined n) return =<< do
lookupLocal n
scopeCheckRecordLine :: ([C.Name], C.Expr) -> ScopeCheck (A.Name, A.Expr)
scopeCheckRecordLine (n : ns, e) = do
x <- scopeCheckProj n
(x,) <$> scopeCheckExprN (foldr C.Lam e ns)
scopeCheckProj :: C.Name -> ScopeCheck A.Name
scopeCheckProj n = do
sig <- getSig
case lookupSigU n sig of
Just (DefI ProjK x) -> return $ A.unqual x
_ -> errorNotAField n
-- | @isProjIdent n = n@ if defined and the name of a projection.
isProjIdent :: C.QName -> ScopeCheck (Maybe A.Name)
isProjIdent n = do
sig <- getSig
return $
case lookupSig n sig of
Just (DefI ProjK x) -> Just $ A.unqual x
_ -> Nothing
isProjection :: C.Expr -> ScopeCheck (Maybe A.Name)
isProjection (C.Ident n) = isProjIdent n
isProjection _ = return Nothing
scopeCheckMeasure :: A.Measure C.Expr -> ScopeCheck (A.Measure A.Expr)
scopeCheckMeasure (A.Measure es) = do
es' <- mapM scopeCheckExprN es
return $ A.Measure es'
scopeCheckBound :: A.Bound C.Expr -> ScopeCheck (A.Bound A.Expr)
scopeCheckBound (A.Bound ltle e1 e2) = do
e1' <- scopeCheckMeasure e1
e2' <- scopeCheckMeasure e2
return $ A.Bound ltle e1' e2'
checkPatternLength :: [C.Clause] -> Maybe Int
checkPatternLength [] = Just 0 -- arity 0
checkPatternLength (C.Clause _ pl _:cl) = cpl (length pl) cl
where
cpl k [] = Just k
cpl k (C.Clause _ pl _ : cl) = if (length pl == k) then (cpl k cl) else Nothing
scopeCheckClause :: Maybe C.Name -> C.Clause -> ScopeCheck A.Clause
scopeCheckClause mname' (C.Clause mname pl mrhs) = do
when (mname /= mname') $ errorClauseIdentifier mname mname'
(pl, delta) <- runStateT (mapM scopeCheckPattern pl) emptyCtx
local (addContext delta) $ do
pl <- mapM scopeCheckDotPattern pl
case mrhs of
Nothing -> return $ A.clause pl Nothing
Just rhs -> A.clause pl . Just <$> scopeCheckExprN rhs
type PatCtx = Context
type SPS = StateT PatCtx ScopeCheck
scopeCheckPatVar :: C.QName -> SPS (A.Pat C.Expr)
scopeCheckPatVar n = do
sig <- lift $ getSig
case lookupSig n sig of
Just (DefI (ConK co) n) -> return $ A.ConP (A.PatternInfo co False False) n []
-- a nullary constructor
Just _ -> errorPatternNotConstructor n
Nothing -> A.VarP <$> addUnique (C.unqual n)
scopeCheckPattern :: C.Pattern -> SPS (A.Pat C.Expr)
scopeCheckPattern p =
case p of
-- case n
C.IdentP n -> scopeCheckPatVar n
C.ConP False n [] -> scopeCheckPatVar n
-- case (i > j):
C.SizeP m n -> do
-- m <- lift $ scopeCheckLocalVar m
A.SizeP m <$> addUnique n
-- case $p
C.SuccP p2 -> A.SuccP <$> scopeCheckPattern p2
-- case (p1,p2)
C.PairP p1 p2 -> A.PairP <$> scopeCheckPattern p1 <*> scopeCheckPattern p2
-- case .n
C.ConP True n [] -> do
-- try projection
ifJustM (lift $ isProjIdent n) (return . A.ProjP) $ do
-- try constructor
sig <- lift $ getSig
case lookupSig n sig of
Just (DefI (ConK co) n) ->
return $ A.ConP (A.PatternInfo co False True) n []
-- fallback: dot pattern
_ -> return $ A.DotP (C.Ident n)
-- case [.]c ps
C.ConP dotted n pl -> do
sig <- lift $ getSig
case lookupSig n sig of
Just (DefI (ConK co) x) ->
A.ConP (A.PatternInfo co False dotted) x <$> mapM scopeCheckPattern pl
_ -> errorPatternNotConstructor n
-- case .e
C.DotP e -> do
isProj <- lift $ isProjection e
case isProj of
Just n -> return $ A.ProjP n
Nothing -> return $ A.DotP e -- dot patterns checked later
-- case ()
C.AbsurdP -> return $ A.AbsurdP
-- | Add pattern variable to pattern context, must not be present yet.
addUnique :: C.Name -> SPS A.Name
addUnique = addPatVar True
addNonUnique :: C.Name -> SPS A.Name
addNonUnique = addPatVar False
addPatVar :: Bool -> C.Name -> SPS A.Name
addPatVar linear n = do
delta <- get
case retrieve n delta of
Just x -> if linear then errorPatternNotLinear n else return x
Nothing -> do
let (x, delta') = newLocal n delta
put delta'
return x
scopeCheckDotPattern :: A.Pat C.Expr -> ScopeCheck A.Pattern
scopeCheckDotPattern p =
case p of
A.DotP e -> A.DotP <$> scopeCheckExprN e
A.PairP p1 p2 -> A.PairP <$> scopeCheckDotPattern p1 <*> scopeCheckDotPattern p2
A.SuccP p -> A.SuccP <$> scopeCheckDotPattern p
A.ConP co n pl -> A.ConP co n <$> mapM scopeCheckDotPattern pl
-- A.SizeP m n -> flip A.SizeP n <$> scopeCheckLocalVar m -- return $ A.SizeP m n
A.SizeP e n -> flip A.SizeP n <$> scopeCheckExprN e
A.VarP n -> return $ A.VarP n -- even though p = A.VarP n, it has wrong type!!
A.ProjP n -> return $ A.ProjP n
A.AbsurdP -> return $ A.AbsurdP
-- impossible cases: ErasedP, UnusableP
-- * Scope checking parameters
parameterVariables :: [C.Expr] -> ScopeCheck Context
parameterVariables es = do
execStateT (mapM_ scopeCheckParameter es) emptyCtx
-- | Extract variables bound by data parameters.
-- We consider a more liberal set of patterns, everything
-- that is injective and does not bind variables.
scopeCheckParameter :: C.Expr -> SPS ()
scopeCheckParameter e =
case e of
C.Set e' -> scopeCheckParameter e'
C.CoSet e' -> scopeCheckParameter e'
C.Size -> return ()
C.Succ e' -> scopeCheckParameter e'
C.Zero -> return ()
C.Infty -> return ()
C.Pair e1 e2 -> scopeCheckParameter e1 >> scopeCheckParameter e2
C.Record fs -> mapM_ (scpField e) fs
C.Ident n -> scpApp e n []
C.App (C.Ident n) es -> scpApp e n es
C.App C.App{} es -> throwErrorMsg $ "scopeCheckParameter " ++ show e ++ ": internal invariant violated"
_ -> errorInvalidParameter e
where
-- we can only treat a record expression as pattern
-- if it does not bind any variables
scpField :: C.Expr -> ([C.Name], C.Expr) -> SPS ()
scpField e ([f], e') = scopeCheckParameter e'
scpField e _ = errorInvalidParameter e
scpApp :: C.Expr -> C.QName -> [C.Expr] -> SPS ()
scpApp e n es = do
sig <- lift $ getSig
case lookupSig n sig of
Just (DefI ConK{} n) -> mapM_ scopeCheckParameter es
Just (DefI DataK n) -> mapM_ scopeCheckParameter es
Just _ -> errorInvalidParameter e
Nothing -> void $ addNonUnique (C.unqual n) -- allow non-linearity
-- * Scope checking errors
errorAlreadyInSignature s n = throwErrorMsg $ show s ++ ": Identifier " ++ show n ++ " already in signature"
errorAlreadyInContext s n = throwErrorMsg $ show s ++ ": Identifier " ++ show n ++ " already in context"
-- errorPatternNotVariable n = throwErrorMsg $ "pattern " ++ n ++ ": Identifier expected"
errorPatternNotConstructor n = throwErrorMsg $ "pattern " ++ show n ++ " is not a constructor"
errorNotAField n = throwErrorMsg $ "record field " ++ show n ++ " unknown"
-- errorUnknownProjection n = throwErrorMsg $ "projection " ++ n ++ " unknown"
errorDuplicateField r = throwErrorMsg $ show r ++ " assigns a field twice"
errorProjectionUsedAsExpression n = throwErrorMsg $ "projection " ++ show n ++ " used as expression"
errorIdentifierUndefined n = throwErrorMsg $ "Identifier " ++ show n ++ " undefined"
errorPatternNotLinear n = throwErrorMsg $ "pattern not linear: " ++ show n
errorClauseIdentifier (Just n) (Just n') = throwErrorMsg $ "Expected identifier " ++ show n' ++ " as clause head, found " ++ show n
errorOnlyOneMeasure = throwErrorMsg "only one measure allowed in a function type"
errorConstraintNotAllowed beta = throwErrorMsg $
show beta ++ ": constraints must follow a quantifier"
errorTargetMustBeAppliedName n t = throwErrorMsg $
"constructor " ++ show n ++ ": target must be data/record type applied to parameters and indices; however, I found " ++ show t
errorWrongTarget c d d' = throwErrorMsg $
"constructor " ++ show c ++ " should target data/record type " ++ show d ++ "; however, I found " ++ show d'
errorNotEnoughParameters c t = throwErrorMsg $
"constructor " ++ show c ++ ": target " ++ show t ++ " is missing parameters"
errorInvalidParameter e = throwErrorMsg $
"expression " ++ show e ++ " is not valid in a parameter"