Skip to content

Commit f14e083

Browse files
committed
Fail when using enum syntax with non-Int literals (#364)
1 parent a3c8c0c commit f14e083

File tree

5 files changed

+59
-11
lines changed

5 files changed

+59
-11
lines changed

src/Fay.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,23 @@ module Fay
2020
) where
2121

2222
import Fay.Compiler
23-
import Fay.Compiler.Misc (ioWarn,
24-
printSrcSpanInfo)
23+
import Fay.Compiler.Misc (ioWarn, printSrcSpanInfo)
2524
import Fay.Compiler.Packages
2625
import Fay.Compiler.Typecheck
2726
import qualified Fay.Exts as F
2827
import Fay.Types
2928

3029
import Control.Applicative
3130
import Control.Monad
32-
import Data.Aeson (encode)
33-
import qualified Data.ByteString.Lazy as L
31+
import Data.Aeson (encode)
32+
import qualified Data.ByteString.Lazy as L
3433
import Data.Default
3534
import Data.List
3635
import Language.Haskell.Exts.Annotated (prettyPrint)
3736
import Language.Haskell.Exts.Annotated.Syntax
3837
import Language.Haskell.Exts.SrcLoc
3938
import Paths_fay
40-
import SourceMap (generate)
39+
import SourceMap (generate)
4140
import SourceMap.Types
4241
import System.FilePath
4342

@@ -166,6 +165,7 @@ showCompileError e = case e of
166165
ShouldBeDesugared s -> "Expected this to be desugared (this is a bug): " ++ s
167166
UnableResolveQualified qname -> "unable to resolve qualified names " ++ prettyPrint qname
168167
UnsupportedDeclaration d -> "unsupported declaration: " ++ prettyPrint d
168+
UnsupportedEnum{} -> "only Int is allowed in enum expressions"
169169
UnsupportedExportSpec es -> "unsupported export specification: " ++ prettyPrint es
170170
UnsupportedExpression expr -> "unsupported expression syntax: " ++ prettyPrint expr
171171
UnsupportedFieldPattern p -> "unsupported field pattern: " ++ prettyPrint p

src/Fay/Compiler/Desugar.hs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Fay.Compiler.Desugar
66
(desugar
77
) where
88

9+
import Fay.Exts.NoAnnotation (unAnn)
910
import Fay.Types (CompileError (..))
1011

