@@ -1,13 +1,23 @@
1+{-# OPTIONS_GHC -Wall #-}
1module Nitpick.PatternMatches (patternMatches) where
2module Nitpick.PatternMatches (patternMatches) where
4+{- The algorithm used here comes from "Warnings for Pattern Matching"
5+by Luc Maranget. Check it out for more information!
7+http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
3−import Control.Arrow (second)
11+import Control.Arrow ((***), second)
4import qualified Data.Foldable as F
12import qualified Data.Foldable as F
13+import qualified Data.List as List
5import qualified Data.Map as Map
14import qualified Data.Map as Map
6import qualified Data.Maybe as Maybe
15import qualified Data.Maybe as Maybe
7import qualified Data.Set as Set
16import qualified Data.Set as Set
9import qualified AST.Expression.Canonical as C
18import qualified AST.Expression.Canonical as C
10import qualified AST.Helpers as Help
19import qualified AST.Helpers as Help
20+import qualified AST.Literal as L
11import qualified AST.Module as Module
21import qualified AST.Module as Module
12import qualified AST.Module.Name as ModuleName
22import qualified AST.Module.Name as ModuleName
13import qualified AST.Pattern as Pattern
23import qualified AST.Pattern as Pattern
@@ -29,100 +39,25 @@ type Result warning a =
29patternMatches :: Module.Interfaces -> Module.Canonical -> Result wrn DT.VariantDict
39patternMatches :: Module.Interfaces -> Module.Canonical -> Result wrn DT.VariantDict
30patternMatches interfaces (Module.Module name _ info) =
40patternMatches interfaces (Module.Module name _ info) =
32− tagDict =
42+ arityDict =
33− toTagDict interfaces name (Module.unions info)
43+ toArityDict interfaces name (Module.unions info)
35− const (Map.map (Map.map length) tagDict)
36− <$> checkExpression tagDict (Module.program info)
44− Map.Map Var.Home (Map.Map Tag [TagInfo])
47−type Tag = String
46+ Map.map (Map.map _size) arityDict
50−data TagInfo = TagInfo
56−toTagDict :: Module.Interfaces -> ModuleName.Canonical -> Module.Unions -> TagDict
57−toTagDict interfaces localName localUnions =
70− Map.singleton Var.BuiltIn $
75− , ("False", boolTags)
80− |> Map.map (toTagMapping . Module.iUnions)
81− |> Map.mapKeysMonotonic Var.Module
82− |> Map.insert (Var.Module localName) (toTagMapping localUnions)
84− Map.union builtinDict interfaceDict
48+ pure variantDict
85−
49+ <* checkExpression arityDict (Module.program info)
87−toTagMapping :: Module.Unions -> Map.Map Tag [TagInfo]
90− toTagAndArity (_tvars, tagInfoList) =
92− info = map (\(tag, args) -> TagInfo tag (length args)) tagInfoList
94− map (second (const info)) tagInfoList
97− |> concatMap toTagAndArity
101−lookupOtherTags :: Var.Canonical -> TagDict -> [TagInfo]
102−lookupOtherTags (Var.Canonical home name) tagDict =
103− case Map.lookup name =<< Map.lookup home tagDict of
108− if Help.isTuple name then
109− [ TagInfo name (read (drop 6 name)) ]
112− "Since the Nitpick phase happens after canonicalization and type \
113− \inference, it is impossible that a pattern in a case cannot be \
118-- CHECK EXPRESSIONS
53-- CHECK EXPRESSIONS
121−checkExpression :: TagDict -> C.Expr -> Result wrn ()
56+checkExpression :: ArityDict -> C.Expr -> Result wrn ()
122−checkExpression tagDict (A.A region expression) =
57+checkExpression arityDict (A.A region expression) =
125− checkExpression tagDict
60+ checkExpression arityDict
127 go2 a b =
62 go2 a b =
128 go a <* go b
63 go a <* go b
@@ -141,7 +76,7 @@ checkExpression tagDict (A.A region expression) =
141 go2 leftExpr rightExpr
76 go2 leftExpr rightExpr
143 C.Lambda pattern@(A.A patRegion _) body ->
78 C.Lambda pattern@(A.A patRegion _) body ->
144− checkPatterns tagDict patRegion Error.Arg [pattern]
79+ checkPatterns arityDict patRegion Error.Arg [pattern]
145 <* go body
80 <* go body
147 C.App func arg ->
82 C.App func arg ->
@@ -156,12 +91,12 @@ checkExpression tagDict (A.A region expression) =
156 <* F.traverse_ goDef defs
91 <* F.traverse_ goDef defs
158 goDef (C.Def _ pattern@(A.A patRegion _) expr _) =
93 goDef (C.Def _ pattern@(A.A patRegion _) expr _) =
159− checkPatterns tagDict patRegion Error.LetBound [pattern]
94+ checkPatterns arityDict patRegion Error.LetBound [pattern]
160 <* go expr
95 <* go expr
162 C.Case expr branches ->
97 C.Case expr branches ->
164− <* checkPatterns tagDict region Error.Case (map fst branches)
99+ <* checkPatterns arityDict region Error.Case (map fst branches)
165 <* F.traverse_ (go . snd) branches
100 <* F.traverse_ (go . snd) branches
167 C.Ctor _ctor exprs ->
102 C.Ctor _ctor exprs ->
@@ -203,180 +138,245 @@ checkExpression tagDict (A.A region expression) =
203-- CHECK PATTERNS
138-- CHECK PATTERNS
206−checkPatterns :: TagDict -> Region.Region -> Error.Origin -> [Pattern.Canonical] -> Result wrn ()
141+checkPatterns :: ArityDict -> Region.Region -> Error.Origin -> [Pattern.Canonical] -> Result wrn ()
207−checkPatterns tagDict region origin patterns =
142+checkPatterns arityDict region origin patterns =
208− checkPatternsHelp tagDict region origin [Anything] patterns
143+ let
145+ map (\p -> [fromCanonicalPattern p]) patterns
147+ if isUseful arityDict matrix [Anything] then
148+ Result.throw region (Error.Incomplete origin (error "TODO"))
216− -> [Pattern.Canonical]
218−checkPatternsHelp tagDict region origin unhandled patterns =
219− case (unhandled, patterns) of
224− Result.throw region (Error.Incomplete origin unhandled)
226− (_, pattern@(A.A localRegion _) : remainingPatterns) ->
155+-- MISSING PATTERNS
227− do newUnhandled <- filterPatterns tagDict localRegion pattern unhandled
228− checkPatternsHelp tagDict region origin newUnhandled remainingPatterns
231−filterPatterns
158+--missingPatterns :: [[Pattern]] -> Int -> [Pattern]
232− :: TagDict
159+--missingPatterns matrix n =
233− -> Region.Region
160+-- case matrix of
234− -> Pattern.Canonical
161+-- [] ->
236− -> Result wrn [Pattern]
237−filterPatterns tagDict region pattern unhandled =
240− fromCanonicalPattern pattern
242− noIntersection pat =
243− intersection pat nitPattern == Nothing
245− if all noIntersection unhandled then
246− Result.throw region Error.Redundant
249− do let complementPatterns = complement tagDict nitPattern
252− (\p -> Maybe.mapMaybe (intersection p) complementPatterns)
166+-- REDUNDANT PATTERNS
257−-- PATTERN INTERSECTION
169+isUseful :: ArityDict -> [[Pattern]] -> [Pattern] -> Bool
170+isUseful arityDict matrix vector =
171+ case (matrix, vector) of
260−intersection :: Pattern -> Pattern -> Maybe Pattern
175+ (_, []) ->
261−intersection pattern1 pattern2 =
176+ False
262− case (pattern1, pattern2) of
263− (Alias _ pattern1', _) ->
264− intersection pattern1' pattern2
266− (_, Alias _ pattern2') ->
178+ (_, firstPattern : patterns) ->
267− intersection pattern1 pattern2'
179+ case firstPattern of
183+ Maybe.mapMaybe (specialize name (length args)) matrix
185+ isUseful arityDict newMatrix (args ++ patterns)
269− (Anything, _) ->
187+ Anything ->
270− Just pattern2
188+ case isComplete arityDict matrix of
190+ isUseful arityDict (Maybe.mapMaybe toDefault matrix) patterns
272− (Var _, _) ->
192+ Just arityInfo ->
273− Just pattern2
193+ let
194+ isUsefulHelp (ctor, arity) =
197+ (Maybe.mapMaybe (specialize ctor arity) matrix)
198+ (replicate arity Anything ++ patterns)
200+ any isUsefulHelp arityInfo
275− (_, Anything) ->
202+ Literal literal ->
276− Just pattern1
203+ let
205+ Maybe.mapMaybe (specializeLiteral literal) matrix
207+ isUseful arityDict newMatrix patterns
281− (Data ctor1 args1, Data ctor2 args2) ->
282− if ctor1 /= ctor2 then
285− Data ctor1 <$> sequence (zipWith intersection args1 args2)
287− (Record _, Record _) ->
211+-- SPECIALIZE MATRICES
290− (Literal lit1, Literal lit2) ->
291− if lit1 == lit2 then
296− (AnythingBut literals, Literal lit) ->
214+specialize :: Var.Canonical -> Int -> [Pattern] -> Maybe [Pattern]
297− if Set.member lit literals then
215+specialize ctorName arity row =
298− Nothing
216+ case row of
300− Just pattern2
218+ error "Compiler error! Empty matrices should not get specialized."
302− (Literal lit, AnythingBut literals) ->
220+ firstPattern : patterns ->
303− if Set.member lit literals then
221+ case firstPattern of
304− Nothing
222+ Ctor name args ->
305− else
223+ if name == ctorName then Just (args ++ patterns) else Nothing
308− (AnythingBut literals1, AnythingBut literals2) ->
225+ Anything ->
309− Just (AnythingBut (Set.union literals1 literals2))
226+ Just (replicate arity Anything ++ patterns)
311− _ ->
228+ Literal _ ->
230+ "Compiler bug! After type checking, constructors and literals\
231+ \ should never align in pattern match exhaustiveness checks."
234+specializeLiteral :: L.Literal -> [Pattern] -> Maybe [Pattern]
235+specializeLiteral literal row =
238+ error "Compiler error! Empty matrices should not get specialized."
316−-- PATTERN COMPLEMENT
240+ firstPattern : patterns ->
241+ case firstPattern of
243+ if lit == literal then Just patterns else Nothing
319−complement :: TagDict -> Pattern -> [Pattern]
248+ Ctor _ _ ->
320−complement tagDict nitPattern =
249+ error $
321− case nitPattern of
250+ "Compiler bug! After type checking, constructors and literals\
322− Record _fields ->
251+ \ should never align in pattern match exhaustiveness checks."
325− Alias _name pattern ->
326− complement tagDict pattern
328− Var _name ->
254+toDefault :: [Pattern] -> Maybe [Pattern]
329− []
255+toDefault row =
331− Anything ->
260+ Ctor _ _ : _ ->
334− Literal lit ->
263+ Anything : patterns ->
335− [AnythingBut (Set.singleton lit)]
264+ Just patterns
337− AnythingBut literals ->
266+ Literal _ : _ ->
338− map Literal (Set.toList literals)
267+ Nothing
340− Data ctor patterns ->
341− complementData tagDict ctor patterns
344−complementData :: TagDict -> Var.Canonical -> [Pattern] -> [Pattern]
271+-- ALL CONSTRUCTORS ARE PRESENT?
345−complementData tagDict tag patterns =
348− lookupOtherTags tag tagDict
351− Maybe.mapMaybe (tagToPattern tag) otherTags
353− arity =
274+isComplete :: ArityDict -> [[Pattern]] -> Maybe [(Var.Canonical, Int)]
354− length patterns
275+isComplete arityDict matrix =
278+ List.foldl' isCompleteHelp Set.empty matrix
356− argComplements =
280+ actual =
357− concat (zipWith (makeArgComplements tagDict tag arity) [0..] patterns)
281+ Set.size ctorSet
359− tagComplements ++ argComplements
283+ if actual == 0 then
288+ (ArityInfo expected info) =
289+ getArityInfo (Set.findMin ctorSet) arityDict
291+ if expected == actual then Just info else Nothing
294+isCompleteHelp :: Set.Set Var.Canonical -> [Pattern] -> Set.Set Var.Canonical
295+isCompleteHelp ctors row =
298+ Set.insert name ctors
309+ Map.Map Var.Home (Map.Map String ArityInfo)
315+ , _info :: [(Var.Canonical, Int)]
362−tagToPattern :: Var.Canonical -> TagInfo -> Maybe Pattern
319+toArityDict :: Module.Interfaces -> ModuleName.Canonical -> Module.Unions -> ArityDict
363−tagToPattern (Var.Canonical home rootTag) (TagInfo tag arity) =
320+toArityDict interfaces localName localUnions =
364− if rootTag == tag then
321+ interfaces
365− Nothing
322+ |> Map.mapWithKey (\name iface -> toArityEntry name (Module.iUnions iface))
366− else
323+ |> Map.mapKeysMonotonic Var.Module
367− Just (Data (Var.Canonical home tag) (replicate arity Anything))
324+ |> Map.insert (Var.Module localName) (toArityEntry localName localUnions)
325+ |> Map.union builtinDict
370−makeArgComplements :: TagDict -> Var.Canonical -> Int -> Int -> Pattern -> [Pattern]
328+builtinDict :: ArityDict
371−makeArgComplements tagDict tag arity index argPattern =
329+builtinDict =
373− complementList =
331+ listInfo =
374− complement tagDict argPattern
332+ ArityInfo 2 [ (Var.builtin "::", 2), (Var.builtin "[]", 0) ]
376− padArgs pattern =
334+ boolInfo =
377− replicate index Anything
335+ ArityInfo 2 [ (Var.builtin "True", 0), (Var.builtin "False", 0) ]
379− : replicate (arity - index - 1) Anything
381− map (Data tag . padArgs) complementList
337+ Map.singleton Var.BuiltIn $ Map.fromList $
340+ , ("True", boolInfo)
341+ , ("False", boolInfo)
343+ ++ map makeTupleInfo [0..8]
346+makeTupleInfo :: Int -> ( String, ArityInfo )
352+ ( name, ArityInfo 1 [ (Var.builtin name, n) ] )
355+toArityEntry :: ModuleName.Canonical -> Module.Unions -> Map.Map String ArityInfo
356+toArityEntry name unions =
357+ Map.fromList (concatMap (toArityEntryHelp name) (Map.elems unions))
360+toArityEntryHelp :: ModuleName.Canonical -> Module.UnionInfo String -> [(String, ArityInfo)]
361+toArityEntryHelp name (_tvars, ctors) =
364+ ArityInfo (length ctors) (map (Var.fromModule name *** length) ctors)
366+ map (second (\_ -> arityInfo)) ctors
369+getArityInfo :: Var.Canonical -> ArityDict -> ArityInfo
370+getArityInfo var@(Var.Canonical home name) arityDict =
371+ case Map.lookup name =<< Map.lookup home arityDict of
376+ if Help.isTuple name then
377+ ArityInfo 1 [ (var, read (drop 6 name)) ]
380+ "Since the Nitpick phase happens after canonicalization and type\
381+ \ inference, it is impossible that a pattern in a case cannot be\