Skip to content

Commit

Permalink
[ fix #119 ] Throw an error when an unknown name appears in generated…
Browse files Browse the repository at this point in the history
… Haskell code
  • Loading branch information
jespercockx committed Oct 10, 2024
1 parent 6a9885a commit 62617a5
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/Agda2Hs/Compile/ClassInstance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ resolveStringName s = do
lookupDefaultImplementations :: QName -> [Hs.Name ()] -> C [Definition]
lookupDefaultImplementations recName fields = do
let modName = qnameToMName recName
isField f _ = (`elem` fields) . unQual <$> compileQName f
isField f _ = (`elem` fields) <$> compileName (qnameName f)
findDefinitions isField modName

classMemberNames :: Definition -> C [Hs.Name ()]
Expand Down
28 changes: 26 additions & 2 deletions src/Agda2Hs/Compile/Name.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import qualified Agda.Syntax.Common.Pretty as P
import Agda.TypeChecking.Datatypes ( isDataOrRecordType )
import Agda.TypeChecking.Pretty
import Agda.TypeChecking.Records ( isRecordConstructor )
import Agda.TypeChecking.Warnings ( warning )

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

Expand Down Expand Up @@ -102,11 +104,28 @@ compileQName f
Just (r, def) | not (_recNamedCon def) -> r -- use record name for unnamed constructors
_ -> f
hf0 <- compileName (qnameName f)
(hf, mimpBuiltin) <- fromMaybe (hf0, Nothing) <$> isSpecialName f
special <- isSpecialName f
let (hf, mimpBuiltin) = fromMaybe (hf0, Nothing) special

parent <- parentName f
par <- traverse (compileName . qnameName) parent
let mod0 = qnameModule $ fromMaybe f parent
mod <- compileModuleName mod0

existsInHaskell <- orM
[ pure $ isJust special
, pure $ isPrimModule mod
, hasCompilePragma f
, isClassFunction f
, isWhereFunction f
, maybe (pure False) hasCompilePragma parent
]

unless existsInHaskell $ do
reportSDoc "agda2hs.name" 20 $ text "DOES NOT EXIST IN HASKELL"
typeError $ CustomBackendError "agda2hs" $ P.text $
"Symbol " ++ Hs.prettyPrint hf ++ " is missing a COMPILE pragma or rewrite rule"

currMod <- hsTopLevelModuleName <$> asks currModule
let skipModule = mod == currMod
|| isJust mimpBuiltin
Expand Down Expand Up @@ -185,14 +204,19 @@ compileQName f

mkImport mod qual par hf maybeIsType
-- make sure the Prelude is properly qualified
| any (`isPrefixOf` pp mod) primModules
| isPrimModule mod
= if isQualified qual then
let mod' = hsModuleName "Prelude"
in (mod', Just (Import mod' qual Nothing hf maybeIsType))
else (mod, Nothing)
| otherwise
= (mod, Just (Import mod qual par hf maybeIsType))

isWhereFunction :: QName -> C Bool
isWhereFunction f = do
whereMods <- asks whereModules
return $ any (qnameModule f `isLeChildModuleOf`) whereMods

hsTopLevelModuleName :: TopLevelModuleName -> Hs.ModuleName ()
hsTopLevelModuleName = hsModuleName . intercalate "." . map unpack
. List1.toList . moduleNameParts
Expand Down
12 changes: 9 additions & 3 deletions src/Agda2Hs/Compile/Record.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Agda.Syntax.Common ( Arg(unArg), defaultArg )
import Agda.Syntax.Internal
import Agda.Syntax.Common.Pretty ( prettyShow )

import Agda.TypeChecking.Pretty ( ($$), (<+>), text, vcat, prettyTCM )
import Agda.TypeChecking.Pretty ( ($$), (<+>), text, vcat, prettyTCM, pretty, prettyList_ )
import Agda.TypeChecking.Substitute ( TelV(TelV), Apply(apply) )
import Agda.TypeChecking.Telescope

Expand All @@ -38,7 +38,10 @@ withMinRecord :: QName -> C a -> C a
withMinRecord m = local $ \ e -> e { minRecordName = Just (qnameToMName m) }

compileMinRecord :: [Hs.Name ()] -> QName -> C MinRecord
compileMinRecord fieldNames m = do
compileMinRecord fieldNames m = withMinRecord m $ do
reportSDoc "agda2hs.record.min" 20 $
text "Compiling minimal record" <+> pretty m <+>
text "with field names" <+> prettyList_ (map (text . pp) fieldNames)
rdef <- getConstInfo m
definedFields <- classMemberNames rdef
let Record{recPars = npars, recTel = tel} = theDef rdef
Expand All @@ -48,9 +51,12 @@ compileMinRecord fieldNames m = do
-- We can't simply compileFun here for two reasons:
-- * it has an explicit dictionary argument
-- * it's using the fields and definitions from the minimal record and not the parent record
compiled <- withMinRecord m $ addContext (defaultDom rtype) $ compileLocal $
compiled <- addContext (defaultDom rtype) $ compileLocal $
fmap concat $ traverse (compileFun False) defaults
let declMap = Map.fromList [ (definedName c, def) | def@(Hs.FunBind _ (c : _)) <- compiled ]
reportSDoc "agda2hs.record.min" 20 $
text "Done compiling minimal record" <+> pretty m <+>
text "defined fields: " <+> prettyList_ (map (text . pp) definedFields)
return (definedFields, declMap)


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 @@ -22,6 +22,7 @@ import qualified Language.Haskell.Exts.Comments as Hs
import Agda.Compiler.Backend
import Agda.Syntax.Position ( Range )
import Agda.Syntax.TopLevelModuleName ( TopLevelModuleName )
import Agda.TypeChecking.Warnings ( MonadWarning )
import Agda.Utils.Null
import Agda.Utils.Impossible

