Skip to content

Commit 471938e

Browse files
committed
fix zero pattern, more work on types
1 parent a293ff8 commit 471938e

File tree

8 files changed

+174
-47
lines changed

8 files changed

+174
-47
lines changed

NOTES.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,25 @@ All the remaining transformations optimize this program, but remain in λ□*.
120120
Rebuild the efficient lookup table.
121121
12. `constructors_as_blocks_transformation`:
122122
Eta-expand and fully apply constructors.
123-
123+
124124
This is currently implemented on our side in the backend,
125125
but it makes way more sense to do it on the Coq side.
126126
Let's use this transformation!
127+
128+
---
129+
130+
- Actually, now that I get a better understanding of typed λ□ environments, it looks like
131+
generation of the type information never fails.
132+
Worst case, it's always `TBox`, but we try to generate as much valid types as we can.
133+
134+
---
135+
136+
- Type erasure is defined in this paper, from ConCert: https://arxiv.org/pdf/2108.02995
137+
I should just look at what they describe.
138+
I think I need something like:
139+
140+
```hs
141+
compileTopLevelType :: Type -> C ([TypeVarInfo], LamBox.Type)
142+
143+
compileType :: Type -> C (LamBox.Type)
144+
```

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ cabal run agda2lambox -- --out-dir build -itest test/Nat.agda
4242
- [x] With-generated lambdas
4343
- [ ] Module applications
4444
- [ ] Projection-like
45+
- [ ] Check that treeless generates exhaustive cases
4546
- [ ] Support primitives (ints and floats)
4647

