summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/deSugar/Check.hs11
-rw-r--r--compiler/deSugar/DsMonad.hs2
-rw-r--r--compiler/deSugar/PmOracle.hs190
-rw-r--r--compiler/deSugar/PmPpr.hs36
-rw-r--r--compiler/deSugar/PmTypes.hs (renamed from compiler/deSugar/PmExpr.hs)287
-rw-r--r--compiler/deSugar/PmTypes.hs-boot (renamed from compiler/deSugar/PmOracle.hs-boot)2
-rw-r--r--compiler/ghc.cabal.in4
-rw-r--r--compiler/typecheck/TcRnTypes.hs2
8 files changed, 264 insertions, 270 deletions
diff --git a/compiler/deSugar/Check.hs b/compiler/deSugar/Check.hs
index 3a1515d0cc..1c9493bbca 100644
--- a/compiler/deSugar/Check.hs
+++ b/compiler/deSugar/Check.hs
@@ -26,7 +26,7 @@ module Check (
import GhcPrelude
-import PmExpr
+import PmTypes
import PmOracle
import PmPpr
import BasicTypes (Origin, isGenerated)
@@ -817,7 +817,7 @@ below is the *right thing to do*:
The case with literals is a bit different. a literal @l@ should be translated
to @x (True <- x == from l)@. Since we want to have better warnings for
overloaded literals as it is a very common feature, we treat them differently.
-They are mainly covered in Note [Undecidable Equality for PmAltCons] in PmExpr.
+They are mainly covered in Note [Undecidable Equality for PmAltCons] in PmTypes.
4. N+K Patterns & Pattern Synonyms
----------------------------------
@@ -949,7 +949,7 @@ additional features added to the coverage checker which are not described in
the paper. This Note serves as a reference for these new features.
* Value abstractions are severely simplified to the point where they are just
- variables. The information about the PmExpr shape of a variable is encoded in
+ variables. The information about the shape of a variable is encoded in
the oracle state 'Delta' instead.
* Handling of uninhabited fields like `!Void`.
See Note [Strict argument type constraints] in PmOracle.
@@ -1397,9 +1397,6 @@ addPatTmCs ps xs k = do
pv <- concat <$> translatePatVec fam_insts ps
locallyExtendPmDelta (\delta -> addVarPatVecCt delta xs pv) k
--- ----------------------------------------------------------------------------
--- * Converting between Value Abstractions, Patterns and PmExpr
-
-- | Add a constraint equating a variable to a 'PatVec'. Picks out the single
-- 'PmPat' of arity 1 and equates x to it. Returns the original Delta if that
-- fails. Otherwise it returns Nothing when the resulting Delta would be
@@ -1422,7 +1419,7 @@ addVarPatVecCt delta (x:xs) (pat:pv)
addVarPatVecCt delta [] pv = ASSERT( patVecArity pv == 0 ) pure (Just delta)
addVarPatVecCt _ (_:_) [] = panic "More match vars than patterns"
--- | Convert a pattern to a 'PmExpr' (will be either 'Nothing' if the pattern is
+-- | Convert a pattern to a 'PmTypes' (will be either 'Nothing' if the pattern is
-- a guard pattern, or 'Just' an expression in all other cases) by dropping the
-- guards
addVarPatCt :: Delta -> Id -> PmPat -> DsM (Maybe Delta)
diff --git a/compiler/deSugar/DsMonad.hs b/compiler/deSugar/DsMonad.hs
index d937b3b134..1bfa25324a 100644
--- a/compiler/deSugar/DsMonad.hs
+++ b/compiler/deSugar/DsMonad.hs
@@ -70,7 +70,7 @@ import BasicTypes ( Origin )
import DataCon
import ConLike
import TyCon
-import {-# SOURCE #-} PmOracle
+import PmTypes
import Id
import Module
import Outputable
diff --git a/compiler/deSugar/PmOracle.hs b/compiler/deSugar/PmOracle.hs
index 93c4d1d959..f9abae481c 100644
--- a/compiler/deSugar/PmOracle.hs
+++ b/compiler/deSugar/PmOracle.hs
@@ -31,7 +31,7 @@ module PmOracle (
import GhcPrelude
-import PmExpr
+import PmTypes
import DynFlags
import Outputable
@@ -77,7 +77,6 @@ import Control.Monad.Trans.State.Strict
import Data.Bifunctor (second)
import Data.Foldable (foldlM)
import Data.List (find)
-import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Semigroup as Semigroup
@@ -100,22 +99,6 @@ mkPmId ty = getUniqueM >>= \unique ->
-----------------------------------------------
-- * Caching possible matches of a COMPLETE set
-type ConLikeSet = UniqDSet ConLike
-
--- | A data type caching the results of 'completeMatchConLikes' with support for
--- deletion of contructors that were already matched on.
-data PossibleMatches
- = PM TyCon (NonEmpty ConLikeSet)
- -- ^ Each ConLikeSet is a (subset of) the constructors in a COMPLETE pragma
- -- 'NonEmpty' because the empty case would mean that the type has no COMPLETE
- -- set at all, for which we have 'NoPM'
- | NoPM
- -- ^ No COMPLETE set for this type (yet). Think of overloaded literals.
-
-instance Outputable PossibleMatches where
- ppr (PM _tc cs) = ppr (NonEmpty.toList cs)
- ppr NoPM = text "<NoPM>"
-
initIM :: Type -> DsM (Maybe PossibleMatches)
initIM ty = case splitTyConApp_maybe ty of
Nothing -> pure Nothing
@@ -235,26 +218,6 @@ equateTyVars ex_tvs1 ex_tvs2
-- * Pattern match oracle
--- | Term and type constraints to accompany each value vector abstraction.
--- For efficiency, we store the term oracle state instead of the term
--- constraints.
-data Delta = MkDelta { delta_ty_cs :: Bag EvVar -- Type oracle; things like a~Int
- , delta_tm_cs :: TmState } -- Term oracle; things like x~Nothing
-
--- | An initial delta that is always satisfiable
-initDelta :: Delta
-initDelta = MkDelta emptyBag initTmState
-
-instance Outputable Delta where
- ppr delta = vcat [
- -- intentionally formatted this way enable the dev to comment in only
- -- the info she needs
- ppr (delta_tm_cs delta),
- ppr (pprEvVarWithType <$> delta_ty_cs delta)
- --ppr (delta_ty_cs delta)
- ]
-
-
{- Note [Recovering from unsatisfiable pattern-matching constraints]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider the following code (see #12957 and #15450):
@@ -603,68 +566,10 @@ tyIsSatisfiable recheck_complete_sets new_ty_cs = SC $ \delta -> do
{- *********************************************************************
* *
- SharedIdEnv
DIdEnv with sharing
* *
********************************************************************* -}
--- | Either @Indirect x@, meaning the value is represented by that of @x@, or
--- an @Entry@ containing containing the actual value it represents.
-data Shared a
- = Indirect Id
- | Entry a
-
--- | A 'DIdEnv' in which entries can be shared by multiple 'Id's.
--- Merge equivalence classes of two Ids by 'setIndirectSDIE' and set the entry
--- of an Id with 'setEntrySDIE'.
-newtype SharedDIdEnv a
- = SDIE { unSDIE :: DIdEnv (Shared a) }
-
-emptySDIE :: SharedDIdEnv a
-emptySDIE = SDIE emptyDVarEnv
-
-lookupReprAndEntrySDIE :: SharedDIdEnv a -> Id -> (Id, Maybe a)
-lookupReprAndEntrySDIE sdie@(SDIE env) x = case lookupDVarEnv env x of
- Nothing -> (x, Nothing)
- Just (Indirect y) -> lookupReprAndEntrySDIE sdie y
- Just (Entry a) -> (x, Just a)
-
--- | @lookupSDIE env x@ looks up an entry for @x@, looking through all
--- 'Indirect's until it finds a shared 'Entry'.
-lookupSDIE :: SharedDIdEnv a -> Id -> Maybe a
-lookupSDIE sdie x = snd (lookupReprAndEntrySDIE sdie x)
-
--- | Check if two variables are part of the same equivalence class.
-sameRepresentative :: SharedDIdEnv a -> Id -> Id -> Bool
-sameRepresentative sdie x y =
- fst (lookupReprAndEntrySDIE sdie x) == fst (lookupReprAndEntrySDIE sdie y)
-
--- | @setIndirectSDIE env x y@ sets @x@'s 'Entry' to @Indirect y@, thereby
--- merging @x@'s equivalence class into @y@'s. This will discard all info on
--- @x@!
-setIndirectSDIE :: SharedDIdEnv a -> Id -> Id -> SharedDIdEnv a
-setIndirectSDIE sdie@(SDIE env) x y =
- SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Indirect y)
-
--- | @setEntrySDIE env x a@ sets the 'Entry' @x@ is associated with to @a@,
--- thereby modifying its whole equivalence class.
-setEntrySDIE :: SharedDIdEnv a -> Id -> a -> SharedDIdEnv a
-setEntrySDIE sdie@(SDIE env) x a =
- SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Entry a)
-
-traverseSharedIdEnv :: Applicative f => (a -> f b) -> SharedDIdEnv a -> f (SharedDIdEnv b)
-traverseSharedIdEnv f = fmap (SDIE . listToUDFM) . traverse g . udfmToList . unSDIE
- where
- g (u, Indirect y) = pure (u,Indirect y)
- g (u, Entry a) = (u,) . Entry <$> f a
-
-instance Outputable a => Outputable (Shared a) where
- ppr (Indirect x) = ppr x
- ppr (Entry a) = ppr a
-
-instance Outputable a => Outputable (SharedDIdEnv a) where
- ppr (SDIE env) = ppr env
-
{- *********************************************************************
* *
@@ -673,66 +578,6 @@ instance Outputable a => Outputable (SharedDIdEnv a) where
* *
********************************************************************* -}
--- | The term oracle state. Stores 'VarInfo' for encountered 'Id's. These
--- entries are possibly shared when we figure out that two variables must be
--- equal, thus represent the same set of values.
---
--- See Note [TmState invariants].
-newtype TmState = TS (SharedDIdEnv VarInfo)
- -- Deterministic so that we generate deterministic error messages
-
--- | Information about an 'Id'. Stores positive ('vi_pos') facts, like @x ~ Just 42@,
--- and negative ('vi_neg') facts, like "x is not (:)".
--- Also caches the type ('vi_ty'), the 'PossibleMatches' of a COMPLETE set
--- ('vi_cache') and the number of times each variable was refined
--- ('vi_n_refines').
---
--- Subject to Note [The Pos/Neg invariant].
-data VarInfo
- = VI
- { vi_ty :: !Type
- -- ^ The type of the variable. Important for rejecting possible GADT
- -- constructors or incompatible pattern synonyms (@Just42 :: Maybe Int@).
-
- , vi_pos :: [(PmAltCon, [Id])]
- -- ^ Positive info: 'PmExprCon's it is (i.e. @x ~ [Just y, PatSyn z]@), all
- -- at the same time (i.e. conjunctive). We need a list because of nested
- -- pattern matches involving pattern synonym
- -- case x of { Just y -> case x of PatSyn z -> ... }
- -- However, no more than one RealDataCon in the list, otherwise contradiction
- -- because of generativity.
-
- , vi_neg :: ![PmAltCon]
- -- ^ Negative info: A list of 'PmAltCon's that it cannot match.
- -- Example, assuming
- --
- -- @
- -- data T = Leaf Int | Branch T T | Node Int T
- -- @
- --
- -- then @x /~ [Leaf, Node]@ means that @x@ cannot match a @Leaf@ or @Node@,
- -- and hence can only match @Branch@. Is orthogonal to anything from 'vi_pos',
- -- in the sense that 'eqPmAltCon' returns @PossiblyOverlap@ for any pairing
- -- between 'vi_pos' and 'vi_neg'.
-
- -- See Note [Why record both positive and negative info?]
-
- , vi_cache :: !PossibleMatches
- -- ^ A cache of the associated COMPLETE sets. At any time a superset of
- -- possible constructors of each COMPLETE set. So, if it's not in here, we
- -- can't possibly match on it. Complementary to 'vi_neg'. We still need it
- -- to recognise completion of a COMPLETE set efficiently for large enums.
-
- , vi_n_refines :: !Int
- -- ^ Purely for Check performance reasons. The number of times this
- -- representative was refined ('refineToAltCon') in the Check's ConVar split.
- -- Sadly, we can't store this info in the Check module, as it's tightly coupled
- -- to the particular 'Delta' and also is per *representative*, not per
- -- syntactic variable. Note that this number does not always correspond to the
- -- length of solutions: 'addVarConCt' might add a solution without
- -- incurring the potential exponential blowup by ConVar.
- }
-
{- Note [The Pos/Neg invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Invariant applying to each VarInfo: Whenever we have @(C, [y,z])@ in 'vi_pos',
@@ -830,19 +675,6 @@ The "list" option is far simpler, but incurs some overhead in representation and
warning messages (which can be alleviated by someone with enough dedication).
-}
--- | Not user-facing.
-instance Outputable TmState where
- ppr (TS state) = ppr state
-
--- | Not user-facing.
-instance Outputable VarInfo where
- ppr (VI ty pos neg cache n)
- = braces (hcat (punctuate comma [ppr ty, ppr pos, ppr neg, ppr cache, ppr n]))
-
--- | Initial state of the oracle.
-initTmState :: TmState
-initTmState = TS emptySDIE
-
-- | A 'SatisfiabilityCheck' based on new term-level constraints.
-- Returns a new 'Delta' if the new constraints are compatible with existing
-- ones.
@@ -1098,15 +930,15 @@ ensureInhabited delta vi = fmap (set_cache vi) <$> test (vi_cache vi) -- This wo
-- impossible matches.
ensureAllPossibleMatchesInhabited :: Delta -> DsM (Maybe Delta)
ensureAllPossibleMatchesInhabited delta@MkDelta{ delta_tm_cs = TS env }
- = runMaybeT (set_tm_cs_env delta <$> traverseSharedIdEnv go env)
+ = runMaybeT (set_tm_cs_env delta <$> traverseSDIE go env)
where
set_tm_cs_env delta env = delta{ delta_tm_cs = TS env }
go vi = MaybeT (ensureInhabited delta vi)
-- | @refineToAltCon delta x con arg_tys ex_tyvars@ instantiates @con@ at
-- @arg_tys@ with fresh variables (equating existentials to @ex_tyvars@).
--- It adds a new term equality equating @x@ is to the resulting 'PmExprCon' and
--- new type equalities arising from GADT matches.
+-- It adds a new term equality equating @x@ is to the resulting 'PmAltCon' app
+-- and new type equalities arising from GADT matches.
-- If successful, returns the new @delta@ and the fresh term variables, or
-- @Nothing@ otherwise.
refineToAltCon :: Delta -> Id -> PmAltCon -> [Type] -> [TyVar] -> DsM (Maybe (Delta, [Id]))
@@ -1286,19 +1118,19 @@ addVarVarCt delta@MkDelta{ delta_tm_cs = TS env } (x, y)
-- class, otherwise we might get cyclic substitutions.
-- Cf. 'extendSubstAndSolve' and
-- @testsuite/tests/pmcheck/should_compile/CyclicSubst.hs@.
- | sameRepresentative env x y = pure delta
- | otherwise = equate delta x y
+ | sameRepresentativeSDIE env x y = pure delta
+ | otherwise = equate delta x y
-- | @equate ts@(TS env) x y@ merges the equivalence classes of @x@ and @y@ by
-- adding an indirection to the environment.
-- Makes sure that the positive and negative facts of @x@ and @y@ are
-- compatible.
--- Preconditions: @not (sameRepresentative env x y)@
+-- Preconditions: @not (sameRepresentativeSDIE env x y)@
--
-- See Note [TmState invariants].
equate :: Delta -> Id -> Id -> MaybeT DsM Delta
equate delta@MkDelta{ delta_tm_cs = TS env } x y
- = ASSERT( not (sameRepresentative env x y) )
+ = ASSERT( not (sameRepresentativeSDIE env x y) )
case (lookupSDIE env x, lookupSDIE env y) of
(Nothing, _) -> pure (delta{ delta_tm_cs = TS (setIndirectSDIE env x y) })
(_, Nothing) -> pure (delta{ delta_tm_cs = TS (setIndirectSDIE env y x) })
@@ -1323,9 +1155,9 @@ equate delta@MkDelta{ delta_tm_cs = TS env } x y
-- go!
pure delta_neg
--- | @addVarConCt x alt args ts@ extends the substitution with a mapping @x: ->
--- PmExprCon alt args@ if compatible with refutable shapes of @x@ and its
--- solution, reject (@Nothing@) otherwise.
+-- | @addVarConCt x alt args ts@ extends the substitution with a solution
+-- @x :-> (alt, args)@ if compatible with refutable shapes of @x@ and its
+-- other solutions, reject (@Nothing@) otherwise.
--
-- See Note [TmState invariants].
addVarConCt :: Delta -> Id -> PmAltCon -> [Id] -> MaybeT DsM Delta
diff --git a/compiler/deSugar/PmPpr.hs b/compiler/deSugar/PmPpr.hs
index bee38ed46b..82e6d0f0a0 100644
--- a/compiler/deSugar/PmPpr.hs
+++ b/compiler/deSugar/PmPpr.hs
@@ -1,6 +1,6 @@
{-# LANGUAGE CPP, ViewPatterns #-}
--- | Provides factilities for pretty-printing 'PmExpr's in a way approriate for
+-- | Provides factilities for pretty-printing 'Delta's in a way appropriate for
-- user facing pattern match warnings.
module PmPpr (
pprUncovered
@@ -22,7 +22,7 @@ import Util
import Maybes
import Data.List.NonEmpty (NonEmpty, nonEmpty, toList)
-import PmExpr
+import PmTypes
import PmOracle
-- | Pretty-print the guts of an uncovered value vector abstraction, i.e., its
@@ -44,7 +44,7 @@ pprUncovered delta vas
| otherwise = hang (fsep vec) 4 $
text "where" <+> vcat (map (pprRefutableShapes . snd) (udfmToList refuts))
where
- ppr_action = mapM (pprPmExprVar 2) vas
+ ppr_action = mapM (pprPmVar 2) vas
(vec, renamings) = runPmPpr delta ppr_action
refuts = prettifyRefuts delta renamings
@@ -128,16 +128,16 @@ checkRefuts x = do
-- | Pretty print a variable, but remember to prettify the names of the variables
-- that refer to neg-literals. The ones that cannot be shown are printed as
-- underscores.
-pprPmExprVar :: Int -> Id -> PmPprM SDoc
-pprPmExprVar prec x = do
+pprPmVar :: Int -> Id -> PmPprM SDoc
+pprPmVar prec x = do
delta <- ask
case lookupSolution delta x of
- Just (alt, args) -> pprPmExprCon prec alt args
+ Just (alt, args) -> pprPmAltCon prec alt args
Nothing -> fromMaybe underscore <$> checkRefuts x
-pprPmExprCon :: Int -> PmAltCon -> [Id] -> PmPprM SDoc
-pprPmExprCon _prec (PmAltLit l) _ = pure (ppr l)
-pprPmExprCon prec (PmAltConLike cl) args = do
+pprPmAltCon :: Int -> PmAltCon -> [Id] -> PmPprM SDoc
+pprPmAltCon _prec (PmAltLit l) _ = pure (ppr l)
+pprPmAltCon prec (PmAltConLike cl) args = do
delta <- ask
pprConLike delta prec cl args
@@ -146,24 +146,24 @@ pprConLike delta _prec cl args
| Just pm_expr_list <- pmExprAsList delta (PmAltConLike cl) args
= case pm_expr_list of
NilTerminated list ->
- brackets . fsep . punctuate comma <$> mapM (pprPmExprVar 0) list
+ brackets . fsep . punctuate comma <$> mapM (pprPmVar 0) list
WcVarTerminated pref x ->
- parens . fcat . punctuate colon <$> mapM (pprPmExprVar 0) (toList pref ++ [x])
+ parens . fcat . punctuate colon <$> mapM (pprPmVar 0) (toList pref ++ [x])
pprConLike _delta _prec (RealDataCon con) args
| isUnboxedTupleCon con
, let hash_parens doc = text "(#" <+> doc <+> text "#)"
- = hash_parens . fsep . punctuate comma <$> mapM (pprPmExprVar 0) args
+ = hash_parens . fsep . punctuate comma <$> mapM (pprPmVar 0) args
| isTupleDataCon con
- = parens . fsep . punctuate comma <$> mapM (pprPmExprVar 0) args
+ = parens . fsep . punctuate comma <$> mapM (pprPmVar 0) args
pprConLike _delta prec cl args
| conLikeIsInfix cl = case args of
- [x, y] -> do x' <- pprPmExprVar 1 x
- y' <- pprPmExprVar 1 y
+ [x, y] -> do x' <- pprPmVar 1 x
+ y' <- pprPmVar 1 y
return (cparen (prec > 0) (x' <+> ppr cl <+> y'))
-- can it be infix but have more than two arguments?
- list -> pprPanic "pprPmExprCon:" (ppr list)
+ list -> pprPanic "pprConLike:" (ppr list)
| null args = return (ppr cl)
- | otherwise = do args' <- mapM (pprPmExprVar 2) args
+ | otherwise = do args' <- mapM (pprPmVar 2) args
return (cparen (prec > 1) (fsep (ppr cl : args')))
-- | The result of 'pmExprAsList'.
@@ -171,7 +171,7 @@ data PmExprList
= NilTerminated [Id]
| WcVarTerminated (NonEmpty Id) Id
--- | Extract a list of 'PmExpr's out of a sequence of cons cells, optionally
+-- | Extract a list of 'Id's out of a sequence of cons cells, optionally
-- terminated by a wildcard variable instead of @[]@. Some examples:
--
-- * @pmExprAsList (1:2:[]) == Just ('NilTerminated' [1,2])@, a regular,
diff --git a/compiler/deSugar/PmExpr.hs b/compiler/deSugar/PmTypes.hs
index f83c63e4fd..fc97f636a7 100644
--- a/compiler/deSugar/PmExpr.hs
+++ b/compiler/deSugar/PmTypes.hs
@@ -1,17 +1,35 @@
{-
Author: George Karachalias <george.karachalias@cs.kuleuven.be>
-
-Haskell expressions (as used by the pattern matching checker) and utilities.
+ Sebastian Graf <sgraf1337@gmail.com>
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE TupleSections #-}
+
+-- | Types used through-out pattern match checking. This module is mostly there
+-- to be imported from "TcRnTypes". The exposed API is that of "PmOracle" and
+-- "Check".
+module PmTypes (
+ -- * Representations for Literals and AltCons
+ PmLit(..), PmLitValue(..), PmAltCon(..), pmLitType, pmAltConType,
+
+ -- ** Equality on 'PmAltCon's
+ PmEquality(..), eqPmAltCon,
+
+ -- ** Operations on 'PmLit'
+ literalToPmLit, negatePmLit, overloadPmLit,
+ pmLitAsStringLit, coreExprAsPmLit,
+
+ -- * Caching partially matched COMPLETE sets
+ ConLikeSet, PossibleMatches(..),
-module PmExpr (
- PmLit(..), PmLitValue(..), PmAltCon(..),
- pmAltConType, PmEquality(..), eqPmAltCon,
- pmLitType, literalToPmLit, negatePmLit, overloadPmLit,
- pmLitAsStringLit, coreExprAsPmLit
+ -- * A 'DIdEnv' where entries may be shared
+ Shared(..), SharedDIdEnv(..), emptySDIE, lookupSDIE, sameRepresentativeSDIE,
+ setIndirectSDIE, setEntrySDIE, traverseSDIE,
+
+ -- * The pattern match oracle
+ VarInfo(..), TmState(..), Delta(..), initDelta,
) where
#include "HsVersions.h"
@@ -19,8 +37,13 @@ module PmExpr (
import GhcPrelude
import Util
+import Bag
import FastString
+import Var (EvVar)
import Id
+import VarEnv
+import UniqDSet
+import UniqDFM
import Name
import DataCon
import ConLike
@@ -34,22 +57,13 @@ import CoreUtils (exprType)
import PrelNames
import TysWiredIn
import TysPrim
+import TcRnTypes (pprEvVarWithType)
import Numeric (fromRat)
import Data.Foldable (find)
+import qualified Data.List.NonEmpty as NonEmpty
import Data.Ratio
-{-
-%************************************************************************
-%* *
- Lifted Expressions
-%* *
-%************************************************************************
--}
-
--- ----------------------------------------------------------------------------
--- ** Types
-
-- | Literals (simple and overloaded ones) for pattern match checking.
--
-- See Note [Undecidable Equality for PmAltCons]
@@ -118,6 +132,39 @@ pmAltConType :: PmAltCon -> [Type] -> Type
pmAltConType (PmAltLit lit) _arg_tys = ASSERT( null _arg_tys ) pmLitType lit
pmAltConType (PmAltConLike con) arg_tys = conLikeResTy con arg_tys
+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 PmLit where
+ ppr (PmLit ty v) = ppr v <> suffix
+ where
+ -- 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
+
-- | 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
@@ -265,12 +312,6 @@ pmLitAsStringLit :: PmLit -> Maybe FastString
pmLitAsStringLit (PmLit _ (PmLitString s)) = Just s
pmLitAsStringLit _ = Nothing
--- ----------------------------------------------------------------------------
--- ** Predicates on PmExpr
-
--- -----------------------------------------------------------------------
--- ** Lift source expressions (HsExpr Id) to PmExpr
-
coreExprAsPmLit :: CoreExpr -> Maybe PmLit
-- coreExprAsPmLit e | pprTrace "coreExprAsPmLit" (ppr e) False = undefined
coreExprAsPmLit (Tick _t e) = coreExprAsPmLit e
@@ -322,43 +363,167 @@ coreExprAsPmLit e = case collectArgs e of
| otherwise
= False
-{-
-%************************************************************************
-%* *
- Pretty printing
-%* *
-%************************************************************************
--}
-
-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
+type ConLikeSet = UniqDSet ConLike
+
+-- | A data type caching the results of 'completeMatchConLikes' with support for
+-- deletion of contructors that were already matched on.
+data PossibleMatches
+ = PM TyCon (NonEmpty.NonEmpty ConLikeSet)
+ -- ^ Each ConLikeSet is a (subset of) the constructors in a COMPLETE pragma
+ -- 'NonEmpty' because the empty case would mean that the type has no COMPLETE
+ -- set at all, for which we have 'NoPM'
+ | NoPM
+ -- ^ No COMPLETE set for this type (yet). Think of overloaded literals.
+
+instance Outputable PossibleMatches where
+ ppr (PM _tc cs) = ppr (NonEmpty.toList cs)
+ ppr NoPM = text "<NoPM>"
+
+-- | Either @Indirect x@, meaning the value is represented by that of @x@, or
+-- an @Entry@ containing containing the actual value it represents.
+data Shared a
+ = Indirect Id
+ | Entry a
+
+-- | A 'DIdEnv' in which entries can be shared by multiple 'Id's.
+-- Merge equivalence classes of two Ids by 'setIndirectSDIE' and set the entry
+-- of an Id with 'setEntrySDIE'.
+newtype SharedDIdEnv a
+ = SDIE { unSDIE :: DIdEnv (Shared a) }
+
+emptySDIE :: SharedDIdEnv a
+emptySDIE = SDIE emptyDVarEnv
+
+lookupReprAndEntrySDIE :: SharedDIdEnv a -> Id -> (Id, Maybe a)
+lookupReprAndEntrySDIE sdie@(SDIE env) x = case lookupDVarEnv env x of
+ Nothing -> (x, Nothing)
+ Just (Indirect y) -> lookupReprAndEntrySDIE sdie y
+ Just (Entry a) -> (x, Just a)
+
+-- | @lookupSDIE env x@ looks up an entry for @x@, looking through all
+-- 'Indirect's until it finds a shared 'Entry'.
+lookupSDIE :: SharedDIdEnv a -> Id -> Maybe a
+lookupSDIE sdie x = snd (lookupReprAndEntrySDIE sdie x)
+
+-- | Check if two variables are part of the same equivalence class.
+sameRepresentativeSDIE :: SharedDIdEnv a -> Id -> Id -> Bool
+sameRepresentativeSDIE sdie x y =
+ fst (lookupReprAndEntrySDIE sdie x) == fst (lookupReprAndEntrySDIE sdie y)
+
+-- | @setIndirectSDIE env x y@ sets @x@'s 'Entry' to @Indirect y@, thereby
+-- merging @x@'s equivalence class into @y@'s. This will discard all info on
+-- @x@!
+setIndirectSDIE :: SharedDIdEnv a -> Id -> Id -> SharedDIdEnv a
+setIndirectSDIE sdie@(SDIE env) x y =
+ SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Indirect y)
+
+-- | @setEntrySDIE env x a@ sets the 'Entry' @x@ is associated with to @a@,
+-- thereby modifying its whole equivalence class.
+setEntrySDIE :: SharedDIdEnv a -> Id -> a -> SharedDIdEnv a
+setEntrySDIE sdie@(SDIE env) x a =
+ SDIE $ extendDVarEnv env (fst (lookupReprAndEntrySDIE sdie x)) (Entry a)
+
+traverseSDIE :: Applicative f => (a -> f b) -> SharedDIdEnv a -> f (SharedDIdEnv b)
+traverseSDIE f = fmap (SDIE . listToUDFM) . traverse g . udfmToList . unSDIE
+ where
+ g (u, Indirect y) = pure (u,Indirect y)
+ g (u, Entry a) = (u,) . Entry <$> f a
-instance Outputable PmLit where
- ppr (PmLit ty v) = ppr v <> suffix
- where
- -- 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 a => Outputable (Shared a) where
+ ppr (Indirect x) = ppr x
+ ppr (Entry a) = ppr a
-instance Outputable PmAltCon where
- ppr (PmAltConLike cl) = ppr cl
- ppr (PmAltLit l) = ppr l
+instance Outputable a => Outputable (SharedDIdEnv a) where
+ ppr (SDIE env) = ppr env
-instance Outputable PmEquality where
- ppr = text . show
+-- | The term oracle state. Stores 'VarInfo' for encountered 'Id's. These
+-- entries are possibly shared when we figure out that two variables must be
+-- equal, thus represent the same set of values.
+--
+-- See Note [TmState invariants].
+newtype TmState = TS (SharedDIdEnv VarInfo)
+ -- Deterministic so that we generate deterministic error messages
+
+-- | Information about an 'Id'. Stores positive ('vi_pos') facts, like @x ~ Just 42@,
+-- and negative ('vi_neg') facts, like "x is not (:)".
+-- Also caches the type ('vi_ty'), the 'PossibleMatches' of a COMPLETE set
+-- ('vi_cache') and the number of times each variable was refined
+-- ('vi_n_refines').
+--
+-- Subject to Note [The Pos/Neg invariant].
+data VarInfo
+ = VI
+ { vi_ty :: !Type
+ -- ^ The type of the variable. Important for rejecting possible GADT
+ -- constructors or incompatible pattern synonyms (@Just42 :: Maybe Int@).
+
+ , vi_pos :: [(PmAltCon, [Id])]
+ -- ^ Positive info: 'PmAltCon' apps it is (i.e. @x ~ [Just y, PatSyn z]@), all
+ -- at the same time (i.e. conjunctive). We need a list because of nested
+ -- pattern matches involving pattern synonym
+ -- case x of { Just y -> case x of PatSyn z -> ... }
+ -- However, no more than one RealDataCon in the list, otherwise contradiction
+ -- because of generativity.
+
+ , vi_neg :: ![PmAltCon]
+ -- ^ Negative info: A list of 'PmAltCon's that it cannot match.
+ -- Example, assuming
+ --
+ -- @
+ -- data T = Leaf Int | Branch T T | Node Int T
+ -- @
+ --
+ -- then @x /~ [Leaf, Node]@ means that @x@ cannot match a @Leaf@ or @Node@,
+ -- and hence can only match @Branch@. Is orthogonal to anything from 'vi_pos',
+ -- in the sense that 'eqPmAltCon' returns @PossiblyOverlap@ for any pairing
+ -- between 'vi_pos' and 'vi_neg'.
+
+ -- See Note [Why record both positive and negative info?]
+
+ , vi_cache :: !PossibleMatches
+ -- ^ A cache of the associated COMPLETE sets. At any time a superset of
+ -- possible constructors of each COMPLETE set. So, if it's not in here, we
+ -- can't possibly match on it. Complementary to 'vi_neg'. We still need it
+ -- to recognise completion of a COMPLETE set efficiently for large enums.
+
+ , vi_n_refines :: !Int
+ -- ^ Purely for Check performance reasons. The number of times this
+ -- representative was refined ('refineToAltCon') in the Check's ConVar split.
+ -- Sadly, we can't store this info in the Check module, as it's tightly coupled
+ -- to the particular 'Delta' and also is per *representative*, not per
+ -- syntactic variable. Note that this number does not always correspond to the
+ -- length of solutions: 'addVarConCt' might add a solution without
+ -- incurring the potential exponential blowup by ConVar.
+ }
+
+-- | Not user-facing.
+instance Outputable TmState where
+ ppr (TS state) = ppr state
+
+-- | Not user-facing.
+instance Outputable VarInfo where
+ ppr (VI ty pos neg cache n)
+ = braces (hcat (punctuate comma [ppr ty, ppr pos, ppr neg, ppr cache, ppr n]))
+
+-- | Initial state of the oracle.
+initTmState :: TmState
+initTmState = TS emptySDIE
+
+-- | Term and type constraints to accompany each value vector abstraction.
+-- For efficiency, we store the term oracle state instead of the term
+-- constraints.
+data Delta = MkDelta { delta_ty_cs :: Bag EvVar -- Type oracle; things like a~Int
+ , delta_tm_cs :: TmState } -- Term oracle; things like x~Nothing
+
+-- | An initial delta that is always satisfiable
+initDelta :: Delta
+initDelta = MkDelta emptyBag initTmState
+
+instance Outputable Delta where
+ ppr delta = vcat [
+ -- intentionally formatted this way enable the dev to comment in only
+ -- the info she needs
+ ppr (delta_tm_cs delta),
+ ppr (pprEvVarWithType <$> delta_ty_cs delta)
+ --ppr (delta_ty_cs delta)
+ ]
diff --git a/compiler/deSugar/PmOracle.hs-boot b/compiler/deSugar/PmTypes.hs-boot
index e099fe78a6..f6e3a7ec5b 100644
--- a/compiler/deSugar/PmOracle.hs-boot
+++ b/compiler/deSugar/PmTypes.hs-boot
@@ -1,4 +1,4 @@
-module PmOracle where
+module PmTypes where
import GhcPrelude ()
diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in
index 884006b487..fc5f581a4d 100644
--- a/compiler/ghc.cabal.in
+++ b/compiler/ghc.cabal.in
@@ -334,9 +334,9 @@ Library
CoreStats
MkCore
PprCore
- PmExpr
- PmPpr
PmOracle
+ PmPpr
+ PmTypes
Check
Coverage
Desugar
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index cd3bf5c1a5..fe7db11404 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -167,7 +167,7 @@ import TcType
import Annotations
import InstEnv
import FamInstEnv
-import {-# SOURCE #-} PmOracle (Delta)
+import {-# SOURCE #-} PmTypes (Delta)
import IOEnv
import RdrName
import Name