-
Notifications
You must be signed in to change notification settings - Fork 13
/
HaskellFrontend.hs
1399 lines (1287 loc) · 59.1 KB
/
HaskellFrontend.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
module Gibbon.HaskellFrontend
( parseFile, primMap, multiArgsToOne, desugarLinearExts ) where
import Control.Monad
import Data.Foldable ( foldrM, foldl' )
import Data.Maybe (catMaybes, isJust)
import qualified Data.Map as M
import qualified Data.Set as S
import Data.IORef
import Language.Haskell.Exts.Extension
import Language.Haskell.Exts.Parser
import Language.Haskell.Exts.Syntax as H
import Language.Haskell.Exts.Pretty
import Language.Haskell.Exts.SrcLoc
import Language.Haskell.Exts.CPP
import System.Environment ( getEnvironment )
import System.Directory
import System.FilePath
import System.Process
import System.Exit
import System.IO
import Gibbon.L0.Syntax as L0
import Gibbon.Common
import Gibbon.DynFlags
--------------------------------------------------------------------------------
{-
Importing modules:
~~~~~~~~~~~~~~~~~~
We use the same notion of search paths as GHC[1], except that GHC also has a
set of "known" packages (base, containers, etc.) where it looks for modules.
Gibbon doesn't have those, and the rootset for our search is a singleton {"."}.
Consider this directory structure:
.
|── A
| └── B
| |── C.hs
| |── D.hs
| |── Foo.hs
|── Bar.hs
If Bar.hs has a `import A.B.C`, we look for a file `./A/B/C.hs`. However, note
that this design is much more primitive than what Cabal/Stack allow. Can A.B.C
import A.B.D? It depends on where we invoke GHC from. If we do it from ".", then
yes, because A.B.D exists at A/B/D.hs. But if we run "ghc C.hs", it will fail since
it expects A.B.D to be at A/B/A/B/D.hs.
[1] https://downloads.haskell.org/ghc/8.6.4/docs/html/users_guide/separate_compilation.html?#the-search-path
-}
parseFile :: Config -> FilePath -> IO (PassM Prog0)
parseFile cfg path = do
pstate0_ref <- newIORef emptyParseState
parseFile' cfg pstate0_ref [] path
data ParseState = ParseState
{ imported :: M.Map (String, FilePath) Prog0 }
emptyParseState :: ParseState
emptyParseState = ParseState M.empty
parseMode :: ParseMode
parseMode = defaultParseMode { extensions = [ EnableExtension ScopedTypeVariables
, EnableExtension CPP
, EnableExtension TypeApplications
]
++ (extensions defaultParseMode)
}
parseFile' :: Config -> IORef ParseState -> [String] -> FilePath -> IO (PassM Prog0)
parseFile' cfg pstate_ref import_route path = do
when (gopt Opt_GhcTc (dynflags cfg)) $
typecheckWithGhc cfg path
str <- readFile path
let cleaned = removeLinearArrows str
-- let parsed = parseModuleWithMode parseMode cleaned
parsed <- parseFileContentsWithCommentsAndCPP defaultCpphsOptions parseMode cleaned
case parsed of
ParseOk (hs,_comments) -> desugarModule cfg pstate_ref import_route (takeDirectory path) hs
ParseFailed loc er -> do
error ("haskell-src-exts failed: " ++ er ++ ", at " ++ prettyPrint loc)
-- | ASSUMPTION: gibbon-stdlib is available to Cabal.
--
-- Currently 'run_all_tests.sh' installs it with 'cabal v1-install . -w ghc-9.0.1'.
typecheckWithGhc :: Config -> FilePath -> IO ()
typecheckWithGhc cfg path = do
when (verbosity cfg >= 3) $
putStr " [compiler] Running pass, GHC typechecker\n => "
let cmd = "ghc-9.0.1 -package gibbon-stdlib-0.1 -XNoImplicitPrelude -fno-code " ++ path
(_, Just hout, Just herr, phandle) <-
createProcess (shell cmd)
{ std_out = CreatePipe
, std_err = CreatePipe
, cwd = Just (takeDirectory path)
}
exitCode <- waitForProcess phandle
case exitCode of
ExitSuccess -> do
when (verbosity cfg >= 3) $ do
out <- hGetContents hout
err <- hGetContents herr
putStrLn out
putStrLn err
pure ()
ExitFailure _ -> do
err <- hGetContents herr
error err
-- | Really basic, and won't catch every occurence of a linear arrow.
--
-- But its only a stop-gap until we move to ghc-lib-parser, which can parse
-- linear types and other things not supported by haskell-src-exts (e.g. CPP).
removeLinearArrows :: String -> String
removeLinearArrows str =
fst $
foldr (\c (acc,saw_one) ->
if saw_one && c == '%'
then (acc, False)
else if saw_one && c /= '%'
then (c:'1':acc, False)
else if c == '1'
then (acc, True)
else (c:acc, False))
([],False)
str
{-
- messup up indendataion and causes compilation errors.
-
- unlines .
- map (unwords .
- map (\w -> if w == "%1->" || w == "%1 ->"
- then "->"
- else w) .
- words) .
- lines
-}
data TopLevel
= HDDef (DDef Ty0)
| HFunDef (FunDef Exp0)
| HMain (Maybe (Exp0, Ty0))
| HInline Var
deriving (Show, Eq)
type TopTyEnv = TyEnv TyScheme
type TypeSynEnv = M.Map TyCon Ty0
desugarModule :: (Show a, Pretty a)
=> Config -> IORef ParseState -> [String] -> FilePath -> Module a -> IO (PassM Prog0)
desugarModule cfg pstate_ref import_route dir (Module _ head_mb _pragmas imports decls) = do
let type_syns = foldl collectTypeSynonyms M.empty decls
-- Since top-level functions and their types can't be declared in
-- single top-level declaration we first collect types and then collect
-- definitions.
funtys = foldr (collectTopTy type_syns) M.empty decls
imported_progs :: [PassM Prog0] <- mapM (processImport cfg pstate_ref (mod_name : import_route) dir) imports
let prog = do
toplevels <- catMaybes <$> mapM (collectTopLevel type_syns funtys) decls
let (defs,_vars,funs,inlines,main) = foldr classify init_acc toplevels
funs' = foldr (\v acc -> M.update (\fn@(FunDef{funMeta}) -> Just (fn { funMeta = funMeta { funInline = Inline }})) v acc) funs inlines
imported_progs' <- mapM id imported_progs
let (defs0,funs0) =
foldr
(\Prog{ddefs,fundefs} (defs1,funs1) ->
let ddef_names1 = M.keysSet defs1
ddef_names2 = M.keysSet ddefs
fn_names1 = M.keysSet funs1
fn_names2 = M.keysSet fundefs
em1 = S.intersection ddef_names1 ddef_names2
em2 = S.intersection fn_names1 fn_names2
conflicts1 = foldr
(\d acc ->
if (ddefs M.! d) /= (defs1 M.! d)
then d : acc
else acc)
[]
em1
conflicts2 = foldr
(\f acc ->
if (fundefs M.! f) /= (funs1 M.! f)
then dbgTraceIt (sdoc ((fundefs M.! f), (funs1 M.! f))) (f : acc)
else acc)
[]
em2
in case (conflicts1, conflicts2) of
([], []) -> (M.union ddefs defs1, M.union fundefs funs1)
(_x:_xs,_) -> error $ "Conflicting definitions of " ++ show conflicts1 ++ " found in " ++ mod_name
(_,_x:_xs) -> error $ "Conflicting definitions of " ++ show (S.toList em2) ++ " found in " ++ mod_name)
(defs, funs')
imported_progs'
pure (Prog defs0 funs0 main)
pure prog
where
init_acc = (M.empty, M.empty, M.empty, S.empty, Nothing)
mod_name = moduleName head_mb
moduleName :: Maybe (ModuleHead a) -> String
moduleName Nothing = "Main"
moduleName (Just (ModuleHead _ mod_name1 _warnings _exports)) =
mnameToStr mod_name1
classify thing (defs,vars,funs,inlines,main) =
case thing of
HDDef d -> (M.insert (tyName d) d defs, vars, funs, inlines, main)
HFunDef f -> (defs, vars, M.insert (funName f) f funs, inlines, main)
HMain m ->
case main of
Nothing -> (defs, vars, funs, inlines, m)
Just _ -> error $ "A module cannot have two main expressions."
++ show mod_name
HInline v -> (defs,vars,funs,S.insert v inlines,main)
desugarModule _ _ _ _ m = error $ "desugarModule: " ++ prettyPrint m
stdlibModules :: [String]
stdlibModules =
[ "Gibbon.Prim"
, "Gibbon.Prelude"
, "Gibbon.Vector"
, "Gibbon.Vector.Parallel"
, "Gibbon.List"
, "Gibbon.PList"
, "Gibbon.ByteString"
]
processImport :: Config -> IORef ParseState -> [String] -> FilePath -> ImportDecl a -> IO (PassM Prog0)
processImport cfg pstate_ref import_route dir decl@ImportDecl{..}
-- When compiling with Gibbon, we should *NOT* inline things defined in Gibbon.Prim.
| mod_name == "Gibbon.Prim" = pure (pure (Prog M.empty M.empty Nothing))
| otherwise = do
when (mod_name `elem` import_route) $
error $ "Circular dependency detected. Import path: "++ show (mod_name : import_route)
when (importQualified) $ error $ "Qualified imports not supported yet. Offending import: " ++ prettyPrint decl
when (isJust importAs) $ error $ "Module aliases not supported yet. Offending import: " ++ prettyPrint decl
when (isJust importSpecs) $ error $ "Selective imports not supported yet. Offending import: " ++ prettyPrint decl
(ParseState imported) <- readIORef pstate_ref
mod_fp <- if mod_name `elem` stdlibModules
then stdlibImportPath mod_name
else modImportPath importModule dir
dbgTrace 5 ("Looking at " ++ mod_name) (pure ())
dbgTrace 5 ("Previously imported: " ++ show (M.keysSet imported)) (pure ())
prog <- case M.lookup (mod_name, mod_fp) imported of
Just prog -> do
dbgTrace 5 ("Already imported " ++ mod_name) (pure ())
pure prog
Nothing -> do
dbgTrace 5 ("Importing " ++ mod_name ++ " from " ++ mod_fp) (pure ())
prog0 <- parseFile' cfg pstate_ref import_route mod_fp
(ParseState imported') <- readIORef pstate_ref
let (prog0',_) = defaultRunPassM prog0
let imported'' = M.insert (mod_name, mod_fp) prog0' imported'
let pstate' = ParseState { imported = imported'' }
writeIORef pstate_ref pstate'
pure prog0'
pure (pure prog)
where
mod_name = mnameToStr importModule
stdlibImportPath :: String -> IO FilePath
stdlibImportPath mod_name = do
env <- getEnvironment
let stdlibPath = case lookup "GIBBONDIR" env of
Just p -> p </> "gibbon-stdlib" </> modNameToFilename mod_name
-- Assume we're running from the compiler dir!
Nothing -> modNameToFilename mod_name
e <- doesFileExist stdlibPath
unless e $ error$ "stdlib.hs file not found at path: "++stdlibPath
++"\n Consider setting GIBBONDIR to repo root.\n"
pure stdlibPath
where
modNameToFilename :: String -> String
modNameToFilename "Gibbon.Prelude" = "Gibbon" </> "Prelude.hs"
modNameToFilename "Gibbon.Vector" = "Gibbon" </> "Vector.hs"
modNameToFilename "Gibbon.Vector.Parallel" = "Gibbon" </> "Vector" </> "Parallel.hs"
modNameToFilename "Gibbon.List" = "Gibbon" </> "List.hs"
modNameToFilename "Gibbon.PList" = "Gibbon" </> "PList.hs"
modNameToFilename "Gibbon.ByteString" = "Gibbon" </> "ByteString.hs"
modNameToFilename oth = error $ "Unknown module: " ++ oth
modImportPath :: ModuleName a -> String -> IO FilePath
modImportPath importModule dir = do
let mod_name = mnameToStr importModule
mb_fp <- findModule dir importModule
case mb_fp of
Nothing -> error $ "Cannot find module: " ++
show mod_name ++ " in " ++ dir
Just mod_fp -> pure mod_fp
-- | Look for a module on the filesystem.
findModule :: FilePath -> ModuleName a -> IO (Maybe FilePath)
findModule dir m = do
let mod_fp = dir </> moduleNameToSlashes m <.> "hs"
doesFileExist mod_fp >>= \b ->
if b
then pure $ Just mod_fp
else pure Nothing
-- | Returns the string version of the module name, with dots replaced by slashes.
--
moduleNameToSlashes :: ModuleName a -> String
moduleNameToSlashes (ModuleName _ s) = dots_to_slashes s
where dots_to_slashes = map (\c -> if c == '.' then pathSeparator else c)
builtinTys :: S.Set Var
builtinTys = S.fromList $
[ "Int", "Float", "Bool", "Sym", "SymHash", "IntHash", "SymSet", "SymDict", "Arena", "Vector" ]
keywords :: S.Set Var
keywords = S.fromList $ map toVar $
-- These cannot be added to primMap because they all require special handling while parsing.
--
[ "quote", "bench", "error", "par", "spawn", "is_big"
-- operations on vectors
, "valloc", "vnth", "vlength", "vslice", "inplacevupdate",
"vsort", "inplacevsort", "vfree", "vfree2"
-- parallel dictionaries
, "alloc_pdict", "insert_pdict", "lookup_pdict", "member_pdict", "fork_pdict", "join_pdict"
-- linked lists
, "alloc_ll", "is_empty_ll", "cons_ll", "head_ll", "tail_ll", "free_ll", "free2_ll", "copy_ll"
] ++ M.keys primMap
desugarTopType :: (Show a, Pretty a) => TypeSynEnv -> Type a -> TyScheme
desugarTopType type_syns ty =
case ty of
-- forall tvs ty.
TyForall _ mb_tvbind _ ty1 ->
let tyvars = case mb_tvbind of
Just bnds -> map desugarTyVarBind bnds
Nothing -> []
in ForAll tyvars (desugarType type_syns ty1)
-- quantify over all tyvars.
_ -> let ty' = desugarType type_syns ty
tyvars = tyVarsInTy ty'
in ForAll tyvars ty'
desugarType :: (Show a, Pretty a) => TypeSynEnv -> Type a -> Ty0
desugarType type_syns ty =
case ty of
H.TyVar _ (Ident _ t) -> L0.TyVar $ UserTv (toVar t)
TyTuple _ Boxed tys -> ProdTy (map (desugarType type_syns) tys)
TyCon _ (Special _ (UnitCon _)) -> ProdTy []
TyCon _ (UnQual _ (Ident _ "Int")) -> IntTy
TyCon _ (UnQual _ (Ident _ "Char")) -> CharTy
TyCon _ (UnQual _ (Ident _ "Float"))-> FloatTy
TyCon _ (UnQual _ (Ident _ "Bool")) -> BoolTy
TyCon _ (UnQual _ (Ident _ "Sym")) -> SymTy0
TyCon _ (UnQual _ (Ident _ "SymSet")) -> SymSetTy
TyCon _ (UnQual _ (Ident _ "SymHash")) -> SymHashTy
TyCon _ (UnQual _ (Ident _ "IntHash")) -> IntHashTy
TyCon _ (UnQual _ (Ident _ con)) ->
case M.lookup con type_syns of
Nothing -> PackedTy con []
Just ty' -> ty'
TyFun _ t1 t2 -> let t1' = desugarType type_syns t1
t2' = desugarType type_syns t2
in ArrowTy [t1'] t2'
TyParen _ ty1 -> desugarType type_syns ty1
TyApp _ tycon arg ->
let ty' = desugarType type_syns tycon in
case ty' of
PackedTy con tyargs ->
case (con,tyargs) of
("Vector",[]) -> VectorTy (desugarType type_syns arg)
("List",[]) -> ListTy (desugarType type_syns arg)
("PDict",[]) ->
let arg' = desugarType type_syns arg in
case arg' of
ProdTy [k, v] -> PDictTy k v
_ -> error $ "desugarType: Unexpected PDictTy argument: " ++ show arg'
_ ->
case M.lookup con type_syns of
Nothing -> PackedTy con (tyargs ++ [desugarType type_syns arg])
Just ty'' -> ty''
_ -> error $ "desugarType: Unexpected type arguments: " ++ show ty'
_ -> error $ "desugarType: Unsupported type: " ++ show ty
-- Like 'desugarTopType' but understands boxity.
desugarTopType' :: (Show a, Pretty a) => TypeSynEnv -> Type a -> (IsBoxed, TyScheme)
desugarTopType' type_syns ty =
case ty of
-- forall tvs ty.
TyForall _ mb_tvbind _ ty1 ->
let tyvars = case mb_tvbind of
Just bnds -> map desugarTyVarBind bnds
Nothing -> []
(boxity, ty') = desugarType' type_syns ty1
in (boxity, ForAll tyvars ty')
-- quantify over all tyvars.
_ -> let (boxity, ty') = desugarType' type_syns ty
tyvars = tyVarsInTy ty'
in (boxity, ForAll tyvars ty')
-- Like 'desugarType' but understands boxity.
desugarType' :: (Show a, Pretty a) => TypeSynEnv -> Type a -> (IsBoxed, Ty0)
desugarType' type_syns ty =
case ty of
TyBang _ _ (NoUnpack _) ty1 -> (True, desugarType type_syns ty1)
_ -> (False, desugarType type_syns ty)
-- | Transform a multi-argument function type to one where all inputs are a
-- single tuple argument. E.g. (a -> b -> c -> d) => ((a,b,c) -> d).
unCurryTopTy :: TyScheme -> TyScheme
unCurryTopTy (ForAll tyvars ty) = ForAll tyvars (unCurryTy ty)
unCurryTy :: Ty0 -> Ty0
unCurryTy ty1 =
case ty1 of
ArrowTy _ ArrowTy{} ->
let (a,b) = go [] ty1
a' = map unCurryTy a
in ArrowTy a' b
_ -> ty1
where
go :: [Ty0] -> Ty0 -> ([Ty0], Ty0)
go acc ty =
case ty of
ArrowTy as b -> (go (acc++as) b)
_ -> (acc,ty)
-- ^ A map between SExp-frontend prefix function names, and Gibbon
-- abstract Primops.
primMap :: M.Map String (Prim a)
primMap = M.fromList
[ ("+", AddP)
, ("-", SubP)
, ("*", MulP)
, ("/", DivP)
, ("div", DivP)
, ("^", ExpP)
, (".+.", FAddP)
, (".-.", FSubP)
, (".*.", FMulP)
, ("./.", FDivP)
, ("sqrt", FSqrtP)
, ("==", EqIntP)
, (".==.", EqFloatP)
, ("*==*", EqCharP)
, ("<", LtP)
, (">", GtP)
, ("<=", LtEqP)
, (">=", GtEqP)
, (".<.", FLtP)
, (".>.", FGtP)
, (".<=.", FLtEqP)
, (".>=.", FGtEqP)
, ("tan", FTanP)
, ("mod", ModP)
, ("||" , OrP)
, ("&&", AndP)
, ("eqsym", EqSymP)
, ("rand", RandP)
, ("frand", FRandP)
, ("intToFloat", IntToFloatP)
, ("floatToInt", FloatToIntP)
, ("sizeParam", SizeParam)
, ("getNumProcessors", GetNumProcessors)
, ("True", MkTrue)
, ("False", MkFalse)
, ("gensym", Gensym)
, ("printint", PrintInt)
, ("printchar", PrintChar)
, ("printfloat", PrintFloat)
, ("printbool", PrintBool)
, ("printsym", PrintSym)
, ("readint", ReadInt)
, ("is_big", IsBig)
, ("empty_set", SymSetEmpty)
, ("insert_set", SymSetInsert)
, ("contains_set", SymSetContains)
, ("empty_hash", SymHashEmpty)
, ("insert_hash", SymHashInsert)
, ("lookup_hash", SymHashLookup)
, ("contains_hash", SymHashContains)
, ("empty_int_hash", IntHashEmpty)
, ("insert_int_hash", IntHashInsert)
, ("lookup_int_hash", IntHashLookup)
]
desugarExp :: (Show a, Pretty a) => TypeSynEnv -> TopTyEnv -> Exp a -> PassM Exp0
desugarExp type_syns toplevel e =
case e of
Paren _ (ExpTypeSig _ (App _ (H.Var _ f) (Lit _ lit)) tyc)
| (qnameToStr f) == "error" -> pure $ PrimAppE (ErrorP (litToString lit) (desugarType type_syns tyc)) []
-- Paren _ (App _ (H.Var _ f) (Lit _ lit))
-- | (qnameToStr f) == "error" -> pure $ PrimAppE (ErrorP (litToString lit
Paren _ e2 -> desugarExp type_syns toplevel e2
H.Var _ qv -> do
let str = qnameToStr qv
v = (toVar str)
if str == "alloc_pdict"
then do
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictAllocP kty vty) []
else if str == "alloc_ll"
then do
ty <- newMetaTy
pure $ PrimAppE (LLAllocP ty) []
else if v == "sync"
then pure SyncE
else if v == "lsync"
then pure SyncE
else if M.member str primMap
then pure $ PrimAppE (primMap M.! str) []
else case M.lookup v toplevel of
Just sigma ->
case tyFromScheme sigma of
ArrowTy{} ->
-- Functions with >0 args must be VarE's here -- the 'App _ e1 e2'
-- case below depends on it.
pure $ VarE v
-- Otherwise, 'v' is a top-level value binding, which we
-- encode as a function which takes no arguments.
_ -> pure $ AppE v [] []
Nothing -> pure $ VarE v
Lit _ lit -> desugarLiteral lit
Lambda _ pats bod -> do
bod' <- desugarExp type_syns toplevel bod
(vars,tys,bindss) <- unzip3 <$> mapM (desugarPatWithTy type_syns) pats
let binds = concat bindss
args = zip vars tys
pure $ Ext $ LambdaE args (mkLets binds bod')
App _ e1 e2 -> do
desugarExp type_syns toplevel e1 >>= \case
(VarE f) ->
case M.lookup (fromVar f) primMap of
Just p -> (\e2' -> PrimAppE p [e2']) <$> desugarExp type_syns toplevel e2
Nothing ->
if f == "quote"
then case e2 of
Lit _ lit -> pure $ LitSymE (toVar $ litToString lit)
_ -> error "desugarExp: quote only accepts string literals. E.g quote \"hello\""
else if f == "eqBenchProg"
then case e2 of
Lit _ lit -> pure $ (PrimAppE (EqBenchProgP (litToString lit)) [])
_ -> error "desugarExp: eqBenchProg only accepts string literals."
else if f == "readArrayFile"
then let go e0 = case e0 of
Con _ (UnQual _ (Ident _ "Nothing")) -> do
t <- newMetaTy
pure $ PrimAppE (ReadArrayFile Nothing t) []
App _ (Con _ (UnQual _ (Ident _ "Just"))) (Tuple _ Boxed [Lit _ name, Lit _ len]) -> do
t <- newMetaTy
pure $ PrimAppE (ReadArrayFile (Just (litToString name, litToInt len)) t) []
Paren _ e3 -> go e3
_ -> error $ "desugarExp: couldn't parse readArrayFile; " ++ show e0
in go e2
else if f == "readPackedFile"
then let go e0 = case e0 of
TypeApp _ (TyCon _ (UnQual _ (Ident _ con))) -> do
let ty = PackedTy con []
pure $ PrimAppE (ReadPackedFile Nothing con Nothing ty) []
_ -> error $ "desugarExp: couldn't parse readPackedFile; " ++ show e0
in go e2
else if f == "writePackedFile"
then
case e2 of
Lit _ fp -> do
ty <- newMetaTy
pure $ PrimAppE (WritePackedFile (litToString fp) ty) []
_ -> error $ "desugarExp: couldn't parse writePackedFile; " ++ show e2
else if f == "bench"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext $ BenchE "HOLE" [] [e2'] False
else if f == "timeit"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ TimeIt e2' ty False
else if f == "iterate"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ TimeIt e2' ty True
else if f == "error"
then case e2 of
Lit _ lit -> pure $ PrimAppE (ErrorP (litToString lit) IntTy) [] -- assume int (!)
_ -> error "desugarExp: error expects String literal."
else if f == "par"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext $ ParE0 [e2']
else if f == "spawn"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ SpawnE "HOLE" [] [e2']
else if f == "valloc"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VAllocP ty) [e2']
else if f == "vfree"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VFreeP ty) [e2']
else if f == "vfree2"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VFree2P ty) [e2']
else if f == "vnth"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VNthP ty) [e2']
else if f == "vlength"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VLengthP ty) [e2']
else if f == "inplacevupdate"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (InplaceVUpdateP ty) [e2']
else if f == "vconcat"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VConcatP ty) [e2']
else if f == "vsort"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VSortP ty) [e2']
else if f == "inplacevsort"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (InplaceVSortP ty) [e2']
else if f == "vslice"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VSliceP ty) [e2']
else if f == "vmerge"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (VMergeP ty) [e2']
else if f == "insert_pdict"
then do
e2' <- desugarExp type_syns toplevel e2
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictInsertP kty vty) [e2']
else if f == "lookup_pdict"
then do
e2' <- desugarExp type_syns toplevel e2
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictLookupP kty vty) [e2']
else if f == "member_pdict"
then do
e2' <- desugarExp type_syns toplevel e2
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictHasKeyP kty vty) [e2']
else if f == "fork_pdict"
then do
e2' <- desugarExp type_syns toplevel e2
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictForkP kty vty) [e2']
else if f == "join_pdict"
then do
e2' <- desugarExp type_syns toplevel e2
kty <- newMetaTy
vty <- newMetaTy
pure $ PrimAppE (PDictJoinP kty vty) [e2']
else if f == "is_empty_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLIsEmptyP ty) [e2']
else if f == "cons_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLConsP ty) [e2']
else if f == "head_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLHeadP ty) [e2']
else if f == "tail_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLTailP ty) [e2']
else if f == "free_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLFreeP ty) [e2']
else if f == "free2_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLFree2P ty) [e2']
else if f == "copy_ll"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ PrimAppE (LLCopyP ty) [e2']
else if f == "fst"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ ProjE 0 e2'
else if f == "snd"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ ProjE 1 e2'
else if f == "printPacked"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ Ext (PrintPacked ty e2')
else if f == "copyPacked"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ Ext (CopyPacked ty e2')
else if f == "travPacked"
then do
e2' <- desugarExp type_syns toplevel e2
ty <- newMetaTy
pure $ Ext (TravPacked ty e2')
else if f == "unsafeAlias"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext (LinearExt (AliasE e2'))
else if f == "unsafeToLinear"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext (LinearExt (ToLinearE e2'))
else if f == "lseq"
then do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext (LinearExt (LseqE e2' undefined))
else if S.member f keywords
then error $ "desugarExp: Keyword not handled: " ++ sdoc f
else AppE f [] <$> (: []) <$> desugarExp type_syns toplevel e2
(DataConE tyapp c as) -> (\e2' -> DataConE tyapp c (as ++ [e2'])) <$> desugarExp type_syns toplevel e2
(Ext (ParE0 ls)) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext $ ParE0 (ls ++ [e2'])
(AppE f [] ls) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ AppE f [] (ls ++ [e2'])
(Ext (BenchE fn [] ls b)) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ Ext $ BenchE fn [] (ls ++ [e2']) b
(SpawnE fn [] ls) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ SpawnE fn [] (ls ++ [e2'])
(PrimAppE (WritePackedFile fp ty) ls) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ PrimAppE (WritePackedFile fp ty) (ls ++ [e2'])
(PrimAppE (ReadPackedFile _mb_fp tycon mb_var ty) []) ->
let go e0 = case e0 of
Con _ (UnQual _ (Ident _ "Nothing")) -> do
pure (PrimAppE (ReadPackedFile Nothing tycon mb_var ty) [])
App _ (Con _ (UnQual _ (Ident _ "Just"))) (Lit _ name) -> do
pure (PrimAppE (ReadPackedFile (Just (litToString name)) tycon mb_var ty) [])
Paren _ e3 -> go e3
_ -> error $ "desugarExp: couldn't parse readPackedFile; " ++ show e0
in go e2
(PrimAppE (VMergeP elty) ls) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ PrimAppE (VMergeP elty) (ls ++ [e2'])
(PrimAppE p ls) -> do
e2' <- desugarExp type_syns toplevel e2
pure $ PrimAppE p (ls ++ [e2'])
TimeIt{} ->
error "desugarExp: TimeIt can only accept 1 expression."
(Ext (LinearExt (LseqE a _))) -> do
e2' <- desugarExp type_syns toplevel e2
pure (Ext (LinearExt (LseqE a e2')))
(Ext (LinearExt (ToLinearE (AppE f [] ls)))) -> do
e2' <- desugarExp type_syns toplevel e2
pure (Ext (LinearExt (ToLinearE (AppE f [] (ls ++ [e2'])))))
(Ext (LinearExt (ToLinearE (DataConE tyapp dcon ls)))) -> do
e2' <- desugarExp type_syns toplevel e2
pure (Ext (LinearExt (ToLinearE (DataConE tyapp dcon (ls ++ [e2'])))))
(Ext (LinearExt (ToLinearE (Ext (LambdaE [(v,ty)] bod))))) -> do
e2' <- desugarExp type_syns toplevel e2
pure (Ext (LinearExt (ToLinearE (LetE (v,[],ty,e2') bod))))
(Ext (LinearExt (ToLinearE (VarE fn)))) -> do
e2' <- desugarExp type_syns toplevel e2
pure (Ext (LinearExt (ToLinearE (AppE fn [] [e2']))))
f -> error ("desugarExp: Couldn't parse function application: (" ++ show f ++ ")")
Let _ (BDecls _ decls) rhs -> do
rhs' <- desugarExp type_syns toplevel rhs
let funtys = foldr (collectTopTy type_syns) M.empty decls
foldrM (generateBind type_syns toplevel funtys) rhs' decls
If _ a b c -> do
a' <- desugarExp type_syns toplevel a
b' <- desugarExp type_syns toplevel b
c' <- desugarExp type_syns toplevel c
pure $ IfE a' b' c'
Tuple _ Unboxed _ -> error $ "desugarExp: Only boxed tuples are allowed: " ++ prettyPrint e
Tuple _ Boxed es -> MkProdE <$> mapM (desugarExp type_syns toplevel) es
Case _ scrt alts -> do
scrt' <- desugarExp type_syns toplevel scrt
CaseE scrt' <$> mapM (desugarAlt type_syns toplevel) alts
Con _ (Special _ (UnitCon _)) -> pure $ MkProdE []
Con _ qname -> do
let dcon = qnameToStr qname
case M.lookup dcon primMap of
Just p -> pure $ PrimAppE p []
Nothing -> do
-- Just a placeholder for now, the typechecker will fill this hole.
ty <- newMetaTy
pure $ DataConE ty dcon []
-- TODO: timeit: parsing it's type isn't straightforward.
InfixApp _ e1 (QVarOp _ (UnQual _ (Symbol _ "!!!"))) e2 -> do
e1' <- desugarExp type_syns toplevel e1
case e2 of
Lit _ lit -> do
let i = litToInt lit
pure $ ProjE i e1'
_ -> error $ "desugarExp: !!! expects a integer. Got: " ++ prettyPrint e2
InfixApp _ e1 op e2 -> do
e1' <- desugarExp type_syns toplevel e1
e2' <- desugarExp type_syns toplevel e2
case op of
QVarOp _ (UnQual _ (Symbol _ "&")) -> do
pure $ Ext (LinearExt (ReverseAppE e2' e1'))
_ -> do
let op' = desugarOp op
pure $ PrimAppE op' [e1', e2']
NegApp _ e1 -> do
e1' <- desugarExp type_syns toplevel e1
pure $ PrimAppE SubP [LitE 0, e1']
_ -> error ("desugarExp: Unsupported expression: " ++ prettyPrint e)
desugarFun :: (Show a, Pretty a) => TypeSynEnv -> TopTyEnv -> TopTyEnv -> Decl a -> PassM (Var, [Var], TyScheme, Exp0)
desugarFun type_syns toplevel env decl =
case decl of
FunBind _ [Match _ fname pats (UnGuardedRhs _ bod) _where] -> do
let fname_str = nameToStr fname
fname_var = toVar (fname_str)
(vars, arg_tys,bindss) <- unzip3 <$> mapM (desugarPatWithTy type_syns) pats
let binds = concat bindss
args = vars
fun_ty <- case M.lookup fname_var env of
Nothing -> do
ret_ty <- newMetaTy
let funty = ArrowTy arg_tys ret_ty
pure $ (ForAll [] funty)
Just ty -> pure ty
bod' <- desugarExp type_syns toplevel bod
pure $ (fname_var, args, unCurryTopTy fun_ty, (mkLets binds bod'))
_ -> error $ "desugarFun: Found a function with multiple RHS, " ++ prettyPrint decl
multiArgsToOne :: [Var] -> [Ty0] -> Exp0 -> (Var, Exp0)
multiArgsToOne args tys ex =
let new_arg = toVar "multi_arg"
in (new_arg, tuplizeRefs new_arg args tys ex)
collectTopTy :: (Show a, Pretty a) => TypeSynEnv -> Decl a -> TopTyEnv -> TopTyEnv
collectTopTy type_syns d env =
case d of
TypeSig _ names ty ->
let ty' = desugarTopType type_syns ty
in foldr (\name acc ->
let tycon_var = toVar (nameToStr name) in
case M.lookup tycon_var acc of
Nothing -> M.insert tycon_var ty' acc
Just{} -> error $ "collectTopTy: Multiple type signatures for: " ++ show tycon_var)
env names
_ -> env
collectTypeSynonyms :: (Show a, Pretty a) => TypeSynEnv -> Decl a -> TypeSynEnv
collectTypeSynonyms env d =
case d of
TypeDecl _ (DHead _ name) ty ->
let ty' = desugarType env ty
tycon = nameToStr name
in case M.lookup tycon env of
Nothing -> M.insert tycon ty' env
Just{} -> error $ "collectTypeSynonyms: Multiple type synonym declarations: " ++ show tycon
_ -> env
collectTopLevel :: (Show a, Pretty a) => TypeSynEnv -> TopTyEnv -> Decl a -> PassM (Maybe TopLevel)
collectTopLevel type_syns env decl =
let toplevel = env in
case decl of
-- 'collectTopTy' takes care of this.
TypeSig{} -> pure Nothing
-- 'collectTypeSynonyms'.
TypeDecl{} -> pure Nothing
DataDecl _ (DataType _) _ctx decl_head cons _deriving_binds -> do
let (ty_name, ty_args) = desugarDeclHead decl_head
cons' = map (desugarConstr type_syns) cons
if ty_name `S.member` builtinTys
then error $ sdoc ty_name ++ " is a built-in type."
else pure $ Just $ HDDef (DDef ty_name ty_args cons')
-- Reserved for HS.
PatBind _ (PVar _ (Ident _ "main")) (UnGuardedRhs _ _) _binds ->
pure Nothing
PatBind _ (PVar _ (Ident _ "gibbon_main")) (UnGuardedRhs _ rhs) _binds -> do
rhs' <- fixupSpawn <$> verifyBenchEAssumptions True <$> desugarExp type_syns toplevel rhs
ty <- newMetaTy
pure $ Just $ HMain $ Just (rhs', ty)
PatBind _ (PVar _ (Ident _ fn)) (UnGuardedRhs _ rhs) _binds ->
case M.lookup (toVar fn) env of
Nothing -> error $ "collectTopLevel: Top-level binding with no type signature: " ++ fn
Just fun_ty ->
-- This is a top-level function binding of the form:
-- f = \x -> ...
case rhs of
Lambda _ pats bod -> do
bod' <- desugarExp type_syns toplevel bod
case pats of
[] -> error "Impossible"
_ -> do
(vars,_tys,bindss) <- unzip3 <$> mapM (desugarPatWithTy type_syns) pats
let binds = concat bindss
args = vars
pure $ Just $ HFunDef (FunDef { funName = toVar fn
, funArgs = args
, funTy = fun_ty
, funBody = fixupSpawn (mkLets binds bod')
, funMeta = FunMeta { funRec = NotRec
, funInline = NoInline
, funCanTriggerGC = False
}
})
-- This is a top-level function that doesn't take any arguments.
_ -> do
rhs' <- desugarExp type_syns toplevel rhs
let fun_ty' = ArrowTy [] (tyFromScheme fun_ty)
fun_ty'' = ForAll (tyVarsInTy fun_ty') fun_ty'
pure $ Just $ HFunDef (FunDef { funName = toVar fn
, funArgs = []
, funTy = fun_ty''
, funBody = fixupSpawn rhs'
, funMeta = FunMeta { funRec = NotRec
, funInline = NoInline
, funCanTriggerGC = False
}
})
FunBind{} -> do (name,args,ty,bod) <- desugarFun type_syns toplevel env decl
pure $ Just $ HFunDef (FunDef { funName = name
, funArgs = args
, funTy = ty
, funBody = fixupSpawn bod