Remove platform CPP from nativeGen/PPC/CodeGen.hs
[ghc-hetmet.git] / compiler / deSugar / MatchCon.lhs
index 6ff502a..d84b901 100644 (file)
@@ -1,31 +1,35 @@
-
+%
+% (c) The University of Glasgow 2006
 % (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
 %
-\section[MatchCon]{Pattern-matching constructors}
+
+Pattern-matching constructors
 
 \begin{code}
+{-# OPTIONS -fno-warn-incomplete-patterns #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+
 module MatchCon ( matchConFamily ) where
 
 #include "HsVersions.h"
 
-import Id( idType )
-
 import {-# SOURCE #-} Match    ( match )
 
-import HsSyn           ( Pat(..), HsConDetails(..) )
-import DsBinds         ( dsLHsBinds )
-import DataCon         ( isVanillaDataCon, dataConInstOrigArgTys )
-import TcType          ( tcTyConAppArgs )
-import Type            ( mkTyVarTys )
-import CoreSyn
+import HsSyn
+import DsBinds
+import DataCon
+import TcType
 import DsMonad
 import DsUtils
-
-import Id              ( Id )
-import Type             ( Type )
-import ListSetOps      ( equivClassesByUniq )
-import SrcLoc          ( unLoc, Located(..) )
-import Unique          ( Uniquable(..) )
+import Util    ( all2, takeList, zipEqual )
+import ListSetOps ( runs )
+import Id
+import NameEnv
+import SrcLoc
 import Outputable
 \end{code}
 
@@ -82,64 +86,141 @@ have-we-used-all-the-constructors? question; the local function
 \begin{code}
 matchConFamily :: [Id]
                -> Type
-              -> [EquationInfo]
+              -> [[EquationInfo]]
               -> DsM MatchResult
-matchConFamily (var:vars) ty eqns_info
-  = let
-       -- Sort into equivalence classes by the unique on the constructor
-       -- All the EqnInfos should start with a ConPat
-       groups = equivClassesByUniq get_uniq eqns_info
-       get_uniq (EqnInfo { eqn_pats = ConPatOut (L _ data_con) _ _ _ _ _ : _}) = getUnique data_con
-
-       -- Get the wrapper from the head of each group.  We're going to
-       -- use it as the pattern in this case expression, so we need to 
-       -- ensure that any type variables it mentions in the pattern are
-       -- in scope.  So we put its wrappers outside the case, and
-       -- zap the wrapper for it. 
-       wraps :: [CoreExpr -> CoreExpr]
-       wraps = map (eqn_wrap . head) groups
-
-       groups' = [ eqn { eqn_wrap = idWrapper } : eqns | eqn:eqns <- groups ]
-    in
-       -- Now make a case alternative out of each group
-    mappM (match_con vars ty) groups'  `thenDs` \ alts ->
-    returnDs (adjustMatchResult (foldr (.) idWrapper wraps) $
-             mkCoAlgCaseMatchResult var ty alts)
+-- Each group of eqns is for a single constructor
+matchConFamily (var:vars) ty groups
+  = do { alts <- mapM (matchOneCon vars ty) groups
+       ; return (mkCoAlgCaseMatchResult var ty alts) }
+
+type ConArgPats = HsConDetails (LPat Id) (HsRecFields Id (LPat Id))
+
+matchOneCon :: [Id]
+            -> Type
+            -> [EquationInfo]
+            -> DsM (DataCon, [Var], MatchResult)
+matchOneCon vars ty (eqn1 : eqns)      -- All eqns for a single constructor
+  = do { arg_vars <- selectConMatchVars arg_tys args1
+               -- Use the first equation as a source of 
+               -- suggestions for the new variables
+
+       -- Divide into sub-groups; see Note [Record patterns]
+        ; let groups :: [[(ConArgPats, EquationInfo)]]
+             groups = runs compatible_pats [ (pat_args (firstPat eqn), eqn) 
+                                           | eqn <- eqn1:eqns ]
+
+       ; match_results <- mapM (match_group arg_vars) groups
+
+       ; return (con1, tvs1 ++ dicts1 ++ arg_vars, 
+                 foldr1 combineMatchResults match_results) }
+  where
+    ConPatOut { pat_con = L _ con1, pat_ty = pat_ty1,
+               pat_tvs = tvs1, pat_dicts = dicts1, pat_args = args1 }
+             = firstPat eqn1
+    fields1 = dataConFieldLabels con1
+       
+    arg_tys  = dataConInstOrigArgTys con1 inst_tys
+    inst_tys = tcTyConAppArgs pat_ty1 ++ 
+              mkTyVarTys (takeList (dataConExTyVars con1) tvs1)
+       -- Newtypes opaque, hence tcTyConAppArgs
+       -- dataConInstOrigArgTys takes the univ and existential tyvars
+       -- and returns the types of the *value* args, which is what we want
+
+    match_group :: [Id] -> [(ConArgPats, EquationInfo)] -> DsM MatchResult
+    -- All members of the group have compatible ConArgPats
+    match_group arg_vars arg_eqn_prs
+      = do { (wraps, eqns') <- mapAndUnzipM shift arg_eqn_prs
+          ; let group_arg_vars = select_arg_vars arg_vars arg_eqn_prs
+          ; match_result <- match (group_arg_vars ++ vars) ty eqns'
+          ; return (adjustMatchResult (foldr1 (.) wraps) match_result) }
+
+    shift (_, eqn@(EqnInfo { eqn_pats = ConPatOut{ pat_tvs = tvs, pat_dicts = ds, 
+                                                  pat_binds = bind, pat_args = args
+                                       } : pats }))
+      = do { ds_ev_binds <- dsTcEvBinds bind
+          ; return (wrapBinds (tvs `zip` tvs1) 
+                   . wrapBinds (ds  `zip` dicts1)
+                   . wrapDsEvBinds ds_ev_binds,
+                   eqn { eqn_pats = conArgPats arg_tys args ++ pats }) }
+
+    -- Choose the right arg_vars in the right order for this group
+    -- Note [Record patterns]
+    select_arg_vars arg_vars ((arg_pats, _) : _)
+      | RecCon flds <- arg_pats
+      , let rpats = rec_flds flds  
+      , not (null rpats)     -- Treated specially; cf conArgPats
+      = ASSERT2( length fields1 == length arg_vars, 
+                 ppr con1 $$ ppr fields1 $$ ppr arg_vars )
+        map lookup_fld rpats
+      | otherwise
+      = arg_vars
+      where
+        fld_var_env = mkNameEnv $ zipEqual "get_arg_vars" fields1 arg_vars
+       lookup_fld rpat = lookupNameEnv_NF fld_var_env 
+                                          (idName (unLoc (hsRecFieldId rpat)))
+
+-----------------
+compatible_pats :: (ConArgPats,a) -> (ConArgPats,a) -> Bool
+-- Two constructors have compatible argument patterns if the number
+-- and order of sub-matches is the same in both cases
+compatible_pats (RecCon flds1, _) (RecCon flds2, _) = same_fields flds1 flds2
+compatible_pats (RecCon flds1, _) _                 = null (rec_flds flds1)
+compatible_pats _                 (RecCon flds2, _) = null (rec_flds flds2)
+compatible_pats _                 _                 = True -- Prefix or infix con
+
+same_fields :: HsRecFields Id (LPat Id) -> HsRecFields Id (LPat Id) -> Bool
+same_fields flds1 flds2 
+  = all2 (\f1 f2 -> unLoc (hsRecFieldId f1) == unLoc (hsRecFieldId f2))
+        (rec_flds flds1) (rec_flds flds2)
+
+
+-----------------
+selectConMatchVars :: [Type] -> ConArgPats -> DsM [Id]
+selectConMatchVars arg_tys (RecCon {})      = newSysLocalsDs arg_tys
+selectConMatchVars _       (PrefixCon ps)   = selectMatchVars (map unLoc ps)
+selectConMatchVars _       (InfixCon p1 p2) = selectMatchVars [unLoc p1, unLoc p2]
+
+conArgPats :: [Type]   -- Instantiated argument types 
+                       -- Used only to fill in the types of WildPats, which
+                       -- are probably never looked at anyway
+          -> ConArgPats
+          -> [Pat Id]
+conArgPats _arg_tys (PrefixCon ps)   = map unLoc ps
+conArgPats _arg_tys (InfixCon p1 p2) = [unLoc p1, unLoc p2]
+conArgPats  arg_tys (RecCon (HsRecFields { rec_flds = rpats }))
+  | null rpats = map WildPat arg_tys
+       -- Important special case for C {}, which can be used for a 
+       -- datacon that isn't declared to have fields at all
+  | otherwise  = map (unLoc . hsRecFieldArg) rpats
 \end{code}
 
-And here is the local function that does all the work.  It is
-more-or-less the @matchCon@/@matchClause@ functions on page~94 in
-Wadler's chapter in SLPJ.  The function @shift_con_pats@ does what the
-list comprehension in @matchClause@ (SLPJ, p.~94) does, except things
-are trickier in real life.  Works for @ConPats@, and we want it to
-fail catastrophically for anything else (which a list comprehension
-wouldn't).  Cf.~@shift_lit_pats@ in @MatchLits@.
+Note [Record patterns]
+~~~~~~~~~~~~~~~~~~~~~~
+Consider 
+        data T = T { x,y,z :: Bool }
+
+        f (T { y=True, x=False }) = ...
+
+We must match the patterns IN THE ORDER GIVEN, thus for the first
+one we match y=True before x=False.  See Trac #246; or imagine 
+matching against (T { y=False, x=undefined }): should fail without
+touching the undefined. 
+
+Now consider:
+
+        f (T { y=True, x=False }) = ...
+        f (T { x=True, y= False}) = ...
+
+In the first we must test y first; in the second we must test x 
+first.  So we must divide even the equations for a single constructor
+T into sub-goups, based on whether they match the same field in the
+same order.  That's what the (runs compatible_pats) grouping.
+
+All non-record patterns are "compatible" in this sense, because the
+positional patterns (T a b) and (a `T` b) all match the arguments
+in order.  Also T {} is special because it's equivalent to (T _ _).
+Hence the (null rpats) checks here and there.
 
-\begin{code}
-match_con vars ty eqns
-  = do { -- Make new vars for the con arguments; avoid new locals where possible
-         arg_vars     <- selectMatchVars (map unLoc arg_pats1) arg_tys
-       ; eqns'        <- mapM shift eqns 
-       ; match_result <- match (arg_vars ++ vars) ty eqns'
-       ; return (con, tvs1 ++ dicts1 ++ arg_vars, match_result) }
-  where
-    ConPatOut (L _ con) tvs1 dicts1 _ (PrefixCon arg_pats1) pat_ty = firstPat (head eqns)
-
-    shift eqn@(EqnInfo { eqn_wrap = wrap, 
-                        eqn_pats = ConPatOut _ tvs ds bind (PrefixCon arg_pats) _ : pats })
-       = do { prs <- dsLHsBinds bind
-            ; return (eqn { eqn_wrap = wrap . wrapBinds (tvs `zip` tvs1) 
-                                            . wrapBinds (ds  `zip` dicts1)
-                                            . mkDsLet (Rec prs),
-                            eqn_pats = map unLoc arg_pats ++ pats }) }
-
-       -- Get the arg types, which we use to type the new vars
-       -- to match on, from the "outside"; the types of pats1 may 
-       -- be more refined, and hence won't do
-    arg_tys = dataConInstOrigArgTys con inst_tys
-    inst_tys | isVanillaDataCon con = tcTyConAppArgs pat_ty    -- Newtypes opaque!
-            | otherwise            = mkTyVarTys tvs1
-\end{code}
 
 Note [Existentials in shift_con_pat]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~