on master help sign in
Merge branch 'warnings' into pr/924

Conflicts: src/Reporting/Error.hs

commit1b95db081f6a429657295ffa12c6bd864444fb6d authorEvan Czaplicki <info@elm-lang.org> dateWed 6 May 2015, 16:11 (11 years ago) parentffe32c7aac63cf
26 files changed+811−463
elm-compiler.cabal+16−3 src/AST/Pattern.hs+8 src/Canonicalize.hs+2−2 src/Transform/Declaration.hs → src/Canonicalize/Declaration.hs+1−1 src/Canonicalize/Port.hs+5−5 src/Canonicalize/Variable.hs+22−14 src/Elm/Compiler.hs+5−22 src/Transform/AddDefaultImports.hs → src/Elm/Compiler/Imports.hs+4−19 src/Elm/Compiler/Module.hs+2−2 src/Parse/Parse.hs+2−2 src/Parse/Pattern.hs+6−1 src/Reporting/Error.hs+15−36 src/Reporting/Error/Canonicalize.hs+133−144 src/Reporting/Error/Syntax.hs+126−12 src/Reporting/Error/Type.hs+101−17 src/Reporting/PrettyPrint.hs−12 src/Reporting/Region.hs−9 src/Reporting/Report.hs+114 src/Transform/Definition.hs−40 src/Type/Constrain/Expression.hs+151−52 src/Type/Constrain/Literal.hs+2−1 src/Type/Constrain/Pattern.hs+2−1 src/Type/Solve.hs+20−11 src/Type/Type.hs+3−2 src/Type/Unify.hs+48−45 src/Validate.hs+23−10
elm-compiler.cabal+16−3
@@ -47,10 +47,10 @@ Library
4747
4848 other-modules:
4949 AST.Declaration,
50+ AST.Expression.Canonical,
5051 AST.Expression.General,
5152 AST.Expression.Source,
5253 AST.Expression.Valid,
53− AST.Expression.Canonical,
5454 AST.Helpers,
5555 AST.Literal,
5656 AST.Module,
@@ -58,22 +58,24 @@ Library
5858 AST.Type,
5959 AST.Variable,
6060 Canonicalize,
61+ Canonicalize.Declaration,
6162 Canonicalize.Environment,
6263 Canonicalize.Port,
64+ Canonicalize.Result,
6365 Canonicalize.Setup,
6466 Canonicalize.Sort,
6567 Canonicalize.Type,
6668 Canonicalize.Variable,
6769 Compile,
70+ Elm.Compiler.Imports,
6871 Elm.Compiler.Type.Extract,
6972 Elm.Compiler.Version,
73+ Generate.Cases,
7074 Generate.JavaScript,
7175 Generate.JavaScript.Helpers,
7276 Generate.JavaScript.Port,
7377 Generate.JavaScript.Variable,
74− Generate.Cases,
7578 Generate.Substitute,
76− Transform.AddDefaultImports,
7779 Parse.Binop,
7880 Parse.Declaration,
7981 Parse.Expression,
@@ -83,6 +85,16 @@ Library
8385 Parse.Parse,
8486 Parse.Pattern,
8587 Parse.Type,
88+ Reporting.Annotation,
89+ Reporting.Error,
90+ Reporting.Error.Canonicalize,
91+ Reporting.Error.Syntax,
92+ Reporting.Error.Type,
93+ Reporting.PrettyPrint,
94+ Reporting.Region,
95+ Reporting.Report,
96+ Reporting.Result,
97+ Reporting.Warning,
8698 Type.Constrain.Expression,
8799 Type.Constrain.Literal,
88100 Type.Constrain.Pattern,
@@ -95,6 +107,7 @@ Library
95107 Type.State,
96108 Type.Type,
97109 Type.Unify,
110+ Validate,
98111 Paths_elm_compiler
99112
100113 Build-depends:
src/AST/Pattern.hs+8
@@ -48,6 +48,14 @@ list end patterns =
4848 A.at start end (Data (Var.Raw "::") [pattern, list end rest])
4949
5050
51+consMany :: R.Position -> [RawPattern] -> RawPattern
52+consMany end patterns =
53+ let cons hd@(A.A (R.Region start _) _) tl =
54+ A.at start end (Data (Var.Raw "::") [hd, tl])
55+ in
56+ foldr1 cons patterns
57+
58+
5159tuple :: [RawPattern] -> RawPattern'
5260tuple patterns =
5361 Data (Var.Raw ("_Tuple" ++ show (length patterns))) patterns
src/Canonicalize.hs+2−2
@@ -23,6 +23,7 @@ import qualified Reporting.Error as Error
2323import qualified Reporting.Error.Canonicalize as CError
2424import qualified Reporting.Result as R
2525import qualified Reporting.Warning as Warning
26+import qualified Canonicalize.Declaration as Decls
2627import qualified Canonicalize.Environment as Env
2728import qualified Canonicalize.Port as Port
2829import qualified Canonicalize.Result as Result
@@ -30,7 +31,6 @@ import qualified Canonicalize.Setup as Setup
3031import qualified Canonicalize.Sort as Sort
3132import qualified Canonicalize.Type as Canonicalize
3233import qualified Canonicalize.Variable as Canonicalize
33−import qualified Transform.Declaration as Transform
3434
3535
3636-- MODULES
@@ -85,7 +85,7 @@ moduleHelp interfaces modul@(Module.Module _ _ exports (defaults, imports) decls
8585 in
8686 Module.CanonicalBody
8787 { program =
88− let expr = Transform.toExpr (Module.names modul) decls
88+ let expr = Decls.toExpr (Module.names modul) decls
8989 in
9090 Sort.definitions (dummyLet expr)
9191
src/Transform/Declaration.hs → src/Canonicalize/Declaration.hsrenamed+1−1
@@ -1,5 +1,5 @@
11{-# OPTIONS_GHC -Wall #-}
2−module Transform.Declaration (toExpr) where
2+module Canonicalize.Declaration (toExpr) where
33
44import qualified AST.Declaration as D
55import qualified AST.Expression.General as E
src/Canonicalize/Port.hs+5−5
@@ -90,7 +90,7 @@ validForeignType name isInbound rootType tipe =
9090 T.Type v ->
9191 case any ($ v) [ Var.isJson, Var.isPrimitive, Var.isTuple ] of
9292 True -> Result.ok ()
93− False -> err "an unsupported type"
93+ False -> err Nothing
9494
9595 T.App t [] ->
9696 valid t
@@ -105,16 +105,16 @@ validForeignType name isInbound rootType tipe =
105105 Foldable.traverse_ valid ts
106106
107107 T.App _ _ ->
108− err "an unsupported type"
108+ err Nothing
109109
110110 T.Var _ ->
111− err "a free type variable"
111+ err (Just "a free type variable")
112112
113113 T.Lambda _ _ ->
114− err "functions"
114+ err (Just "a function")
115115
116116 T.Record _ (Just _) ->
117− err "extended records with free type variables"
117+ err (Just "an extended record")
118118
119119 T.Record fields Nothing ->
120120 Foldable.traverse_ (\(k,v) -> (,) k <$> valid v) fields
src/Canonicalize/Variable.hs+22−14
@@ -128,7 +128,7 @@ preferLocals' region env extract kind possibilities var =
128128 name == Env._home env
129129
130130 ambiguous possibleVars =
131− Result.err (A.A region (Error.var kind var (Error.Ambiguous vars)))
131+ Result.err (A.A region (Error.variable kind var Error.Ambiguous vars))
132132 where
133133 vars = map (Var.toString . extract) possibleVars
134134
@@ -173,7 +173,7 @@ notFound region kind possibilities var =
173173 possibleNames =
174174 map toVarName possibilities
175175
176− problem =
176+ (problem, suggestions) =
177177 case name of
178178 Left _ ->
179179 exposedProblem name possibleNames
@@ -181,10 +181,10 @@ notFound region kind possibilities var =
181181 Right (modul, varName) ->
182182 qualifiedProblem modul varName (Either.rights possibleNames)
183183 in
184− Result.err (A.A region (Error.var kind var problem))
184+ Result.err (A.A region (Error.variable kind var problem suggestions))
185185
186186
187−exposedProblem :: VarName -> [VarName] -> Error.VarProblem
187+exposedProblem :: VarName -> [VarName] -> (Error.VarProblem, [String])
188188exposedProblem name possibleNames =
189189 let (exposed, qualified) =
190190 possibleNames
@@ -192,27 +192,35 @@ exposedProblem name possibleNames =
192192 |> Error.nearbyNames noQualifier name
193193 |> Either.partitionEithers
194194 in
195− Error.ExposedUnknown exposed (map qualifiedToString qualified)
195+ ( Error.ExposedUnknown
196+ , exposed ++ map qualifiedToString qualified
197+ )
196198
197199
198200qualifiedProblem
199201 :: Module.Name
200202 -> String
201203 -> [(Module.Name, String)]
202− -> Error.VarProblem
204+ -> (Error.VarProblem, [String])
203205qualifiedProblem moduleName name allQualified =
204206 let availableModules =
205207 Set.fromList (map fst allQualified)
208+
209+ moduleNameString =
210+ Module.nameToString moduleName
206211 in
207212 case Set.member moduleName availableModules of
208213 True ->
209− allQualified
210− |> filter ((==) moduleName . fst)
211− |> map snd
212− |> Error.nearbyNames id name
213− |> Error.QualifiedUnknown
214+ ( Error.QualifiedUnknown moduleNameString
215+ , allQualified
216+ |> filter ((==) moduleName . fst)
217+ |> map snd
218+ |> Error.nearbyNames id name
219+ )
214220
215221 False ->
216− Set.toList availableModules
217− |> Error.nearbyNames Module.nameToString moduleName
218− |> Error.UnknownQualifier
222+ ( Error.UnknownQualifier moduleNameString
223+ , Set.toList availableModules
224+ |> map Module.nameToString
225+ |> Error.nearbyNames id moduleNameString
226+ )
src/Elm/Compiler.hs+5−22
@@ -3,8 +3,8 @@
33module Elm.Compiler
44 ( version, rawVersion
55 , parseDependencies, compile
6− , Error, errorToString, errorToJson
7− , Warning, warningToString, warningToJson
6+ , Error, errorToString
7+ , Warning
88 ) where
99
1010import Control.Monad.Error.Class (MonadError, throwError)
@@ -83,28 +83,11 @@ compile user packageName source interfaces =
8383newtype Error = Error (A.Located Error.Error)
8484
8585
86−errorToString :: Error -> String
87−errorToString (Error err) =
88− Error.toString err
89−
90−
91−errorToJson :: Error -> String
92−errorToJson (Error err) =
93− Error.toJson err
86+errorToString :: String -> String -> Error -> String
87+errorToString location source (Error err) =
88+ Error.toString location source err
9489
9590
9691-- WARNINGS
9792
9893newtype Warning = Warning (A.Located Warning.Warning)
99−
100−
101−warningToString :: Warning -> String
102−warningToString (Warning warning) =
103− Warning.toString warning
104−
105−
106−warningToJson :: Warning -> String
107−warningToJson (Warning warning) =
108− Warning.toJson warning
109−
110−
src/Transform/AddDefaultImports.hs → src/Elm/Compiler/Imports.hsrenamed+4−19
@@ -1,17 +1,17 @@
1−module Transform.AddDefaultImports (add, defaultImports) where
1+module Elm.Compiler.Imports (defaults) where
22
33import qualified AST.Module as Module
44import qualified AST.Variable as Var
55
66
7−-- DESCRIPTION OF DEFAULT IMPORTS
7+-- DEFAULT IMPORTS
88
99(==>) :: a -> b -> (a,b)
1010(==>) = (,)
1111
1212
13−defaultImports :: [Module.DefaultImport]
14−defaultImports =
13+defaults :: [Module.DefaultImport]
14+defaults =
1515 [ ["Basics"] ==> Module.ImportMethod Nothing Var.openListing
1616 , ["List"] ==> exposing [Var.Value "::"]
1717 , ["Maybe"] ==> exposing [Var.Union "Maybe" Var.openListing]
@@ -23,18 +23,3 @@ defaultImports =
2323exposing :: [Var.Value] -> Module.ImportMethod
2424exposing vars =
2525 Module.ImportMethod Nothing (Var.listing vars)
26−
27−
28−-- ADDING DEFAULT IMPORTS TO A MODULE
29−
30−add
31− :: Bool
32− -> [Module.UserImport]
33− -> ([Module.DefaultImport], [Module.UserImport])
34−add needsDefaults imports =
35− (defaults, imports)
36− where
37− defaults =
38− if needsDefaults
39− then defaultImports
40− else []
src/Elm/Compiler/Module.hs+2−2
@@ -18,7 +18,7 @@ import qualified Data.Text as Text
1818import System.FilePath ((</>))
1919
2020import qualified AST.Module as Module
21−import qualified Transform.AddDefaultImports as Defaults
21+import qualified Elm.Compiler.Imports as Imports
2222import qualified Elm.Compiler.Type as Type
2323import qualified Elm.Compiler.Type.Extract as Extract
2424
@@ -34,7 +34,7 @@ newtype Name = Name [String]
3434
3535defaultImports :: [Name]
3636defaultImports =
37− map (Name . fst) Defaults.defaultImports
37+ map (Name . fst) Imports.defaults
3838
3939
4040-- POKING AROUND INTERFACES
src/Parse/Parse.hs+2−2
@@ -9,13 +9,13 @@ import qualified Text.Parsec.Error as Parsec
99
1010import qualified AST.Declaration as D
1111import qualified AST.Module as M
12+import qualified Elm.Compiler.Imports as Imports
1213import Parse.Helpers
1314import qualified Parse.Module as Module
1415import qualified Parse.Declaration as Decl
1516import qualified Reporting.Region as R
1617import qualified Reporting.Error.Syntax as Error
1718import qualified Reporting.Result as Result
18−import qualified Transform.AddDefaultImports as DefaultImports
1919import qualified Validate
2020
2121
@@ -31,7 +31,7 @@ program needsDefaults table src =
3131 validDecls <- Validate.declarations sourceDecls
3232
3333 let ammendedImports =
34− DefaultImports.add needsDefaults imports
34+ (if needsDefaults then Imports.defaults else [], imports)
3535
3636 return (M.Module names filePath exports ammendedImports validDecls)
3737
src/Parse/Pattern.hs+6−1
@@ -104,4 +104,9 @@ patternConstructor =
104104
105105expr :: IParser P.RawPattern
106106expr =
107− asPattern (patternConstructor <|> term) <?> "pattern"
107+ asPattern subPattern <?> "pattern"
108+ where
109+ subPattern =
110+ do patterns <- consSep1 (patternConstructor <|> term)
111+ end <- getMyPosition
112+ return (P.consMany end patterns)
src/Reporting/Error.hs+15−36
@@ -1,15 +1,12 @@
11{-# LANGUAGE OverloadedStrings #-}
22module Reporting.Error where
33
4−import Data.Aeson ((.=))
5−import qualified Data.Aeson as Json
6−import qualified Data.ByteString.Lazy.Char8 as BS
7−
84import qualified Reporting.Annotation as A
95import qualified Reporting.Error.Canonicalize as Canonicalize
106import qualified Reporting.Error.Syntax as Syntax
117import qualified Reporting.Error.Type as Type
128import qualified Reporting.Error.CheckMatch as CheckMatch
9+import qualified Reporting.Report as Report
1310
1411
1512-- ALL POSSIBLE ERRORS
@@ -23,42 +20,24 @@ data Error
2320
2421-- TO STRING
2522
26−toString :: A.Located Error -> String
27−toString (A.A region err) =
28− case err of
29− Syntax syntaxError ->
30− Syntax.toString region syntaxError
31−
32− Canonicalize canonicalizeError ->
33− Canonicalize.toString region canonicalizeError
34−
35− Type typeError ->
36− Type.toString region typeError
37−
38− CheckMatch matchError ->
39− CheckMatch.toString region matchError
40−
41−-- JSON
42−
43−toJson :: A.Located Error -> String
44−toJson (A.A region err) =
45− let json =
23+toString :: String -> String -> A.Located Error -> String
24+toString location source (A.A region err) =
25+ let
26+ (tag, report) =
4627 case err of
4728 Syntax syntaxError ->
48− Syntax.toJson syntaxError
29+ ( "SYNTAX ERROR"
30+ , Syntax.toReport syntaxError
31+ )
4932
5033 Canonicalize canonicalizeError ->
51− Canonicalize.toJson canonicalizeError
34+ ( "NAMING ERROR"
35+ , Canonicalize.toReport canonicalizeError
36+ )
5237
5338 Type typeError ->
54− Type.toJson typeError
55−
56− CheckMatch matchError ->
57− CheckMatch.toJson matchError
39+ ( "TYPE ERROR"
40+ , Type.toReport typeError
41+ )
5842 in
59− BS.unpack $ Json.encode $
60− Json.object
61− [ "tag" .= ("error" :: String)
62− , "region" .= region
63− , "error" .= json
64− ]
43+ Report.toString tag location region report source
src/Reporting/Error/Canonicalize.hs+133−144
@@ -2,18 +2,18 @@
22{-# LANGUAGE OverloadedStrings #-}
33module Reporting.Error.Canonicalize where
44
5−--import Data.Aeson ((.=))
6−import qualified Data.Aeson as Json
7−--import qualified Data.Aeson.Types as Json
85import Data.Function (on)
96import qualified Data.List as List
107import qualified Text.EditDistance as Dist
8+import qualified Text.PrettyPrint as P
119
1210import qualified AST.Module as Module
1311import qualified AST.Type as Type
1412import qualified AST.Variable as Var
1513import Elm.Utils ((|>))
16−import qualified Reporting.Region as R
14+import qualified Reporting.PrettyPrint as P
15+import qualified Reporting.Region as Region
16+import qualified Reporting.Report as Report
1717
1818
1919data Error
@@ -31,19 +31,20 @@ data VarError = VarError
3131 { varKind :: String
3232 , varName :: String
3333 , varProblem :: VarProblem
34+ , varSuggestions :: [String]
3435 }
3536
3637
3738data VarProblem
38− = Ambiguous [String]
39− | UnknownQualifier [Module.Name]
40− | QualifiedUnknown [String]
41− | ExposedUnknown { closeExposed :: [String], closeQualified :: [String] }
39+ = Ambiguous
40+ | UnknownQualifier String
41+ | QualifiedUnknown String
42+ | ExposedUnknown
4243
4344
44−var :: String -> String -> VarProblem -> Error
45−var kind name problem =
46− Var (VarError kind name problem)
45+variable :: String -> String -> VarProblem -> [String] -> Error
46+variable kind name problem suggestions =
47+ Var (VarError kind name problem suggestions)
4748
4849
4950-- PATTERN
@@ -79,7 +80,7 @@ valueNotFound name value possibilities =
7980data AliasError
8081 = ArgMismatch Var.Canonical Int Int
8182 | SelfRecursive String [String] Type.Raw
82− | MutuallyRecursive [(R.Region, String, [String], Type.Raw)]
83+ | MutuallyRecursive [(Region.Region, String, [String], Type.Raw)]
8384
8485
8586alias :: Var.Canonical -> Int -> Int -> Error
@@ -94,7 +95,7 @@ data PortError = PortError
9495 , portIsInbound :: Bool
9596 , portRootType :: Type.Canonical
9697 , portLocalType :: Type.Canonical
97− , portMessage :: String
98+ , portMessage :: Maybe String
9899 }
99100
100101
@@ -103,10 +104,10 @@ port
103104 -> Bool
104105 -> Type.Canonical
105106 -> Type.Canonical
106− -> String
107+ -> Maybe String
107108 -> Error
108−port name isInbound rootType localType message =
109− Port (PortError name isInbound rootType localType message)
109+port name isInbound rootType localType maybeMessage =
110+ Port (PortError name isInbound rootType localType maybeMessage)
110111
111112
112113-- NEARBY NAMES
@@ -128,138 +129,126 @@ distance x y =
128129 Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
129130
130131
131−-- TO STRING
132+-- TO REPORT
132133
133−toString :: R.Region -> Error -> String
134−toString region err =
134+toReport :: Error -> Report.Report
135+toReport err =
135136 case err of
136− _ -> error "Canonicalize.toString" region
137−
138−
139−-- TO JSON
140−
141−toJson :: Error -> Json.Value
142−toJson err =
143− case err of
144− _ -> error "Canonicalize.toJson"
145−
146−{--
147−(.=.) key value =
148− key .= (value :: String)
149−
150−
151−errorToJson :: LError -> Json.Value
152−errorToJson (A.A region err) =
153− Json.object (generals ++ specifics)
154− where
155− generals =
156− [ "category" .=. "canonicalization"
157− , "region" .= region
158− ]
159−
160− specifics =
161− case err of
162− Var (VarError kind name varProblem) ->
163− [ "kind" .= kind
164− , "name" .= name
165− ]
166− ++ varProblemToJson varProblem
167−
168− Alias aliasError ->
169− case aliasError of
170− ArgMismatch name expected actual ->
171− [ "tag" .=. "alias-args"
172− , "name" .= Var.toString name
173− , "expected-args" .= expected
174− , "actual-args" .= actual
175− ]
176−
177− Recursive [(name, tvars, tipe)] ->
178− [ "tag" .=. "recursive-alias"
179− ]
180−
181− Recursive aliases ->
182− [ "tag" .=. "mutually-recursive-aliases"
183− ]
184−
185− Import moduleName importError ->
186− case importError of
187− ModuleNotFound suggestions ->
188− [ "tag" .=. "unknown-import"
189− , "name" .= moduleName
190− , "suggestions" .= suggestions
191− ]
192− ValueNotFound name suggestions ->
193− []
194−
195− Export name suggestions ->
196− []
197−
198− Port portError ->
199− []
200−
201−
202−varProblemToJson :: VarProblem -> [Json.Pair]
203−varProblemToJson varProblem =
204− let details tag suggestions =
205− [ "tag" .=. tag
206− , "suggestions" .= suggestions
207− ]
137+ Var (VarError kind name problem suggestions) ->
138+ let var = kind ++ " '" ++ name ++ "'"
139+ in
140+ case problem of
141+ Ambiguous ->
142+ Report.simple
143+ ("This usage of " ++ var ++ " is ambiguous.")
144+ (maybeYouWant suggestions)
145+
146+ UnknownQualifier qualifier ->
147+ Report.simple
148+ ("Cannot find " ++ var ++ ", qualifier '" ++ qualifier ++ "' is not in scope.")
149+ (maybeYouWant suggestions)
150+
151+ QualifiedUnknown qualifier ->
152+ Report.simple
153+ ("Cannot find " ++ var ++ ", module '" ++ qualifier ++ "' does not expose that value.")
154+ (maybeYouWant suggestions)
155+
156+ ExposedUnknown ->
157+ Report.simple
158+ ("Cannot find " ++ var)
159+ (maybeYouWant suggestions)
160+
161+ Pattern patternError ->
162+ case patternError of
163+ PatternArgMismatch var expected actual ->
164+ argMismatchReport "Pattern" var expected actual
165+
166+ Alias aliasError ->
167+ case aliasError of
168+ ArgMismatch var expected actual ->
169+ argMismatchReport "Type" var expected actual
170+
171+ SelfRecursive name tvars tipe ->
172+ Report.simple "This type alias is recursive, forming an infinite type!" $
173+ "Type aliases are just names for more fundamental types, so I go through\n"
174+ ++ "and expand them all to know the real underlying type. The problem is that\n"
175+ ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
176+ ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
177+ ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
178+ ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
179+ ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
180+ ++ "like List which are recursive, but do not get expanded into infinite types."
181+
182+ MutuallyRecursive aliases ->
183+ Report.simple "This type alias is part of a mutually recursive set of type aliases." $
184+ "The following type aliases all depend on each other in some way:\n"
185+ ++ concatMap showName aliases ++ "\n\n"
186+ ++ "The problem is that when you try to expand any of these type aliases, they\n"
187+ ++ "expand infinitely! To fix, first try to centralize them into one alias."
188+ where
189+ showName (Region.Region start _, name, _, _) =
190+ "\n " ++ name ++ replicate (65 - length name) ' '
191+ ++ "line " ++ show (Region.line start)
192+
193+ Import name importError ->
194+ let moduleName = Module.nameToString name
195+ in
196+ case importError of
197+ ModuleNotFound suggestions ->
198+ Report.simple
199+ ("Could not find a module named '" ++ moduleName ++ "'")
200+ (maybeYouWant (map Module.nameToString suggestions))
201+
202+ ValueNotFound value suggestions ->
203+ Report.simple
204+ ("Module '" ++ moduleName ++ "' does not expose '" ++ value ++ "'")
205+ (maybeYouWant suggestions)
206+
207+ Export name suggestions ->
208+ Report.simple
209+ ("Could not export '" ++ name ++ "' which is not defined in this module.")
210+ (maybeYouWant suggestions)
211+
212+ Port (PortError name isInbound _rootType localType maybeMessage) ->
213+ let
214+ boundPort =
215+ if isInbound then "inbound port" else "outbound port"
216+
217+ preHint =
218+ "Trying to send " ++ maybe "an unsupported type" id maybeMessage
219+ ++ " through " ++ boundPort ++ " '" ++ name ++ "'"
220+
221+ postHint =
222+ "The specific unsupported type is:\n\n"
223+ ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
224+ ++ "The types of values that can flow through " ++ boundPort ++ "s include:\n"
225+ ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
226+ ++ "Json.Values, and concrete records."
227+ in
228+ Report.simple preHint postHint
229+
230+
231+argMismatchReport :: String -> Var.Canonical -> Int -> Int -> Report.Report
232+argMismatchReport kind var expected actual =
233+ let
234+ preHint =
235+ kind ++ " " ++ Var.toString var ++ " has too "
236+ ++ (if actual < expected then "few" else "many")
237+ ++ "arguments."
238+
239+ postHint =
240+ "Expecting " ++ show expected ++ ", but got " ++ show actual ++ "."
208241 in
209− case varProblem of
210− Ambiguous possibilities ->
211− details "ambiguous" possibilities
212−
213− UnknownQualifier possibleModules ->
214− details
215− "unknown-qualifier"
216− (map Module.nameToString possibleModules)
217−
218− QualifiedUnknown possibilities ->
219− details "qualified-unknown" possibilities
220−
221− ExposedUnknown closeExposed closeQualified ->
222− details "exposed-unknown" (closeExposed ++ closeQualified)
223−--}
224−
225−{-- TO STRING
226−
227−mutuallyRecursiveMessage :: String
228−mutuallyRecursiveMessage =
229− "The following type aliases are mutually recursive, forming an \
230− \infinite type. When you expand them, they just keep getting bigger:"
231−
232−
233−typeAliasErrorSegue :: String
234−typeAliasErrorSegue =
235− "Try this instead:"
236−
237−
238−typeAliasErrorExplanation :: String
239−typeAliasErrorExplanation =
240− "It looks very similar, but the 'type' keyword creates a brand new type, \
241− \not just an alias for an existing one. This lets us avoid infinitely \
242− \expanding it during type inference."
243−
244−
245−typeAlias
246− :: (String, [String], Type.Type var)
247− -> D.Declaration' pk def var expr
248−typeAlias (n,ts,t) =
249− D.TypeAlias n ts t
250−
242+ Report.Report Nothing preHint postHint
251243
252−datatype
253− :: (String, [String], Type.Type var)
254− -> D.Declaration' pk def var expr
255−datatype (n,ts,t) =
256− D.Datatype n ts [(n,[t])]
257244
245+maybeYouWant :: [String] -> String
246+maybeYouWant suggestions =
247+ case suggestions of
248+ [] ->
249+ ""
258250
259−indented :: [D.ValidDecl] -> Doc
260−indented decls =
261− P.vcat (map prty decls) <> P.text "\n"
262− where
263− prty decl = P.text "\n " <> pretty decl
251+ _:_ ->
252+ "Maybe you want one of the following?\n"
253+ ++ concatMap ("\n "++) suggestions
264254
265−--}
src/Reporting/Error/Syntax.hs+126−12
@@ -1,10 +1,16 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Syntax where
33
4−import qualified Data.Aeson as Json
4+import qualified Data.List as List
55import qualified Text.Parsec.Error as Parsec
6+import qualified Text.PrettyPrint as P
7+import Text.PrettyPrint ((<+>))
68
7−import qualified Reporting.Region as R
9+import qualified AST.Helpers as Help
10+import qualified AST.Type as Type
11+import Elm.Utils ((|>))
12+import qualified Reporting.PrettyPrint as P
13+import qualified Reporting.Report as Report
814
915
1016data Error
@@ -15,21 +21,129 @@ data Error
1521 | DuplicateValueDeclaration String
1622 | DuplicateTypeDeclaration String
1723 | DuplicateDefinition String
18− | UnboundTypeVar String String
24+ | UnboundTypeVarsInAlias String [String] String [String] Type.Raw
25+ | UnboundTypeVarsInUnion String [String] String [String] [(String, [Type.Raw])]
1926
2027
21−-- TO STRING
28+-- TO REPORT
2229
23−toString :: R.Region -> Error -> String
24−toString region err =
30+toReport :: Error -> Report.Report
31+toReport err =
2532 case err of
26− _ -> error "Syntax.toString" region
33+ Parse messages ->
34+ Report.simple
35+ "Problem when parsing your code!"
36+ (unlines (map parseErrorHint messages))
2737
38+ InfixDuplicate opName ->
39+ Report.simple
40+ ("The infix declarations for " ++ operator ++ " must be removed.")
41+ ("The precedence and associativity can only be set in one place, and\n"
42+ ++ "this information has already been set somewhere else."
43+ )
44+ where
45+ operator =
46+ if Help.isOp opName
47+ then "(" ++ opName ++ ")"
48+ else "`" ++ opName ++ "`"
2849
29−-- TO JSON
50+ TypeWithoutDefinition valueName ->
51+ Report.simple
52+ ("There is a type annotation for '" ++ valueName ++ "' but there"
53+ ++ "is no corresponding definition!"
54+ )
55+ ("Directly below the type annotation, put a definition like:\n\n"
56+ ++ " " ++ valueName ++ " = 42"
57+ )
3058
31−toJson :: Error -> Json.Value
32−toJson err =
33− case err of
34− _ -> error "Syntax.toJson"
59+ PortWithoutAnnotation portName ->
60+ Report.simple
61+ ("Port '" ++ portName ++ "' does not have a type annotation!")
62+ ("Directly above the port definition, I need something like this:\n\n"
63+ ++ " port " ++ portName ++ " : Signal Int"
64+ )
65+
66+ DuplicateValueDeclaration name ->
67+ Report.simple
68+ ("Naming multiple top-level values '" ++ name ++ "' makes things\n"
69+ ++ "ambiguous. When you say '" ++ name ++ "' which one do you want?!"
70+ )
71+ ("Find all the top-level values named '" ++ name ++ "' and\n"
72+ ++ "do some renaming. Make sure the names are distinct!"
73+ )
74+
75+ DuplicateTypeDeclaration name ->
76+ Report.simple
77+ ("Naming multiple types '" ++ name ++ "' makes things ambiguous\n"
78+ ++ "When you say '" ++ name ++ "' which one do you want?!"
79+ )
80+ ("Find all the types named '" ++ name ++ "' and\n"
81+ ++ "do some renaming. Make sure the names are distinct!"
82+ )
83+
84+ DuplicateDefinition name ->
85+ Report.simple
86+ ("Naming multiple values '" ++ name ++ "' in a single let-expression makes\n"
87+ ++ "things ambiguous. When you say '" ++ name ++ "' which one do you want?!"
88+ )
89+ ("Find all the values named '" ++ name ++ "' in this let-expression and\n"
90+ ++ "do some renaming. Make sure the names are distinct!"
91+ )
92+
93+ UnboundTypeVarsInAlias typeName givenVars tvar tvars tipe ->
94+ unboundTypeVars typeName tvar tvars $ P.render $
95+ P.hang
96+ (P.text "type alias" <+> P.text typeName <+> P.hsep vars <+> P.equals)
97+ 4
98+ (P.pretty False tipe)
99+ where
100+ vars = map P.text (givenVars ++ tvar : tvars)
101+
102+
103+ UnboundTypeVarsInUnion typeName givenVars tvar tvars ctors ->
104+ unboundTypeVars typeName tvar tvars $ P.render $
105+ P.vcat
106+ [ P.text "type" <+> P.text typeName <+> P.hsep vars
107+ , map toDoc ctors
108+ |> zipWith (<+>) (P.text "=" : repeat (P.text "|"))
109+ |> P.vcat
110+ |> P.nest 4
111+ ]
112+ where
113+ vars = map P.text (givenVars ++ tvar : tvars)
114+ toDoc (ctor, args) =
115+ P.text ctor <+> P.hsep (map (P.pretty True) args)
116+
117+
118+unboundTypeVars :: String -> String -> [String] -> String -> Report.Report
119+unboundTypeVars typeName tvar tvars revisedDeclaration =
120+ let
121+ s = if null tvars then "" else "s"
122+ in
123+ Report.simple
124+ ("Type '" ++ typeName ++ "' uses unbound type variable" ++ s ++ ": "
125+ ++ List.intercalate ", " (tvar:tvars)
126+ )
127+ ("All type variables must be listed to avoid sneaky type errors.\n"
128+ ++ "Imagine one '" ++ typeName ++ "' where '" ++ tvar ++ "' is an Int and\n"
129+ ++ "another where it is a Bool. They both look like a '" ++ typeName ++ "'\n"
130+ ++ "to the type checker, but they are actually different types!\n\n"
131+ ++ "Maybe you want a definition like this?\n"
132+ ++ concatMap ("\n "++) (lines revisedDeclaration)
133+ )
134+
135+
136+parseErrorHint :: Parsec.Message -> String
137+parseErrorHint message =
138+ case message of
139+ Parsec.SysUnExpect msg ->
140+ "SysUnExpect: " ++ msg
141+
142+ Parsec.UnExpect msg ->
143+ "UnExpect: " ++ msg
144+
145+ Parsec.Expect msg ->
146+ "UnExpect: " ++ msg
35147
148+ Parsec.Message msg ->
149+ "UnExpect: " ++ msg
src/Reporting/Error/Type.hs+101−17
@@ -1,39 +1,123 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Type where
33
4−--import Data.Aeson ((.=))
5−import qualified Data.Aeson as Json
4+import qualified Text.PrettyPrint as P
65
76import qualified AST.Type as T
8−import qualified Reporting.Region as R
7+import qualified Reporting.PrettyPrint as P
8+import qualified Reporting.Region as Region
9+import qualified Reporting.Report as Report
910
1011
1112data Error
1213 = Mismatch Mismatch
13− | InfiniteType R.Region
14+ | InfiniteType String T.Canonical
1415 | BadMain T.Canonical
15− | ProblemWithInferenceAlgorithm String
1616
1717
1818data Mismatch = MismatchInfo
19− { hint :: Maybe String
20− , leftType :: T.Canonical
21− , rightType :: T.Canonical
19+ { _hint :: Hint
20+ , _leftType :: T.Canonical
21+ , _rightType :: T.Canonical
22+ , _note :: Note
2223 }
2324
2425
25−-- TO STRING
26+data Hint
27+ = None
28+ | CaseBranch Int Region.Region
29+ | Case
30+ | IfBranches
31+ | MultiIfBranch Int Region.Region
32+ | If
2633
27−toString :: R.Region -> Error -> String
28−toString region err =
29− case err of
30− _ -> error "Type.toString" region
34+
35+data Note
36+ = PreNote String
37+ | NoNote
38+ | PostNote String
3139
3240
33−-- TO JSON
41+-- TO REPORT
3442
35−toJson :: Error -> Json.Value
36−toJson err =
43+toReport :: Error -> Report.Report
44+toReport err =
3745 case err of
38− _ -> error "Type.toJson"
46+ Mismatch (MismatchInfo hint leftType rightType note) ->
47+ let
48+ (subRegion, preHint) = hintToString hint
49+
50+ postHint =
51+ "To be more specific, as I infer all the types, I am seeing a conflict between\n"
52+ ++ "this type:\n\n"
53+ ++ P.render (P.nest 4 (P.pretty False leftType))
54+ ++ "\n\nand this type:\n\n"
55+ ++ P.render (P.nest 4 (P.pretty False rightType))
56+ in
57+ case note of
58+ PreNote msg ->
59+ Report.Report subRegion (preHint ++ "\n\n" ++ msg) postHint
60+
61+ NoNote ->
62+ Report.Report subRegion preHint postHint
63+
64+ PostNote msg ->
65+ Report.Report subRegion preHint (postHint ++ "\n\n" ++ msg)
66+
67+
68+
69+ InfiniteType var tipe ->
70+ Report.simple "This expression is leading me to infer an infinite type." $
71+ "Maybe you are trying to do some tricky recursion? Try breaking the expression\n"
72+ ++ "into smaller pieces. Give each piece a name and try to write down its type.\n\n"
73+ ++ "Type inference got stuck when type '" ++ var ++ "' needed equal to:\n\n"
74+ ++ P.render (P.nest 4 (P.pretty False tipe))
75+ ++ "\n\nNotice that type variable '" ++ var ++ "' appears there too, so if we\n"
76+ ++ "expanded this type, it would just keep getting bigger and bigger."
77+
78+ BadMain tipe ->
79+ Report.simple "The 'main' value has an unsupported type." $
80+ "I need an Element, Html, (Signal Element), or (Signal Html) so I can render it\n"
81+ ++ "on screen, but you gave me:\n\n"
82+ ++ P.render (P.nest 4 (P.pretty False tipe))
83+
84+
85+hintToString :: Hint -> (Maybe Region.Region, String)
86+hintToString hint =
87+ case hint of
88+ None ->
89+ ( Nothing
90+ , "This expression is triggering a type mismatch."
91+ )
92+
93+ CaseBranch branchNumber region ->
94+ ( Just region
95+ , "The branches of this case-expression return different types of values.\n\n"
96+ ++ "I noticed the mismatch in branch #" ++ show branchNumber ++ ", but go through and make sure every\n"
97+ ++ "branch returns the same type of value."
98+ )
99+
100+ Case ->
101+ ( Nothing
102+ , "All the branches of this case-expression are consistent, but the overall\n"
103+ ++ "type does not match how it is used elsewhere."
104+ )
105+
106+ IfBranches ->
107+ ( Nothing
108+ , "The branches of this if-expression return different types of values.\n"
109+ ++ "All branches must have the same return type!"
110+ )
111+
112+ MultiIfBranch branchNumber region ->
113+ ( Just region
114+ , "The branches of this if-expression return different types of values.\n\n"
115+ ++ "I noticed the mismatch in branch #" ++ show branchNumber ++ ", but go through and make sure every\n"
116+ ++ "branch returns the same type of value."
117+ )
39118
119+ If ->
120+ ( Nothing
121+ , "All the branches of this if-expression are consistent, but the overall\n"
122+ ++ "type does not match how it is used elsewhere."
123+ )
src/Reporting/PrettyPrint.hs−12
@@ -32,15 +32,3 @@ parensIf bool doc =
3232variable :: String -> Doc
3333variable x =
3434 if Help.isOp x then parens (text x) else text x
35−
36−
37−eightyCharLines :: Int -> String -> String
38−eightyCharLines indent message = answer
39− where
40− (answer,_,_) = foldl step (spaces, indent-1, "") chunks
41−
42− chunks = map (\w -> (w, length w)) (words message)
43− spaces = replicate indent ' '
44− step (sentence, slen, space) (word, wlen)
45− | slen + wlen > 79 = (sentence ++ "\n" ++ spaces ++ word, indent + wlen, " ")
46− | otherwise = (sentence ++ space ++ word, slen + wlen + length space, " ")
src/Reporting/Region.hs−9
@@ -36,15 +36,6 @@ merge (Region start _) (Region _ end) =
3636
3737toString :: Region -> String
3838toString (Region start end) =
39− let posToString pos =
40− "(line " ++ show (line pos) ++
41− ", column " ++ show (column pos) ++ ")"
42− in
43− posToString start ++ " to " ++ posToString end
44−
45−
46−toPrettyString :: Region -> String
47−toPrettyString (Region start end) =
4839 case line start == line end of
4940 False ->
5041 "between lines " ++ show (line start)
src/Reporting/Report.hsnew file+114
@@ -0,0 +1,114 @@
1+module Reporting.Report
2+ ( Report(Report)
3+ , simple
4+ , toString
5+ ) where
6+
7+import Elm.Utils ((|>))
8+import qualified Reporting.Region as R
9+
10+
11+data Report = Report
12+ { highlight :: Maybe R.Region
13+ , preHint :: String
14+ , postHint :: String
15+ }
16+
17+
18+simple :: String -> String -> Report
19+simple pre post =
20+ Report Nothing pre post
21+
22+
23+toString :: String -> String -> R.Region -> Report -> String -> String
24+toString tag location region report source =
25+ concat
26+ [ messageBar tag location
27+ , preHint report ++ "\n\n"
28+ , grabRegion (highlight report) region source ++ "\n"
29+ , postHint report ++ "\n\n\n"
30+ ]
31+
32+
33+-- REPORT HEADER
34+
35+messageBar :: String -> String -> String
36+messageBar tag location =
37+ let usedSpace = 4 + length tag + 1 + length location
38+ in
39+ "-- " ++ tag ++ " "
40+ ++ replicate (max 1 (80 - usedSpace)) '-'
41+ ++ " " ++ location ++ "\n\n"
42+
43+
44+-- REGIONS
45+
46+grabRegion :: Maybe R.Region -> R.Region -> String -> String
47+grabRegion maybeSubRegion (R.Region start end) source =
48+ let
49+ (R.Position startLine startColumn) = start
50+ (R.Position endLine endColumn) = end
51+
52+ relevantLines =
53+ lines source
54+ |> drop (startLine - 1)
55+ |> take (endLine - startLine + 1)
56+ in
57+ case relevantLines of
58+ [] ->
59+ error "something has gone badly wrong with reporting source locations"
60+
61+ [theLine] ->
62+ let
63+ n = startLine
64+ w = length (show (n + 1))
65+ underline =
66+ replicate (startColumn - 1) ' '
67+ ++ replicate (max 1 (endColumn - startColumn)) '^'
68+ number =
69+ addLineNumber Nothing w
70+ in
71+ unlines
72+ [ number (n-1) ""
73+ , number n theLine
74+ , number (n+1) underline
75+ ]
76+
77+ firstLine : rest ->
78+ let
79+ filteredFirstLine =
80+ replicate (startColumn - 1) ' '
81+ ++ drop (startColumn - 1) firstLine
82+
83+ filteredLastLine =
84+ take (endColumn) (last rest)
85+
86+ focusedRelevantLines =
87+ filteredFirstLine : init rest ++ [filteredLastLine]
88+
89+ lineNumbersWidth =
90+ length (show (endLine + 1))
91+
92+ numberedLines =
93+ zipWith
94+ (addLineNumber maybeSubRegion lineNumbersWidth)
95+ [startLine - 1 .. endLine + 1]
96+ ("" : focusedRelevantLines ++ [""])
97+ in
98+ unlines numberedLines
99+
100+
101+addLineNumber :: Maybe R.Region -> Int -> Int -> String -> String
102+addLineNumber maybeSubRegion width n line =
103+ let
104+ number =
105+ if n < 0 then " " else show n
106+
107+ spacer (R.Region start end) =
108+ if R.line start <= n && n <= R.line end
109+ then ">"
110+ else " "
111+ in
112+ replicate (width - length number) ' ' ++ number
113+ ++ "|" ++ maybe " " spacer maybeSubRegion
114+ ++ line
src/Transform/Definition.hsdeleted−40
@@ -1,40 +0,0 @@
1−{-# OPTIONS_GHC -Wall #-}
2−module Transform.Definition where
3−
4−import Control.Applicative ((<$>))
5−import qualified AST.Pattern as P
6−import qualified AST.Expression.Source as Source
7−import qualified AST.Expression.Valid as Valid
8−import qualified Transform.Expression as Expr
9−
10−combineAnnotations :: [Source.Def] -> Either String [Valid.Def]
11−combineAnnotations =
12− go
13− where
14− errorMessage x =
15− "Syntax Error: The type annotation for '" ++ x ++
16− "' must be directly above its definition."
17−
18− exprCombineAnnotations =
19− Expr.crawlLet combineAnnotations
20−
21− go defs =
22− case defs of
23− Source.TypeAnnotation name tipe : rest ->
24− case rest of
25− Source.Definition pat@(P.Var name') expr : rest'
26− | name == name' ->
27− do expr' <- exprCombineAnnotations expr
28− let def = Valid.Definition pat expr' (Just tipe)
29− (:) def <$> go rest'
30−
31− _ ->
32− Left (errorMessage name)
33−
34− Source.Definition pat expr : rest ->
35− do expr' <- exprCombineAnnotations expr
36− let def = Valid.Definition pat expr' Nothing
37− (:) def <$> go rest
38−
39− [] ->
40− return []
src/Type/Constrain/Expression.hs+151−52
@@ -13,6 +13,8 @@ import qualified AST.Pattern as P
1313import qualified AST.Type as ST
1414import qualified AST.Variable as V
1515import qualified Reporting.Annotation as A
16+import qualified Reporting.Error.Type as Error
17+import qualified Reporting.Region as R
1618import qualified Type.Constrain.Literal as Literal
1719import qualified Type.Constrain.Pattern as Pattern
1820import qualified Type.Environment as Env
@@ -27,7 +29,7 @@ constrain
2729 -> IO TypeConstraint
2830constrain env (A.A region expression) tipe =
2931 let list t = Env.get env Env.types "List" <| t
30− (===) = CEqual region
32+ (===) = CEqual Error.None region
3133 (<?) = CInstance region
3234 in
3335 case expression of
@@ -62,60 +64,51 @@ constrain env (A.A region expression) tipe =
6264 in
6365 return (if name == E.saveEnvName then CSaveEnv else name <? tipe)
6466
65− E.Range lo hi ->
67+ E.Range lowExpr highExpr ->
6668 existsNumber $ \n ->
67− do clo <- constrain env lo n
68− chi <- constrain env hi n
69− return $ CAnd [clo, chi, list n === tipe]
69+ do lowCon <- constrain env lowExpr n
70+ highCon <- constrain env highExpr n
71+ return $ CAnd [lowCon, highCon, list n === tipe]
7072
7173 E.ExplicitList exprs ->
7274 exists $ \x ->
7375 do constraints <- mapM (\e -> constrain env e x) exprs
7476 return $ CAnd (list x === tipe : constraints)
7577
76− E.Binop op e1 e2 ->
77− exists $ \t1 ->
78− exists $ \t2 ->
79− do c1 <- constrain env e1 t1
80− c2 <- constrain env e2 t2
81− return $ CAnd [ c1, c2, V.toString op <? (t1 ==> t2 ==> tipe) ]
78+ E.Binop op leftExpr rightExpr ->
79+ exists $ \leftType ->
80+ exists $ \rightType ->
81+ do leftCon <- constrain env leftExpr leftType
82+ rightCon <- constrain env rightExpr rightType
83+ return $ CAnd
84+ [ leftCon
85+ , rightCon
86+ , V.toString op <? (leftType ==> rightType ==> tipe)
87+ ]
8288
8389 E.Lambda pattern body ->
84− exists $ \t1 ->
85− exists $ \t2 ->
86− do fragment <- Pattern.constrain env pattern t1
87− c2 <- constrain env body t2
88− let c = ex (Fragment.vars fragment)
89− (CLet [monoscheme (Fragment.typeEnv fragment)]
90− (Fragment.typeConstraint fragment /\ c2)
91− )
92− return $ c /\ tipe === (t1 ==> t2)
93−
94− E.App e1 e2 ->
90+ exists $ \argType ->
91+ exists $ \resType ->
92+ do fragment <- Pattern.constrain env pattern argType
93+ bodyCon <- constrain env body resType
94+ let con =
95+ ex (Fragment.vars fragment)
96+ (CLet [monoscheme (Fragment.typeEnv fragment)]
97+ (Fragment.typeConstraint fragment /\ bodyCon)
98+ )
99+ return $ con /\ tipe === (argType ==> resType)
100+
101+ E.App func arg ->
95102 exists $ \t ->
96− do c1 <- constrain env e1 (t ==> tipe)
97− c2 <- constrain env e2 t
98− return $ c1 /\ c2
103+ do funcCon <- constrain env func (t ==> tipe)
104+ argCon <- constrain env arg t
105+ return (funcCon /\ argCon)
99106
100107 E.MultiIf branches ->
101− CAnd <$> mapM constrain' branches
102− where
103− bool = Env.get env Env.types "Bool"
104−
105− constrain' (cond, expr) =
106− do cb <- constrain env cond bool
107− ce <- constrain env expr tipe
108− return (cb /\ ce)
108+ constrainIf env region branches tipe
109109
110110 E.Case expr branches ->
111− exists $ \t ->
112− do exprConstraint <- constrain env expr t
113− branchConstraints <- mapM (branch t) branches
114− return (CAnd (exprConstraint : branchConstraints))
115− where
116− branch t (pattern, branchExpr) =
117− do fragment <- Pattern.constrain env pattern t
118− CLet [Fragment.toScheme fragment] <$> constrain env branchExpr tipe
111+ constrainCase env region expr branches tipe
119112
120113 E.Data name exprs ->
121114 do vars <- Monad.forM exprs $ \_ -> variable Flexible
@@ -136,12 +129,13 @@ constrain env (A.A region expression) tipe =
136129 constrain env expr (record (Map.singleton label [t]) tipe)
137130
138131 E.Insert expr label value ->
139− exists $ \tVal ->
140− exists $ \tRec ->
141− do cVal <- constrain env value tVal
142− cRec <- constrain env expr tRec
143− let c = tipe === record (Map.singleton label [tVal]) tRec
144− return (CAnd [cVal, cRec, c])
132+ exists $ \valueType ->
133+ exists $ \recordType ->
134+ do valueCon <- constrain env value valueType
135+ recordCon <- constrain env expr recordType
136+ let newRecordType =
137+ record (Map.singleton label [valueType]) recordType
138+ return (CAnd [valueCon, recordCon, tipe === newRecordType])
145139
146140 E.Modify expr fields ->
147141 exists $ \t ->
@@ -158,14 +152,18 @@ constrain env (A.A region expression) tipe =
158152 return $ cOld /\ ex newVars (CAnd (cNew : cs))
159153
160154 E.Record fields ->
161− do vars <- Monad.forM fields $ \_ -> variable Flexible
162− cs <- Monad.zipWithM (constrain env) (map snd fields) (map varN vars)
155+ do vars <- Monad.forM fields (\_ -> variable Flexible)
156+ fieldCons <-
157+ Monad.zipWithM
158+ (constrain env)
159+ (map snd fields)
160+ (map varN vars)
163161 let fields' = ST.fieldMap (zip (map fst fields) (map varN vars))
164162 let recordType = record fields' (termN EmptyRecord1)
165− return . ex vars . CAnd $ tipe === recordType : cs
163+ return (ex vars (CAnd (fieldCons ++ [tipe === recordType])))
166164
167165 E.Let defs body ->
168− do c <- constrain env body tipe
166+ do bodyCon <- constrain env body tipe
169167
170168 (Info schemes rqs fqs headers c2 c1) <-
171169 Monad.foldM
@@ -175,7 +173,7 @@ constrain env (A.A region expression) tipe =
175173
176174 let letScheme = [ Scheme rqs fqs (CLet [monoscheme headers] c2) headers ]
177175
178− return $ CLet schemes (CLet letScheme (c1 /\ c))
176+ return $ CLet schemes (CLet letScheme (c1 /\ bodyCon))
179177
180178 E.Port impl ->
181179 case impl of
@@ -189,6 +187,107 @@ constrain env (A.A region expression) tipe =
189187 constrain env expr tipe
190188
191189
190+-- CONSTRAIN IF EXPRESSIONS
191+
192+constrainIf
193+ :: Env.Environment
194+ -> R.Region
195+ -> [(Canonical.Expr, Canonical.Expr)]
196+ -> Type
197+ -> IO TypeConstraint
198+constrainIf env region branches tipe =
199+ do (branchInfo, branchExprCons) <-
200+ unzip <$> mapM constrainBranch branches
201+
202+ return $
203+ ex (map fst branchInfo)
204+ (CAnd (branchExprCons ++ branchCons branchInfo))
205+ where
206+ bool = Env.get env Env.types "Bool"
207+
208+ constrainBranch (cond, expr@(A.A branchRegion _)) =
209+ do branchVar <- variable Flexible
210+ condCon <- constrain env cond bool
211+ exprCon <- constrain env expr (varN branchVar)
212+ return
213+ ( (branchVar, branchRegion)
214+ , condCon /\ exprCon
215+ )
216+
217+ branchCons branchInfo =
218+ case branchInfo of
219+ [(thenVar, _), (elseVar, _)] ->
220+ [ CEqual Error.IfBranches region (varN thenVar) (varN elseVar)
221+ , CEqual Error.If region tipe (varN thenVar)
222+ ]
223+
224+ _ ->
225+ buildBranchCons 2 branchInfo
226+
227+ buildBranchCons n branchInfo =
228+ case branchInfo of
229+ [] ->
230+ []
231+
232+ (var,_) : [] ->
233+ [ CEqual Error.If region tipe (varN var) ]
234+
235+ (var,_) : (var',region') : rest ->
236+ let
237+ hint = Error.MultiIfBranch n region'
238+ con = CEqual hint region (varN var) (varN var')
239+ in
240+ con : buildBranchCons (n+1) ((var', region') : rest)
241+
242+
243+-- CONSTRAIN CASE EXPRESSIONS
244+
245+constrainCase
246+ :: Env.Environment
247+ -> R.Region
248+ -> Canonical.Expr
249+ -> [(P.CanonicalPattern, Canonical.Expr)]
250+ -> Type
251+ -> IO TypeConstraint
252+constrainCase env region expr branches tipe =
253+ do exprVar <- variable Flexible
254+
255+ exprCon <- constrain env expr (varN exprVar)
256+
257+ (branchInfo, branchExprCons) <-
258+ unzip <$> mapM (branch (varN exprVar)) branches
259+
260+ return $
261+ ex (exprVar : map fst branchInfo)
262+ (CAnd (exprCon : branchExprCons ++ branchCons 2 branchInfo))
263+ where
264+ branch patternType (pattern, branchExpr@(A.A branchRegion _)) =
265+ do branchVar <- variable Flexible
266+ fragment <- Pattern.constrain env pattern patternType
267+ branchCon <- constrain env branchExpr (varN branchVar)
268+ return
269+ ( (branchVar, branchRegion)
270+ , CLet [Fragment.toScheme fragment] branchCon
271+ )
272+
273+ branchCons n branchInfo =
274+ case branchInfo of
275+ [] ->
276+ []
277+
278+ (var,_) : [] ->
279+ [ CEqual Error.Case region tipe (varN var) ]
280+
281+ (var,_) : (var',region') : rest ->
282+ let
283+ hint = Error.CaseBranch n region'
284+ con = CEqual hint region (varN var) (varN var')
285+ in
286+ con : branchCons (n+1) ((var', region') : rest)
287+
288+
289+-- EXPAND PATTERNS
290+
192291expandPattern :: Canonical.Def -> [Canonical.Def]
193292expandPattern def@(Canonical.Definition lpattern expr maybeType) =
194293 let (A.A patternRegion pattern) = lpattern
src/Type/Constrain/Literal.hs+2−1
@@ -1,6 +1,7 @@
11module Type.Constrain.Literal where
22
33import qualified AST.Literal as L
4+import qualified Reporting.Error.Type as Error
45import qualified Reporting.Region as R
56import qualified Type.Type as T
67import qualified Type.Environment as Env
@@ -14,7 +15,7 @@ constrain
1415 -> IO T.TypeConstraint
1516constrain env region literal tipe =
1617 do tipe' <- litType
17− return (T.CEqual region tipe tipe')
18+ return (T.CEqual Error.None region tipe tipe')
1819 where
1920 prim name =
2021 return (Env.get env Env.types name)
src/Type/Constrain/Pattern.hs+2−1
@@ -9,6 +9,7 @@ import qualified Data.Map as Map
99import qualified AST.Pattern as P
1010import qualified AST.Variable as V
1111import qualified Reporting.Annotation as A
12+import qualified Reporting.Error.Type as Error
1213import qualified Type.Constrain.Literal as Literal
1314import qualified Type.Environment as Env
1415import Type.Fragment
@@ -21,7 +22,7 @@ constrain
2122 -> Type
2223 -> IO Fragment
2324constrain env (A.A region pattern) tipe =
24− let (===) = CEqual region
25+ let (===) = CEqual Error.None region
2526 in
2627 case pattern of
2728 P.Anything ->
src/Type/Solve.hs+20−11
@@ -133,10 +133,10 @@ actuallySolve constraint =
133133 CSaveEnv ->
134134 TS.saveLocalEnv
135135
136− CEqual region term1 term2 ->
136+ CEqual hint region term1 term2 ->
137137 do t1 <- TS.flatten term1
138138 t2 <- TS.flatten term2
139− unify region t1 t2
139+ unify hint region t1 t2
140140
141141 CAnd cs ->
142142 mapM_ actuallySolve cs
@@ -167,7 +167,7 @@ actuallySolve constraint =
167167 else error ("Could not find '" ++ name ++ "' when solving type constraints.")
168168
169169 t <- TS.flatten term
170− unify region freshCopy t
170+ unify Error.None region freshCopy t
171171
172172
173173solveScheme :: TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
@@ -208,12 +208,12 @@ allDistinct vars =
208208 do desc <- liftIO $ UF.descriptor var
209209 case structure desc of
210210 Just _ ->
211− TS.addError (error "Cannot generalize something that is not a type variable.") undefined
211+ crash "Cannot generalize something that is not a type variable."
212212
213213 Nothing ->
214− do when (mark desc == seen) $
215− TS.addError (error "Duplicate variable during generalization.") undefined
216− liftIO $ UF.setDescriptor var (desc { mark = seen })
214+ if mark desc == seen
215+ then crash "Duplicate variable during generalization."
216+ else liftIO (UF.setDescriptor var (desc { mark = seen }))
217217
218218
219219-- Check that a variable has rank == noRank, meaning that it can be generalized.
@@ -222,8 +222,16 @@ isGeneric var =
222222 do desc <- liftIO $ UF.descriptor var
223223 if rank desc == noRank
224224 then return ()
225− else
226− TS.addError (error "Unable to generalize a type variable. It is not unranked.") undefined
225+ else crash "Unable to generalize a type variable. It is not unranked."
226+
227+
228+crash :: String -> a
229+crash msg =
230+ error $
231+ "It looks like something went wrong with the type inference algorithm.\n"
232+ ++ msg ++ "\n"
233+ ++ "Please create a minimal example that triggers this problem and report it to\n"
234+ ++ "<https://github.com/elm-lang/elm-compiler/issues>"
227235
228236
229237occurs :: (String, Variable) -> StateT TS.SolverState IO ()
@@ -235,12 +243,13 @@ occurs (_name, variable) =
235243
236244 var : _ ->
237245 do desc <- liftIO $ UF.descriptor var
246+ let region = error "TODO:occurs"
238247 case structure desc of
239248 Nothing ->
240− error "occurs check triggered"
249+ TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
241250
242251 Just _ ->
243− error "occurs check triggered"
252+ TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
244253 where
245254 infiniteVars :: [Variable] -> Variable -> IO [Variable]
246255 infiniteVars seen var =
src/Type/Type.hs+3−2
@@ -13,6 +13,7 @@ import System.IO.Unsafe
1313
1414import qualified AST.Type as T
1515import qualified AST.Variable as Var
16+import qualified Reporting.Error.Type as Error
1617import qualified Reporting.Region as R
1718import Type.PrettyPrint
1819
@@ -104,7 +105,7 @@ data SuperType
104105data Constraint a b
105106 = CTrue
106107 | CSaveEnv
107− | CEqual R.Region a a
108+ | CEqual Error.Hint R.Region a a
108109 | CAnd [Constraint a b]
109110 | CLet [Scheme a b] (Constraint a b)
110111 | CInstance R.Region SchemeName a
@@ -307,7 +308,7 @@ instance (PrettyType a, PrettyType b) => PrettyType (Constraint a b) where
307308 CSaveEnv ->
308309 P.text "SaveTheEnvironment!!!"
309310
310− CEqual _region a b ->
311+ CEqual _ _ a b ->
311312 prty a <+> P.text "=" <+> prty b
312313
313314 CAnd [] ->
src/Type/Unify.hs+48−45
@@ -5,44 +5,55 @@ import Control.Monad.Except (ExceptT, throwError, runExceptT)
55import Control.Monad.State as State
66import qualified Data.List as List
77import qualified Data.Map as Map
8−import qualified Data.Maybe as Maybe
98import qualified Data.UnionFind.IO as UF
109import Text.PrettyPrint (render)
1110
11+import qualified AST.Type as AstType
1212import qualified AST.Variable as Var
1313import qualified Reporting.Region as R
1414import qualified Reporting.Error.Type as Error
1515import qualified Type.State as TS
16−import Type.Type
16+import Type.Type as Type
1717import Type.PrettyPrint
1818import Elm.Utils ((|>))
1919
2020
21−unify :: R.Region -> Variable -> Variable -> StateT TS.SolverState IO ()
22−unify region variable1 variable2 =
21+unify
22+ :: Error.Hint
23+ -> R.Region
24+ -> Variable
25+ -> Variable
26+ -> StateT TS.SolverState IO ()
27+unify hint region variable1 variable2 =
2328 do result <- runExceptT (unifyHelp variable1 variable2)
2429 case result of
2530 Right state ->
2631 return state
2732
28− Left mismatch ->
29− TS.addError region (Error.Mismatch mismatch)
33+ Left (RawMismatch (leftType, rightType, note)) ->
34+ TS.addError region $ Error.Mismatch $
35+ Error.MismatchInfo hint leftType rightType note
3036
3137
3238-- ACTUALLY UNIFY STUFF
3339
34−type Unify = ExceptT Error.Mismatch (StateT TS.SolverState IO)
40+type Unify =
41+ ExceptT RawMismatch (StateT TS.SolverState IO)
42+
43+
44+newtype RawMismatch =
45+ RawMismatch (AstType.Canonical, AstType.Canonical, Error.Note)
3546
3647
3748typeError
38− :: Maybe String
39− -> UF.Point Descriptor
49+ :: UF.Point Descriptor
4050 -> UF.Point Descriptor
51+ -> Error.Note
4152 -> Unify a
42−typeError hint leftType rightType =
53+typeError leftType rightType note =
4354 do leftSrcType <- liftIO (toSrcType leftType)
4455 rightSrcType <- liftIO (toSrcType rightType)
45− throwError (Error.MismatchInfo hint leftSrcType rightSrcType)
56+ throwError (RawMismatch (leftSrcType, rightSrcType, note))
4657
4758
4859unifyHelp :: Variable -> Variable -> Unify ()
@@ -110,23 +121,16 @@ actuallyUnify variable1 variable2 = do
110121 Var.BuiltIn | name `elem` ["Int","Float"] -> flexAndUnify svar
111122 Var.Local | List.isPrefixOf "number" name -> flexAndUnify svar
112123 _ ->
113− let hint = "Looks like something besides an Int or Float is being used as a number."
114− in
115− typeError (Just hint) variable1 variable2
116−
117− comparableError maybe =
118− typeError (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
119− where
120− msg =
121− "Looks like you want something comparable, but the only valid comparable\n\
122− \types are Int, Float, Char, String, lists, or tuples."
123−
124− appendableError maybe =
125− typeError (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
126− where
127− msg =
128− "Looks like you want something appendable, but the only Strings, Lists,\n\
129− \and Text can be appended with the (++) operator."
124+ typeError variable1 variable2 Error.NoNote
125+
126+ comparableError maybeHint =
127+ typeError variable1 variable2 $ Error.PostNote $
128+ "Note: comparable types include Int, Float, Char, String, lists, and tuples."
129+ ++ maybe "" ("\n"++) maybeHint
130+
131+ appendableError =
132+ typeError variable1 variable2 $ Error.PostNote $
133+ "Note: appendable types include only Strings, Lists, and Text."
130134
131135 unifyComparable v (Var.Canonical home name) =
132136 case home of
@@ -146,7 +150,8 @@ actuallyUnify variable1 variable2 = do
146150
147151 Tuple vs
148152 | length vs > 6 ->
149− comparableError $ Just "Cannot compare a tuple with more than 6 elements."
153+ comparableError $ Just $
154+ "Furthermore, it cannot be a tuple with more than 6 elements."
150155 | otherwise ->
151156 do flexAndUnify varSuper
152157 cmpVars <- liftIO $ forM [1..length vs] $ \_ -> variable (Is Comparable)
@@ -156,15 +161,15 @@ actuallyUnify variable1 variable2 = do
156161 do struct <- liftIO $ collectApps varFlex
157162 case struct of
158163 List _ -> flexAndUnify varSuper
159− _ -> appendableError Nothing
164+ _ -> appendableError
160165
161166 rigidError var =
162− typeError (Just hint) variable1 variable2
163− where
164− hint =
165− "Could not unify rigid type variable '" ++ render (pretty Never var) ++ "'.\n" ++
166− "The problem probably relates to the type variable being shared between a\n\
167− \top-level type annotation and a related let-bound type annotation."
167+ let v = render (pretty Never var)
168+ in
169+ typeError variable1 variable2 $ Error.PreNote $
170+ "Could not unify rigid type variable '" ++ v ++ "'. The problem probably\n"
171+ ++ "relates to the type variable being shared between two type annotations.\n"
172+ ++ "Try commenting out some type annotations and see what happens."
168173
169174 superUnify =
170175 case (flex desc1, flex desc2, name desc1, name desc2) of
@@ -190,7 +195,7 @@ actuallyUnify variable1 variable2 = do
190195
191196 (Rigid, _, _, _) -> rigidError variable1
192197 (_, Rigid, _, _) -> rigidError variable2
193− _ -> typeError Nothing variable1 variable2
198+ _ -> typeError variable1 variable2 Error.NoNote
194199
195200 case (structure desc1, structure desc2) of
196201 (Nothing, Nothing) | flex desc1 == Flexible && flex desc1 == Flexible ->
@@ -234,7 +239,7 @@ actuallyUnify variable1 variable2 = do
234239 (Record1 _ _, Record1 _ _) ->
235240 recordUnify fresh variable1 variable2
236241
237− _ -> typeError Nothing variable1 variable2
242+ _ -> typeError variable1 variable2 Error.NoNote
238243
239244
240245-- RECORD UNIFICATION
@@ -257,15 +262,14 @@ recordUnify fresh variable1 variable2 =
257262 let uniqueFields2 = diffFields fields2 fields1
258263
259264 let addFieldMismatchError missingFields =
260− let msg = fieldMismatchError missingFields
261− in
262− typeError (Just msg) variable1 variable2
265+ typeError variable1 variable2 $ Error.PreNote $
266+ fieldMismatchError missingFields
263267
264268 case (ext1, ext2) of
265269 (Empty _, Empty _) ->
266270 case Map.null uniqueFields1 && Map.null uniqueFields2 of
267271 True -> return ()
268− False -> typeError Nothing variable1 variable2
272+ False -> typeError variable1 variable2 Error.NoNote
269273
270274 (Empty var1, Extension var2) ->
271275 case (Map.null uniqueFields1, Map.null uniqueFields2) of
@@ -352,10 +356,9 @@ fieldMismatchError missingFields =
352356 case Map.keys missingFields of
353357 [] -> ""
354358 [key] ->
355− "Looks like a record is missing the field '" ++ key ++ "'.\n " ++
356− "Maybe there is a misspelling in a record access or record update?"
359+ "Looks like a record is missing the field '" ++ key ++ "'"
357360 keys ->
358− "Looks like one record is missing fields "
361+ "Looks like a record is missing fields "
359362 ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
360363
361364
src/Validate.hs+23−10
@@ -340,16 +340,30 @@ extractValues (A.A region decl) =
340340-- UNBOUND TYPE VARIABLES
341341
342342checkDecl :: D.ValidDecl -> Result.Result wrn Error.Error ()
343−checkDecl (A.A _ decl) =
343+checkDecl (A.A region decl) =
344344 case decl of
345345 D.Definition _ ->
346346 return ()
347347
348348 D.Datatype name boundVars ctors ->
349− F.traverse_ (checkTypeVars name boundVars) (concatMap snd ctors)
349+ let frees = concatMap freeVars (concatMap snd ctors)
350+ in
351+ case findUnbound boundVars frees of
352+ [] ->
353+ return ()
354+
355+ v:vs ->
356+ Result.throw region
357+ (Error.UnboundTypeVarsInUnion name boundVars v vs ctors)
350358
351359 D.TypeAlias name boundVars tipe ->
352− checkTypeVars name boundVars tipe
360+ case findUnbound boundVars (freeVars tipe) of
361+ [] ->
362+ return ()
363+
364+ v:vs ->
365+ Result.throw region
366+ (Error.UnboundTypeVarsInAlias name boundVars v vs tipe)
353367
354368 D.Port _ ->
355369 return ()
@@ -358,16 +372,15 @@ checkDecl (A.A _ decl) =
358372 return ()
359373
360374
361−checkTypeVars :: String -> [String] -> Type.Raw -> Result.Result wrn Error.Error ()
362−checkTypeVars name boundVars tipe =
375+findUnbound :: [String] -> [A.Located String] -> [String]
376+findUnbound boundVars frees =
363377 let bounds = Set.fromList boundVars
364378
365− check (A.A region free) =
366− if Set.member free bounds
367− then return ()
368− else Result.throw region (Error.UnboundTypeVar name free)
379+ isUnbound x =
380+ Set.notMember x bounds
369381 in
370− F.traverse_ check (freeVars tipe)
382+ filter isUnbound (map A.drop frees)
383+
371384
372385
373386freeVars :: Type.Raw -> [A.Located String]