alms-0.6.0: src/Statics.hs
-- | The external interface to the type checker
module Statics (
-- * Type checking state
StaticsState, staticsState0, staticsState0',
-- ** Initial state
addSignature, addPrimType,
-- * Type checking operations
typeCheckDecls, typeCheckProg,
-- * Renaming and typing info
-- ** Renaming
Statics.getRenamingInfo, RenamingInfo(..),
-- ** Constraint solving
getConstraint,
-- ** Type checking
getVarInfo, getTypeInfo, getConInfo,
-- ** Printing nice type names
addTyNameContext, makeTyNames, makeT2SContext, staticsEnterScope,
) where
import Util
import Util.MonadRef
import AST.Ident (Raw, Renamed, Id(..))
import qualified AST
import Syntax.PprClass (Doc, setTyNames)
import Type
import Statics.Env
import Statics.Error
import Statics.Constraint
import Statics.Rename
import Statics.Decl
import Prelude ()
import qualified Data.List as List
---
--- TYPE CHECKING STATE
---
-- | The state of the renamer and type checker, parameterized by
-- a reference type.
data StaticsState r
= StaticsState {
ssRen ∷ !RenameState,
ssCon ∷ !(ConstraintState (TV r) r),
ssEnv ∷ !(Γ (TV r))
}
deriving Show
-- | The initial state of the type checker, parameterized by the
-- initial renaming state.
staticsState0 ∷ RenameState → StaticsState r
staticsState0 rs
= StaticsState {
ssRen = rs,
ssCon = constraintState0,
ssEnv = mempty
}
-- | The initial state of the type checker with no initial renaming
-- state.
staticsState0' ∷ StaticsState r
staticsState0' = staticsState0 renameState0
---
--- TYPE CHECKING OPERATIONS
---
-- | Type check a sequence of declarations.
typeCheckDecls ∷ (MonadAlmsError m, MonadRef r m) ⇒
StaticsState r →
[AST.Decl Raw] →
m ([AST.Decl Renamed],
[AST.SigItem R],
StaticsState r)
typeCheckDecls ss ds = do
(ds', rs) ← bailoutIfError $
runRenamingM True bogus (ssRen ss) (renameDecls ds)
((ds'', γ', sig), cs) ← bailoutIfError . runConstraintT (ssCon ss) $ do
tcDecls [] (ssEnv ss) ds' <* ensureSatisfiability
let ss' = ss {
ssRen = rs,
ssCon = cs,
ssEnv = γ'
}
return (ds'', sigItemToStx (makeTyNames ss') <$> sig, ss')
-- | Type check a program.
typeCheckProg ∷ (MonadAlmsError m, MonadRef r m) ⇒
StaticsState r →
AST.Prog Raw →
m (AST.Prog Renamed, Maybe (AST.Type Renamed))
typeCheckProg ss p = do
(p', _) ← bailoutIfError $
runRenamingM True bogus (ssRen ss) (renameProg p)
((p'', mσ), _) ← bailoutIfError . runConstraintT (ssCon ss) $ do
tcProg (ssEnv ss) p' <* ensureSatisfiability
return (p'', typeToStx' <$> mσ)
---
--- ADDING INITIAL BINDINGS
---
-- | Bind the contents of a signature in the environment. This is used
-- for setting up some primitive types and values.
addSignature ∷ (MonadAlmsError m, MonadRef r m) ⇒
StaticsState r →
AST.SigExp Renamed →
m (StaticsState r)
addSignature ss sigexp = do
(sig, cs') ← runConstraintT (ssCon ss) (tcSigExp (ssEnv ss) sigexp)
return ss {
ssCon = cs',
ssEnv = ssEnv ss =+= sigToEnv sig
}
-- | Bind a primitive type constructor at top level.
addPrimType ∷ StaticsState r → TypId → TyCon → StaticsState r
addPrimType ss tid tc = ss { ssEnv = ssEnv ss =+= tid =:= tc }
---
--- INTERFACE FOR GETTING TYPE INFO
---
-- | Find out the renamed name of an identifier and where it was defined.
getRenamingInfo ∷ AST.Ident Raw → StaticsState r → [RenamingInfo]
getRenamingInfo = Statics.Rename.getRenamingInfo <$.> ssRen
-- | Find out the type of a variable
getVarInfo :: QVarId -> StaticsState r -> Maybe (AST.Type R)
getVarInfo vid ss = typeToStx (makeT2SContext ss) <$> ssEnv ss =..= vid
-- | Find out about a type
getTypeInfo :: QTypId -> StaticsState r -> Maybe TyCon
getTypeInfo tid ss = ssEnv ss =..= tid
-- Find out about a data constructor. If it's an exception constructor,
-- return 'Right' with its parameter, otherwise return the type construtor
-- of the result type
getConInfo :: QConId -> StaticsState r ->
Maybe (Either TyCon (Maybe (AST.Type R)))
getConInfo cid ss = typeToStx (makeT2SContext ss) <$$$> ssEnv ss =..= cid
-- | Get a printable representation of the current constraint-solving
-- state.
getConstraint ∷ StaticsState r → Doc
getConstraint = pprConstraintState . ssCon
-- Open the given module, if it exists.
staticsEnterScope :: ModId -> StaticsState r -> StaticsState r
staticsEnterScope mid ss =
case ssEnv ss =..= mid of
Just (_, e') -> ss { ssEnv = ssEnv ss =+= e' }
Nothing -> ss
---
--- CHOOSING BEST TYPE NAMES
---
-- | Given the type checker state, add it to the context of a document
-- for printing type names in that document.
addTyNameContext :: StaticsState r → Doc → Doc
addTyNameContext = setTyNames . makeTyNames
-- | Get the type name lookup gadget from the type checker state
makeTyNames :: StaticsState r → TyNames
makeTyNames ss
= TyNames {
tnLookup = getBestName ss,
tnEnter = \mid → makeTyNames (staticsEnterScope mid ss)
}
-- | Make the type-syntactifying context from the type checker state
makeT2SContext :: StaticsState r →
T2SContext CurrentImpArrPrintingRule tv
makeT2SContext ss = t2sContext0 { t2sTyNames = makeTyNames ss }
-- | The status of a type name in an environment
data NameStatus
-- | Bound to the expected type
= Match
-- | Not bound
| NoMatch
-- | Shadowed
| Interfere
deriving Eq
-- | In the given environment, what is the status of the given
-- type name?
getNameStatus :: StaticsState r → Int → QTypId → NameStatus
getNameStatus ss tag tid =
case [ tid'
| TyconAt _ tid' <- Statics.getRenamingInfo ql ss ] of
tid':_ ->
case getTypeInfo tid' ss of
Just tc | tcId tc == tag -> Match
| otherwise -> Interfere
_ -> NoMatch
_ -> NoMatch
where ql = J (map (AST.renId bogus) (jpath tid))
(AST.ident (AST.idName (jname tid)))
-- | Names known to the pretty-printer that should always be used
-- exactly like this so that things print nicely.
intrinsicNames ∷ [(Int, String)]
intrinsicNames = first tcId <$>
[ (tcVariant, AST.tnVariant),
(tcRecord, AST.tnRecord),
(tcRowEnd, AST.tnRowEnd),
(tcRowHole, AST.tnRowHole),
(tcRowMap, AST.tnRowMap),
(tcUn, AST.tnUn),
(tcAf, AST.tnAf) ]
-- | Find the best name to refer to a type constructor.
-- The goal here is to get the shortest unambiguous name.
-- 1. If the first parameter is True, we want an accurate name, so
-- skip to step 3.
-- 2. If the unqualified name is bound to either the same type
-- or to nothing, then use the unqualified name.
-- 3. Try qualifiying the name, starting with the last segment
-- and adding one at a time, and if any of these match, then
-- use that.
-- 4. Otherwise, uglify the name, because it's probably gone
-- out of scope.
getBestName :: StaticsState r -> Int -> QTypId -> QTypId
getBestName ss tag qtid
| Just str ← lookup tag intrinsicNames = qident str
| otherwise =
case tryQuals (jpath qtid) (jname qtid) of
Just qtid' → qtid'
Nothing
| AST.isTrivial (idTag (jname qtid))
, NoMatch ← getNameStatus ss tag qtid
→ qtid
| otherwise → uglify
where
tryQuals mids tid = msum
[ case getNameStatus ss tag (J mids' tid) of
Match -> Just (J mids' tid)
_ -> Nothing
| mids' <- reverse (List.tails mids) ]
uglify
| '?':_ ← show qtid = qtid
| otherwise = qtid { jpath = ident ('?':show tag) : jpath qtid }