on master help sign in
fix oversights in Parse.Module.checkEffects
0e16843Evan Czaplicki
compiler/src/Parse/Module.hs+58−21
@@ -29,14 +29,37 @@ import qualified Reporting.Error.Syntax as E
29-- FROM BYTE STRING
29-- FROM BYTE STRING
30
30
31
31
32−fromByteString :: Pkg.Name -> BS.ByteString -> Either E.Error Src.Module
32+fromByteString :: ProjectType -> BS.ByteString -> Either E.Error Src.Module
33−fromByteString pkg source =
33+fromByteString projectType source =
34− case P.fromByteString (chompModule pkg) E.ModuleBadEnd source of
34+ case P.fromByteString (chompModule projectType) E.ModuleBadEnd source of
35− Right modul -> checkModule modul
35+ Right modul -> checkModule projectType modul
36 Left err -> Left (E.ParseError err)
36 Left err -> Left (E.ParseError err)
37
37
38
38
39
39
40+-- PROJECT TYPE
41+
42+
43+data ProjectType
44+ = Package Pkg.Name
45+ | Application
46+
47+
48+isCore :: ProjectType -> Bool
49+isCore projectType =
50+ case projectType of
51+ Package pkg -> pkg == Pkg.core
52+ Application -> False
53+
54+
55+isKernel :: ProjectType -> Bool
56+isKernel projectType =
57+ case projectType of
58+ Package pkg -> Pkg.isKernel pkg
59+ Application -> False
60+
61+
62+
40-- MODULE
63-- MODULE
41
64
42
65
@@ -49,11 +72,11 @@ data Module =
49 }
72 }
50
73
51
74
52−chompModule :: Pkg.Name -> Parser E.Module Module
75+chompModule :: ProjectType -> Parser E.Module Module
53−chompModule pkg =
76+chompModule projectType =
54 do header <- chompHeader
77 do header <- chompHeader
55− imports <- chompImports (if pkg == Pkg.core then [] else Imports.defaults)
78+ imports <- chompImports (if isCore projectType then [] else Imports.defaults)
56− infixes <- if Pkg.isKernel pkg then chompInfixes [] else return []
79+ infixes <- if isKernel projectType then chompInfixes [] else return []
57 decls <- specialize E.Declarations $ chompDecls []
80 decls <- specialize E.Declarations $ chompDecls []
58 return (Module header imports infixes decls)
81 return (Module header imports infixes decls)
59
82
@@ -62,15 +85,15 @@ chompModule pkg =
62-- CHECK MODULE
85-- CHECK MODULE
63
86
64
87
65−checkModule :: Module -> Either E.Error Src.Module
88+checkModule :: ProjectType -> Module -> Either E.Error Src.Module
66−checkModule (Module maybeHeader imports infixes decls) =
89+checkModule projectType (Module maybeHeader imports infixes decls) =
67 let
90 let
68 (values, unions, aliases, ports) = categorizeDecls [] [] [] [] decls
91 (values, unions, aliases, ports) = categorizeDecls [] [] [] [] decls
69 in
92 in
70 case maybeHeader of
93 case maybeHeader of
71 Just (Header name effects exports docs) ->
94 Just (Header name effects exports docs) ->
72 Src.Module (Just name) exports (toDocs docs decls) imports values unions aliases infixes
95 Src.Module (Just name) exports (toDocs docs decls) imports values unions aliases infixes
73− <$> checkEffects ports effects
96+ <$> checkEffects projectType ports effects
74
97
75 Nothing ->
98 Nothing ->
76 Right $
99 Right $
@@ -80,23 +103,37 @@ checkModule (Module maybeHeader imports infixes decls) =
80 _:_ -> Src.Ports ports
103 _:_ -> Src.Ports ports
81
104
82
105
83−checkEffects :: [Src.Port] -> Effects -> Either E.Error Src.Effects
106+checkEffects :: ProjectType -> [Src.Port] -> Effects -> Either E.Error Src.Effects
84−checkEffects ports effects =
107+checkEffects projectType ports effects =
85 case effects of
108 case effects of
86 NoEffects region ->
109 NoEffects region ->
87 case ports of
110 case ports of
111+ [] ->
88− [] -> Right Src.NoEffects
112+ Right Src.NoEffects
113+
114+ Src.Port name _ : _ ->
115+ case projectType of
116+ Package _ -> Left (E.NoPortsInPackage name)
89− _:_ -> Left (E.UnexpectedPort region)
117+ Application -> Left (E.UnexpectedPort region)
90
118
91 Ports region ->
119 Ports region ->
92− case ports of
120+ case projectType of
121+ Package _ ->
122+ Left (E.NoPortModulesInPackage region)
123+
124+ Application ->
125+ case ports of
93− [] -> Left (E.NoPorts region)
126+ [] -> Left (E.NoPorts region)
94− _:_ -> Right (Src.Ports ports)
127+ _:_ -> Right (Src.Ports ports)
95
128
96 Manager region manager ->
129 Manager region manager ->
130+ if isKernel projectType then
97− case ports of
131+ case ports of
98− [] -> Right (Src.Manager region manager)
132+ [] -> Right (Src.Manager region manager)
99− _:_ -> Left (E.UnexpectedPort region)
133+ _:_ -> Left (E.UnexpectedPort region)
134+ else
135+ Left (E.NoEffectsOutsideKernel region)
136+
100
137
101
138
102categorizeDecls :: [A.Located Src.Value] -> [A.Located Src.Union] -> [A.Located Src.Alias] -> [Src.Port] -> [Decl.Decl] -> ( [A.Located Src.Value], [A.Located Src.Union], [A.Located Src.Alias], [Src.Port] )
139categorizeDecls :: [A.Located Src.Value] -> [A.Located Src.Union] -> [A.Located Src.Alias] -> [Src.Port] -> [Decl.Decl] -> ( [A.Located Src.Value], [A.Located Src.Union], [A.Located Src.Alias], [Src.Port] )
compiler/src/Reporting/Error/Syntax.hs+77
@@ -69,6 +69,9 @@ data Error
69 | ModuleNameMismatch ModuleName.Raw (A.Located ModuleName.Raw)
69 | ModuleNameMismatch ModuleName.Raw (A.Located ModuleName.Raw)
70 | UnexpectedPort A.Region
70 | UnexpectedPort A.Region
71 | NoPorts A.Region
71 | NoPorts A.Region
72+ | NoPortsInPackage (A.Located Name.Name)
73+ | NoPortModulesInPackage A.Region
74+ | NoEffectsOutsideKernel A.Region
72 | ParseError Module
75 | ParseError Module
73
76
74
77
@@ -561,10 +564,84 @@ toReport source err =
561 ]
564 ]
562 )
565 )
563
566
567+ NoPortsInPackage (A.At region _) ->
568+ Report.Report "PACKAGES CANNOT HAVE PORTS" region [] $
569+ Code.toSnippet source region Nothing
570+ (
571+ D.reflow $
572+ "Packages cannot declare any ports, so I am getting stuck here:"
573+ ,
574+ D.stack
575+ [ D.reflow $
576+ "Remove this port declaration."
577+ , noteForPortsInPackage
578+ ]
579+ )
580+
581+ NoPortModulesInPackage region ->
582+ Report.Report "PACKAGES CANNOT HAVE PORTS" region [] $
583+ Code.toSnippet source region Nothing
584+ (
585+ D.reflow $
586+ "Packages cannot declare any ports, so I am getting stuck here:"
587+ ,
588+ D.stack
589+ [ D.fillSep $
590+ ["Remove","the",D.cyan "port","keyword","and","I"
591+ ,"should","be","able","to","continue."
592+ ]
593+ , noteForPortsInPackage
594+ ]
595+ )
596+
597+ NoEffectsOutsideKernel region ->
598+ Report.Report "INVALID EFFECT MODULE" region [] $
599+ Code.toSnippet source region Nothing
600+ (
601+ D.reflow $
602+ "It is not possible to declare an `effect module` outside the @elm organization,\
603+ \ so I am getting stuck here:"
604+ ,
605+ D.stack
606+ [ D.reflow $
607+ "Switch to a normal module declaration."
608+ , D.toSimpleNote $
609+ "Effect modules are designed to allow certain core functionality to be\
610+ \ defined separately from the compiler. So the @elm organization has access to\
611+ \ this so that certain changes, extensions, and fixes can be introduced without\
612+ \ needing to release new Elm binaries. For example, we want to make it possible\
613+ \ to test effects, but this may require changes to the design of effect modules.\
614+ \ By only having them defined in the @elm organization, that kind of design work\
615+ \ can proceed much more smoothly."
616+ ]
617+ )
618+
564 ParseError modul ->
619 ParseError modul ->
565 toParseErrorReport source modul
620 toParseErrorReport source modul
566
621
567
622
623+noteForPortsInPackage :: D.Doc
624+noteForPortsInPackage =
625+ D.stack
626+ [ D.toSimpleNote $
627+ "One of the major goals of the package ecosystem is to be completely written\
628+ \ in Elm. This means when you install an Elm package, you can be sure you are safe\
629+ \ from security issues (like code that runs side-effects on install) and that you\
630+ \ are not going to get any runtime exceptions coming from your new dependency.\
631+ \ This design also sets the Elm ecosystem up well to target other platforms (like\
632+ \ mobile phones, WebAssembly, etc.) since no community code explicitly depends on\
633+ \ JavaScript even existing."
634+ , D.reflow $
635+ "Given that overall goal, allowing ports in packages would lead to some pretty\
636+ \ surprising behavior. If ports were allowed in packages, you could install a\
637+ \ package but not realize that it brings in an indirect dependency that defines a\
638+ \ port. Now you have a program that does not work and the fix is to realize that\
639+ \ some JavaScript needs to be added. That would be extremely frustrating! So why\
640+ \ not allow the package author to include the necessary JS code as well? Now we\
641+ \ are back in conflict with our goals for the ecosystem overall."
642+ ]
643+
644+
568toParseErrorReport :: Code.Source -> Module -> Report.Report
645toParseErrorReport :: Code.Source -> Module -> Report.Report
569toParseErrorReport source modul =
646toParseErrorReport source modul =
570 case modul of
647 case modul of