rzk-0.10.0: src/Rzk/TypeCheck/BinderTypes.hs
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | The elaborated types of a declaration's local binders, for LSP hover.
--
-- Every node of a typed term carries its type, so even a bare λ's binder is typed
-- — by the domain of the λ's own Π-type.
--
-- The old version threaded a @BinderNames@ environment down through the term, to
-- say what each variable it met was called. There is no need: entering a binder
-- puts it in the context, and the context already knows what everything in it is
-- called (see "Rzk.TypeCheck.Display").
module Rzk.TypeCheck.BinderTypes where
import Control.Monad.Except (catchError)
import Control.Monad.Reader (asks)
import Data.Bifoldable (bifoldr)
import Data.Maybe (fromMaybe)
import Control.Monad.Foil (Distinct)
import Control.Monad.Free.Foil (AST (Node, Var))
import Control.Monad.Free.Foil.Annotated (AnnSig (..))
import Language.Rzk.Foil.Syntax
import Language.Rzk.Foil.Names (Binder (..), TModality (..),
TypeInfo (..), VarIdent)
import Rzk.TypeCheck.Context
import Rzk.TypeCheck.Decl
import Rzk.TypeCheck.Display
import Rzk.TypeCheck.Eval
import Rzk.TypeCheck.Monad
-- | A binder's displayed type: a plain type, or a cube together with a tope, for a
-- shaped binder like @(t : I | φ t)@.
data BinderTypeView
= TypeView Rendered
| ShapeView Rendered Rendered
deriving (Eq, Show)
-- | Render a term with the names of the context it is in.
renderHere :: TermT n -> TypeCheck n Rendered
renderHere t = do
naming <- asks namingOfContext
pure (renderTerm naming (untyped t))
-- | The memoised weak head normal form of a typed term, if present.
memoWHNF :: TermT n -> TermT n
memoWHNF t = fromMaybe t (typeInfoOf t >>= infoWHNF)
-- | The variables a binder introduces, with their rendered types.
--
-- A pair binder splits its type along Σ-types and cube products; when the shape is
-- not syntactic (the type is a defined name applied to arguments, as in
-- @((η , (ϵ , (α , β))) : has-quasi-diagrammatic-adj A B f u)@), the type is put in
-- weak head normal form first, which needs the top-level definitions in scope. The
-- dependent part is rendered under the earlier component's display name, giving
-- @q : B p@.
binderTypeEntries
:: Distinct n => Binder -> TermT n -> TypeCheck n [(VarIdent, BinderTypeView)]
binderTypeEntries binder ty = case binder of
BinderUnit -> pure []
BinderVar Nothing -> pure []
BinderVar (Just x) -> do
rendered <- renderHere ty
pure [(x, TypeView rendered)]
BinderPair l r -> splitViewM ty >>= \case
Just (TypeSigmaT _ _ md a bscope) -> do
ls <- binderTypeEntries l a
rs <- inScope l md a bscope $ \bBody -> binderTypeEntries r bBody
pure (ls ++ rs)
Just (CubeProductT _ a b) ->
(++) <$> binderTypeEntries l a <*> binderTypeEntries r b
_ -> pure [] -- unrecognised shape; the surface annotation is the fallback
-- | View a type as a Σ-type or a cube product: syntactically, or through the
-- memoised WHNF if possible, computing the WHNF otherwise. Never throws.
splitViewM :: Distinct n => TermT n -> TypeCheck n (Maybe (TermT n))
splitViewM ty = case splitView (memoWHNF ty) of
Just t -> pure (Just t)
Nothing -> (splitView <$> whnfT ty) `catchError` \_ -> pure Nothing
where
splitView t = case stripTypeRestrictions t of
t'@TypeSigmaT{} -> Just t'
t'@CubeProductT{} -> Just t'
_ -> Nothing
-- | The elaborated types of the local binders of a typed term, keyed by the
-- binder's original identifier (whose position points at its defining occurrence).
binderTypesOfTerm
:: Distinct n => TermT n -> TypeCheck n [(VarIdent, BinderTypeView)]
binderTypesOfTerm = go
where
go :: Distinct n => TermT n -> TypeCheck n [(VarIdent, BinderTypeView)]
go t = case t of
Var _ -> pure []
LambdaT info binder mparam body -> do
(paramType, paramTope) <- case mparam of
Just (LambdaParam _ ty mtope) -> pure (Just ty, mtope)
-- A bare λ: the domain (and shape) of its own Π-type.
Nothing -> case funView (memoWHNF (infoType info)) of
Just (p, mtope) -> pure (Just p, mtope)
Nothing ->
(maybe (Nothing, Nothing) (\(p, mtope) -> (Just p, mtope)) . funView
<$> whnfT (infoType info))
`catchError` \_ -> pure (Nothing, Nothing)
entries <- shapedBinderEntries binder paramType paramTope
let md = maybe Id (\(LambdaParam m _ _) -> m) mparam
annEntries <- case mparam of
Nothing -> pure []
Just (LambdaParam _ ty mtope) -> do
tyEntries <- go ty
topeEntries <- case mtope of
Nothing -> pure []
Just tope -> inScope binder md ty tope go
pure (tyEntries ++ topeEntries)
bodyEntries <-
inScope binder md (fromMaybe universeT paramType) body go
pure (entries ++ annEntries ++ bodyEntries)
TypeFunT _ binder md param mtope ret -> do
entries <- shapedBinderEntries binder (Just param) mtope
paramEntries <- go param
topeEntries <- case mtope of
Nothing -> pure []
Just tope -> inScope binder md param tope go
retEntries <- inScope binder md param ret go
pure (entries ++ paramEntries ++ topeEntries ++ retEntries)
TypeSigmaT _ binder md a bscope -> do
entries <- binderTypeEntries binder a
aEntries <- go a
bEntries <- inScope binder md a bscope go
pure (entries ++ aEntries ++ bEntries)
LetT _ binder manno value body -> do
let valueType = case typeInfoOf value of
Just valueInfo -> Just (infoType valueInfo)
Nothing -> manno
entries <- maybe (pure []) (binderTypeEntries binder) valueType
annEntries <- maybe (pure []) go manno
valueEntries <- go value
bodyEntries <- inScopeWith binder Id (fromMaybe universeT valueType) (Just value) body go
pure (entries ++ annEntries ++ valueEntries ++ bodyEntries)
LetModT _ binder _nu mu manno value body -> do
unwrapped <- case typeInfoOf value of
Nothing -> pure Nothing
Just valueInfo -> do
let vt = infoType valueInfo
case modalView (memoWHNF vt) of
Just a -> pure (Just a)
Nothing -> (modalView <$> whnfT vt) `catchError` \_ -> pure Nothing
entries <- maybe (pure []) (binderTypeEntries binder) unwrapped
annEntries <- maybe (pure []) go manno
valueEntries <- go value
bodyEntries <- inScope binder mu (fromMaybe universeT unwrapped) body go
pure (entries ++ annEntries ++ valueEntries ++ bodyEntries)
Node (AnnSig _ f) ->
concat <$> mapM go (bifoldr (\_ acc -> acc) (:) [] f)
funView t = case stripTypeRestrictions t of
TypeFunT _ _ _ param mtope _ -> Just (param, mtope)
_ -> Nothing
modalView t = case stripTypeRestrictions t of
TypeModalT _ _ a -> Just a
_ -> Nothing
-- A shaped plain binder shows its cube together with the tope, as it is
-- written: (t : I | φ t). A shaped pair binder splits along the cube, the tope
-- constraining the components jointly (as in the surface tier).
shapedBinderEntries
:: Distinct n
=> Binder -> Maybe (TermT n) -> Maybe (ScopedTermT n)
-> TypeCheck n [(VarIdent, BinderTypeView)]
shapedBinderEntries binder mty mtope = case (binder, mty, mtope) of
(BinderVar (Just x), Just cube, Just tope) -> do
cubeR <- renderHere cube
topeR <- inScope binder Id cube tope renderHere
pure [(x, ShapeView cubeR topeR)]
(_, Just ty, _) -> binderTypeEntries binder ty
_ -> pure []
-- | All the local binder types of a declaration: Π and Σ binders from its type,
-- λ and let binders from its value.
declBinderTypes
:: Distinct n => Decl n -> TypeCheck n [(VarIdent, BinderTypeView)]
declBinderTypes decl =
(++) <$> binderTypesOfTerm (declType decl)
<*> maybe (pure []) binderTypesOfTerm (declValue decl)
-- | The elaborated types of the local binders of one file's declarations, with the
-- rest of the run's declarations in scope so that 'whnfT' can unfold definitions
-- when splitting pair binders.
--
-- Pure at the interface: it runs the checker silently and returns no entries where
-- it fails.
binderTypesOfFile :: Checked -> FilePath -> [(VarIdent, BinderTypeView)]
binderTypesOfFile (Checked ctx decls _errs) path =
case runTypeCheckIn ctx action of
Left _ -> []
Right entries -> entries
where
fileDecls = concat [ ds | (p, ds) <- decls, p == path ]
action = localVerbosity Silent $
concat <$> mapM declBinderTypes fileDecls