summaryrefslogtreecommitdiff
path: root/compiler/deSugar/TmOracle.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/deSugar/TmOracle.hs')
-rw-r--r--compiler/deSugar/TmOracle.hs362
1 files changed, 0 insertions, 362 deletions
diff --git a/compiler/deSugar/TmOracle.hs b/compiler/deSugar/TmOracle.hs
deleted file mode 100644
index db3ce6d770..0000000000
--- a/compiler/deSugar/TmOracle.hs
+++ /dev/null
@@ -1,362 +0,0 @@
-{-
-Author: George Karachalias <george.karachalias@cs.kuleuven.be>
--}
-
-{-# LANGUAGE CPP, MultiWayIf #-}
-
--- | The term equality oracle. The main export of the module are the functions
--- 'tmOracle', 'solveOneEq' and 'addSolveRefutableAltCon'.
---
--- If you are looking for an oracle that can solve type-level constraints, look
--- at 'TcSimplify.tcCheckSatisfiability'.
-module TmOracle (
-
- -- re-exported from PmExpr
- PmExpr(..), PmLit(..), PmAltCon(..), TmVarCt(..), TmVarCtEnv,
- PmRefutEnv, eqPmLit, isNotPmExprOther, lhsExprToPmExpr, hsExprToPmExpr,
-
- -- the term oracle
- tmOracle, TmState, initialTmState, wrapUpTmState, solveOneEq,
- extendSubst, canDiverge, isRigid,
- addSolveRefutableAltCon, lookupRefutableAltCons,
-
- -- misc.
- exprDeepLookup, pmLitType
- ) where
-
-#include "HsVersions.h"
-
-import GhcPrelude
-
-import PmExpr
-
-import Util
-import Id
-import Name
-import Type
-import HsLit
-import TcHsSyn
-import MonadUtils
-import ListSetOps (insertNoDup, unionLists)
-import Maybes
-import Outputable
-import NameEnv
-import UniqFM
-import UniqDFM
-
-{-
-%************************************************************************
-%* *
- The term equality oracle
-%* *
-%************************************************************************
--}
-
--- | Pretty much a @['TmVarCt']@ association list where the domain is 'Name'
--- instead of 'Id'. This is the type of 'tm_pos', where we store solutions for
--- rigid pattern match variables.
-type TmVarCtEnv = NameEnv PmExpr
-
--- | An environment assigning shapes to variables that immediately lead to a
--- refutation. So, if this maps @x :-> [3]@, then trying to solve a 'TmVarCt'
--- like @x ~ 3@ immediately leads to a contradiction.
--- Determinism is important since we use this for warning messages in
--- 'PmPpr.pprUncovered'. We don't do the same for 'TmVarCtEnv', so that is a plain
--- 'NameEnv'.
---
--- See also Note [Refutable shapes] in TmOracle.
-type PmRefutEnv = DNameEnv [PmAltCon]
-
--- | The state of the term oracle. Tracks all term-level facts of the form "x is
--- @True@" ('tm_pos') and "x is not @5@" ('tm_neg').
---
--- Subject to Note [The Pos/Neg invariant].
-data TmState = TmS
- { tm_pos :: !TmVarCtEnv
- -- ^ A substitution with solutions we extend with every step and return as a
- -- result. The substitution is in /triangular form/: It might map @x@ to @y@
- -- where @y@ itself occurs in the domain of 'tm_pos', rendering lookup
- -- non-idempotent. This means that 'varDeepLookup' potentially has to walk
- -- along a chain of var-to-var mappings until we find the solution but has the
- -- advantage that when we update the solution for @y@ above, we automatically
- -- update the solution for @x@ in a union-find-like fashion.
- -- Invariant: Only maps to other variables ('PmExprVar') or to WHNFs
- -- ('PmExprLit', 'PmExprCon'). Ergo, never maps to a 'PmExprOther'.
- , tm_neg :: !PmRefutEnv
- -- ^ Maps each variable @x@ to a list of 'PmAltCon's that @x@ definitely
- -- cannot match. Example, @x :-> [3, 4]@ means that @x@ cannot match a literal
- -- 3 or 4. Should we later solve @x@ to a variable @y@
- -- ('extendSubstAndSolve'), we merge the refutable shapes of @x@ into those of
- -- @y@. See also Note [The Pos/Neg invariant].
- }
-
-{- Note [The Pos/Neg invariant]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Invariant: In any 'TmState', The domains of 'tm_pos' and 'tm_neg' are disjoint.
-
-For example, it would make no sense to say both
- tm_pos = [...x :-> 3 ...]
- tm_neg = [...x :-> [4,42]... ]
-The positive information is strictly more informative than the negative.
-
-Suppose we are adding the (positive) fact @x :-> e@ to 'tm_pos'. Then we must
-delete any binding for @x@ from 'tm_neg', to uphold the invariant.
-
-But there is more! Suppose we are adding @x :-> y@ to 'tm_pos', and 'tm_neg'
-contains @x :-> cs, y :-> ds@. Then we want to update 'tm_neg' to
-@y :-> (cs ++ ds)@, to make use of the negative information we have about @x@.
--}
-
-instance Outputable TmState where
- ppr state = braces (fsep (punctuate comma (pos ++ neg)))
- where
- pos = map pos_eq (nonDetUFMToList (tm_pos state))
- neg = map neg_eq (udfmToList (tm_neg state))
- pos_eq (l, r) = ppr l <+> char '~' <+> ppr r
- neg_eq (l, r) = ppr l <+> char '≁' <+> ppr r
-
--- | Initial state of the oracle.
-initialTmState :: TmState
-initialTmState = TmS emptyNameEnv emptyDNameEnv
-
--- | Wrap up the term oracle's state once solving is complete. Return the
--- flattened 'tm_pos' and 'tm_neg'.
-wrapUpTmState :: TmState -> (TmVarCtEnv, PmRefutEnv)
-wrapUpTmState solver_state
- = (flattenTmVarCtEnv (tm_pos solver_state), tm_neg solver_state)
-
--- | Flatten the triangular subsitution.
-flattenTmVarCtEnv :: TmVarCtEnv -> TmVarCtEnv
-flattenTmVarCtEnv env = mapNameEnv (exprDeepLookup env) env
-
--- | Check whether a constraint (x ~ BOT) can succeed,
--- given the resulting state of the term oracle.
-canDiverge :: Name -> TmState -> Bool
-canDiverge x TmS{ tm_pos = pos, tm_neg = neg }
- -- If the variable seems not evaluated, there is a possibility for
- -- constraint x ~ BOT to be satisfiable. That's the case when we haven't found
- -- a solution (i.e. some equivalent literal or constructor) for it yet.
- | (_, PmExprVar y) <- varDeepLookup pos x -- seems not forced
- -- Even if we don't have a solution yet, it might be involved in a negative
- -- constraint, in which case we must already have evaluated it earlier.
- , Nothing <- lookupDNameEnv neg y
- = True
- -- Variable x is already in WHNF or we know some refutable shape, so the
- -- constraint is non-satisfiable
- | otherwise = False
-
--- | Check whether the equality @x ~ e@ leads to a refutation. Make sure that
--- @x@ and @e@ are completely substituted before!
-isRefutable :: Name -> PmExpr -> PmRefutEnv -> Bool
-isRefutable x e env
- = fromMaybe False $ elem <$> exprToAlt e <*> lookupDNameEnv env x
-
--- | Solve an equality (top-level).
-solveOneEq :: TmState -> TmVarCt -> Maybe TmState
-solveOneEq solver_env (TVC x e) = unify solver_env (PmExprVar (idName x), e)
-
-exprToAlt :: PmExpr -> Maybe PmAltCon
-exprToAlt (PmExprLit l) = Just (PmAltLit l)
-exprToAlt _ = Nothing
-
--- | Record that a particular 'Id' can't take the shape of a 'PmAltCon' in the
--- 'TmState' and return @Nothing@ if that leads to a contradiction.
-addSolveRefutableAltCon :: TmState -> Id -> PmAltCon -> Maybe TmState
-addSolveRefutableAltCon original@TmS{ tm_pos = pos, tm_neg = neg } x nalt
- = case exprToAlt e of
- -- We have to take care to preserve Note [The Pos/Neg invariant]
- Nothing -> Just extended -- Not solved yet
- Just alt -- We have a solution
- | alt == nalt -> Nothing -- ... which is contradictory
- | otherwise -> Just original -- ... which is compatible, rendering the
- where -- refutation redundant
- (y, e) = varDeepLookup pos (idName x)
- extended = original { tm_neg = neg' }
- neg' = alterDNameEnv (delNulls (insertNoDup nalt)) neg y
-
--- | When updating 'tm_neg', we want to delete any 'null' entries. This adapter
--- intends to provide a suitable interface for 'alterDNameEnv'.
-delNulls :: ([a] -> [a]) -> Maybe [a] -> Maybe [a]
-delNulls f mb_entry
- | ret@(_:_) <- f (fromMaybe [] mb_entry) = Just ret
- | otherwise = Nothing
-
--- | Return all 'PmAltCon' shapes that are impossible for 'Id' to take, i.e.
--- would immediately lead to a refutation by the term oracle.
-lookupRefutableAltCons :: Id -> TmState -> [PmAltCon]
-lookupRefutableAltCons x TmS { tm_neg = neg }
- = fromMaybe [] (lookupDNameEnv neg (idName x))
-
--- | Is the given variable /rigid/ (i.e., we have a solution for it) or
--- /flexible/ (i.e., no solution)? Returns the solution if /rigid/. A
--- semantically helpful alias for 'lookupNameEnv'.
-isRigid :: TmState -> Name -> Maybe PmExpr
-isRigid TmS{ tm_pos = pos } x = lookupNameEnv pos x
-
--- | @isFlexible tms = isNothing . 'isRigid' tms@
-isFlexible :: TmState -> Name -> Bool
-isFlexible tms = isNothing . isRigid tms
-
--- | Try to unify two 'PmExpr's and record the gained knowledge in the
--- 'TmState'.
---
--- Returns @Nothing@ when there's a contradiction. Returns @Just tms@
--- when the constraint was compatible with prior facts, in which case @tms@ has
--- integrated the knowledge from the equality constraint.
-unify :: TmState -> (PmExpr, PmExpr) -> Maybe TmState
-unify tms eq@(e1, e2) = case eq of
- -- We cannot do a thing about these cases
- (PmExprOther _,_) -> boring
- (_,PmExprOther _) -> boring
-
- (PmExprLit l1, PmExprLit l2) -> case eqPmLit l1 l2 of
- -- See Note [Undecidable Equality for Overloaded Literals]
- True -> boring
- False -> unsat
-
- (PmExprCon c1 ts1, PmExprCon c2 ts2)
- | c1 == c2 -> foldlM unify tms (zip ts1 ts2)
- | otherwise -> unsat
-
- (PmExprVar x, PmExprVar y)
- | x == y -> boring
-
- -- It's important to handle both rigid cases first, otherwise we get cyclic
- -- substitutions. Cf. 'extendSubstAndSolve' and
- -- @testsuite/tests/pmcheck/should_compile/CyclicSubst.hs@.
- (PmExprVar x, _)
- | Just e1' <- isRigid tms x -> unify tms (e1', e2)
- (_, PmExprVar y)
- | Just e2' <- isRigid tms y -> unify tms (e1, e2')
- (PmExprVar x, PmExprVar y) -> Just (equate x y tms)
- (PmExprVar x, _) -> trySolve x e2 tms
- (_, PmExprVar y) -> trySolve y e1 tms
-
- _ -> WARN( True, text "unify: Catch all" <+> ppr eq)
- boring -- I HATE CATCH-ALLS
- where
- boring = Just tms
- unsat = Nothing
-
--- | Merges the equivalence classes of @x@ and @y@ by extending the substitution
--- with @x :-> y@.
--- Preconditions: @x /= y@ and both @x@ and @y@ are flexible (cf.
--- 'isFlexible'/'isRigid').
-equate :: Name -> Name -> TmState -> TmState
-equate x y tms@TmS{ tm_pos = pos, tm_neg = neg }
- = ASSERT( x /= y )
- ASSERT( isFlexible tms x )
- ASSERT( isFlexible tms y )
- tms'
- where
- pos' = extendNameEnv pos x (PmExprVar y)
- -- Be careful to uphold Note [The Pos/Neg invariant] by merging the refuts
- -- of x into those of y
- nalts = fromMaybe [] (lookupDNameEnv neg x)
- neg' = alterDNameEnv (delNulls (unionLists nalts)) neg y
- `delFromDNameEnv` x
- tms' = TmS { tm_pos = pos', tm_neg = neg' }
-
--- | Extend the substitution with a mapping @x: -> e@ if compatible with
--- refutable shapes of @x@ and its solution, reject (@Nothing@) otherwise.
---
--- Precondition: @x@ is flexible (cf. 'isFlexible'/'isRigid').
--- Precondition: @e@ is a 'PmExprCon' or 'PmExprLit'
-trySolve:: Name -> PmExpr -> TmState -> Maybe TmState
-trySolve x e _tms@TmS{ tm_pos = pos, tm_neg = neg }
- | ASSERT( isFlexible _tms x )
- ASSERT( _is_whnf e )
- isRefutable x e neg
- = Nothing
- | otherwise
- = Just (TmS (extendNameEnv pos x e) (delFromDNameEnv neg x))
- where
- _is_whnf PmExprCon{} = True
- _is_whnf PmExprLit{} = True
- _is_whnf _ = False
-
--- | When we know that a variable is fresh, we do not actually have to
--- check whether anything changes, we know that nothing does. Hence,
--- @extendSubst@ simply extends the substitution, unlike what
--- 'extendSubstAndSolve' does.
-extendSubst :: Id -> PmExpr -> TmState -> TmState
-extendSubst y e solver_state@TmS{ tm_pos = pos }
- | isNotPmExprOther simpl_e
- = solver_state { tm_pos = extendNameEnv pos x simpl_e }
- | otherwise = solver_state
- where
- x = idName y
- simpl_e = exprDeepLookup pos e
-
--- | Apply an (un-flattened) substitution to a variable and return its
--- representative in the triangular substitution @env@ and the completely
--- substituted expression. The latter may just be the representative wrapped
--- with 'PmExprVar' if we haven't found a solution for it yet.
-varDeepLookup :: TmVarCtEnv -> Name -> (Name, PmExpr)
-varDeepLookup env x = case lookupNameEnv env x of
- Just (PmExprVar y) -> varDeepLookup env y
- Just e -> (x, exprDeepLookup env e) -- go deeper
- Nothing -> (x, PmExprVar x) -- terminal
-{-# INLINE varDeepLookup #-}
-
--- | Apply an (un-flattened) substitution to an expression.
-exprDeepLookup :: TmVarCtEnv -> PmExpr -> PmExpr
-exprDeepLookup env (PmExprVar x) = snd (varDeepLookup env x)
-exprDeepLookup env (PmExprCon c es) = PmExprCon c (map (exprDeepLookup env) es)
-exprDeepLookup _ other_expr = other_expr -- PmExprLit, PmExprOther
-
--- | External interface to the term oracle.
-tmOracle :: TmState -> [TmVarCt] -> Maybe TmState
-tmOracle tm_state eqs = foldlM solveOneEq tm_state eqs
-
--- | Type of a PmLit
-pmLitType :: PmLit -> Type -- should be in PmExpr but gives cyclic imports :(
-pmLitType (PmSLit lit) = hsLitType lit
-pmLitType (PmOLit _ lit) = overLitType lit
-
-{- Note [Refutable shapes]
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Consider a pattern match like
-
- foo x
- | 0 <- x = 42
- | 0 <- x = 43
- | 1 <- x = 44
- | otherwise = 45
-
-This will result in the following initial matching problem:
-
- PatVec: x (0 <- x)
- ValVec: $tm_y
-
-Where the first line is the pattern vector and the second line is the value
-vector abstraction. When we handle the first pattern guard in Check, it will be
-desugared to a match of the form
-
- PatVec: x 0
- ValVec: $tm_y x
-
-In LitVar, this will split the value vector abstraction for `x` into a positive
-`PmLit 0` and a negative `PmLit x [0]` value abstraction. While the former is
-immediately matched against the pattern vector, the latter (vector value
-abstraction `~[0] $tm_y`) is completely uncovered by the clause.
-
-`pmcheck` proceeds by *discarding* the the value vector abstraction involving
-the guard to accomodate for the desugaring. But this also discards the valuable
-information that `x` certainly is not the literal 0! Consequently, we wouldn't
-be able to report the second clause as redundant.
-
-That's a typical example of why we need the term oracle, and in this specific
-case, the ability to encode that `x` certainly is not the literal 0. Now the
-term oracle can immediately refute the constraint `x ~ 0` generated by the
-second clause and report the clause as redundant. After the third clause, the
-set of such *refutable* literals is again extended to `[0, 1]`.
-
-In general, we want to store a set of refutable shapes (`PmAltCon`) for each
-variable. That's the purpose of the `PmRefutEnv`. `addSolveRefutableAltCon` will
-add such a refutable mapping to the `PmRefutEnv` in the term oracles state and
-check if causes any immediate contradiction. Whenever we record a solution in
-the substitution via `extendSubstAndSolve`, the refutable environment is checked
-for any matching refutable `PmAltCon`.
--}