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

Fix for #357 #358

Merged
merged 6 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions lib/Haskell/Control/Monad.agda
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ open import Haskell.Prim
open import Haskell.Prim.Bool
open import Haskell.Prim.Monad
open import Haskell.Prim.String
open import Haskell.Extra.Erase

guard : {{ MonadFail m }} → (b : Bool) → m (b ≡ True)
guard True = return refl
guard : {{ MonadFail m }} → (b : Bool) → m (Erase (b ≡ True))
guard True = return (Erased refl)
guard False = fail "Guard was not True"
6 changes: 4 additions & 2 deletions lib/Haskell/Extra/Erase.agda
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ module Haskell.Extra.Erase where
@0 xs : List a

record Erase (@0 a : Set ℓ) : Set ℓ where
constructor Erased
field @0 get : a
instance constructor iErased
field @0 {{get}} : a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation for changing the interface of Erase and making the field an instance? Seems like it forces explicit brackets {{}} everywhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it was to make sure that instance search could actually find an element of type Erase A when there is an erased instance of type A in scope. But I found a better way to get the same effect, which does not break backwards compatibility (see newly pushed version).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a situation that arises often? Having instances of a laying around? For equality proofs I get, but otherwise not so sure. But I trust you that the interface hasn't changed :). (I would have kept the old Erase definition and defined iErased separately but it probably doesn't matter much. And with yours one can decide to match and directly introduce an instance, which I don't know if you can do otherwise)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opposite way (defining Erased as the constructor and iErased as the pattern synonym) is not accepted by Agda:

.../agda2hs/lib/Haskell/Extra/Erase.agda:22,21-22: error: [IllegalInstanceVariableInPatternSynonym]
Variable is bound as instance in pattern synonym, but does not
resolve as instance in pattern: x
when scope checking the declaration
  pattern iErased ⦃ x ⦄ = Erased x

open Erase public
{-# COMPILE AGDA2HS Erase tuple #-}

pattern Erased x = iErased {{x}}

infixr 4 ⟨_⟩_
record Σ0 (@0 a : Set) (b : @0 a → Set) : Set where
constructor ⟨_⟩_
Expand Down
1 change: 1 addition & 0 deletions src/Agda2Hs/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ initCompileEnv tlm rewrites = CompileEnv
, compilingLocal = False
, copatternsEnabled = False
, rewrites = rewrites
, writeImports = True
}

initCompileState :: CompileState
Expand Down
4 changes: 3 additions & 1 deletion src/Agda2Hs/Compile/Name.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import Agda.TypeChecking.Records ( isRecordConstructor )

import qualified Agda.Utils.List1 as List1
import Agda.Utils.Maybe ( isJust, isNothing, whenJust, fromMaybe, caseMaybeM )
import Agda.Utils.Monad ( whenM )

import Agda2Hs.AgdaUtils
import Agda2Hs.Compile.Types
Expand Down Expand Up @@ -121,7 +122,8 @@ compileQName f
qf = qualify mod' hf qual

-- add (possibly qualified) import
whenJust (mimpBuiltin <|> mimp) tellImport
whenM (asks writeImports) $
whenJust (mimpBuiltin <|> mimp) tellImport

reportSDoc "agda2hs.name" 25 $ text
$ "-------------------------------------------------"
Expand Down
7 changes: 6 additions & 1 deletion src/Agda2Hs/Compile/Term.hs
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,14 @@ compileArgs' ty (x:xs) = do
compileDom a >>= \case
DODropped -> rest
DOInstance -> checkInstance x *> rest
DOType -> rest
DOType -> checkValidType x *> rest
DOTerm -> second . (:) <$> compileTerm (unDom a) x <*> rest

-- We check that type arguments compile to a valid Haskell type
-- before dropping them, see issue #357.
checkValidType :: Term -> C ()
checkValidType x = noWriteImports (compileType x) *> return ()

