module DDC.Type.Check.Judge.Kind
(checkTypeM)
where
import DDC.Type.DataDef
import DDC.Type.Check.Context
import DDC.Type.Check.Error
import DDC.Type.Check.ErrorMessage ()
import DDC.Type.Check.CheckCon
import DDC.Type.Check.Config
import DDC.Type.Check.Base
import DDC.Type.Check.Judge.Eq
import DDC.Type.Universe
import Data.List
import Control.Monad
import DDC.Type.Pretty ()
import DDC.Type.Env (KindEnv)
import qualified DDC.Type.Sum as TS
import qualified DDC.Type.Env as Env
import qualified Data.Map as Map
checkTypeM
:: (Ord n, Show n, Pretty n)
=> Config n
-> KindEnv n
-> Context n
-> Universe
-> Type n
-> Mode n
-> CheckM n
( Type n
, Kind n
, Context n)
checkTypeM config env ctx0 uni tt@(TVar u) mode
| UniverseKind <- uni
, Just n <- takeNameOfBound u
, Just isHole <- configNameIsHole config
, isHole n
= case mode of
Recon
-> do throw $ ErrorUndefined u
Synth
-> do i <- newExists sComp
let t = typeOfExists i
let ctx' = pushExists i ctx0
return (t, sComp, ctx')
Check sExpected
-> do i <- newExists sExpected
let t = typeOfExists i
let ctx' = pushExists i ctx0
return (t, sExpected, ctx')
| UniverseSpec <- uni
, Just n <- takeNameOfBound u
, Just isHole <- configNameIsHole config
, isHole n
= case mode of
Recon
-> do throw $ ErrorUndefined u
Synth
-> do iK <- newExists sComp
let k = typeOfExists iK
let ctx1 = pushExists iK ctx0
iT <- newExists k
let t = typeOfExists iT
let ctx2 = pushExists iT ctx1
return (t, k, ctx2)
Check kExpected
-> do iT <- newExists kExpected
let t = typeOfExists iT
let ctx1 = pushExists iT ctx0
return (t, kExpected, ctx1)
| UniverseSpec <- uni
= let
getActual
| Just (k, _role) <- lookupKind u ctx0
= return k
| Just k <- Env.lookup u env
= return k
| UPrim _ k <- u
= return k
| otherwise
= throw $ ErrorUndefined u
in do
kActual <- getActual
let kActual' = applyContext ctx0 kActual
case mode of
Check kExpected
-> do
let kExpected' = applyContext ctx0 kExpected
ctx1 <- makeEq config ctx0 kActual' kExpected'
$ ErrorMismatch uni kActual' kExpected' tt
return (tt, kActual', ctx1)
_ -> return (tt, kActual', ctx0)
| otherwise
= throw $ ErrorUniverseMalfunction tt uni
checkTypeM config env ctx0 uni tt@(TCon tc) mode
= let
getActual
| TyConSort _ <- tc
, UniverseSort <- uni
= throw $ ErrorNakedSort tt
| TyConKind kc <- tc
, UniverseKind <- uni
= case takeSortOfKiCon kc of
Just s -> return (tt, s)
Nothing -> throw $ ErrorUnappliedKindFun
| TyConWitness tcw <- tc
, UniverseSpec <- uni
= return (tt, kindOfTwCon tcw)
| TyConSpec tcc <- tc
, UniverseSpec <- uni
= return (tt, kindOfTcCon tcc)
| TyConBound u k <- tc
= case u of
UName n
| Just def <- Map.lookup n
$ dataDefsTypes $ configDataDefs config
, UniverseSpec <- uni
-> let k' = kindOfDataType def
in return (TCon (TyConBound u k'), k')
| Just k' <- Env.lookupName n env
, UniverseSpec <- uni
-> return (TCon (TyConBound u k'), k')
| otherwise
-> throw $ ErrorUndefinedTypeCtor u
UPrim{} -> return (tt, k)
UIx{} -> throw $ ErrorUndefinedTypeCtor u
| TyConExists _ t <- tc
, uni == UniverseSpec || uni == UniverseKind
= return (tt, t)
| otherwise
= throw $ ErrorUniverseMalfunction tt uni
in do
(tt', kActual) <- getActual
let kActual' = applyContext ctx0 kActual
case mode of
Check kExpected
-> do
let kExpected' = applyContext ctx0 kExpected
ctx1 <- makeEq config ctx0 kActual' kExpected'
$ ErrorMismatch uni kActual' kExpected' tt
return (tt', kActual', ctx1)
_ -> return (tt', kActual', ctx0)
checkTypeM config kenv ctx0 uni@UniverseSpec
tt@(TForall b1 t2) mode
= case mode of
Recon
-> do
let t1 = typeOfBind b1
_ <- checkTypeM config kenv ctx0 UniverseKind t1 Recon
let (ctx1, pos1) = markContext ctx0
let ctx2 = pushKind b1 RoleAbstract ctx1
(t2', k2, ctx3) <- checkTypeM config kenv ctx2 UniverseSpec t2 Recon
let k2' = applyContext ctx3 k2
when ( not (isDataKind k2')
&& not (isWitnessKind k2'))
$ throw $ ErrorForallKindInvalid tt t2 k2'
let ctx_cut = popToPos pos1 ctx3
return (TForall b1 t2', k2', ctx_cut)
Synth
-> do
let k1 = typeOfBind b1
(k1', _s1, ctx1) <- checkTypeM config kenv ctx0 UniverseKind k1 Synth
let b1' = replaceTypeOfBind k1' b1
let (ctx2, pos1) = markContext ctx1
let ctx3 = pushKind b1' RoleAbstract ctx2
(t2', k2, ctx4) <- checkTypeM config kenv ctx3 UniverseSpec t2 Synth
let k2' = applyContext ctx4 k2
(k2'', ctx5)
<- if isTExists k2'
then do
ctx5 <- makeEq config ctx4 k2' kData
$ ErrorMismatch uni k2' kData tt
return (applyContext ctx5 k2', ctx5)
else do
return (k2', ctx4)
when ( not (isDataKind k2'')
&& not (isWitnessKind k2''))
$ throw $ ErrorForallKindInvalid tt t2 k2''
let ctx_cut = popToPos pos1 ctx5
return (TForall b1' t2', k2'', ctx_cut)
Check kExpected
-> do
let k1 = typeOfBind b1
(k1', _s1, ctx1) <- checkTypeM config kenv ctx0 UniverseKind k1 Synth
let b1' = replaceTypeOfBind k1' b1
let (ctx2, pos1) = markContext ctx1
let ctx3 = pushKind b1' RoleAbstract ctx2
(t2', k2, ctx4) <- checkTypeM config kenv ctx3 UniverseSpec t2 Synth
let k2' = applyContext ctx4 k2
(k2'', ctx5)
<- if isTExists k2' && isTExists kExpected
then do
ctx' <- makeEq config ctx4 k2' kExpected
$ ErrorMismatch uni k2' kExpected tt
ctx5 <- makeEq config ctx' k2' kData
$ ErrorMismatch uni k2' kData tt
return (applyContext ctx5 k2', ctx5)
else do
ctx5 <- makeEq config ctx4 k2' kExpected
$ ErrorMismatch uni k2' kExpected tt
return (applyContext ctx5 k2', ctx4)
when ( not (isDataKind k2'')
&& not (isWitnessKind k2''))
$ throw $ ErrorForallKindInvalid tt t2 k2'
let ctx_cut = popToPos pos1 ctx5
return (TForall b1' t2', k2'', ctx_cut)
checkTypeM config kenv ctx0 uni@UniverseKind
tt@(TApp (TApp (TCon (TyConKind KiConFun)) k1) k2) mode
= case mode of
Recon
-> do
_ <- checkTypeM config kenv ctx0 uni k1 Recon
(_, s2, _) <- checkTypeM config kenv ctx0 uni k2 Recon
return (tt, s2, ctx0)
Synth
-> do
(k1', _, ctx1) <- checkTypeM config kenv ctx0 uni k1 Synth
(k2', s2, ctx2) <- checkTypeM config kenv ctx1 uni k2 Synth
return (kFun k1' k2', s2, ctx2)
Check sExpected
-> do
(k1', _, ctx1) <- checkTypeM config kenv ctx0 uni k1 Synth
(k2', s2, ctx2) <- checkTypeM config kenv ctx1 uni k2 (Check sExpected)
return (kFun k1' k2', s2, ctx2)
checkTypeM config env ctx0 uni@UniverseSpec
tt@(TApp (TApp tC@(TCon (TyConWitness TwConImpl)) t1) t2) mode
= case mode of
Recon
-> do
(t1', k1, ctx1) <- checkTypeM config env ctx0 uni t1 Recon
(t2', k2, ctx2) <- checkTypeM config env ctx1 uni t2 Recon
let tt' = TApp (TApp tC t1') t2'
if isWitnessKind k1 && isWitnessKind k2
then return (tt', kWitness, ctx2)
else if isWitnessKind k1 && isDataKind k2
then return (tt', kData, ctx2)
else throw $ ErrorWitnessImplInvalid tt t1 k1 t2 k2
Synth
-> do
(t1', _k1, ctx1) <- checkTypeM config env ctx0 uni t1 Synth
(t2', k2, ctx2) <- checkTypeM config env ctx1 uni t2 Synth
return (tImpl t1' t2', k2, ctx2)
Check kExpected
-> do
(t1', _k1, ctx1) <- checkTypeM config env ctx0 uni t1 Synth
(t2', k2, ctx2) <- checkTypeM config env ctx1 uni t2 (Check kExpected)
return (tImpl t1' t2', k2, ctx2)
checkTypeM config kenv ctx0 UniverseSpec
tt@(TApp tFn tArg) mode
= case mode of
Recon
-> do
(tFn', kFn, ctx1)
<- checkTypeM config kenv ctx0 UniverseSpec tFn Recon
(tArg', kArg, ctx2)
<- checkTypeM config kenv ctx1 UniverseSpec tArg Recon
case kFn of
TApp (TApp (TCon (TyConKind KiConFun)) kParam) kBody
| equivT kParam kArg
-> return (tApp tFn' tArg', kBody, ctx2)
| otherwise
-> throw $ ErrorAppArgMismatch tt tFn' kFn tArg' kArg
_ -> throw $ ErrorAppNotFun tt tFn' kFn tArg'
Synth
-> do
(tFn', kFn, ctx1)
<- checkTypeM config kenv ctx0 UniverseSpec tFn Synth
(kResult, tArg', ctx2)
<- synthTAppArg config kenv ctx1
tFn' (applyContext ctx1 kFn )
tArg
return (TApp tFn' tArg', kResult, ctx2)
Check kExpected
-> do
(t1', k1, ctx1)
<- checkTypeM config kenv ctx0 UniverseSpec tt Synth
let k1' = applyContext ctx1 k1
let kExpected' = applyContext ctx1 kExpected
ctx2 <- makeEq config ctx1 k1' kExpected'
$ ErrorMismatch UniverseSpec k1' kExpected' tt
return (t1', k1', ctx2)
checkTypeM config kenv ctx0 UniverseSpec tt@(TSum ss) mode
= case mode of
Recon
-> do
(ts', ks, ctx1)
<- checkTypesM config kenv ctx0 UniverseSpec Recon
$ TS.toList ss
let kExpect = TS.kindOfSum ss
k' <- case nub ks of
[] -> return $ TS.kindOfSum ss
[k] -> return k
_ -> throw $ ErrorSumKindMismatch kExpect ss ks
if (k' == kEffect || k' == kClosure)
then return (TSum (TS.fromList k' ts'), k', ctx1)
else throw $ ErrorSumKindInvalid ss k'
Synth
-> do
(ts, ks, ctx1)
<- checkTypesM config kenv ctx0 UniverseSpec Synth
$ TS.toList ss
case ks of
k : _ksMore
-> do
(ts'', _, ctx2)
<- checkTypesM config kenv ctx1 UniverseSpec (Check k) ts
let k' = applyContext ctx2 k
return (TSum (TS.fromList k' ts''), k', ctx2)
[] | isBot (TS.kindOfSum ss)
-> return ( TSum (TS.fromList kEffect [])
, kEffect, ctx0)
| otherwise
-> return ( TSum (TS.empty (TS.kindOfSum ss))
, TS.kindOfSum ss, ctx0)
Check kExpected
-> do
(t1', k1, ctx1)
<- checkTypeM config kenv ctx0 UniverseSpec tt Synth
let k1' = applyContext ctx1 k1
let kExpected' = applyContext ctx1 kExpected
ctx2 <- makeEq config ctx1 k1' kExpected'
$ ErrorMismatch UniverseSpec k1' kExpected' tt
return (t1', k1, ctx2)
checkTypeM _ _ _ uni tt _mode
= throw $ ErrorUniverseMalfunction tt uni
checkTypesM
:: (Ord n, Show n, Pretty n)
=> Config n
-> KindEnv n
-> Context n
-> Universe
-> Mode n
-> [Type n]
-> CheckM n
( [Type n]
, [Kind n]
, Context n)
checkTypesM _ _ ctx0 _ _ []
= return ([], [], ctx0)
checkTypesM config kenv ctx0 uni mode (t : ts)
= do (t', k', ctx1) <- checkTypeM config kenv ctx0 uni t mode
(ts', ks', ctx') <- checkTypesM config kenv ctx1 uni mode ts
return (t' : ts', k' : ks', ctx')
synthTAppArg
:: (Show n, Ord n, Pretty n)
=> Config n
-> KindEnv n
-> Context n
-> Type n
-> Kind n
-> Type n
-> CheckM n
( Kind n
, Type n
, Context n)
synthTAppArg config kenv ctx0 tFn kFn tArg
| Just iFn <- takeExists kFn
= do
iParam <- newExists sComp
let kParam = typeOfExists iParam
iBody <- newExists sComp
let kBody = typeOfExists iBody
let Just ctx1 = updateExists [iBody, iParam] iFn
(kFun kParam kBody) ctx0
(tArg', _kArg, ctx2)
<- checkTypeM config kenv ctx1 UniverseSpec tArg (Check kParam)
return (kBody, tArg', ctx2)
| TApp (TApp (TCon (TyConKind KiConFun)) kParam) kBody <- kFn
= do
(tArg', _kArg, ctx1)
<- checkTypeM config kenv ctx0 UniverseSpec tArg (Check kParam)
return (kBody, tArg', ctx1)
| otherwise
= throw $ ErrorAppNotFun (TApp tFn tArg) tFn kFn tArg