Skip to content

Commit 048e4a3

Browse files
committed
fully-apply constructors
1 parent 54ed5b8 commit 048e4a3

File tree

6 files changed

+70
-14
lines changed

6 files changed

+70
-14
lines changed

src/Agda/Utils.hs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE FlexibleContexts, OverloadedStrings #-}
1+
{-# LANGUAGE FlexibleContexts, OverloadedStrings, BangPatterns #-}
22

33
-- | Agda utilities.
44
module Agda.Utils where
@@ -11,9 +11,12 @@ import Control.Arrow ( first )
1111
import Data.List ( partition )
1212
import qualified Data.List.NonEmpty as NE ( fromList )
1313
import Data.Maybe ( isJust, isNothing )
14+
import Data.Bifunctor ( second )
1415

1516
import Utils
1617
import Agda.Lib
18+
import Agda.TypeChecking.Free.Lazy (underBinder)
19+
import Agda.TypeChecking.Datatypes (getConstructorInfo, ConstructorInfo(..))
1720

1821
-- ** useful monad constraint kinds
1922

@@ -59,6 +62,58 @@ unqual = pp . last . qnameToList0
5962
hasPragma :: QName -> TCM Bool
6063
hasPragma qn = isJust <$> getUniqueCompilerPragma "AGDA2LAMBOX" qn
6164

65+
66+
-- ** eta-expansion of constructors
67+
68+
-- NOTE(flupe):
69+
-- We do this because CertiCoq requires fully-applied constructors.
70+
-- In the future, this transformation should be made on the Coq side.
71+
-- (as it would be proven correct).
72+
-- NB: MetaCoq provides eta-expansion of constructors, but only for eprograms.
73+
-- (see : erasure/theories/EConstructorsAsBlocks.v)
74+
75+
-- | Check whether a treeless term is a constructor applied to (many) terms.
76+
unSpineCon :: TTerm -> Maybe (QName, [TTerm])
77+
unSpineCon (TCon q) = Just (q, [])
78+
unSpineCon (TApp u v) = second (++ v) <$> unSpineCon u
79+
unSpineCon _ = Nothing
80+
81+
-- | Return the arity of a constructor. Ignores parameters.
82+
conArity :: ConstructorInfo -> Int
83+
conArity (DataCon a) = a
84+
conArity (RecordCon _ _ a _) = a
85+
86+
-- | Eta-expand treeless constructors.
87+
etaExpandCtor :: TTerm -> TCM TTerm
88+
etaExpandCtor t | Just (q, args) <- unSpineCon t = do
89+
arity <- conArity <$> getConstructorInfo q
90+
let nargs = length args
91+
92+
if nargs >= arity then -- should really be (==), but just in case
93+
TApp (TCon q) <$> mapM etaExpandCtor args
94+
else do
95+
let nlam = arity - nargs
96+
exargs <- mapM (etaExpandCtor . raise nlam) args
97+
let vars = TVar <$> [(nlam - 1) .. 0]
98+
pure $ iterate TLam (TApp (TCon q) $ exargs ++ vars) !! nlam
99+
100+
etaExpandCtor t | otherwise = case t of
101+
TApp u v -> TApp <$> etaExpandCtor u <*> mapM etaExpandCtor v
102+
TLam u -> TLam <$> etaExpandCtor u
103+
TLet u v -> TLet <$> etaExpandCtor u <*> etaExpandCtor v
104+
TCase k ci tdef alts -> TCase k ci <$> etaExpandCtor tdef
105+
<*> mapM etaExpandAlt alts
106+
TCoerce u -> TCoerce <$> etaExpandCtor u
107+
_ -> pure t
108+
109+
etaExpandAlt :: TAlt -> TCM TAlt
110+
etaExpandAlt = \case
111+
TACon q a b -> TACon q a <$> etaExpandCtor b
112+
TAGuard a b -> TAGuard a <$> etaExpandCtor b
113+
TALit l b -> TALit l <$> etaExpandCtor b
114+
115+
116+
62117
{-
63118
lookupCtx :: MonadTCEnv m => Int -> m (String, Type)
64119
lookupCtx i = first transcribe . (!! i) {-. reverse-} <$> currentCtx

src/Agda2Lambox/Convert/Function.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ convertFunctionBody :: Definition :~> Term
4040
convertFunctionBody Defn{defName} =
4141
withCurrentModule (qnameModule defName) do
4242
Just t <- liftTCM $ toTreeless EagerEvaluation defName
43-
convert t
43+
convert =<< liftTCM (etaExpandCtor t)
4444

4545
-- | Whether a function is a (true) record projection.
4646
isProjection :: Either ProjectionLikenessMissing Projection -> Bool

src/Agda2Lambox/Convert/Term.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ import qualified LambdaBox as L
2222
import Agda2Lambox.Monad
2323
import Agda2Lambox.Convert.Class
2424

25-
-- quick fix, remove this asap
26-
app :: L.Term -> L.Term -> L.Term
27-
app (LCtor ind k es) arg = LCtor ind k (es ++ [arg])
28-
app t arg = LApp t arg
2925

3026
-- | Compiling (treeless) Agda terms into Lambox expressions.
3127
instance A.TTerm ~> L.Term where
@@ -37,19 +33,22 @@ instance A.TTerm ~> L.Term where
3733
return case qn `elemIndex` mutuals of
3834
Nothing -> LConst $ qnameToKerName qn
3935
Just i -> LRel $ i + boundVars
36+
-- fully-applied constructors
37+
A.TApp tc@(A.TCon q) args -> do
38+
LCtor ind i [] <- go tc
39+
LCtor ind i <$> mapM go args
4040
A.TApp t args -> do
4141
ct <- go t
4242
cargs <- mapM go args
43-
return $ foldl app ct cargs
43+
return $ foldl LApp ct cargs
4444
A.TLam t -> inBoundVar $ LLam <$> go t
4545
A.TLit l -> go l
4646
A.TCon qn -> do
4747
dt <- liftTCM $ getConstructorData qn
4848
ctrs <- liftTCM $ getConstructors dt
4949
Just i <- pure $ qn `elemIndex` ctrs
5050
return $ LCtor (L.Inductive (qnameToKerName dt) 0) i []
51-
-- TODO(flupe): I *think* constructors have to be fully-applied
52-
-- TODO(flupe): mutual inductives
51+
-- TODO(flupe): ^ mutual inductives
5352
A.TLet tt tu -> LLet <$> go tt <*> inBoundVar (go tu)
5453
A.TCase n A.CaseInfo{..} tt talts ->
5554
case caseErased of
@@ -91,7 +90,7 @@ instance A.Literal ~> L.Term where
9190

9291
let indnat = L.Inductive (qnameToKerName qnat) 0
9392

94-
return $ (!! fromInteger n) $ iterate (app (LCtor indnat 1 [])) (LCtor indnat 0 [])
93+
return $ (!! fromInteger n) $ iterate (LApp (LCtor indnat 1 [])) (LCtor indnat 0 [])
9594

9695
LitWord64 w -> fail "Literal int64 not supported"
9796
LitFloat f -> fail "Literal float not supported"

test/Map.agda

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ map f [] = []
1919
map f (x :: xs) = f x :: map f xs
2020

2121
ys : List Nat
22-
ys = map (\x -> add x x) xs
22+
ys = map succ xs
2323
{-# COMPILE AGDA2LAMBOX ys #-}

theories/CheckWF.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ Notation "x 'eqn:' p" := (exist _ x p) (only parsing, at level 20).
2626
Definition eflags : EEnvFlags :=
2727
{| has_axioms := true;
2828
term_switches := all_term_flags;
29-
has_cstr_params := false; (* CertiCoq doesn't want params in ctors *)
30-
cstr_as_blocks := true (* CertiCoq seem to require fully-applied ctors *)
29+
has_cstr_params := false; (* Agda already drops constructor params *)
30+
cstr_as_blocks := true; (* The backend fully applies ctors *)
3131
|}.
3232

3333
Fixpoint check_fresh_global (k : kername) (decls : global_declarations) : bool :=

theories/Eval.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ From MetaCoq.Erasure Require Import EAst.
66
From CertiCoq.Common Require Import Common.
77
From CertiCoq.LambdaBoxMut Require Import compile term program wcbvEval.
88

9+
(* TODO: eta-expand here *)
10+
911
(* convert a lambda box program to certicoq lambda box mut, and run it *)
1012
Definition eval_program (p : EAst.program) : exception Term :=
1113
let prog := {| env := LambdaBoxMut.compile.compile_ctx (fst p);
1214
main := compile (snd p)
13-
|}
15+
|}
1416
in wcbvEval (env prog) (2 ^ 14) (main prog).

0 commit comments

Comments
 (0)