clauseToAlt :: Hs.Match () -> C (Hs.Alt ())
clauseToAlt (Hs.Match _ _ [p] rhs wh) = pure $ Hs.Alt () p rhs wh
clauseToAlt (Hs.Match _ _ ps _ _) = genericError "Pattern matching lambdas must take a single argument"
Expand Down
29 changes: 20 additions & 9 deletions src/Agda2Hs/Compile/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import Agda.Syntax.Internal
import Agda.Syntax.Common.Pretty ( prettyShow )

import Agda.TypeChecking.Pretty
import Agda.TypeChecking.Reduce ( reduce, unfoldDefinitionStep )
import Agda.TypeChecking.Reduce ( reduce, unfoldDefinitionStep, instantiate )
import Agda.TypeChecking.Substitute
import Agda.TypeChecking.Telescope

import Agda.Utils.Impossible ( __IMPOSSIBLE__ )
import Agda.Utils.List ( downFrom )
import Agda.Utils.Maybe ( ifJustM, fromMaybe )
import Agda.Utils.Monad ( ifM, unlessM, and2M, or2M )
import Agda.Utils.Monad ( ifM, whenM, unlessM, and2M, or2M )
import Agda.Utils.Size ( Sized(size) )
import Agda.Utils.Functor ( ($>) )

Expand Down Expand Up @@ -89,7 +89,9 @@ compileType t = do
reportSDoc "agda2hs.compile.type" 12 $ text "Compiling type" <+> prettyTCM t
reportSDoc "agda2hs.compile.type" 22 $ text "Compiling type" <+> pretty t

case t of
whenM (isErasedBaseType t) fail

instantiate t >>= \case
Pi a b -> do
let compileB = underAbstraction a b (compileType . unEl)
compileDomType (absName b) a >>= \case
Expand Down Expand Up @@ -126,12 +128,21 @@ compileType t = do

Sort s -> return (Hs.TyStar ())

-- TODO: we should also drop lambdas that can be erased based on their type
-- (e.g. argument is of type Level/Size or in a Prop) but currently we do
-- not have access to the type of the lambda here.
Lam argInfo restAbs
| hasQuantity0 argInfo -> underAbstraction_ restAbs compileType
| otherwise -> genericDocError =<< text "Not supported: type-level lambda" <+> prettyTCM t
Lam argInfo restAbs -> do
(body , x0) <- underAbstraction_ restAbs $ \b ->
(,) <$> compileType b <*> (hsName <$> compileDBVar 0)

-- TODO: we should also drop lambdas that can be erased based on their type
-- (e.g. argument is of type Level/Size or in a Prop) but currently we do
-- not have access to the type of the lambda here.
if | hasQuantity0 argInfo -> return body
-- Rewrite `\x -> (a -> x)` to `(->) a`
| Hs.TyFun _ a (Hs.TyVar _ y) <- body
, y == x0 -> return $ Hs.TyApp () (Hs.TyCon () $ Hs.Special () $ Hs.FunCon ()) a
-- Rewrite `\x -> ((->) x)` to `(->)`
| Hs.TyApp _ (Hs.TyCon _ (Hs.Special _ (Hs.FunCon _))) (Hs.TyVar _ y) <- body
, y == x0 -> return $ Hs.TyCon () $ Hs.Special () $ Hs.FunCon ()
| otherwise -> genericDocError =<< text "Not supported: type-level lambda" <+> prettyTCM t

_ -> fail
where fail = genericDocError =<< text "Bad Haskell type:" <?> prettyTCM t
Expand Down
2 changes: 2 additions & 0 deletions src/Agda2Hs/Compile/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ data CompileEnv = CompileEnv
-- ^ whether copatterns should be allowed when compiling patterns
, rewrites :: SpecialRules
-- ^ Special compilation rules.
, writeImports :: Bool
-- ^ whether we should add imports of compiled names
}

