Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type-based compilation of everything + fix for #264 #304

Merged
merged 22 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
03e2db1
[ refactor ] Remove two outdated comments
jespercockx Mar 7, 2024
55bdefa
[ refactor ] Extract endsInSort as a top-level function
jespercockx Mar 7, 2024
e6875e9
[ refactor ] Add separate output `DOType` for `compileDom`
jespercockx Mar 7, 2024
ded138f
[ refactor ] Change `compileTeleBinds` to not rely on `keepArg`
jespercockx Mar 7, 2024
9922be0
[ refactor ] Remove check for hidden/erased variables from `compileDB…
jespercockx Mar 7, 2024
b8c645d
Type-based compilation of type-level arguments
jespercockx Mar 7, 2024
d750347
[ refactor ] Implement `keepClause` in terms of `compileDom`
jespercockx Mar 8, 2024
4fde5a3
Use type information to check which fields of unboxed record are erased
jespercockx Mar 8, 2024
ad439c7
[ refactor ] Remove unused `checkVar` field from CompileEnv
jespercockx Mar 8, 2024
2cf8362
Remove unnecessary check on type-level lambdas
jespercockx Mar 8, 2024
629fe56
[ refactor ] Use compileDom instead of keepArg in compileKind
jespercockx Mar 8, 2024
31cd768
Use compileDom instead of keepArg for compiling arguments to type ali…
jespercockx Mar 8, 2024
a2a2132
[ refactor ] Use compileTypeArgs in compileInstRule
jespercockx Mar 8, 2024
56aef97
Use keepClause instead of keepArg for compileInstanceClause
jespercockx Mar 8, 2024
e9c4b57
Use compileDom instead of keepArg for compiling default methods
jespercockx Mar 8, 2024
f85891c
[ refactor ] Remove keepArg function (no longer used)
jespercockx Mar 8, 2024
9a41c6e
Don't use absBody in compileInstanceClause, instead apply to dummy term
jespercockx Mar 8, 2024
ad07a7d
Allow compilation of hidden parameters to data/record types
jespercockx Mar 8, 2024
a99740a
[ fix #264 ] Compile all non-erased module parameters to explicit for…
jespercockx Mar 8, 2024
b13563e
[ fix #301 ] Already fixed, adding test case
jespercockx Mar 8, 2024
558783e
[ refactor ] Two small refactorings
jespercockx Mar 12, 2024
dc366ab
Reinstate check for erasure on type-level lambdas + add todo and test…
jespercockx Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Agda2Hs/AgdaUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import Agda.Syntax.TopLevelModuleName

import Agda.TypeChecking.Monad ( topLevelModuleName )
import Agda.TypeChecking.Pretty
import Agda.TypeChecking.Sort
import Agda.TypeChecking.Substitute
import Agda.TypeChecking.Telescope
import Agda.TypeChecking.Reduce ( reduceDefCopy )

import Agda.Utils.Either ( isRight )
Expand Down Expand Up @@ -136,3 +138,8 @@ isForcedPat = \case
ProjP{} -> False
IApplyP{} -> False
DefP{} -> False

endsInSort :: (PureTCM m, MonadBlock m) => Type -> m Bool
endsInSort t = do
TelV tel b <- telView t
addContext tel $ ifIsSort b (\_ -> return True) (return False)
4 changes: 2 additions & 2 deletions src/Agda2Hs/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ initCompileEnv tlm rewrites = CompileEnv
{ currModule = tlm
, minRecordName = Nothing
, locals = []
, compilingLocal = False
, copatternsEnabled = False
, checkVar = False
, rewrites = rewrites
}

Expand Down Expand Up @@ -85,7 +85,7 @@ compile opts tlm _ def =
case (p , theDef def) of
(NoPragma , _ ) -> return []
(ExistingClassPragma , _ ) -> return []
(UnboxPragma s , defn ) -> [] <$ checkUnboxPragma defn
(UnboxPragma s , Record{} ) -> [] <$ checkUnboxPragma def
(TransparentPragma , Function{}) -> [] <$ checkTransparentPragma def
(InlinePragma , Function{}) -> [] <$ checkInlinePragma def

Expand Down
40 changes: 31 additions & 9 deletions src/Agda2Hs/Compile/ClassInstance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Agda.TypeChecking.Records
import Agda.TypeChecking.Telescope ( mustBePi )

import Agda.Utils.Lens
import Agda.Utils.Monad ( ifNotM )
import Agda.Utils.Impossible ( __IMPOSSIBLE__ )

import Agda2Hs.AgdaUtils
Expand Down Expand Up @@ -84,7 +85,8 @@ compileInstance ToDefinition def@Defn{..} =
compileInstRule :: [Hs.Asst ()] -> Term -> C (Hs.InstRule ())
compileInstRule cs ty = case unSpine1 ty of
Def f es | Just args <- allApplyElims es -> do
vs <- mapM (compileType . unArg) $ filter keepArg args
fty <- defType <$> getConstInfo f
vs <- compileTypeArgs fty args
f <- compileQName f
return $
Hs.IRule () Nothing (ctx cs) $ foldl (Hs.IHApp ()) (Hs.IHCon () f) (map pars vs)
Expand All @@ -100,6 +102,7 @@ compileInstRule cs ty = case unSpine1 ty of
DomConstraint hsA ->
underAbstraction a b (compileInstRule (cs ++ [hsA]) . unEl)
DomType _ t -> __IMPOSSIBLE__
DomForall _ -> __IMPOSSIBLE__
_ -> __IMPOSSIBLE__


Expand Down Expand Up @@ -131,7 +134,8 @@ etaExpandClause cl@Clause{namedClausePats = ps, clauseBody = Just t} = do


compileInstanceClause :: ModuleName -> Type -> Clause -> C ([Hs.InstDecl ()], [QName])
compileInstanceClause curModule ty c = do
compileInstanceClause curModule ty c = ifNotM (keepClause c) (return ([], [])) $ do

let naps = namedClausePats c

-- there are no projection patterns: we need to eta-expand the clause
Expand Down Expand Up @@ -162,7 +166,11 @@ compileInstanceClause' curModule ty (p:ps) c

-- retrieve the type of the projection
Just (unEl -> Pi a b) <- getDefType q ty
let ty' = absBody b
-- We don't really have the information available to reconstruct the instance
-- head. However, all dependencies on the instance head are in erased positions,
-- so we can just use a dummy term instead
let instanceHead = __DUMMY_TERM__
ty' = b `absApp` instanceHead

reportSDoc "agda2hs.compile.instance" 15 $
text "Compiling instance clause for" <+> prettyTCM (Arg arg $ Def q [])
Expand Down Expand Up @@ -193,14 +201,15 @@ compileInstanceClause' curModule ty (p:ps) c
_ -> return d

if
| isInstance arg, usableModality arg -> do
-- Instance field: check canonicity.
| isInstance arg -> do
unless (null ps) $ genericDocError =<< text "not allowed: explicitly giving superclass"
body <- case clauseBody c' of
Nothing -> genericDocError =<< text "not allowed: absurd clause for superclass"
Just b -> return b
checkInstance body
return ([], [])
| not (keepArg arg) -> return ([], [])

-- Projection of a primitive field: chase down definition and inline as instance clause.
| Clause {namedClausePats = [], clauseBody = Just (Def n es)} <- c'
, [(_, f)] <- mapMaybe isProjElim es
Expand All @@ -210,7 +219,7 @@ compileInstanceClause' curModule ty (p:ps) c
reportSDoc "agda2hs.compile.instance" 20 $
text $ "raw projection:" ++ prettyShow (Def n [])
d <- chaseDef n
fc <- compileFun False d
fc <- compileLocal $ compileFun False d
let hd = hsName $ prettyShow $ nameConcrete $ qnameName $ defName d
let fc' = {- dropPatterns 1 $ -} replaceName hd uf fc
return (map (Hs.InsDecl ()) fc', [n])
Expand All @@ -219,7 +228,21 @@ compileInstanceClause' curModule ty (p:ps) c
-- same (minimal) dictionary as the primitive fields.
| Clause {namedClausePats = [], clauseBody = Just (Def n es)} <- c'
, n .~ q -> do
case map unArg . filter keepArg <$> allApplyElims es of
let err = genericDocError =<< text "illegal instance declaration: instances using default methods should use a named definition or an anonymous `λ where`."
filterArgs :: Type -> [Term] -> C [Term]
filterArgs ty [] = return []
filterArgs ty (v:vs) = do
reportSDoc "agda2hs.compile.instance" 25 $ text "filterArgs: v =" <+> prettyTCM v
(a,b) <- mustBePi ty
reportSDoc "agda2hs.compile.instance" 25 $ text "filterArgs: a =" <+> prettyTCM a
let rest = underAbstraction a b $ \b' -> filterArgs b' vs
compileDom a >>= \case
DODropped -> rest
DOType -> rest
DOTerm -> (v:) <$> rest
DOInstance -> err
ty <- defType <$> getConstInfo n
traverse (filterArgs ty) (map unArg <$> allApplyElims es) >>= \case
Just [ Def f _ ] -> do
reportSDoc "agda2hs.compile.instance" 20 $ vcat
[ text "Dropping default instance clause" <+> prettyTCM c'
Expand All @@ -228,8 +251,7 @@ compileInstanceClause' curModule ty (p:ps) c
reportSDoc "agda2hs.compile.instance" 40 $
text $ "raw dictionary:" ++ prettyShow f
return ([], [f])

_ -> genericDocError =<< text "illegal instance declaration: instances using default methods should use a named definition or an anonymous `λ where`."
_ -> err

-- No minimal dictionary used, proceed with compiling as a regular clause.
| otherwise -> do
Expand Down
4 changes: 3 additions & 1 deletion src/Agda2Hs/Compile/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ compileData newtyp ds def = do
DomDropped -> allIndicesErased (unAbs t)
DomType{} -> genericDocError =<< text "Not supported: indexed datatypes"
DomConstraint{} -> genericDocError =<< text "Not supported: constraints in types"
DomForall{} -> genericDocError =<< text "Not supported: indexed datatypes"
_ -> return ()

compileConstructor :: [Arg Term] -> QName -> C (Hs.QualConDecl ())
compileConstructor params c = checkingVars $ do
compileConstructor params c = do
reportSDoc "agda2hs.data.con" 15 $ text "compileConstructor" <+> prettyTCM c
reportSDoc "agda2hs.data.con" 20 $ text " params = " <+> prettyTCM params
ty <- (`piApplyM` params) . defType =<< getConstInfo c
Expand All @@ -74,3 +75,4 @@ compileConstructorArgs (ExtendTel a tel) = compileDomType (absName tel) a >>= \c
(ty :) <$> underAbstraction a tel compileConstructorArgs
DomConstraint hsA -> genericDocError =<< text "Not supported: constructors with class constraints"
DomDropped -> underAbstraction a tel compileConstructorArgs
DomForall{} -> __IMPOSSIBLE__
89 changes: 49 additions & 40 deletions src/Agda2Hs/Compile/Function.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
module Agda2Hs.Compile.Function where

import Control.Monad ( (>=>), filterM, forM_ )
import Control.Monad.Reader ( asks )
import Control.Monad.Reader ( asks, local )

import Data.Generics
import Data.List
import Data.Maybe ( fromMaybe, isJust )
import qualified Data.Text as Text

import qualified Language.Haskell.Exts.Syntax as Hs
import qualified Language.Haskell.Exts.Build as Hs
import qualified Language.Haskell.Exts as Hs

import Agda.Compiler.Backend
import Agda.Compiler.Common
Expand Down Expand Up @@ -41,7 +40,7 @@ import Agda.Utils.Size ( Sized(size) )
import Agda2Hs.AgdaUtils
import Agda2Hs.Compile.Name ( compileQName )
import Agda2Hs.Compile.Term ( compileTerm, usableDom )
import Agda2Hs.Compile.Type ( compileTopLevelType, compileDom, DomOutput(..) )
import Agda2Hs.Compile.Type ( compileType, compileDom, DomOutput(..) )
import Agda2Hs.Compile.TypeDefinition ( compileTypeDef )
import Agda2Hs.Compile.Types
import Agda2Hs.Compile.Utils
Expand Down Expand Up @@ -116,50 +115,50 @@ compileFun withSig def@Defn{..} =
$ compileFun' withSig def

-- inherit existing (instantiated) locals
compileFun' withSig def@Defn{..} = do
compileFun' withSig def@Defn{..} = inTopContext $ do
jespercockx marked this conversation as resolved.
Show resolved Hide resolved
reportSDoc "agda2hs.compile" 6 $ "Compiling function: " <+> prettyTCM defName

whenM ((withSig &&) <$> inRecordMod defName) $
genericDocError =<< text "not supported by agda2hs: functions inside a record module"

withCurrentModule m $ do
-- We add the module parameters to the context
-- if we are compiling a local function (e.g. a where declaration or pattern-matching lambda)
isLocalFun <- asks compilingLocal
tel <- if isLocalFun then lookupSection m else return EmptyTel

withCurrentModule m $ addContext tel $ do
ifM (endsInSort defType)
-- if the function type ends in Sort, it's a type alias!
(ensureNoLocals err >> compileTypeDef x def)
-- otherwise, we have to compile clauses.
$ do

pars <- getContextArgs
reportSDoc "agda2hs.compile.type" 8 $ "Function module parameters: " <+> prettyTCM pars

when withSig $ checkValidFunName x

compileTopLevelType withSig defType $ \ty -> do

let filtered = filter keepClause funClauses
weAreOnTop <- isJust <$> liftTCM (currentModule >>= isTopLevelModule)
pars <- getContextArgs
reportSDoc "agda2hs.compile.type" 8 $ "Function type (before instantiation): " <+> inTopContext (prettyTCM defType)
typ <- piApplyM defType pars
reportSDoc "agda2hs.compile.type" 8 $ "Function type (after instantiation): " <+> prettyTCM typ

-- We only instantiate the clauses to the current module parameters
-- if the current module isn't the toplevel module
unless weAreOnTop $
reportSDoc "agda2hs.compile.type" 8 $ "Applying module parameters to clauses: " <+> prettyTCM pars
let clauses = if weAreOnTop then filtered else filtered `apply` pars
sig <- if not withSig then return [] else do
checkValidFunName x
jespercockx marked this conversation as resolved.
Show resolved Hide resolved
ty <- compileType $ unEl typ
reportSDoc "agda2hs.compile.type" 8 $ "Compiled function type: " <+> text (Hs.prettyPrint ty)
return [Hs.TypeSig () [x] ty]

typ <- if weAreOnTop then pure defType else piApplyM defType pars
let clauses = funClauses `apply` pars
cs <- mapMaybeM (compileClause m (Just defName) x typ) clauses

cs <- mapMaybeM (compileClause m (Just defName) x typ) clauses
when (null cs) $ genericDocError
=<< text "Functions defined with absurd patterns exclusively are not supported."
<+> text "Use function `error` from the Haskell.Prelude instead."

when (null cs) $ genericDocError
=<< text "Functions defined with absurd patterns exclusively are not supported."
<+> text "Use function `error` from the Haskell.Prelude instead."

return $ [Hs.TypeSig () [x] ty | withSig ] ++ [Hs.FunBind () cs]
return $ sig ++ [Hs.FunBind () cs]
where
Function{..} = theDef
m = qnameModule defName
n = qnameName defName
x = hsName $ prettyShow n
endsInSort t = do
TelV tel b <- telView t
addContext tel $ ifIsSort b (\_ -> return True) (return False)
err = "Not supported: type definition with `where` clauses"


Expand All @@ -169,16 +168,15 @@ compileClause curModule mproj x t c =
compileClause' curModule mproj x t c

compileClause' :: ModuleName -> Maybe QName -> Hs.Name () -> Type -> Clause -> C (Maybe (Hs.Match ()))
compileClause' curModule projName x _ c@Clause{clauseBody = Nothing} = pure Nothing
compileClause' curModule projName x ty c@Clause{..} = do
reportSDoc "agda2hs.compile" 7 $ "compiling clause: " <+> prettyTCM c
reportSDoc "agda2hs.compile" 17 $ "Old context: " <+> (prettyTCM =<< getContext)
reportSDoc "agda2hs.compile" 17 $ "Clause telescope: " <+> prettyTCM clauseTel
reportSDoc "agda2hs.compile" 17 $ "Clause type: " <+> prettyTCM clauseType
reportSDoc "agda2hs.compile" 17 $ "Function type: " <+> prettyTCM ty
reportSDoc "agda2hs.compile" 17 $ "Clause patterns: " <+> text (prettyShow namedClausePats)

addContext (KeepNames clauseTel) $ do
ifNotM (keepClause c) (pure Nothing) $ addContext (KeepNames clauseTel) $ do
reportSDoc "agda2hs.compile" 17 $ "Old context: " <+> (inTopContext . prettyTCM =<< getContext)
reportSDoc "agda2hs.compile" 17 $ "Clause telescope: " <+> inTopContext (prettyTCM clauseTel)
reportSDoc "agda2hs.compile" 17 $ "Clause type: " <+> prettyTCM clauseType
reportSDoc "agda2hs.compile" 17 $ "Function type: " <+> prettyTCM ty
reportSDoc "agda2hs.compile" 17 $ "Clause patterns: " <+> text (prettyShow namedClausePats)

toDrop <- case projName of
Nothing -> pure 0
Expand All @@ -198,7 +196,7 @@ compileClause' curModule projName x ty c@Clause{..} = do
/\ (curModule `isFatherModuleOf`) . qnameModule

children <- filter isWhereDecl <$> asks locals
whereDecls <- mapM (getConstInfo >=> compileFun' True) children
whereDecls <- compileLocal $ mapM (getConstInfo >=> compileFun' True) children

-- Jesper, 2023-10-30: We should compile the body in the module of the
-- `where` declarations (if there are any) in order to drop the arguments
Expand All @@ -215,12 +213,22 @@ compileClause' curModule projName x ty c@Clause{..} = do

let rhs = Hs.UnGuardedRhs () hsBody
whereBinds | null whereDecls = Nothing
| otherwise = Just $ Hs.BDecls () (concat whereDecls)
| otherwise = Just $ Hs.BDecls () (concat whereDecls)
jespercockx marked this conversation as resolved.
Show resolved Hide resolved
match = case (x, ps) of
(Hs.Symbol{}, p : q : ps) -> Hs.InfixMatch () p x (q : ps) rhs whereBinds
_ -> Hs.Match () x ps rhs whereBinds
return $ Just match

keepClause :: Clause -> C Bool
keepClause c@Clause{..} = case (clauseBody, clauseType) of
(Nothing, _) -> pure False
(_, Nothing) -> pure False
(Just body, Just cty) -> compileDom (domFromArg cty) >>= \case
DODropped -> pure False
DOInstance -> pure False -- not __IMPOSSIBLE__ because of current hacky implementation of `compileInstanceClause`
DOType -> __IMPOSSIBLE__
DOTerm -> pure True
jespercockx marked this conversation as resolved.
Show resolved Hide resolved


-- TODO(flupe): projection-like definitions are missing the first (variable) patterns
-- (that are however present in the type)
Expand All @@ -241,11 +249,12 @@ compilePats ty ((namedArg -> pat):ps) = do
(a, b) <- mustBePi ty
reportSDoc "agda2hs.compile.pattern" 10 $ text "Compiling pattern:" <+> prettyTCM pat
let rest = compilePats (absApp b (patternToTerm pat)) ps
when (usableDom a) checkForced
compileDom a >>= \case
DOInstance -> rest
DODropped -> rest <* when (usableDom a) checkForced
DOKept -> do
checkForced
DODropped -> rest
DOType -> rest
DOTerm -> do
checkNoAsPatterns pat
(:) <$> compilePat (unDom a) pat <*> rest
where checkForced = when (isForcedPat pat) $ genericDocError =<< "not supported by agda2hs: forced (dot) patterns in non-erased positions"
Expand Down
Loading
Loading