quox/lib/Quox/Typing/Error.idr

403 lines
12 KiB
Idris
Raw Normal View History

2023-03-13 16:41:57 -04:00
module Quox.Typing.Error
2023-05-01 21:06:25 -04:00
import Quox.Loc
2023-03-13 16:41:57 -04:00
import Quox.Syntax
import Quox.Typing.Context
import Quox.Typing.EqMode
import Quox.Pretty
import Data.List
2023-03-31 13:23:30 -04:00
import Control.Eff
2023-03-13 16:41:57 -04:00
2023-04-15 09:13:01 -04:00
public export
record NameContexts d n where
constructor MkNameContexts
2023-05-01 21:06:25 -04:00
dnames : BContext d
tnames : BContext n
2023-04-15 09:13:01 -04:00
namespace NameContexts
export
empty : NameContexts 0 0
empty = MkNameContexts [<] [<]
export
2023-05-01 21:06:25 -04:00
extendDimN : BContext s -> NameContexts d n -> NameContexts (s + d) n
2023-04-15 09:13:01 -04:00
extendDimN xs = {dnames $= (++ toSnocVect' xs)}
export
2023-05-01 21:06:25 -04:00
extendDim : BindName -> NameContexts d n -> NameContexts (S d) n
2023-04-15 09:13:01 -04:00
extendDim i = extendDimN [< i]
namespace TyContext
public export
(.names) : TyContext d n -> NameContexts d n
(MkTyContext {dnames, tnames, _}).names =
MkNameContexts {dnames, tnames}
namespace EqContext
public export
(.names) : (e : EqContext n) -> NameContexts e.dimLen n
(MkEqContext {dnames, tnames, _}).names =
MkNameContexts {dnames, tnames}
public export
(.names0) : EqContext n -> NameContexts 0 n
(MkEqContext {tnames, _}).names0 =
MkNameContexts {dnames = [<], tnames}
namespace WhnfContext
public export
(.names) : WhnfContext d n -> NameContexts d n
(MkWhnfContext {dnames, tnames, _}).names =
MkNameContexts {dnames, tnames}
2023-03-13 16:41:57 -04:00
public export
2023-04-01 13:16:43 -04:00
data Error
2023-05-01 21:06:25 -04:00
= ExpectedTYPE Loc (NameContexts d n) (Term d n)
| ExpectedPi Loc (NameContexts d n) (Term d n)
| ExpectedSig Loc (NameContexts d n) (Term d n)
| ExpectedEnum Loc (NameContexts d n) (Term d n)
| ExpectedEq Loc (NameContexts d n) (Term d n)
| ExpectedNat Loc (NameContexts d n) (Term d n)
| ExpectedBOX Loc (NameContexts d n) (Term d n)
| BadUniverse Loc Universe Universe
| TagNotIn Loc TagVal (SortedSet TagVal)
| BadCaseEnum Loc (SortedSet TagVal) (SortedSet TagVal)
| BadQtys Loc String (TyContext d n) (List (QOutput n, Term d n))
2023-03-13 16:41:57 -04:00
-- first term arg of ClashT is the type
2023-05-01 21:06:25 -04:00
| ClashT Loc (EqContext n) EqMode (Term 0 n) (Term 0 n) (Term 0 n)
| ClashTy Loc (EqContext n) EqMode (Term 0 n) (Term 0 n)
| ClashE Loc (EqContext n) EqMode (Elim 0 n) (Elim 0 n)
| ClashU Loc EqMode Universe Universe
| ClashQ Loc Qty Qty
| NotInScope Loc Name
2023-03-13 16:41:57 -04:00
2023-05-01 21:06:25 -04:00
| NotType Loc (TyContext d n) (Term d n)
| WrongType Loc (EqContext n) (Term 0 n) (Term 0 n)
2023-03-13 16:41:57 -04:00
2023-05-01 21:06:25 -04:00
| MissingEnumArm Loc TagVal (List TagVal)
2023-04-15 09:13:01 -04:00
2023-03-13 16:41:57 -04:00
-- extra context
| WhileChecking
2023-04-01 13:16:43 -04:00
(TyContext d n) Qty
(Term d n) -- term
(Term d n) -- type
Error
2023-03-13 16:41:57 -04:00
| WhileCheckingTy
2023-04-01 13:16:43 -04:00
(TyContext d n)
(Term d n)
2023-03-13 16:41:57 -04:00
(Maybe Universe)
2023-04-01 13:16:43 -04:00
Error
2023-03-13 16:41:57 -04:00
| WhileInferring
2023-04-01 13:16:43 -04:00
(TyContext d n) Qty
(Elim d n)
Error
2023-03-13 16:41:57 -04:00
| WhileComparingT
2023-04-01 13:16:43 -04:00
(EqContext n) EqMode
(Term 0 n) -- type
(Term 0 n) (Term 0 n) -- lhs/rhs
Error
2023-03-13 16:41:57 -04:00
| WhileComparingE
2023-04-01 13:16:43 -04:00
(EqContext n) EqMode
(Elim 0 n) (Elim 0 n)
Error
2023-03-13 16:41:57 -04:00
%name Error err
public export
2023-04-01 13:16:43 -04:00
ErrorEff : Type -> Type
ErrorEff = Except Error
2023-03-13 16:41:57 -04:00
export
Located Error where
(ExpectedTYPE loc _ _).loc = loc
(ExpectedPi loc _ _).loc = loc
(ExpectedSig loc _ _).loc = loc
(ExpectedEnum loc _ _).loc = loc
(ExpectedEq loc _ _).loc = loc
(ExpectedNat loc _ _).loc = loc
(ExpectedBOX loc _ _).loc = loc
(BadUniverse loc _ _).loc = loc
(TagNotIn loc _ _).loc = loc
(BadCaseEnum loc _ _).loc = loc
(BadQtys loc _ _ _).loc = loc
(ClashT loc _ _ _ _ _).loc = loc
(ClashTy loc _ _ _ _).loc = loc
(ClashE loc _ _ _ _).loc = loc
(ClashU loc _ _ _).loc = loc
(ClashQ loc _ _).loc = loc
(NotInScope loc _).loc = loc
(NotType loc _ _).loc = loc
(WrongType loc _ _ _).loc = loc
(MissingEnumArm loc _ _).loc = loc
(WhileChecking _ _ _ _ err).loc = err.loc
(WhileCheckingTy _ _ _ err).loc = err.loc
(WhileInferring _ _ _ err).loc = err.loc
(WhileComparingT _ _ _ _ _ err).loc = err.loc
(WhileComparingE _ _ _ _ err).loc = err.loc
2023-03-13 16:41:57 -04:00
||| whether the error is surrounded in some context
||| (e.g. "while checking s : A, …")
public export
2023-04-01 13:16:43 -04:00
isErrorContext : Error -> Bool
2023-03-13 16:41:57 -04:00
isErrorContext (WhileChecking {}) = True
isErrorContext (WhileCheckingTy {}) = True
isErrorContext (WhileInferring {}) = True
isErrorContext (WhileComparingT {}) = True
isErrorContext (WhileComparingE {}) = True
isErrorContext _ = False
||| remove one layer of context
export
2023-04-01 13:16:43 -04:00
peelContext : (e : Error) -> (0 _ : So (isErrorContext e)) =>
(Error -> Error, Error)
2023-03-13 16:41:57 -04:00
peelContext (WhileChecking ctx x s t err) =
(WhileChecking ctx x s t, err)
peelContext (WhileCheckingTy ctx s k err) =
(WhileCheckingTy ctx s k, err)
peelContext (WhileInferring ctx x e err) =
(WhileInferring ctx x e, err)
peelContext (WhileComparingT ctx x s t r err) =
(WhileComparingT ctx x s t r, err)
peelContext (WhileComparingE ctx x e f err) =
(WhileComparingE ctx x e f, err)
||| separates out all the error context layers
||| (e.g. "while checking s : A, …")
export
2023-04-01 13:16:43 -04:00
explodeContext : Error -> (List (Error -> Error), Error)
2023-03-13 16:41:57 -04:00
explodeContext err =
case choose $ isErrorContext err of
Left y =>
let (f, inner) = peelContext err
(fs, root) = explodeContext $ assert_smaller err inner in
(f :: fs, root)
Right n => ([], err)
||| leaves the outermost context layer, and the innermost (up to) n, and removes
||| the rest if there are more than n+1 in total
export
2023-04-01 13:16:43 -04:00
trimContext : Nat -> Error -> Error
2023-03-13 16:41:57 -04:00
trimContext n err =
case explodeContext err of
([], err) => err
(f :: fs, err) => f $ foldr apply err $ takeEnd n fs
where
takeEnd : Nat -> List a -> List a
takeEnd n = reverse . take n . reverse
2023-03-31 13:23:30 -04:00
private
expect : Has (Except e) fs =>
(a -> a -> e) -> (a -> a -> Bool) -> a -> a -> Eff fs ()
expect err cmp x y = unless (x `cmp` y) $ throw $ err x y
2023-03-13 16:41:57 -04:00
2023-05-01 21:06:25 -04:00
parameters {auto _ : Has ErrorEff fs} (loc : Loc)
2023-03-13 16:41:57 -04:00
export %inline
2023-04-01 13:16:43 -04:00
expectEqualQ : Qty -> Qty -> Eff fs ()
2023-05-01 21:06:25 -04:00
expectEqualQ = expect (ClashQ loc) (==)
2023-03-13 16:41:57 -04:00
export %inline
2023-04-01 13:16:43 -04:00
expectCompatQ : Qty -> Qty -> Eff fs ()
2023-05-01 21:06:25 -04:00
expectCompatQ = expect (ClashQ loc) compat
2023-03-13 16:41:57 -04:00
export %inline
2023-03-31 13:23:30 -04:00
expectModeU : EqMode -> Universe -> Universe -> Eff fs ()
2023-05-01 21:06:25 -04:00
expectModeU mode = expect (ClashU loc mode) $ ucmp mode
2023-03-13 16:41:57 -04:00
2023-03-15 10:54:51 -04:00
private
prettyMode : EqMode -> Doc HL
prettyMode Equal = "equal to"
prettyMode Sub = "a subtype of"
prettyMode Super = "a supertype of"
private
prettyModeU : EqMode -> Doc HL
prettyModeU Equal = "equal to"
prettyModeU Sub = "less than or equal to"
prettyModeU Super = "greater than or equal to"
private
isTypeInUniverse : Maybe Universe -> Doc HL
isTypeInUniverse Nothing = "is a type"
isTypeInUniverse (Just k) = "is a type in universe" <++> prettyUniverse k
2023-04-01 13:16:43 -04:00
parameters (unicode : Bool)
2023-03-15 10:54:51 -04:00
private
2023-04-15 09:13:01 -04:00
termn : NameContexts d n -> Term d n -> Doc HL
termn ctx = hang 4 . prettyTerm unicode ctx.dnames ctx.tnames
2023-03-15 10:54:51 -04:00
private
2023-04-15 09:13:01 -04:00
dstermn : {s : Nat} -> NameContexts d n -> DScopeTermN s d n -> Doc HL
dstermn ctx (S i t) = termn (extendDimN i ctx) t.term
2023-03-15 10:54:51 -04:00
private
2023-05-01 21:06:25 -04:00
filterSameQtys : BContext n -> List (QOutput n, z) ->
Exists $ \n' => (BContext n', List (QOutput n', z))
2023-04-15 09:13:01 -04:00
filterSameQtys [<] qts = Evidence 0 ([<], qts)
filterSameQtys (ns :< n) qts =
let (qs, qts) = unzip $ map (\(qs :< q, t) => (q, qs, t)) qts
Evidence l (ns, qts) = filterSameQtys ns qts
2023-03-15 10:54:51 -04:00
in
2023-04-15 09:13:01 -04:00
if allSame qs
then Evidence l (ns, qts)
else Evidence (S l) (ns :< n, zipWith (\(qs, t), q => (qs :< q, t)) qts qs)
2023-03-15 10:54:51 -04:00
where
2023-04-01 13:16:43 -04:00
allSame : List Qty -> Bool
2023-03-15 10:54:51 -04:00
allSame [] = True
allSame (q :: qs) = all (== q) qs
2023-04-15 09:13:01 -04:00
private
printCaseQtys : TyContext d n ->
2023-05-01 21:06:25 -04:00
BContext n' -> List (QOutput n', Term d n) ->
2023-04-15 09:13:01 -04:00
List (Doc HL)
printCaseQtys ctx ns qts =
let Evidence l (ns, qts) = filterSameQtys ns qts in
map (line ns) qts
where
commaList : PrettyHL a => Context' a l -> Doc HL
commaList = hseparate comma . map (pretty0 unicode) . toList'
2023-05-01 21:06:25 -04:00
line : BContext l -> (QOutput l, Term d n) -> Doc HL
2023-04-15 09:13:01 -04:00
line ns (qs, t) =
"-" <++> asep ["the term", termn ctx.names t,
2023-05-01 21:06:25 -04:00
"uses variables", commaList $ (TV . name) <$> ns,
2023-04-15 09:13:01 -04:00
"with quantities", commaList qs]
2023-03-15 10:54:51 -04:00
export
prettyErrorNoLoc : (showContext : Bool) -> Error -> Doc HL
prettyErrorNoLoc showContext = \case
ExpectedTYPE _ ctx s =>
sep ["expected a type universe, but got", termn ctx s]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ExpectedPi loc ctx s =>
sep ["expected a function type, but got", termn ctx s]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ExpectedSig loc ctx s =>
sep ["expected a pair type, but got", termn ctx s]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ExpectedEnum loc ctx s =>
sep ["expected an enumeration type, but got", termn ctx s]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ExpectedEq loc ctx s =>
sep ["expected an equality type, but got", termn ctx s]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ExpectedNat loc ctx s {d, n} =>
sep ["expected the type",
2023-05-01 21:06:25 -04:00
pretty0 unicode $ Nat noLoc {d, n}, "but got", termn ctx s]
2023-03-31 13:11:35 -04:00
2023-05-01 21:06:25 -04:00
ExpectedBOX loc ctx s =>
sep ["expected a box type, but got", termn ctx s]
2023-03-26 08:40:54 -04:00
2023-05-01 21:06:25 -04:00
BadUniverse loc k l =>
sep ["the universe level", prettyUniverse k,
2023-03-15 10:54:51 -04:00
"is not strictly less than", prettyUniverse l]
2023-05-01 21:06:25 -04:00
TagNotIn loc tag set =>
sep [hsep ["tag", prettyTag tag, "is not contained in"],
2023-05-01 21:06:25 -04:00
termn empty (Enum set noLoc)]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
BadCaseEnum loc type arms =>
sep ["case expression has head of type",
2023-05-01 21:06:25 -04:00
termn empty (Enum type noLoc),
"but cases for", termn empty (Enum arms noLoc)]
2023-05-01 21:06:25 -04:00
BadQtys loc what ctx arms =>
2023-03-15 10:54:51 -04:00
hang 4 $ sep $
hsep ["inconsistent variable usage in", fromString what]
2023-05-01 21:06:25 -04:00
:: printCaseQtys ctx ctx.tnames arms
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ClashT loc ctx mode ty s t =>
2023-03-15 10:54:51 -04:00
inEContext ctx $
sep ["the term", termn ctx.names0 s,
2023-04-15 09:13:01 -04:00
hsep ["is not", prettyMode mode], termn ctx.names0 t,
"at type", termn ctx.names0 ty]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ClashTy loc ctx mode a b =>
2023-03-15 10:54:51 -04:00
inEContext ctx $
sep ["the type", termn ctx.names0 a,
2023-04-15 09:13:01 -04:00
hsep ["is not", prettyMode mode], termn ctx.names0 b]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ClashE loc ctx mode e f =>
2023-03-15 10:54:51 -04:00
inEContext ctx $
sep ["the term", termn ctx.names0 $ E e,
2023-04-15 09:13:01 -04:00
hsep ["is not", prettyMode mode], termn ctx.names0 $ E f]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
ClashU loc mode k l =>
sep ["the universe level", prettyUniverse k,
2023-03-15 10:54:51 -04:00
hsep ["is not", prettyMode mode], prettyUniverse l]
2023-05-01 21:06:25 -04:00
ClashQ loc pi rh =>
sep ["the quantity", pretty0 unicode pi,
2023-03-15 10:54:51 -04:00
"is not equal to", pretty0 unicode rh]
2023-05-01 21:06:25 -04:00
NotInScope loc x =>
hsep [hl' Free $ pretty0 unicode x, "is not in scope"]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
NotType loc ctx s =>
2023-03-15 10:54:51 -04:00
inTContext ctx $
sep ["the term", termn ctx.names s, "is not a type"]
2023-03-15 10:54:51 -04:00
2023-05-01 21:06:25 -04:00
WrongType loc ctx ty s =>
2023-03-15 10:54:51 -04:00
inEContext ctx $
sep ["the term", termn ctx.names0 s,
2023-04-15 09:13:01 -04:00
"cannot have type", termn ctx.names0 ty]
2023-05-01 21:06:25 -04:00
MissingEnumArm loc tag tags =>
sep [hsep ["the tag", hl Tag $ pretty tag, "is not contained in"],
2023-05-01 21:06:25 -04:00
termn empty $ Enum (fromList tags) noLoc]
2023-03-15 10:54:51 -04:00
WhileChecking ctx pi s a err =>
vsep [inTContext ctx $
2023-04-15 09:13:01 -04:00
sep ["while checking", termn ctx.names s,
"has type", termn ctx.names a,
2023-03-15 10:54:51 -04:00
hsep ["with quantity", pretty0 unicode pi]],
prettyErrorNoLoc showContext err]
2023-03-15 10:54:51 -04:00
WhileCheckingTy ctx a k err =>
vsep [inTContext ctx $
2023-04-15 09:13:01 -04:00
sep ["while checking", termn ctx.names a,
2023-03-15 10:54:51 -04:00
isTypeInUniverse k],
prettyErrorNoLoc showContext err]
2023-03-15 10:54:51 -04:00
WhileInferring ctx pi e err =>
vsep [inTContext ctx $
2023-04-15 09:13:01 -04:00
sep ["while inferring the type of", termn ctx.names $ E e,
2023-03-15 10:54:51 -04:00
hsep ["with quantity", pretty0 unicode pi]],
prettyErrorNoLoc showContext err]
2023-03-15 10:54:51 -04:00
2023-03-26 10:15:30 -04:00
WhileComparingT ctx mode a s t err =>
2023-03-15 10:54:51 -04:00
vsep [inEContext ctx $
2023-04-15 09:13:01 -04:00
sep ["while checking that", termn ctx.names0 s,
hsep ["is", prettyMode mode], termn ctx.names0 t,
"at type", termn ctx.names0 a],
prettyErrorNoLoc showContext err]
2023-03-15 10:54:51 -04:00
WhileComparingE ctx mode e f err =>
vsep [inEContext ctx $
2023-04-15 09:13:01 -04:00
sep ["while checking that", termn ctx.names0 $ E e,
hsep ["is", prettyMode mode], termn ctx.names0 $ E f],
prettyErrorNoLoc showContext err]
2023-03-15 10:54:51 -04:00
where
2023-04-01 13:16:43 -04:00
inTContext : TyContext d n -> Doc HL -> Doc HL
2023-03-15 10:54:51 -04:00
inTContext ctx doc =
2023-03-26 10:10:39 -04:00
if showContext && not (null ctx) then
2023-03-15 10:54:51 -04:00
vsep [sep ["in context", prettyTyContext unicode ctx], doc]
else doc
2023-04-01 13:16:43 -04:00
inEContext : EqContext n -> Doc HL -> Doc HL
2023-03-15 10:54:51 -04:00
inEContext ctx doc =
2023-03-26 10:10:39 -04:00
if showContext && not (null ctx) then
2023-03-15 10:54:51 -04:00
vsep [sep ["in context", prettyEqContext unicode ctx], doc]
else doc
export
prettyError : (showContext : Bool) -> Error -> Doc HL
prettyError showContext err =
sep [prettyLoc err.loc, indent 4 $ prettyErrorNoLoc showContext err]