type Qualifier = Maybe (Maybe (Hs.ModuleName ()))
Expand Down
15 changes: 12 additions & 3 deletions src/Agda2Hs/Compile/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,17 @@ canErase :: Type -> C Bool
canErase a = do
TelV tel b <- telView a
addContext tel $ orM
[ isLevelType b -- Level
, isJust <$> isSizeType b -- Size
, isPropSort (getSort b) -- _ : Prop
[ isErasedBaseType (unEl b)
, isPropSort (getSort b) -- _ : Prop
]

isErasedBaseType :: Term -> C Bool
isErasedBaseType x = orM
[ isLevelType b -- Level
, isJust <$> isSizeType b -- Size
]
where b = El __DUMMY_SORT__ x

-- Exploits the fact that the name of the record type and the name of the record module are the
-- same, including the unique name ids.
isClassFunction :: QName -> C Bool
Expand Down Expand Up @@ -374,3 +380,6 @@ checkNoAsPatterns = \case
checkPatternInfo :: PatternInfo -> C ()
checkPatternInfo i = unless (null $ patAsNames i) $
genericDocError =<< text "not supported by agda2hs: as patterns"

noWriteImports :: C a -> C a
noWriteImports = local $ \e -> e { writeImports = False }
2 changes: 2 additions & 0 deletions test/AllFailTests.agda
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ import Fail.NonCanonicalSpecialFunction
import Fail.TypeLambda
import Fail.NonCanonicalSuperclass
import Fail.Issue125
import Fail.Issue357a
import Fail.Issue357b
2 changes: 2 additions & 0 deletions test/AllTests.agda
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import Issue317
import ErasedPatternLambda
import CustomTuples
import ProjectionLike
import FunCon

{-# FOREIGN AGDA2HS
import Issue14
Expand Down Expand Up @@ -173,4 +174,5 @@ import Issue317
import ErasedPatternLambda
import CustomTuples
import ProjectionLike
import FunCon
#-}
12 changes: 12 additions & 0 deletions test/Fail/Issue357a.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
open import Haskell.Prelude
open import Agda.Primitive

module Fail.Issue357a where

k : a → b → a
k x _ = x
{-# COMPILE AGDA2HS k #-}

testK : Nat
testK = k 42 lzero
{-# COMPILE AGDA2HS testK #-}
16 changes: 16 additions & 0 deletions test/Fail/Issue357b.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
open import Haskell.Prelude
open import Agda.Primitive

module Fail.Issue357b where

k : a → b → a
k x _ = x
{-# COMPILE AGDA2HS k #-}

l : Level → Nat
l = k 42
{-# COMPILE AGDA2HS l #-}

testK : Nat
testK = l lzero
{-# COMPILE AGDA2HS testK #-}
22 changes: 22 additions & 0 deletions test/FunCon.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

open import Haskell.Prelude

data D1 (t : Set → Set) : Set where
C1 : t Bool → D1 t

{-# COMPILE AGDA2HS D1 #-}

f1 : D1 (λ a → Int → a)
f1 = C1 (_== 0)

{-# COMPILE AGDA2HS f1 #-}

data D2 (t : Set → Set → Set) : Set where
C2 : t Int Int → D2 t

{-# COMPILE AGDA2HS D2 #-}

f2 : D2 (λ a b → a → b)
f2 = C2 (_+ 1)

{-# COMPILE AGDA2HS f2 #-}
1 change: 1 addition & 0 deletions test/golden/AllTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ import Issue317
import ErasedPatternLambda
import CustomTuples
import ProjectionLike
import FunCon

12 changes: 12 additions & 0 deletions test/golden/FunCon.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module FunCon where

data D1 t = C1 (t Bool)

f1 :: D1 ((->) Int)
f1 = C1 (== 0)

data D2 t = C2 (t Int Int)

f2 :: D2 (->)
f2 = C2 (+ 1)

2 changes: 2 additions & 0 deletions test/golden/Issue357a.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test/Fail/Issue357a.agda:10,1-6
Bad Haskell type: Level
2 changes: 2 additions & 0 deletions test/golden/Issue357b.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test/Fail/Issue357b.agda:10,1-2
Bad Haskell type: Level
Loading