module GHC.HsToCore.Match.Constructor ( matchConFamily, matchPatSyn ) where
#include "HsVersions.h"
import GHC.Prelude
import GHC.HsToCore.Match ( match )
import GHC.Hs
import GHC.HsToCore.Binds
import GHC.Core.ConLike
import GHC.Types.Basic ( Origin(..) )
import GHC.Tc.Utils.TcType
import GHC.Core.Multiplicity
import GHC.HsToCore.Monad
import GHC.HsToCore.Utils
import GHC.Core ( CoreExpr )
import GHC.Core.Make ( mkCoreLets )
import GHC.Utils.Misc
import GHC.Types.Id
import GHC.Types.Name.Env
import GHC.Types.FieldLabel ( flSelector )
import GHC.Types.SrcLoc
import GHC.Utils.Outputable
import GHC.Utils.Panic
import Control.Monad(liftM)
import Data.List (groupBy)
import Data.List.NonEmpty (NonEmpty(..))
matchConFamily :: NonEmpty Id
-> Type
-> NonEmpty (NonEmpty EquationInfo)
-> DsM (MatchResult CoreExpr)
matchConFamily (var :| vars) ty groups
= do let mult = idMult var
alts <- mapM (fmap toRealAlt . matchOneConLike vars ty mult) groups
return (mkCoAlgCaseMatchResult var ty alts)
where
toRealAlt alt = case alt_pat alt of
RealDataCon dcon -> alt{ alt_pat = dcon }
_ -> panic "matchConFamily: not RealDataCon"
matchPatSyn :: NonEmpty Id
-> Type
-> NonEmpty EquationInfo
-> DsM (MatchResult CoreExpr)
matchPatSyn (var :| vars) ty eqns
= do let mult = idMult var
alt <- fmap toSynAlt $ matchOneConLike vars ty mult eqns
return (mkCoSynCaseMatchResult var ty alt)
where
toSynAlt alt = case alt_pat alt of
PatSynCon psyn -> alt{ alt_pat = psyn }
_ -> panic "matchPatSyn: not PatSynCon"
type ConArgPats = HsConPatDetails GhcTc
matchOneConLike :: [Id]
-> Type
-> Mult
-> NonEmpty EquationInfo
-> DsM (CaseAlt ConLike)
matchOneConLike vars ty mult (eqn1 :| eqns)
= do { let inst_tys = ASSERT( all tcIsTcTyVar ex_tvs )
ASSERT( tvs1 `equalLength` ex_tvs )
arg_tys ++ mkTyVarTys tvs1
val_arg_tys = conLikeInstOrigArgTys con1 inst_tys
match_group :: [Id]
-> [(ConArgPats, EquationInfo)] -> DsM (MatchResult CoreExpr)
match_group arg_vars arg_eqn_prs
= ASSERT( notNull arg_eqn_prs )
do { (wraps, eqns') <- liftM unzip (mapM 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 $ foldr1 (.) wraps <$> match_result
}
shift (_, eqn@(EqnInfo
{ eqn_pats = ConPat
{ pat_args = args
, pat_con_ext = ConPatTc
{ cpt_tvs = tvs
, cpt_dicts = ds
, cpt_binds = bind
}
} : pats
}))
= do ds_bind <- dsTcEvBinds bind
return ( wrapBinds (tvs `zip` tvs1)
. wrapBinds (ds `zip` dicts1)
. mkCoreLets ds_bind
, eqn { eqn_orig = Generated
, eqn_pats = conArgPats val_arg_tys args ++ pats }
)
shift (_, (EqnInfo { eqn_pats = ps })) = pprPanic "matchOneCon/shift" (ppr ps)
; let scaled_arg_tys = map (scaleScaled mult) val_arg_tys
; arg_vars <- selectConMatchVars scaled_arg_tys args1
; let groups :: [[(ConArgPats, EquationInfo)]]
groups = groupBy compatible_pats [ (pat_args (firstPat eqn), eqn)
| eqn <- eqn1:eqns ]
; match_results <- mapM (match_group arg_vars) groups
; return $ MkCaseAlt{ alt_pat = con1,
alt_bndrs = tvs1 ++ dicts1 ++ arg_vars,
alt_wrapper = wrapper1,
alt_result = foldr1 combineMatchResults match_results } }
where
ConPat { pat_con = L _ con1
, pat_args = args1
, pat_con_ext = ConPatTc
{ cpt_arg_tys = arg_tys
, cpt_wrap = wrapper1
, cpt_tvs = tvs1
, cpt_dicts = dicts1
}
} = firstPat eqn1
fields1 = map flSelector (conLikeFieldLabels con1)
ex_tvs = conLikeExTyCoVars con1
select_arg_vars :: [Id] -> [(ConArgPats, EquationInfo)] -> [Id]
select_arg_vars arg_vars ((arg_pats, _) : _)
| RecCon flds <- arg_pats
, let rpats = rec_flds flds
, not (null rpats)
= ASSERT2( fields1 `equalLength` 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 (L _ rpat) = lookupNameEnv_NF fld_var_env
(idName (unLoc (hsRecFieldId rpat)))
select_arg_vars _ [] = panic "matchOneCon/select_arg_vars []"
compatible_pats :: (ConArgPats,a) -> (ConArgPats,a) -> Bool
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
same_fields :: HsRecFields GhcTc (LPat GhcTc) -> HsRecFields GhcTc (LPat GhcTc)
-> Bool
same_fields flds1 flds2
= all2 (\(L _ f1) (L _ f2)
-> unLoc (hsRecFieldId f1) == unLoc (hsRecFieldId f2))
(rec_flds flds1) (rec_flds flds2)
selectConMatchVars :: [Scaled Type] -> ConArgPats -> DsM [Id]
selectConMatchVars arg_tys con = case con of
(RecCon {}) -> newSysLocalsDsNoLP arg_tys
(PrefixCon _ ps) -> selectMatchVars (zipMults arg_tys ps)
(InfixCon p1 p2) -> selectMatchVars (zipMults arg_tys [p1, p2])
where
zipMults = zipWithEqual "selectConMatchVar" (\a b -> (scaledMult a, unLoc b))
conArgPats :: [Scaled Type]
-> ConArgPats
-> [Pat GhcTc]
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 (map scaledThing arg_tys)
| otherwise = map (unLoc . hsRecFieldArg . unLoc) rpats