on master help sign in
Show proper errors for infinite types again

Fix #939, fix #942

commitc62bea9f869a2e11275b20541a833d71711eaeac authorEvan Czaplicki <info@elm-lang.org> dateSat 30 May 2015, 16:40 (11 years ago) parent6ea2189
8 files changed+114−70
src/Reporting/Error/Type.hs+28−9 src/Type/Constrain/Expression.hs+14−11 src/Type/Constrain/Pattern.hs+9−5 src/Type/Fragment.hs+2−1 src/Type/Inference.hs+2−2 src/Type/Solve.hs+53−37 src/Type/State.hs+1−1 src/Type/Type.hs+5−4
src/Reporting/Error/Type.hs+28−9
@@ -1,6 +1,7 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Type where
33
4+import Text.PrettyPrint ((<+>))
45import qualified Text.PrettyPrint as P
56
67import qualified AST.Helpers as Help
@@ -13,7 +14,7 @@ import qualified Reporting.Report as Report
1314
1415data Error
1516 = Mismatch Mismatch
16− | InfiniteType String Type.Canonical
17+ | InfiniteType InfiniteType
1718 | BadMain Type.Canonical
1819
1920
@@ -25,6 +26,13 @@ data Mismatch = MismatchInfo
2526 }
2627
2728
29+data InfiniteType = InfiniteTypeInfo
30+ { _name :: String
31+ , _var :: Type.Canonical
32+ , _type :: Type.Canonical
33+ }
34+
35+
2836data Hint
2937 = None
3038 | CaseBranch Int Region.Region
@@ -73,14 +81,25 @@ toReport dealiaser err =
7381 PostNote msg ->
7482 Report.Report "TYPE MISMATCH" subRegion preHint (postHint ++ "\n\n" ++ msg)
7583
76− InfiniteType var tipe ->
77− Report.simple "INFINITE TYPE" "This expression is leading me to infer an infinite type." $
78− "Maybe you are trying to do some tricky recursion? Try breaking the expression\n"
79− ++ "into smaller pieces. Give each piece a name and try to write down its type.\n\n"
80− ++ "Type inference got stuck when type '" ++ var ++ "' needed equal to:\n\n"
81− ++ P.render (P.nest 4 (P.pretty dealiaser False tipe))
82− ++ "\n\nNotice that type variable '" ++ var ++ "' appears there too, so if we\n"
83− ++ "expanded this type, it would just keep getting bigger and bigger."
84+ InfiniteType (InfiniteTypeInfo name var tipe) ->
85+ let
86+ prettyVar =
87+ P.pretty dealiaser False var
88+
89+ prettyType =
90+ P.pretty dealiaser False tipe
91+ in
92+ Report.simple "INFINITE TYPE"
93+ ( "I am inferring weird self-referential type for '" ++ name ++ "'"
94+ )
95+ ( "The bit of the type that is self-referential looks like this:\n\n"
96+ ++ P.render (P.nest 4 (prettyVar <+> P.equals <+> prettyType))
97+ ++ "\n\nThe cause is often that the usage of '" ++ name ++ "' is flipped around.\n\n"
98+ ++ "Maybe you are inserting a data structure into an element? Maybe you are giving\n"
99+ ++ "a function to an argument? Either way, something is probably backwards!\n\n"
100+ ++ "Try breaking the code related to '" ++ name ++ "' into smaller pieces.\n"
101+ ++ "Give each piece a name and try to write down its type."
102+ )
84103
85104 BadMain tipe ->
86105 Report.simple "BAD MAIN TYPE" "The 'main' value has an unsupported type." $
src/Type/Constrain/Expression.hs+14−11
@@ -175,7 +175,8 @@ constrain env (A.A region expression) tipe =
175175 (Info [] [] [] Map.empty CTrue CTrue)
176176 (concatMap expandPattern defs)
177177
178− let letScheme = [ Scheme rqs fqs (CLet [monoscheme headers] c2) headers ]
178+ let letScheme =
179+ [ Scheme rqs fqs (CLet [monoscheme headers] c2) headers ]
179180
180181 return $ CLet schemes (CLet letScheme (c1 /\ bodyCon))
181182
@@ -409,22 +410,22 @@ data Info = Info
409410 { iSchemes :: [TypeScheme]
410411 , iRigid :: [Variable]
411412 , iFlex :: [Variable]
412− , iHeaders :: Map.Map String Type
413+ , iHeaders :: Map.Map String (A.Located Type)
413414 , iC2 :: TypeConstraint
414415 , iC1 :: TypeConstraint
415416 }
416417
417418
418419constrainDef :: Env.Environment -> Info -> Canonical.Def -> IO Info
419−constrainDef env info (Canonical.Definition (A.A _ pattern) expr maybeTipe) =
420+constrainDef env info (Canonical.Definition (A.A patternRegion pattern) expr maybeTipe) =
420421 let qs = [] -- should come from the def, but I'm not sure what would live there...
421422 in
422423 case (pattern, maybeTipe) of
423− (P.Var name, Just (A.A region tipe)) ->
424− constrainAnnotatedDef env info qs region name expr tipe
424+ (P.Var name, Just (A.A typeRegion tipe)) ->
425+ constrainAnnotatedDef env info qs patternRegion typeRegion name expr tipe
425426
426427 (P.Var name, Nothing) ->
427− constrainUnannotatedDef env info qs name expr
428+ constrainUnannotatedDef env info qs patternRegion name expr
428429
429430 _ -> error ("problem in constrainDef with " ++ show pattern)
430431
@@ -434,11 +435,12 @@ constrainAnnotatedDef
434435 -> Info
435436 -> [String]
436437 -> R.Region
438+ -> R.Region
437439 -> String
438440 -> Canonical.Expr
439441 -> ST.Canonical
440442 -> IO Info
441−constrainAnnotatedDef env info qs region name expr tipe =
443+constrainAnnotatedDef env info qs patternRegion typeRegion name expr tipe =
442444 do -- Some mistake may be happening here. Currently, qs is always [].
443445 rigidVars <- Monad.forM qs (\_ -> variable Rigid)
444446
@@ -455,13 +457,13 @@ constrainAnnotatedDef env info qs region name expr tipe =
455457 { rigidQuantifiers = []
456458 , flexibleQuantifiers = flexiVars ++ vars
457459 , constraint = CTrue
458− , header = Map.singleton name typ
460+ , header = Map.singleton name (A.A patternRegion typ)
459461 }
460462
461463 var <- variable Flexible
462464 defCon <- constrain env' expr (varN var)
463465 let annCon =
464− CEqual (Error.BadTypeAnnotation name) region typ (varN var)
466+ CEqual (Error.BadTypeAnnotation name) typeRegion typ (varN var)
465467
466468 return $ info
467469 { iSchemes = scheme : iSchemes info
@@ -473,10 +475,11 @@ constrainUnannotatedDef
473475 :: Env.Environment
474476 -> Info
475477 -> [String]
478+ -> R.Region
476479 -> String
477480 -> Canonical.Expr
478481 -> IO Info
479−constrainUnannotatedDef env info qs name expr =
482+constrainUnannotatedDef env info qs patternRegion name expr =
480483 do -- Some mistake may be happening here. Currently, qs is always [].
481484 rigidVars <- Monad.forM qs (\_ -> variable Rigid)
482485
@@ -493,6 +496,6 @@ constrainUnannotatedDef env info qs name expr =
493496 return $ info
494497 { iRigid = rigidVars ++ iRigid info
495498 , iFlex = v : iFlex info
496− , iHeaders = Map.insert name tipe (iHeaders info)
499+ , iHeaders = Map.insert name (A.A patternRegion tipe) (iHeaders info)
497500 , iC2 = con /\ iC2 info
498501 }
src/Type/Constrain/Pattern.hs+9−5
@@ -22,7 +22,11 @@ constrain
2222 -> Type
2323 -> IO Fragment
2424constrain env (A.A region pattern) tipe =
25− let (===) = CEqual Error.None region
25+ let
26+ (===) = CEqual Error.None region
27+
28+ rvar v =
29+ A.A region (varN v)
2630 in
2731 case pattern of
2832 P.Anything ->
@@ -35,7 +39,7 @@ constrain env (A.A region pattern) tipe =
3539 P.Var name ->
3640 do v <- variable Flexible
3741 return $ Fragment
38− { typeEnv = Map.singleton name (varN v)
42+ { typeEnv = Map.singleton name (rvar v)
3943 , vars = [v]
4044 , typeConstraint = varN v === tipe
4145 }
@@ -44,7 +48,7 @@ constrain env (A.A region pattern) tipe =
4448 do v <- variable Flexible
4549 fragment <- constrain env p tipe
4650 return $ fragment
47− { typeEnv = Map.insert name (varN v) (typeEnv fragment)
51+ { typeEnv = Map.insert name (rvar v) (typeEnv fragment)
4852 , vars = v : vars fragment
4953 , typeConstraint = varN v === tipe /\ typeConstraint fragment
5054 }
@@ -62,8 +66,8 @@ constrain env (A.A region pattern) tipe =
6266
6367 P.Record fields ->
6468 do pairs <- mapM (\name -> (,) name <$> variable Flexible) fields
65− let tenv = Map.fromList (map (second varN) pairs)
66− c <- exists $ \t -> return (tipe === record (Map.map (:[]) tenv) t)
69+ let tenv = Map.fromList (map (second rvar) pairs)
70+ c <- exists $ \t -> return (tipe === record (Map.map (\v -> [A.drop v]) tenv) t)
6771 return $ Fragment
6872 { typeEnv = tenv
6973 , vars = map snd pairs
src/Type/Fragment.hs+2−1
@@ -4,11 +4,12 @@ module Type.Fragment where
44import qualified Data.List as List
55import qualified Data.Map as Map
66
7+import Reporting.Annotation as A
78import Type.Type
89
910
1011data Fragment = Fragment
11− { typeEnv :: Map.Map String Type
12+ { typeEnv :: Map.Map String (A.Located Type)
1213 , vars :: [Variable]
1314 , typeConstraint :: TypeConstraint
1415 }
src/Type/Inference.hs+2−2
@@ -33,7 +33,7 @@ infer interfaces modul =
3333 state <- Solve.solve constraint
3434
3535 let header' = Map.delete "::" header
36− let types = Map.difference (TS.sSavedEnv state) header'
36+ let types = Map.map A.drop (Map.difference (TS.sSavedEnv state) header')
3737
3838 liftIO (Traverse.traverse T.toSrcType types)
3939
@@ -57,7 +57,7 @@ genConstraints interfaces modul =
5757 let allTypes = concat (ctors : importedVars)
5858 let vars = concatMap (fst . snd) allTypes
5959 let header = Map.map snd (Map.fromList allTypes)
60− let environ = T.CLet [ T.Scheme vars [] T.CTrue header ]
60+ let environ = T.CLet [ T.Scheme vars [] T.CTrue (Map.map (A.A undefined) header) ]
6161
6262 fvar <- T.variable T.Flexible
6363
src/Type/Solve.hs+53−37
@@ -1,14 +1,16 @@
11module Type.Solve (solve) where
22
3−import Control.Applicative ((<$>), (<*>))
3+import Control.Applicative ((<$>), (<*>), (<|>))
44import Control.Monad
55import Control.Monad.State (StateT, execStateT, liftIO)
66import Control.Monad.Except (ExceptT, throwError)
7+import qualified Data.Foldable as F
78import qualified Data.List as List
89import qualified Data.Map as Map
9−import qualified Data.Traversable as Traversable
10+import qualified Data.Traversable as T
1011import qualified Data.UnionFind.IO as UF
1112
13+import qualified AST.Type as Type
1214import qualified Reporting.Annotation as A
1315import qualified Reporting.Error.Type as Error
1416import qualified Type.State as TS
@@ -45,7 +47,7 @@ generalize youngPool =
4547 if isRedundant then return var else TS.register var
4648
4749 let rankDict' = Map.delete youngRank rankDict
48− Traversable.traverse (mapM registerIfNotRedundant) rankDict'
50+ T.traverse (mapM registerIfNotRedundant) rankDict'
4951
5052 -- For variables with rank youngRank
5153 -- If rank < youngRank: register in oldPool
@@ -159,8 +161,9 @@ actuallySolve constraint =
159161 do env <- TS.getEnv
160162 freshCopy <-
161163 case Map.lookup name env of
162− Just tipe ->
164+ Just (A.A _ tipe) ->
163165 TS.makeInstance tipe
166+
164167 Nothing ->
165168 if List.isPrefixOf "Native." name
166169 then liftIO (variable Flexible)
@@ -170,12 +173,18 @@ actuallySolve constraint =
170173 unify Error.None region freshCopy t
171174
172175
173−solveScheme :: TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
176+solveScheme
177+ :: TypeScheme
178+ -> StateT TS.SolverState IO (Map.Map String (A.Located Variable))
174179solveScheme scheme =
180+ let
181+ flatten (A.A region term) =
182+ A.A region <$> TS.flatten term
183+ in
175184 case scheme of
176185 Scheme [] [] constraint header ->
177186 do actuallySolve constraint
178− Traversable.traverse TS.flatten header
187+ T.traverse flatten header
179188
180189 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
181190 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
@@ -185,7 +194,7 @@ solveScheme scheme =
185194 freshPool <- TS.nextRankPool
186195 TS.switchToPool freshPool
187196 mapM TS.introduce quantifiers
188− header' <- Traversable.traverse TS.flatten header
197+ header' <- T.traverse flatten header
189198 actuallySolve constraint
190199
191200 allDistinct rigidQuantifiers
@@ -234,46 +243,53 @@ crash msg =
234243 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
235244
236245
237−occurs :: (String, Variable) -> StateT TS.SolverState IO ()
238−occurs (_name, variable) =
239− do vars <- liftIO $ infiniteVars [] variable
240− case vars of
241− [] ->
242− return ()
243−
244− var : _ ->
245− do desc <- liftIO $ UF.descriptor var
246− let region = error "TODO:occurs"
247− case structure desc of
248− Nothing ->
249− TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
250−
251− Just _ ->
252− TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
253− where
254− infiniteVars :: [Variable] -> Variable -> IO [Variable]
255− infiniteVars seen var =
256− let go = infiniteVars (var:seen)
257− in
258− if var `elem` seen
259− then return [var]
260− else do
261− desc <- UF.descriptor var
246+occurs :: (String, A.Located Variable) -> StateT TS.SolverState IO ()
247+occurs (name, A.A region variable) =
248+ do maybePair <- liftIO $ occursHelp [] variable
249+ case maybePair of
250+ Nothing ->
251+ return ()
252+
253+ Just (t1, t2) ->
254+ let
255+ info = Error.InfiniteTypeInfo name t1 t2
256+ in
257+ TS.addError region (Error.InfiniteType info)
258+
259+
260+occursHelp
261+ :: [Variable]
262+ -> Variable
263+ -> IO (Maybe (Type.Canonical, Type.Canonical))
264+occursHelp seen var =
265+ if elem var seen then
266+ do desc <- UF.descriptor var
267+ UF.setDescriptor var (desc { structure = Nothing })
268+ var' <- UF.fresh desc
269+ srcVar <- toSrcType var
270+ srcVar' <- toSrcType var'
271+ return (Just (srcVar, srcVar'))
272+ else
273+ do desc <- UF.descriptor var
262274 case structure desc of
263− Nothing -> return []
275+ Nothing ->
276+ return Nothing
277+
264278 Just struct ->
279+ let go = occursHelp (var:seen)
280+ in
265281 case struct of
266282 App1 a b ->
267− (++) <$> go a <*> go b
283+ (<|>) <$> go a <*> go b
268284
269285 Fun1 a b ->
270− (++) <$> go a <*> go b
286+ (<|>) <$> go a <*> go b
271287
272288 Var1 a ->
273289 go a
274290
275291 EmptyRecord1 ->
276− return []
292+ return Nothing
277293
278294 Record1 fields ext ->
279− concat <$> mapM go (ext : concat (Map.elems fields))
295+ F.asum <$> mapM go (ext : concat (Map.elems fields))
src/Type/State.hs+1−1
@@ -33,7 +33,7 @@ emptyPool =
3333 }
3434
3535
36−type Env = Map.Map String Variable
36+type Env = Map.Map String (A.Located Variable)
3737
3838
3939-- Keeps track of the environment, type variable pool, and a list of errors
src/Type/Type.hs+5−4
@@ -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.Annotation as A
1617import qualified Reporting.Error.Type as Error
1718import qualified Reporting.Region as R
1819import Type.PrettyPrint
@@ -118,7 +119,7 @@ data Scheme a b = Scheme
118119 { rigidQuantifiers :: [b]
119120 , flexibleQuantifiers :: [b]
120121 , constraint :: Constraint a b
121− , header :: Map.Map String a
122+ , header :: Map.Map String (A.Located a)
122123 }
123124
124125
@@ -164,7 +165,7 @@ variable flex = UF.fresh $ Descriptor
164165
165166-- CONSTRAINT HELPERS
166167
167−monoscheme :: Map.Map String a -> Scheme a b
168+monoscheme :: Map.Map String (A.Located a) -> Scheme a b
168169monoscheme headers =
169170 Scheme [] [] CTrue headers
170171
@@ -373,8 +374,8 @@ instance (PrettyType a, PrettyType b) => PrettyType (Scheme a b) where
373374 dict =
374375 P.parens . commaSep . map prettyPair $ Map.toList headers
375376
376− prettyPair (n,t) =
377− P.text n <+> P.text ":" <+> pretty Never t
377+ prettyPair (fieldName, A.A _ tipe) =
378+ P.text fieldName <+> P.text ":" <+> pretty Never tipe
378379
379380
380381-- CONVERT TO SOURCE TYPES