summaryrefslogtreecommitdiff
path: root/compiler/deSugar/PmExpr.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/deSugar/PmExpr.hs')
-rw-r--r--compiler/deSugar/PmExpr.hs450
1 files changed, 267 insertions, 183 deletions
diff --git a/compiler/deSugar/PmExpr.hs b/compiler/deSugar/PmExpr.hs
index 3697dd8cc1..f83c63e4fd 100644
--- a/compiler/deSugar/PmExpr.hs
+++ b/compiler/deSugar/PmExpr.hs
@@ -6,29 +6,38 @@ Haskell expressions (as used by the pattern matching checker) and utilities.
{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module PmExpr (
- PmExpr(..), PmLit(..), PmAltCon(..), TmVarCt(..),
- eqPmLit, isNotPmExprOther, lhsExprToPmExpr, hsExprToPmExpr
+ PmLit(..), PmLitValue(..), PmAltCon(..),
+ pmAltConType, PmEquality(..), eqPmAltCon,
+ pmLitType, literalToPmLit, negatePmLit, overloadPmLit,
+ pmLitAsStringLit, coreExprAsPmLit
) where
#include "HsVersions.h"
import GhcPrelude
-import BasicTypes (SourceText)
-import FastString (FastString, unpackFS)
-import HsSyn
+import Util
+import FastString
import Id
import Name
import DataCon
import ConLike
-import TcEvidence (isErasableHsWrapper)
-import TcType (isStringTy)
-import TysWiredIn
import Outputable
-import SrcLoc
+import Maybes
+import Type
+import TyCon
+import Literal
+import CoreSyn
+import CoreUtils (exprType)
+import PrelNames
+import TysWiredIn
+import TysPrim
+
+import Numeric (fromRat)
+import Data.Foldable (find)
+import Data.Ratio
{-
%************************************************************************
@@ -38,52 +47,135 @@ import SrcLoc
%************************************************************************
-}
-{- Note [PmExprOther in PmExpr]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Since there is no plan to extend the (currently pretty naive) term oracle in
-the near future, instead of playing with the verbose (HsExpr Id), we lift it to
-PmExpr. All expressions the term oracle does not handle are wrapped by the
-constructor PmExprOther. Note that we do not perform substitution in
-PmExprOther. Because of this, we do not even print PmExprOther, since they may
-refer to variables that are otherwise substituted away.
--}
-
-- ----------------------------------------------------------------------------
-- ** Types
--- | Lifted expressions for pattern match checking.
-data PmExpr = PmExprVar Name
- | PmExprCon ConLike [PmExpr]
- | PmExprLit PmLit
- | PmExprOther (HsExpr GhcTc) -- Note [PmExprOther in PmExpr]
-
-
-mkPmExprData :: DataCon -> [PmExpr] -> PmExpr
-mkPmExprData dc args = PmExprCon (RealDataCon dc) args
-
-- | Literals (simple and overloaded ones) for pattern match checking.
-data PmLit = PmSLit (HsLit GhcTc) -- simple
- | PmOLit Bool {- is it negated? -} (HsOverLit GhcTc) -- overloaded
-
--- | Equality between literals for pattern match checking.
-eqPmLit :: PmLit -> PmLit -> Bool
-eqPmLit (PmSLit l1) (PmSLit l2) = l1 == l2
-eqPmLit (PmOLit b1 l1) (PmOLit b2 l2) = b1 == b2 && l1 == l2
- -- See Note [Undecidable Equality for Overloaded Literals]
-eqPmLit _ _ = False
-
--- | Represents a match against a literal. We mostly use it to to encode shapes
--- for a variable that immediately lead to a refutation.
--
--- See Note [Refutable shapes] in TmOracle. Really similar to 'CoreSyn.AltCon'.
-newtype PmAltCon = PmAltLit PmLit
- deriving Outputable
-
+-- See Note [Undecidable Equality for PmAltCons]
+data PmLit = PmLit
+ { pm_lit_ty :: Type
+ , pm_lit_val :: PmLitValue }
+
+data PmLitValue
+ = PmLitInt Integer
+ | PmLitRat Rational
+ | PmLitChar Char
+ -- We won't actually see PmLitString in the oracle since we desugar strings to
+ -- lists
+ | PmLitString FastString
+ | PmLitOverInt Int {- How often Negated? -} Integer
+ | PmLitOverRat Int {- How often Negated? -} Rational
+ | PmLitOverString FastString
+
+-- | Undecidable semantic equality result.
+-- See Note [Undecidable Equality for PmAltCons]
+data PmEquality
+ = Equal
+ | Disjoint
+ | PossiblyOverlap
+ deriving (Eq, Show)
+
+-- | When 'PmEquality' can be decided. @True <=> Equal@, @False <=> Disjoint@.
+decEquality :: Bool -> PmEquality
+decEquality True = Equal
+decEquality False = Disjoint
+
+-- | Undecidable equality for values represented by 'PmLit's.
+-- See Note [Undecidable Equality for PmAltCons]
+--
+-- * @Just True@ ==> Surely equal
+-- * @Just False@ ==> Surely different (non-overlapping, even!)
+-- * @Nothing@ ==> Equality relation undecidable
+eqPmLit :: PmLit -> PmLit -> PmEquality
+eqPmLit (PmLit t1 v1) (PmLit t2 v2)
+ -- no haddock | pprTrace "eqPmLit" (ppr t1 <+> ppr v1 $$ ppr t2 <+> ppr v2) False = undefined
+ | not (t1 `eqType` t2) = Disjoint
+ | otherwise = go v1 v2
+ where
+ go (PmLitInt i1) (PmLitInt i2) = decEquality (i1 == i2)
+ go (PmLitRat r1) (PmLitRat r2) = decEquality (r1 == r2)
+ go (PmLitChar c1) (PmLitChar c2) = decEquality (c1 == c2)
+ go (PmLitString s1) (PmLitString s2) = decEquality (s1 == s2)
+ go (PmLitOverInt n1 i1) (PmLitOverInt n2 i2)
+ | n1 == n2 && i1 == i2 = Equal
+ go (PmLitOverRat n1 r1) (PmLitOverRat n2 r2)
+ | n1 == n2 && r1 == r2 = Equal
+ go (PmLitOverString s1) (PmLitOverString s2)
+ | s1 == s2 = Equal
+ go _ _ = PossiblyOverlap
+
+-- | Syntactic equality.
+instance Eq PmLit where
+ a == b = eqPmLit a b == Equal
+
+-- | Type of a 'PmLit'
+pmLitType :: PmLit -> Type
+pmLitType (PmLit ty _) = ty
+
+-- | Type of a 'PmAltCon'
+pmAltConType :: PmAltCon -> [Type] -> Type
+pmAltConType (PmAltLit lit) _arg_tys = ASSERT( null _arg_tys ) pmLitType lit
+pmAltConType (PmAltConLike con) arg_tys = conLikeResTy con arg_tys
+
+-- | Undecidable equality for values represented by 'ConLike's.
+-- See Note [Undecidable Equality for PmAltCons].
+-- 'PatSynCon's aren't enforced to be generative, so two syntactically different
+-- 'PatSynCon's might match the exact same values. Without looking into and
+-- reasoning about the pattern synonym's definition, we can't decide if their
+-- sets of matched values is different.
+--
+-- * @Just True@ ==> Surely equal
+-- * @Just False@ ==> Surely different (non-overlapping, even!)
+-- * @Nothing@ ==> Equality relation undecidable
+eqConLike :: ConLike -> ConLike -> PmEquality
+eqConLike (RealDataCon dc1) (RealDataCon dc2) = decEquality (dc1 == dc2)
+eqConLike (PatSynCon psc1) (PatSynCon psc2)
+ | psc1 == psc2
+ = Equal
+eqConLike _ _ = PossiblyOverlap
+
+-- | Represents the head of a match against a 'ConLike' or literal.
+-- Really similar to 'CoreSyn.AltCon'.
+data PmAltCon = PmAltConLike ConLike
+ | PmAltLit PmLit
+
+-- | We can't in general decide whether two 'PmAltCon's match the same set of
+-- values. In addition to the reasons in 'eqPmLit' and 'eqConLike', a
+-- 'PmAltConLike' might or might not represent the same value as a 'PmAltLit'.
+-- See Note [Undecidable Equality for PmAltCons].
+--
+-- * @Just True@ ==> Surely equal
+-- * @Just False@ ==> Surely different (non-overlapping, even!)
+-- * @Nothing@ ==> Equality relation undecidable
+--
+-- Examples (omitting some constructor wrapping):
+--
+-- * @eqPmAltCon (LitInt 42) (LitInt 1) == Just False@: Lit equality is
+-- decidable
+-- * @eqPmAltCon (DataCon A) (DataCon B) == Just False@: DataCon equality is
+-- decidable
+-- * @eqPmAltCon (LitOverInt 42) (LitOverInt 1) == Nothing@: OverLit equality
+-- is undecidable
+-- * @eqPmAltCon (PatSyn PA) (PatSyn PB) == Nothing@: PatSyn equality is
+-- undecidable
+-- * @eqPmAltCon (DataCon I#) (LitInt 1) == Nothing@: DataCon to Lit
+-- comparisons are undecidable without reasoning about the wrapped @Int#@
+-- * @eqPmAltCon (LitOverInt 1) (LitOverInt 1) == Just True@: We assume
+-- reflexivity for overloaded literals
+-- * @eqPmAltCon (PatSyn PA) (PatSyn PA) == Just True@: We assume reflexivity
+-- for Pattern Synonyms
+eqPmAltCon :: PmAltCon -> PmAltCon -> PmEquality
+eqPmAltCon (PmAltConLike cl1) (PmAltConLike cl2) = eqConLike cl1 cl2
+eqPmAltCon (PmAltLit l1) (PmAltLit l2) = eqPmLit l1 l2
+eqPmAltCon _ _ = PossiblyOverlap
+
+-- | Syntactic equality.
instance Eq PmAltCon where
- PmAltLit l1 == PmAltLit l2 = eqPmLit l1 l2
+ a == b = eqPmAltCon a b == Equal
-{- Note [Undecidable Equality for Overloaded Literals]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+{- Note [Undecidable Equality for PmAltCons]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Equality on overloaded literals is undecidable in the general case. Consider
the following example:
@@ -96,25 +188,19 @@ the following example:
f 1 = () -- Clause A
f 2 = () -- Clause B
-Clause B is redundant but to detect this, we should be able to solve the
-constraint: False ~ (fromInteger 2 ~ fromInteger 1) which means that we
-have to look through function `fromInteger`, whose implementation could
+Clause B is redundant but to detect this, we must decide the constraint:
+@fromInteger 2 ~ fromInteger 1@ which means that we
+have to look through function @fromInteger@, whose implementation could
be anything. This poses difficulties for:
1. The expressive power of the check.
We cannot expect a reasonable implementation of pattern matching to detect
- that fromInteger 2 ~ fromInteger 1 is True, unless we unfold function
+ that @fromInteger 2 ~ fromInteger 1@ is True, unless we unfold function
fromInteger. This puts termination at risk and is undecidable in the
general case.
-2. Performance.
- Having an unresolved constraint False ~ (fromInteger 2 ~ fromInteger 1)
- lying around could become expensive really fast. Ticket #11161 illustrates
- how heavy use of overloaded literals can generate plenty of those
- constraints, effectively undermining the term oracle's performance.
-
-3. Error nessages/Warnings.
- What should our message for `f` above be? A reasonable approach would be
+2. Error messages/Warnings.
+ What should our message for @f@ above be? A reasonable approach would be
to issue:
Pattern matches are (potentially) redundant:
@@ -122,8 +208,13 @@ be anything. This poses difficulties for:
but seems to complex and confusing for the user.
-We choose to treat overloaded literals that look different as different. The
-impact of this is the following:
+We choose to equate only obviously equal overloaded literals, in all other cases
+we signal undecidability by returning Nothing from 'eqPmAltCons'. We do
+better for non-overloaded literals, because we know their fromInteger/fromString
+implementation is actually injective, allowing us to simplify the constraint
+@fromInteger 1 ~ fromInteger 2@ to @1 ~ 2@, which is trivially unsatisfiable.
+
+The impact of this treatment of overloaded literals is the following:
* Redundancy checking is rather conservative, since it cannot see that clause
B above is redundant.
@@ -136,120 +227,100 @@ impact of this is the following:
* The warnings issued are simpler.
- * We do not play on the safe side, strictly speaking. The assumption that
- 1 /= 2 makes the redundancy check more conservative but at the same time
- makes its dual (exhaustiveness check) unsafe. This we can live with, mainly
- for two reasons:
- 1. At the moment we do not use the results of the check during compilation
- where this would be a disaster (could result in runtime errors even if
- our function was deemed exhaustive).
- 2. Pattern matcing on literals can never be considered exhaustive unless we
- have a catch-all clause. Hence, this assumption affects mainly the
- appearance of the warnings and is, in practice safe.
+Similar reasoning applies to pattern synonyms: In contrast to data constructors,
+which are generative, constraints like F a ~ G b for two different pattern
+synonyms F and G aren't immediately unsatisfiable. We assume F a ~ F a, though.
-}
--- | A term constraint. @TVC x e@ encodes that @x@ is equal to @e@.
-data TmVarCt = TVC !Id !PmExpr
+literalToPmLit :: Type -> Literal -> Maybe PmLit
+literalToPmLit ty l = PmLit ty <$> go l
+ where
+ go (LitChar c) = Just (PmLitChar c)
+ go (LitFloat r) = Just (PmLitRat r)
+ go (LitDouble r) = Just (PmLitRat r)
+ go (LitString s) = Just (PmLitString (mkFastStringByteString s))
+ go (LitNumber _ i _) = Just (PmLitInt i)
+ go _ = Nothing
+
+negatePmLit :: PmLit -> Maybe PmLit
+negatePmLit (PmLit ty v) = PmLit ty <$> go v
+ where
+ go (PmLitInt i) = Just (PmLitInt (-i))
+ go (PmLitRat r) = Just (PmLitRat (-r))
+ go (PmLitOverInt n i) = Just (PmLitOverInt (n+1) i)
+ go (PmLitOverRat n r) = Just (PmLitOverRat (n+1) r)
+ go _ = Nothing
+
+overloadPmLit :: Type -> PmLit -> Maybe PmLit
+overloadPmLit ty (PmLit _ v) = PmLit ty <$> go v
+ where
+ go (PmLitInt i) = Just (PmLitOverInt 0 i)
+ go (PmLitRat r) = Just (PmLitOverRat 0 r)
+ go (PmLitString s)
+ | ty `eqType` stringTy = Just v
+ | otherwise = Just (PmLitOverString s)
+ go _ = Nothing
-instance Outputable TmVarCt where
- ppr (TVC x e) = ppr x <+> char '~' <+> ppr e
+pmLitAsStringLit :: PmLit -> Maybe FastString
+pmLitAsStringLit (PmLit _ (PmLitString s)) = Just s
+pmLitAsStringLit _ = Nothing
-- ----------------------------------------------------------------------------
-- ** Predicates on PmExpr
--- | Check if an expression is lifted or not
-isNotPmExprOther :: PmExpr -> Bool
-isNotPmExprOther (PmExprOther _) = False
-isNotPmExprOther _expr = True
-
-- -----------------------------------------------------------------------
-- ** Lift source expressions (HsExpr Id) to PmExpr
-lhsExprToPmExpr :: LHsExpr GhcTc -> PmExpr
-lhsExprToPmExpr (dL->L _ e) = hsExprToPmExpr e
-
-hsExprToPmExpr :: HsExpr GhcTc -> PmExpr
-
--- Translating HsVar to flexible meta variables in the unification problem is
--- morally wrong, but it does the right thing for now.
--- In contrast to the situation in pattern matches, HsVars in expression syntax
--- are object language variables, most similar to rigid variables with an
--- unknown solution. The correct way would be to handle them through PmExprOther
--- and identify syntactically equal occurrences by the same rigid meta variable,
--- but we can't compare the wrapped HsExpr for equality. Hence we are stuck with
--- this hack.
-hsExprToPmExpr (HsVar _ x) = PmExprVar (idName (unLoc x))
-
--- Translating HsConLikeOut to a flexible meta variable is misleading.
--- For an example why, consider `consAreRigid` in
--- `testsuite/tests/pmcheck/should_compile/PmExprVars.hs`.
--- hsExprToPmExpr (HsConLikeOut _ c) = PmExprVar (conLikeName c)
-
--- Desugar literal strings as a list of characters. For other literal values,
--- keep it as it is.
--- See `translatePat` in Check.hs (the `NPat` and `LitPat` case), and
--- Note [Translate Overloaded Literal for Exhaustiveness Checking].
-hsExprToPmExpr (HsOverLit _ olit)
- | OverLit (OverLitTc False ty) (HsIsString src s) _ <- olit, isStringTy ty
- = stringExprToList src s
- | otherwise = PmExprLit (PmOLit False olit)
-hsExprToPmExpr (HsLit _ lit)
- | HsString src s <- lit
- = stringExprToList src s
- | otherwise = PmExprLit (PmSLit lit)
-
-hsExprToPmExpr e@(NegApp _ (dL->L _ neg_expr) _)
- | PmExprLit (PmOLit False olit) <- hsExprToPmExpr neg_expr
- -- NB: DON'T simply @(NegApp (NegApp olit))@ as @x@. when extension
- -- @RebindableSyntax@ enabled, (-(-x)) may not equals to x.
- = PmExprLit (PmOLit True olit)
- | otherwise = PmExprOther e
-
-hsExprToPmExpr (HsPar _ (dL->L _ e)) = hsExprToPmExpr e
-
-hsExprToPmExpr e@(ExplicitTuple _ ps boxity)
- | all tupArgPresent ps = mkPmExprData tuple_con tuple_args
- | otherwise = PmExprOther e
- where
- tuple_con = tupleDataCon boxity (length ps)
- tuple_args = [ lhsExprToPmExpr e | (dL->L _ (Present _ e)) <- ps ]
-
-hsExprToPmExpr e@(ExplicitList _ mb_ol elems)
- | Nothing <- mb_ol = foldr cons nil (map lhsExprToPmExpr elems)
- | otherwise = PmExprOther e {- overloaded list: No PmExprApp -}
- where
- cons x xs = mkPmExprData consDataCon [x,xs]
- nil = mkPmExprData nilDataCon []
-
--- we want this but we would have to make everything monadic :/
--- ./compiler/deSugar/DsMonad.hs:397:dsLookupDataCon :: Name -> DsM DataCon
---
--- hsExprToPmExpr (RecordCon c _ binds) = do
--- con <- dsLookupDataCon (unLoc c)
--- args <- mapM lhsExprToPmExpr (hsRecFieldsArgs binds)
--- return (PmExprCon con args)
-hsExprToPmExpr e@(RecordCon {}) = PmExprOther e
-
-hsExprToPmExpr (HsTick _ _ e) = lhsExprToPmExpr e
-hsExprToPmExpr (HsBinTick _ _ _ e) = lhsExprToPmExpr e
-hsExprToPmExpr (HsTickPragma _ _ _ _ e) = lhsExprToPmExpr e
-hsExprToPmExpr (HsSCC _ _ _ e) = lhsExprToPmExpr e
-hsExprToPmExpr (HsCoreAnn _ _ _ e) = lhsExprToPmExpr e
-hsExprToPmExpr (ExprWithTySig _ e _) = lhsExprToPmExpr e
-hsExprToPmExpr (HsWrap _ w e)
- -- A dictionary application spoils e and we have no choice but to return an
- -- PmExprOther. Same thing for other stuff that can't erased in the
- -- compilation process. Otherwise this bites in
- -- teststuite/tests/pmcheck/should_compile/PmExprVars.hs.
- | isErasableHsWrapper w = hsExprToPmExpr e
-hsExprToPmExpr e = PmExprOther e -- the rest are not handled by the oracle
-
-stringExprToList :: SourceText -> FastString -> PmExpr
-stringExprToList src s = foldr cons nil (map charToPmExpr (unpackFS s))
+coreExprAsPmLit :: CoreExpr -> Maybe PmLit
+-- coreExprAsPmLit e | pprTrace "coreExprAsPmLit" (ppr e) False = undefined
+coreExprAsPmLit (Tick _t e) = coreExprAsPmLit e
+coreExprAsPmLit (Lit l) = literalToPmLit (literalType l) l
+coreExprAsPmLit e = case collectArgs e of
+ (Var x, [Lit l])
+ | Just dc <- isDataConWorkId_maybe x
+ , dc `elem` [intDataCon, wordDataCon, charDataCon, floatDataCon, doubleDataCon]
+ -> literalToPmLit (exprType e) l
+ (Var x, [_ty, Lit n, Lit d])
+ | Just dc <- isDataConWorkId_maybe x
+ , dataConName dc == ratioDataConName
+ -- HACK: just assume we have a literal double. This case only occurs for
+ -- overloaded lits anyway, so we immediately override type information
+ -> literalToPmLit (exprType e) (mkLitDouble (litValue n % litValue d))
+ (Var x, args)
+ -- Take care of -XRebindableSyntax. The last argument should be the (only)
+ -- integer literal, otherwise we can't really do much about it.
+ | [Lit l] <- dropWhile (not . is_lit) args
+ -- getOccFS because of -XRebindableSyntax
+ , getOccFS (idName x) == getOccFS fromIntegerName
+ -> literalToPmLit (literalType l) l >>= overloadPmLit (exprType e)
+ (Var x, args)
+ -- Similar to fromInteger case
+ | [r] <- dropWhile (not . is_ratio) args
+ , getOccFS (idName x) == getOccFS fromRationalName
+ -> coreExprAsPmLit r >>= overloadPmLit (exprType e)
+ (Var x, [Type _ty, _dict, s])
+ | idName x == fromStringName
+ -- NB: Calls coreExprAsPmLit and then overloadPmLit, so that we return PmLitOverStrings
+ -> coreExprAsPmLit s >>= overloadPmLit (exprType e)
+ -- These last two cases handle String literals
+ (Var x, [Type ty])
+ | Just dc <- isDataConWorkId_maybe x
+ , dc == nilDataCon
+ , ty `eqType` charTy
+ -> literalToPmLit stringTy (mkLitString "")
+ (Var x, [Lit l])
+ | idName x `elem` [unpackCStringName, unpackCStringUtf8Name]
+ -> literalToPmLit stringTy l
+ _ -> Nothing
where
- cons x xs = mkPmExprData consDataCon [x,xs]
- nil = mkPmExprData nilDataCon []
- charToPmExpr c = PmExprLit (PmSLit (HsChar src c))
+ is_lit Lit{} = True
+ is_lit _ = False
+ is_ratio (Type _) = False
+ is_ratio r
+ | Just (tc, _) <- splitTyConApp_maybe (exprType r)
+ = tyConName tc == ratioTyConName
+ | otherwise
+ = False
{-
%************************************************************************
@@ -259,22 +330,35 @@ stringExprToList src s = foldr cons nil (map charToPmExpr (unpackFS s))
%************************************************************************
-}
-instance Outputable PmLit where
- ppr (PmSLit l) = pmPprHsLit l
- ppr (PmOLit neg l) = (if neg then char '-' else empty) <> ppr l
+instance Outputable PmLitValue where
+ ppr (PmLitInt i) = ppr i
+ ppr (PmLitRat r) = ppr (double (fromRat r)) -- good enough
+ ppr (PmLitChar c) = pprHsChar c
+ ppr (PmLitString s) = pprHsString s
+ ppr (PmLitOverInt n i) = minuses n (ppr i)
+ ppr (PmLitOverRat n r) = minuses n (ppr (double (fromRat r)))
+ ppr (PmLitOverString s) = pprHsString s
+
+-- Take care of negated literals
+minuses :: Int -> SDoc -> SDoc
+minuses n sdoc = iterate (\sdoc -> parens (char '-' <> sdoc)) sdoc !! n
-instance Outputable PmExpr where
- ppr = go (0 :: Int)
+instance Outputable PmLit where
+ ppr (PmLit ty v) = ppr v <> suffix
where
- go _ (PmExprLit l) = ppr l
- go _ (PmExprVar v) = ppr v
- go _ (PmExprOther e) = angleBrackets (ppr e)
- go _ (PmExprCon (RealDataCon dc) args)
- | isTupleDataCon dc = parens $ comma_sep $ map ppr args
- | dc == consDataCon = brackets $ comma_sep $ map ppr (list_cells args)
- where
- comma_sep = fsep . punctuate comma
- list_cells (hd:tl) = hd : list_cells tl
- list_cells _ = []
- go prec (PmExprCon cl args)
- = cparen (null args || prec > 0) (hcat (ppr cl:map (go 1) args))
+ -- Some ad-hoc hackery for displaying proper lit suffixes based on type
+ tbl = [ (intPrimTy, primIntSuffix)
+ , (int64PrimTy, primInt64Suffix)
+ , (wordPrimTy, primWordSuffix)
+ , (word64PrimTy, primWord64Suffix)
+ , (charPrimTy, primCharSuffix)
+ , (floatPrimTy, primFloatSuffix)
+ , (doublePrimTy, primDoubleSuffix) ]
+ suffix = fromMaybe empty (snd <$> find (eqType ty . fst) tbl)
+
+instance Outputable PmAltCon where
+ ppr (PmAltConLike cl) = ppr cl
+ ppr (PmAltLit l) = ppr l
+
+instance Outputable PmEquality where
+ ppr = text . show