Expand Down Expand Up @@ -174,6 +175,7 @@ instance MonadStConcreteNames C where
runStConcreteNames m = rwsT $ \r s -> runStConcreteNames $ StateT $ \ns -> do
((x, ns'), s', w) <- runRWST (runStateT m ns) r s
pure ((x, s', w), ns')
instance MonadWarning C where
instance IsString a => IsString (C a) where fromString = pure . fromString
instance PureTCM C where
instance Null a => Null (C a) where
Expand Down
15 changes: 15 additions & 0 deletions src/Agda2Hs/Compile/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Control.Monad.State ( put, modify )

import Data.Maybe ( isJust )
import qualified Data.Map as M
import Data.List ( isPrefixOf )

import qualified Language.Haskell.Exts as Hs

Expand Down Expand Up @@ -60,6 +61,8 @@ primModules =
, "Haskell.Law"
]

isPrimModule :: Hs.ModuleName () -> Bool
isPrimModule mod = any (`isPrefixOf` pp mod) primModules

concatUnzip :: [([a], [b])] -> ([a], [b])
concatUnzip = (concat *** concat) . unzip
Expand Down Expand Up @@ -152,6 +155,18 @@ isErasedBaseType x = orM
]
where b = El __DUMMY_SORT__ x

hasCompilePragma :: QName -> C Bool
hasCompilePragma q = processPragma q <&> \case
NoPragma{} -> False
InlinePragma{} -> True
DefaultPragma{} -> True
ClassPragma{} -> True
ExistingClassPragma{} -> True
UnboxPragma{} -> True
TransparentPragma{} -> True
NewTypePragma{} -> True
DerivePragma{} -> True

-- 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
17 changes: 11 additions & 6 deletions test/Delay.agda
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ open import Haskell.Extra.Delay
open import Agda.Builtin.Size

postulate
div : Int Int Int
mod : Int Int Int
div' : Int Int Int
mod' : Int Int Int

even : Int Bool
even x = mod x 2 == 0
{-# COMPILE AGDA2HS div' #-}
{-# COMPILE AGDA2HS mod' #-}

even' : Int Bool
even' x = mod' x 2 == 0

{-# COMPILE AGDA2HS even' #-}

collatz : {@0 j} Int Delay Int j
collatz x =
collatz x =
if x == 0 then now 0
else if even x then later (λ where .force collatz (div x 2))
else if even' x then later (λ where .force collatz (div' x 2))
else later λ where .force collatz (3 * x + 1)

{-# COMPILE AGDA2HS collatz #-}
13 changes: 13 additions & 0 deletions test/Fail/Issue119.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Fail.Issue119 where

open import Haskell.Prelude

aaa : Int
aaa = 21

-- Oops, forgot compile pragma for aaa

bbb : Int
bbb = aaa + aaa

{-# COMPILE AGDA2HS bbb #-}
8 changes: 1 addition & 7 deletions test/TypeOperatorImport.agda
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ module TypeOperatorImport where

{-# FOREIGN AGDA2HS {-# LANGUAGE TypeOperators #-} #-}

open import Agda.Builtin.Unit
open import Agda.Builtin.Bool
open import Haskell.Prelude using (_∘_)
open import Haskell.Prelude hiding (_<_)
open import TypeOperatorExport

not : Bool Bool
not true = false
not false = true

test1 : ⊤ < Bool
test1 = tt
{-# COMPILE AGDA2HS test1 #-}
Expand Down
11 changes: 10 additions & 1 deletion test/golden/Delay.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
module Delay where

div' :: Int -> Int -> Int
div' = error "postulate: Int -> Int -> Int"

mod' :: Int -> Int -> Int
mod' = error "postulate: Int -> Int -> Int"

even' :: Int -> Bool
even' x = mod' x 2 == 0

collatz :: Int -> Int
collatz x
= if x == 0 then 0 else
if even x then collatz (div x 2) else collatz (3 * x + 1)
if even' x then collatz (div' x 2) else collatz (3 * x + 1)

2 changes: 2 additions & 0 deletions test/golden/Issue119.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test/Fail/Issue119.agda:10,1-4
agda2hs: Symbol aaa is missing a COMPILE pragma or rewrite rule

0 comments on commit 62617a5

Please sign in to comment.