1112
import Control.Applicative
@@ -116,10 +117,10 @@ desugarExp ex = case ex of
116117
Paren l e -> Paren l <$> desugarExp e
117118
RecConstr l q f -> RecConstr l (desugarQName q) <$> mapM desugarFieldUpdate f
118119
RecUpdate l e f -> RecUpdate l <$> desugarExp e <*> mapM desugarFieldUpdate f
119-
EnumFrom l e -> EnumFrom l <$> desugarExp e
120-
EnumFromTo l e1 e2 -> EnumFromTo l <$> desugarExp e1 <*> desugarExp e2
121-
EnumFromThen l e1 e2 -> EnumFromThen l <$> desugarExp e1 <*> desugarExp e2
122-
EnumFromThenTo l e1 e2 e3 -> EnumFromThenTo l <$> desugarExp e1 <*> desugarExp e2 <*> desugarExp e3
120+
e@(EnumFrom l e1) -> checkEnum e >> (EnumFrom l <$> desugarExp e1)
121+
e@(EnumFromTo l e1 e2) -> checkEnum e >> (EnumFromTo l <$> desugarExp e1 <*> desugarExp e2)
122+
e@(EnumFromThen l e1 e2) -> checkEnum e >> (EnumFromThen l <$> desugarExp e1 <*> desugarExp e2)
123+
e@(EnumFromThenTo l e1 e2 e3) -> checkEnum e >> (EnumFromThenTo l <$> desugarExp e1 <*> desugarExp e2 <*> desugarExp e3)
123124
ListComp l e qs -> ListComp l <$> desugarExp e <*> mapM desugarQualStmt qs
124125
ParComp l e qqs -> ParComp l <$> desugarExp e <*> mapM (mapM desugarQualStmt) qqs
125126
ExpTypeSig l e t -> ExpTypeSig l <$> desugarExp e <*> return (desugarType t)
@@ -286,3 +287,32 @@ desugarTupleSec l xs = do
286287
(rn, re) <- genSlotNames l rest ns
287288
e' <- desugarExp e
288289
return (rn, e' : re)
290+
291+
-- | We only have Enum instance for Int, but GHC hard codes [x..y]
292+
-- syntax to GHC.Base.Enum instead of using our Enum class so we check
293+
-- for obviously incorrect usages and throw an error on them. This can
294+
-- only checks literals, but it helps a bit.
295+
checkEnum :: Exp l -> Desugar ()
296+
checkEnum exp = case exp of
297+
EnumFrom _ e -> checkIntOrUnknown [e]
298+
EnumFromTo _ e1 e2 -> checkIntOrUnknown [e1,e2]
299+
EnumFromThen _ e1 e2 -> checkIntOrUnknown [e1,e2]
300+
EnumFromThenTo _ e1 e2 e3 -> checkIntOrUnknown [e1,e2,e3]
301+
_ -> error "checkEnum: Only for Enums"
302+
where
303+
checkIntOrUnknown :: [Exp l] -> Desugar ()
304+
checkIntOrUnknown es = if any isIntOrUnknown es
305+
then return ()
306+
else throwError . UnsupportedEnum $ unAnn exp
307+
isIntOrUnknown :: Exp l -> Bool
308+
isIntOrUnknown e = case e of
309+
Con {} -> False
310+
Lit _ Int{} -> True
311+
Lit {} -> False
312+
Tuple {} -> False
313+
List {} -> False
314+
EnumFrom {} -> False
315+
EnumFromTo {} -> False
316+
EnumFromThen {} -> False
317+
EnumFromThenTo {} -> False
318+
_ -> True

src/Fay/Types.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ data CompileError
208208
| ShouldBeDesugared String
209209
| UnableResolveQualified N.QName
210210
| UnsupportedDeclaration S.Decl
211+
| UnsupportedEnum N.Exp
211212
| UnsupportedExportSpec N.ExportSpec
212213
| UnsupportedExpression S.Exp
213214
| UnsupportedFieldPattern S.PatField

src/tests/Test/Compile.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ import System.Environment
1818
import Test.Framework
1919
import Test.Framework.Providers.HUnit
2020
import Test.Framework.TH
21-
import Test.HUnit (Assertion, assertBool,
22-
assertEqual, assertFailure)
21+
import Test.HUnit (Assertion, assertBool, assertEqual, assertFailure)
2322
import Test.Util
2423

2524
tests :: Test
@@ -97,3 +96,12 @@ case_strictWrapper = do
9796
defConf :: CompileConfig
9897
defConf = addConfigDirectoryIncludePaths ["tests/"]
9998
$ def { configTypecheck = False }
99+
100+
case_charEnum :: Assertion
101+
case_charEnum = do
102+
whatAGreatFramework <- fmap (lookup "HASKELL_PACKAGE_SANDBOX") getEnvironment
103+
res <- compileFile defConf { configPackageConf = whatAGreatFramework, configTypecheck = True, configFilePath = Just "tests/Compile/EnumChar.hs" } "tests/Compile/EnumChar.hs"
104+
case res of
105+
Left UnsupportedEnum{} -> return ()
106+
Left l -> assertFailure $ "Should have failed with UnsupportedEnum, but failed with: " ++ show l
107+
Right _ -> assertFailure $ "Should have failed with UnsupportedEnum, but compiled"

tests/Compile/EnumChar.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module EnumChar where
2+
3+
import Prelude
4+
5+
f :: [Char]
6+
f = ['a'..'z']
7+
8+
main :: Fay ()
9+
main = print f

0 commit comments

Comments
 (0)