4748
- [ ] Setup compilation to Wasm/Rust using Certicoq
@@ -62,8 +63,8 @@ Things that ought to be implemented, but not right now.
6263
- [Verified Extraction from Coq to OCaml](https://github.com/yforster/coq-verified-extraction/)
6364
and its [accompanying paper](https://dl.acm.org/doi/10.1145/3656379)
6465
- [Certified Erasure for Coq, in Coq](https://inria.hal.science/hal-04077552)
65-
- [Syntax of LambdaBox in MetaCoq](https://github.com/MetaCoq/metacoq/blob/coq-8.20/erasure/theories/Typed/ExAst.v)
66-
- [Erasure of types in MetaCoq](https://github.com/MetaCoq/metacoq/blob/coq-8.20/erasure/theories/Typed/Erasure.v#L765)
66+
- [Extracting functional programs from Coq, in Coq](https://arxiv.org/pdf/2108.02995)
67+
- [Lambdabox syntax and untyped environments](https://github.com/MetaCoq/metacoq/blob/coq-8.20/erasure/theories/EAst.v)
68+
- [Lambdabox typed environments](https://github.com/MetaCoq/metacoq/blob/coq-8.20/erasure/theories/Typed/ExAst.v)
6769
- [Coq Extraction Pipeline](https://gist.github.com/4ever2/991007b4418b0ba44f2ee7ed51147e19)
68-
- [MetaCoq Extracted Terms](https://metacoq.github.io/metacoq/html/MetaCoq.Erasure.EAst.html)
6970
- [Extraction Example](https://gist.github.com/4ever2/7fbfb3bf843c4773c933c2fdf6315b5c)

src/Agda2Lambox/Compile.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ compileDefinition target defn@Defn{..} = setCurrentRange defName do
3939
case theDef of
4040

4141
Axiom{} -> do
42-
typ <- whenTyped target $ liftTCM $ ([],) <$> compileType defType
42+
typ <- whenTyped target $ ([],) <$> compileType defType
4343
pure $ Just $ ConstantDecl $ ConstantBody typ Nothing
4444

4545
Constructor{conData} -> Nothing <$ requireDef conData
@@ -52,7 +52,7 @@ compileDefinition target defn@Defn{..} = setCurrentRange defName do
5252
reportSDoc "agda2lambox.compile" 5 $
5353
"Found primitive: " <> prettyTCM defName <> ". Compiling it as axiom."
5454

55-
typ <- liftTCM $ whenTyped target $ ([],) <$> compileType defType
55+
typ <- whenTyped target $ ([],) <$> compileType defType
5656
pure $ Just $ ConstantDecl $ ConstantBody typ Nothing
5757

5858
_ -> genericError $ "Cannot compile: " <> prettyShow defName

src/Agda2Lambox/Compile/Function.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ compileFunction (t :: Target t) defn@Defn{theDef, defType} = do
7272
let mnames = map defName mdefs
7373

7474
-- compile function type, if necessary
75-
typ <- liftTCM $ whenTyped t $ ([],) <$> compileType defType
75+
typ <- whenTyped t $ ([],) <$> compileType defType
7676

7777
let builder :: LBox.Term -> Maybe (LBox.GlobalDecl t)
7878
builder = Just . ConstantDecl . ConstantBody typ . Just

src/Agda2Lambox/Compile/Inductive.hs

+24-10
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import Agda.TypeChecking.Monad.Env ( withCurrentModule )
1919
import Agda.TypeChecking.Datatypes ( ConstructorInfo(..), getConstructorInfo, isDatatype )
2020
import Agda.TypeChecking.Pretty
2121
import Agda.TypeChecking.Telescope (telViewUpTo, piApplyM, teleArgs, telView)
22-
import Agda.TypeChecking.Substitute (TelView, TelV(theTel))
23-
import Agda.Compiler.Backend ( getConstInfo, lookupMutualBlock, reportSDoc, AddContext (addContext))
22+
import Agda.TypeChecking.Substitute (TelView, TelV(theTel), apply)
23+
import Agda.Compiler.Backend ( getConstInfo, lookupMutualBlock, reportSDoc, AddContext (addContext), constructorForm)
2424
import Agda.Syntax.Common.Pretty ( prettyShow )
2525
import Agda.Syntax.Common (Arg)
2626
import Agda.Syntax.Internal ( ConHead(..), unDom, Term)
@@ -69,19 +69,25 @@ compileInductive t defn@Defn{defName} = do
6969
unless (all (isDataOrRecDef . theDef) defs) $
7070
genericError "Mutually-defined datatypes/records *and* functions not supported."
7171

72-
bodies <- liftTCM $ forM defs $ actuallyConvertInductive t
72+
bodies <- forM defs $ actuallyConvertInductive t
7373

7474
return $ Just $ LBox.InductiveDecl $ LBox.MutualInductive
7575
{ indFinite = LBox.Finite -- TODO(flupe)
7676
, indPars = 0
7777
, indBodies = NEL.toList bodies
7878
}
7979

80-
actuallyConvertInductive :: forall t. Target t -> Definition -> TCM (LBox.OneInductiveBody t)
80+
-- TODO(flupe):
81+
-- currently the compilation of constructor types is incorrect
82+
-- indeed, it doesn't account for the fact that for a given constructor,
83+
-- each argument is in scope for the next ones
84+
85+
-- TODO(flupe):
86+
-- actually really unify the compilation of both, they do exactly the same thing
87+
actuallyConvertInductive :: forall t. Target t -> Definition -> CompileM (LBox.OneInductiveBody t)
8188
actuallyConvertInductive t defn@Defn{defName, theDef} = case theDef of
8289
Datatype{..} -> do
8390
params <- theTel <$> telViewUpTo dataPars (defType defn)
84-
8591
reportSDoc "agda2lambox.compile.inductive" 10 $
8692
"Datatype parameters:" <+> prettyTCM params
8793

@@ -101,14 +107,13 @@ actuallyConvertInductive t defn@Defn{defName, theDef} = case theDef of
101107

102108
ctors :: [LBox.ConstructorBody t] <-
103109
forM dataCons \cname -> do
104-
DataCon arity <- getConstructorInfo cname
110+
DataCon arity <- liftTCM $ getConstructorInfo cname
105111

106112
typs <- whenTyped t do
107113
conType <- liftTCM $ (`piApplyM` pvars) =<< defType <$> getConstInfo cname
108114
conTel <- toList . theTel <$> telView conType
109115

110-
forM conTel \dom ->
111-
(LBox.Anon,) <$> compileType (unDom dom)
116+
forM conTel \dom -> (LBox.Anon,) <$> compileType (unDom dom)
112117

113118
pure LBox.Constructor
114119
{ cstrName = prettyShow $ qnameName cname
@@ -129,9 +134,18 @@ actuallyConvertInductive t defn@Defn{defName, theDef} = case theDef of
129134

130135
let ConHead{conName, conFields} = recConHead
131136

137+
params <- theTel <$> telViewUpTo recPars (defType defn)
138+
reportSDoc "agda2lambox.compile.inductive" 10 $
139+
"Record parameters:" <+> prettyTCM params
140+
let pvars :: [Arg Term] = reverse $ teleArgs params
141+
132142
-- TODO
133143
tyvars <- whenTyped t $ pure []
134-
contyps <- whenTyped t $ pure []
144+
145+
-- constructor arg types
146+
typ <- whenTyped t $
147+
let conTel = toList $ recTel `apply` pvars
148+
in forM conTel \dom -> (LBox.Anon,) <$> compileType (unDom dom)
135149

136150
pure LBox.OneInductive
137151
{ indName = prettyShow $ qnameName defName
@@ -141,7 +155,7 @@ actuallyConvertInductive t defn@Defn{defName, theDef} = case theDef of
141155
[ LBox.Constructor
142156
(prettyShow $ qnameName conName)
143157
(length conFields)
144-
contyps
158+
typ
145159
]
146160
, indProjs = []
147161
, indTypeVars = tyvars

src/Agda2Lambox/Compile/Term.hs

+45-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE NamedFieldPuns, DerivingVia #-}
1+
{-# LANGUAGE NamedFieldPuns, DerivingVia, OverloadedStrings #-}
22
module Agda2Lambox.Compile.Term
33
( compileTerm
44
) where
@@ -17,7 +17,7 @@ import Agda.Compiler.Backend ( getConstInfo, theDef, pattern Datatype, dataMutua
1717
import Agda.Syntax.Abstract.Name ( ModuleName(..), QName(..) )
1818
import Agda.Syntax.Builtin ( builtinNat, builtinZero, builtinSuc )
1919
import Agda.Syntax.Common ( Erased(..) )
20-
import Agda.Syntax.Common.Pretty ( prettyShow )
20+
import Agda.Syntax.Common.Pretty
2121
import Agda.Syntax.Literal
2222
import Agda.Syntax.Treeless ( TTerm(..), TAlt(..), CaseInfo(..), CaseType(..) )
2323
import Agda.TypeChecking.Datatypes ( getConstructorData, getConstructors )
@@ -149,16 +149,53 @@ compileLit = \case
149149

150150
l -> genericError $ "unsupported literal: " <> prettyShow l
151151

152+
compileLitPattern :: Literal -> C LBox.Term
153+
compileLitPattern = \case
154+
155+
LitNat i -> do
156+
qn <- liftTCM $ getBuiltinName_ builtinNat
157+
qz <- liftTCM $ getBuiltinName_ builtinZero
158+
qs <- liftTCM $ getBuiltinName_ builtinSuc
159+
z <- liftTCM $ toConApp qz []
160+
let ss = take (fromInteger i) $ repeat (toConApp qs . singleton)
161+
lift $ requireDef qn
162+
liftTCM $ foldrM ($) z ss
163+
164+
l -> genericError $ "unsupported literal: " <> prettyShow l
165+
152166
compileCaseType :: CaseType -> C LBox.Inductive
153167
compileCaseType = \case
154-
CTData qn -> do lift $ requireDef qn
155-
liftTCM $ toInductive qn
156-
_ -> genericError "case type not supported"
157-
168+
CTData qn -> do
169+
lift $ requireDef qn
170+
liftTCM $ toInductive qn
171+
CTNat -> do
172+
qn <- liftTCM $ getBuiltinName_ builtinNat
173+
lift $ requireDef qn
174+
liftTCM $ toInductive qn
175+
ct -> genericError $ "Case type not supported: " <> show ct
176+
177+
-- TODO: flupe
178+
-- literal patterns are a bit annoying.
179+
-- 3 -> body
180+
-- should get compiled to:
181+
-- succ n -> case n of
182+
-- succ n -> case n of
183+
-- succ n -> case n of
184+
-- zero -> body
185+
-- but how do we handle the generation of other branches?
186+
-- perhaps there's already a treeless translation to prevent this
187+
-- to inverstigate...
158188

159189
compileAlt :: TAlt -> C ([LBox.Name], LBox.Term)
160190
compileAlt = \case
161191
TACon{..} -> let names = take aArity $ repeat LBox.Anon
162192
in (names,) <$> underBinders aArity (compileTermC aBody)
163-
TALit{..} -> genericError "literal patterns not supported"
164-
TAGuard{..} -> genericError "case guards not supported"
193+
194+
-- hardcoded support for zero nat literal pattern
195+
TALit (LitNat 0) body -> do
196+
qn <- liftTCM $ getBuiltinName_ builtinNat
197+
lift $ requireDef qn
198+
([],) <$> compileTermC body
199+
200+
lit@TALit{..} -> genericError $ "Literal pattern not supported:" <> show lit
201+
TAGuard{..} -> genericError "case guards not supported"

src/Agda2Lambox/Compile/Type.hs

+70-21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Agda2Lambox.Compile.Type
55
) where
66

77

8+
import Control.Monad.Reader
89
import Control.Monad ( mapM )
910
import Data.List ( foldl' )
1011
import Data.Function ( (&) )
@@ -18,27 +19,80 @@ import qualified LambdaBox as LBox
1819
import Agda2Lambox.Compile.Utils ( qnameToKName )
1920
import Agda2Lambox.Compile.Monad
2021

22+
-- NOTE(flupe):
23+
-- strategy for type compilation (for future me)
24+
-- like for terms, we need to keep track of bound variables.
25+
-- In particular, in λ□ type syntax, variables refer to type variables, using DeBruijn *levels*.
26+
-- So, when compiling var k,
27+
-- we should check that if k is below the amount of locally bound bars.
28+
-- If such => tBox.
29+
-- otherwise, it HAS to point to a type variable, and we
30+
--
2131

22-
compileType :: Type -> TCM LBox.Type
23-
compileType = compileType' . unEl
32+
-- | λ□ type compilation environment.
33+
data CompileEnv = CompileEnv
34+
{ typeVars :: Int
35+
-- ^ Type variables, bound outside of the type.
36+
, boundVars :: Int
37+
-- ^ Amount of locally-bound variables.
38+
}
2439

40+
initEnv :: Int -> CompileEnv
41+
initEnv tvs = CompileEnv
42+
{ typeVars = tvs
43+
, boundVars = 0
44+
}
45+
46+
runC :: Int -> C a -> CompileM a
47+
runC tvs m = runReaderT m (initEnv tvs)
48+
49+
-- | Increment the number of locally-bound variables.
50+
underBinder :: C a -> C a
51+
underBinder = local \e -> e { boundVars = boundVars e + 1 }
52+
53+
-- | Compilation monad.
54+
type C a = ReaderT CompileEnv CompileM a
55+
56+
57+
compileTopLevelType :: Type -> CompileM (LBox.Type)
58+
compileTopLevelType = undefined
59+
60+
-- | Compile a type, given a number of type variables in scope.
61+
compileType :: Type -> CompileM LBox.Type
62+
compileType = runC 0 . compileType'
63+
64+
compileType' :: Type -> C LBox.Type
65+
compileType' = compileType'' . unEl
66+
67+
compileType'' :: Term -> C LBox.Type
68+
compileType'' = \case
69+
Var n es -> do
70+
CompileEnv{..} <- ask
71+
if n < boundVars then
72+
pure LBox.TBox -- NOTE(flupe): should we still apply the parameters to the box?
73+
else do
74+
let k = typeVars - (n - boundVars)
75+
-- NOTE(flupe): reading the paper, type variables are restricted to Hindley-Milner
76+
-- so cannot be type constructors: we don't compile elims
77+
pure $ LBox.TVar k
78+
-- foldl' LBox.TApp (LBox.TVar k) <$> compileElims es
2579

26-
compileType' :: Term -> TCM LBox.Type
27-
compileType' = \case
28-
Var n es -> foldl' LBox.TApp (LBox.TVar n) <$> compileElims es
2980
Def q es -> do
30-
-- TODO(flupe): check if it's an inductive
81+
-- TODO(flupe): check if it's an inductive, or a type alias
3182
foldl' LBox.TApp (LBox.TConst $ qnameToKName q) <$> compileElims es
3283
Pi dom abs ->
33-
LBox.TArr <$> compileType (unDom dom)
34-
<*> compileType (unAbs abs)
84+
LBox.TArr <$> compileType' (unDom dom)
85+
<*> underBinder (compileType' (unAbs abs))
3586

36-
Lit{} -> genericError "type-level literals not supported."
37-
Lam{} -> genericError "type-level abstractions not supported."
38-
Con{} -> genericError "type-level constructors not supported."
39-
Sort{} -> pure LBox.TBox
40-
Level{} -> pure LBox.TBox
41-
t -> genericError $ "unsupported type: " <> prettyShow t
87+
-- NOTE(flupe):
88+
-- My current understanding of typed lambox is that the type translation
89+
t -> pure LBox.TBox
90+
91+
compileElims :: Elims -> C [LBox.Type]
92+
compileElims = mapM \case
93+
Apply a -> compileType'' $ unArg a
94+
Proj{} -> genericError "type-level projection elim not supported."
95+
IApply{} -> genericError "type-level cubical path application not supported."
4296

4397
-- See: https://github.com/MetaCoq/metacoq/blob/coq-8.20/erasure/theories/Typed/Erasure.v#L780-L817
4498
-- | Compile a telescope (of parameters) into a list of λ□ type variables.
@@ -57,12 +111,7 @@ compileTele tel =
57111
, tvarIsArity = False
58112
-- ^ type t is an arity if it is "an n-ary dependent function ending with a sort"
59113
, tvarIsSort = False
60-
-- ^ t : Prop?
114+
-- ^ if the type of the parameter ends in a sort.
115+
-- say, @(T : Type)@ or @(T : nat -> Type)@ or @(T : Type -> Type)@.
61116
}
62117

63-
64-
compileElims :: Elims -> TCM [LBox.Type]
65-
compileElims = mapM \case
66-
Apply a -> compileType' $ unArg a
67-
Proj{} -> genericError "type-level projection elim not supported."
68-
IApply{} -> genericError "type-level cubical path application not supported."

src/LambdaBox/Env.hs

+9-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ data ProjectionBody t = Projection
3838
data TypeVarInfo = TypeVarInfo
3939
{ tvarName :: Name
4040
, tvarIsLogical :: Bool
41+
-- ^ A parameter @t@ is *logical* if it is "a proposition when fully applied"
42+
-- i.e @t : Prop@ means t is logical,
43+
-- @t a₁ a₂ : Prop@ means t is logical
4144
, tvarIsArity :: Bool
45+
-- ^ a parameter @t@ is an arity if it is a type when fully applied.
46+
-- Consider @(T : Type → Type) (A : T Nat)@. @A@ is an arity, because @T : @
4247
, tvarIsSort :: Bool
4348
}
4449

@@ -96,7 +101,10 @@ instance Pretty (OneInductiveBody t) where
96101
instance Pretty (GlobalDecl t) where
97102
pretty = \case
98103
ConstantDecl ConstantBody{..} ->
99-
hang "constant:" 2 $ pretty cstBody
104+
vsep
105+
[ hang "constant:" 2 $ pretty cstBody
106+
, foldMap (("type:" <+>) . pretty) cstType
107+
]
100108

101109
InductiveDecl MutualInductive{..} ->
102110
hang "mutual inductive(s):" 2 $

0 commit comments

Comments
 (0)