diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,6 +2,35 @@
 
 ## NEXT
 
+## 0.9.6.3.4 (2026-01-14)
+
+- Implement `--sortedsolution` to keep elaborated sorts in fqout/solution [#821](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/821)
+- Retire old parser for horn queries [#820](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/820)
+- Stop the parser from simplifying expressions during parsing [#819](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/819)
+- Add `--explicitKvars` option and generalize horn syntax to accept expressions for kvars arguments [#818](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/818)
+- Provide more comments in the SMT queries to relate them to the source code [#814](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/814)
+- Remove `--no-lazy-ple` [#813](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/813)
+- Allow PLE to unfold in kvar solutions  [#811](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/811)
+- Remove redundant question marks from expression [#807](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/807)
+- Remove distinction of predicates and expressions in the parser [#805](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/805)
+- Shrink kvar solutions [#799](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/799) [#809](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/809) [#821](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/821)
+- Disable the progress bar when not on a terminal [#798](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/798)
+- Retire existential binds [#797](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/797)
+- Provide stack traces for more crashes [#794](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/794)
+- Support string operators [#793](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/793)
+- Apply kvar solutions to constraints before PLE [#792](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/792)
+- Retire implementation of gradual refinement types [#789](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/789)
+- Retire old PLE variations [#788](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/788)
+- Add set cardinality support when using cvc5 [#774](https:://github.com/ucsd-progsys/liquid-fixpoint/pull/774)
+- Have `--scrape` consider global constants and ADTs [#772](https://github.com/ucsd-progsys/liquid-fixpoint/pull/772)
+- Shorten flags of a few flags and add git-version [#762](https://github.com/ucsd-progsys/liquid-fixpoint/pull/762)
+- Add support/conversions for Bitv8 and Bitv16 [#759](https://github.com/ucsd-progsys/liquid-fixpoint/pull/759)
+- Support the finite field theory of CVC5 [#755](https://github.com/ucsd-progsys/liquid-fixpoint/pull/755)
+- Fix SMT crashes on reflected functions on polymorphic data types [#753](https://github.com/ucsd-progsys/liquid-fixpoint/pull/753)
+- Allow function names to start with prefix mod [#751](https://github.com/ucsd-progsys/liquid-fixpoint/pull/751)
+- Implement let bindings for Horn queries [#748](https://github.com/ucsd-progsys/liquid-fixpoint/pull/748)
+- Fix elaboration of `define_fun` declarations [#747](https://github.com/ucsd-progsys/liquid-fixpoint/pull/747) [#749](https://github.com/ucsd-progsys/liquid-fixpoint/pull/749)
+
 ## 0.9.6.3.3 (2025-03-22)
 
 - Add support for GHC HEAD (9.13) [#745](https://github.com/ucsd-progsys/liquid-fixpoint/pull/745).
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 ===============
 
 
-[![Hackage](https://img.shields.io/hackage/v/liquid-fixpoint.svg)](https://hackage.haskell.org/package/liquid-fixpoint) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/liquid-fixpoint.svg)](http://packdeps.haskellers.com/feed?needle=liquid-fixpoint) 
+[![Hackage](https://img.shields.io/hackage/v/liquid-fixpoint.svg)](https://hackage.haskell.org/package/liquid-fixpoint) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/liquid-fixpoint.svg)](http://packdeps.haskellers.com/feed?needle=liquid-fixpoint)
 [![CircleCI](https://circleci.com/gh/ucsd-progsys/liquid-fixpoint.svg?style=svg)](https://circleci.com/gh/ucsd-progsys/liquid-fixpoint)
 [![hlint](https://github.com/ucsd-progsys/liquid-fixpoint/actions/workflows/hlint.yml/badge.svg)](https://github.com/ucsd-progsys/liquid-fixpoint/actions/workflows/hlint.yml)
 [![cabal](https://github.com/ucsd-progsys/liquid-fixpoint/actions/workflows/cabal.yml/badge.svg)](https://github.com/ucsd-progsys/liquid-fixpoint/actions/workflows/cabal.yml)
@@ -15,7 +15,13 @@
 + [Houdini](https://users.soe.ucsc.edu/~cormac/papers/fme01.pdf)
 + [Cartesian predicate abstraction](http://swt.informatik.uni-freiburg.de/berit/papers/boolean-and-cartesian-....pdf)
 
+Algorithms implemented in liquid-fixpoint:
 
++ [FUSION](https://ranjitjhala.github.io/static/local_refinement_typing.pdf) Local refinement typing
++ [PLE](https://ranjitjhala.github.io/static/refinement_reflection.pdf) Refinement Reflection: Complete Verification with SMT
++ [REST](https://drops.dagstuhl.de/entities/document/10.4230/DARTS.8.2.12) REST: Integrating Term Rewriting with Program Verification
+
+
 Requirements
 ------------
 
@@ -23,6 +29,7 @@
 
 - [Z3](https://github.com/Z3Prover/z3)
 - [CVC4](https://cvc4.github.io/)
+- [CVC5](https://cvc5.github.io/)
 - [MathSat](http://mathsat.fbk.eu/download.html)
 
 If on Windows, please make sure to place the binary and any associated DLLs somewhere
@@ -60,9 +67,10 @@
 
     * Z3
     * CVC4
+    * CVC5
     * MathSat
 
-"Horn" Format 
+"Horn" Format
 -------------
 
 See the examples in `tests/horn/{pos, neg}` eg
@@ -73,8 +81,8 @@
 
 For how to write VCs "by hand".
 
-See [this tutorial](https://arxiv.org/abs/2010.07763) 
-with [accompanying code](https://github.com/ranjitjhala/sprite-lang) 
+See [this tutorial](https://arxiv.org/abs/2010.07763)
+with [accompanying code](https://github.com/ranjitjhala/sprite-lang)
 for an example of how to generate Horn queries.
 
 The main datatypes are described in [src/Language/Fixpoint/Horn/Types.hs](src/Language/Fixpoint/Horn/Types.hs)
@@ -145,7 +153,7 @@
 ```
 $ more tests/horn/pos/test01.smt2 | fixpoint --stdin
 
-Liquid-Fixpoint Copyright 2013-21 Regents of the University of California.
+Liquid-Fixpoint Copyright 2009-25 Regents of the University of California.
 All Rights Reserved.
 
 Working 166% [===============================================================]
@@ -226,15 +234,20 @@
   Then a single `IBindEnv` should only mention _at most_
   one of `1` or `12`.
 
-* There is also a "tree-shape" property that its a bit hard
-  to describe ... TODO     
+* [NOTE:TREE-LIKE] There is also a "tree-shape" property required by PLE:
 
+```
+  forall constraints c, c'.
+    if i in c and i in c' then
+      forall 0 <= j < i, j in c and j in c'
+```
+
 ### LHS
 
 Each `slhs` of a constraint is a `SortedReft`.
 
 - Each `SortredReft` is basically a `Reft` -- a logical predicate.
-  The important bit is that a `KVar` i.e. terms of the formalized
+  The important bit is that a `KVar` i.e. terms of the form
 
 ```
      $k1[x1:=y1][x2:=y2]...[xn:=yn]
@@ -271,16 +284,35 @@
 
   is not. The exact definition is formalized in `Language.Fixpoint.SortCheck`
 
-
 ### RHS
 
 Similarly each `rhs` of a `SubC` must either be a single `$k[...]` or an plain `$k`-free `Expr`.
 
+### KVar occurrences
+
+* Each `KVar` that appears in any binding or constraint must have exactly one
+  associated wf constraint.
+
+* Each `KVar` that appears in any binding or constraint must have an
+  accompanying substitution whose domain has the same symbols as the environment
+  of the corresponding wf constraint plus the symbol of its refinement type.
+
+For example, if the wf constraint is
+```
+    x:int, y: int |- {v:int | $k_##42 }
+```
+
+any occurrence of `$k_##42` must be of the form
+
+```
+    $k_##42 [x:=e1][y:=e2][v:=e3]
+```
+
 ### Global vs. Distinct Literals
 
 ```
      , gLits    :: !(SEnv Sort)               -- ^ Global Constant symbols
-     , dLits    :: !(SEnv Sort)       
+     , dLits    :: !(SEnv Sort)
 ```
 
 The _global_ literals `gLits` are symbols that
@@ -326,15 +358,10 @@
 
 > What's the difference between an FTC and an FObj?
 
-In early versions of fixpoint, there was support for 
-three sorts for expressions (`Expr`) that were sent 
-to the SMT solver:
-
-1. `int`
-2. `bool`
-3. "other"
-
-The `FObj` sort was introduced to represent essentially _all_ 
+In early versions of fixpoint, there was support for
+three sorts for expressions (`Expr`) that were sent
+to the SMT solver: `int`, `bool` and "other". The
+`FObj` sort was introduced to represent essentially _all_
 non-int and non-bool values (e.g. tuples, lists, trees, pointers...)
 
 However, we later realized that it is valuable to keep _more_
@@ -351,17 +378,27 @@
 > Does that then make FTC types that the SMT solver does
 > know about (bools, ints, lists, sets, etc.)?
 
-The SMT solver knows about `bool`, `int` and `set` (also `bitvector` 
-and `map`) but _all_ other types are _currently_ represented as plain 
-`Int` inside the SMT solver. However, we _will be_ changing this 
+The SMT solvers we currently use know about following sorts:
+
+* `bool`
+* `int`
+* `real`
+* `string`
+* `array` (aka `map`)
+* `bitvector`
+* `set` and `bag` (in Z3, they are both also represented internally as `array`s)
+* `finitefield` (CVC5 only)
+
+_All_ other types are _currently_ represented as plain
+`Int` inside the SMT solver. However, we _will be_ changing this
 to make use of SMT support for ADTs ...
 
-To sum up: the `FObj` is there for historical reasons; it has been 
-subsumed by `FTC` which is what I recomend you use. However `FObj` 
-is there if you want a simple "unitype" / "any" type for terms 
+To sum up: the `FObj` is there for historical reasons; it has been
+subsumed by `FTC` which is what I recomend you use. However `FObj`
+is there if you want a simple "unitype" / "any" type for terms
 that are not "interpreted".
 
-## Qualifier Patterns 
+## Qualifier Patterns
 
 ```haskell
 λ> doParse' (qualParamP sortP) "" "z as (mon . $1) : int"
diff --git a/bin/Fixpoint.hs b/bin/Fixpoint.hs
--- a/bin/Fixpoint.hs
+++ b/bin/Fixpoint.hs
@@ -24,10 +24,10 @@
       | otherwise  = solveFQ
 
 isHorn :: F.Config -> Bool
-isHorn cfg = F.isExtFile F.Smt2 file 
+isHorn cfg = F.isExtFile F.Smt2 file
           || F.isExtFile F.Json file
           || F.stdin cfg
-  where 
+  where
     file = F.srcFile cfg
 
 errorExit :: F.Error -> IO ExitCode
diff --git a/liquid-fixpoint.cabal b/liquid-fixpoint.cabal
--- a/liquid-fixpoint.cabal
+++ b/liquid-fixpoint.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               liquid-fixpoint
-version:            0.9.6.3.3
+version:            0.9.6.3.4
 synopsis:           Predicate Abstraction-based Horn-Clause/Implication Constraint Solver
 description:
   This package implements an SMTLIB based Horn-Clause\/Logical Implication constraint
@@ -56,6 +56,7 @@
 library
   import: warnings
   exposed-modules:  Data.ShareMap
+                    Control.Exception.Compat
                     Language.Fixpoint.Conditional.Z3
                     Language.Fixpoint.Defunctionalize
                     Language.Fixpoint.Graph
@@ -82,9 +83,6 @@
                     Language.Fixpoint.Solver.Eliminate
                     Language.Fixpoint.Solver.EnvironmentReduction
                     Language.Fixpoint.Solver.Extensionality
-                    Language.Fixpoint.Solver.GradualSolution
-                    Language.Fixpoint.Solver.GradualSolve
-                    Language.Fixpoint.Solver.Instantiate
                     Language.Fixpoint.Solver.Interpreter
                     Language.Fixpoint.Solver.Monad
                     Language.Fixpoint.Solver.PLE
@@ -105,8 +103,8 @@
                     Language.Fixpoint.Types.Constraints
                     Language.Fixpoint.Types.Environments
                     Language.Fixpoint.Types.Errors
-                    Language.Fixpoint.Types.Graduals
                     Language.Fixpoint.Types.Names
+                    Language.Fixpoint.Types.SMTPrint
                     Language.Fixpoint.Types.PrettyPrint
                     Language.Fixpoint.Types.Refinements
                     Language.Fixpoint.Types.Solutions
@@ -144,12 +142,12 @@
                   , directory
                   , fgl
                   , filepath
+                  , gitrev              >= 1.3.1
                   , hashable
                   , intern
                   , lens-family
                   , megaparsec           >= 7.0.0 && < 10
                   , mtl
-                  , parallel
                   , parser-combinators
                   , pretty               >= 1.1.3.1
                   , process
@@ -194,7 +192,7 @@
   autogen-modules:  Paths_liquid_fixpoint
   hs-source-dirs:   bin
   ghc-options:      -threaded -W -Wno-missing-methods
-  build-depends:    base >= 4.9.1.0 && < 5, liquid-fixpoint
+  build-depends:    base >= 4.9.1.0 && < 5, liquid-fixpoint, gitrev >= 1.3.1
   if flag(devel)
     ghc-options: -Werror
   default-language: Haskell98
@@ -236,10 +234,15 @@
                     ShareMapReference
                     ShareMapTests
                     SimplifyInterpreter
+                    SimplifyKVarTests
                     SimplifyPLE
                     SimplifyTests
                     UndoANFTests
                     Paths_liquid_fixpoint
+  if impl(ghc>=9.12.1)
+    hs-source-dirs: tests/tasty/ghc-9.12.1
+  else
+    hs-source-dirs: tests/tasty/ghc-before-9.12.1
   autogen-modules:  Paths_liquid_fixpoint
   hs-source-dirs:   tests/tasty
   ghc-options:      -threaded
diff --git a/src/Control/Exception/Compat.hs b/src/Control/Exception/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Exception/Compat.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE CPP #-}
+module Control.Exception.Compat
+  ( ExceptionWithContext(..)
+  , displayExceptionContext
+  , wrapExceptionWithContext
+  ) where
+
+#if MIN_VERSION_base(4,20,0)
+
+import Control.Exception (ExceptionWithContext(..))
+import Control.Exception.Context (displayExceptionContext)
+
+wrapExceptionWithContext :: ExceptionWithContext a -> ExceptionWithContext a
+wrapExceptionWithContext = id
+
+#else
+
+data ExceptionWithContext a = ExceptionWithContext ExceptionContext a
+
+data ExceptionContext = ExceptionContext
+
+displayExceptionContext :: ExceptionContext -> String
+displayExceptionContext _ = "Exception context not available in this version of ghc."
+
+wrapExceptionWithContext :: a -> ExceptionWithContext a
+wrapExceptionWithContext = ExceptionWithContext ExceptionContext
+#endif
+
diff --git a/src/Language/Fixpoint/Defunctionalize.hs b/src/Language/Fixpoint/Defunctionalize.hs
--- a/src/Language/Fixpoint/Defunctionalize.hs
+++ b/src/Language/Fixpoint/Defunctionalize.hs
@@ -85,6 +85,9 @@
                               (i2, e2') = go e2
                           in (max i1 i2, EApp e1' e2')
     go (ECst e s)       = fmap (`ECst` s) (go e)
+    go (ELet x e1 e2)   = let (i1, e1') = go e1
+                              (i2, e2') = go e2
+                          in (max i1 i2, ELet x e1' e2')
     go (EIte e1 e2 e3)  = let (i1, e1') = go e1
                               (i2, e2') = go e2
                               (i3, e3') = go e3
@@ -152,11 +155,6 @@
     let (x, t, k) = wrft wf
     t' <- defunc t
     return $ wf { wrft = (x, t', k) }
-  defunc wf@GWfC{} = do
-    let (x, t, k) = wrft wf
-    t' <- defunc t
-    e' <- defunc $ wexpr wf
-    return $ wf { wrft = (x, t', k), wexpr = e' }
 
 instance Defunc SortedReft where
   defunc (RR s r) = RR s <$> defunc r
diff --git a/src/Language/Fixpoint/Graph/Deps.hs b/src/Language/Fixpoint/Graph/Deps.hs
--- a/src/Language/Fixpoint/Graph/Deps.hs
+++ b/src/Language/Fixpoint/Graph/Deps.hs
@@ -205,12 +205,16 @@
 edgeGraph es = KVGraph [(v, v, vs) | (v, vs) <- groupList es ]
 
 -- need to plumb list of ebinds
+
+-- | Compute dependencies between constraints and kvars.
+--
+-- @(k, c)@ means that constraint @c@ uses kvar @k@ on the LHS.
+-- @(c, k)@ means that constraint @c@ uses kvar @k@ on the RHS.
 {-# SCC kvEdges #-}
 kvEdges :: (F.TaggedC c a) => F.GInfo c a -> [CEdge]
-kvEdges fi = selfes ++ concatMap (subcEdges bs) cs ++ concatMap (ebindEdges ebs bs) cs
+kvEdges fi = selfes ++ concatMap (subcEdges bs) cs
   where
     bs     = F.bs fi
-    ebs    = F.ebinds fi
     cs     = M.elems (F.cm fi)
     ks     = fiKVars fi
     selfes =  [(Cstr i , Cstr  i) | c <- cs, let i = F.subcId c]
@@ -220,21 +224,6 @@
 fiKVars :: F.GInfo c a -> [F.KVar]
 fiKVars = M.keys . F.ws
 
-ebindEdges :: (F.TaggedC c a) => [F.BindId] -> F.BindEnv a -> c a -> [CEdge]
-ebindEdges ebs bs c =  [(EBind k, Cstr i ) | k  <- envEbinds xs bs c ]
-                    ++ [(Cstr i, EBind k') | k' <- rhsEbinds xs c ]
-  where
-    i          = F.subcId c
-    xs         = fst3 . flip F.lookupBindEnv bs <$> ebs
-
-envEbinds :: (F.TaggedC c a, Foldable t) =>
-             t F.Symbol -> F.BindEnv a -> c a -> [F.Symbol]
-envEbinds xs be c = [ x | x <- envBinds , x `elem` xs ]
-  where envBinds = fst <$> F.clhs be c
-rhsEbinds :: (Foldable t, F.TaggedC c a) =>
-             t F.Symbol -> c a -> [F.Symbol]
-rhsEbinds xs c = [ x | x <- F.syms (F.crhs c) , x `elem` xs ]
-
 subcEdges :: (F.TaggedC c a) => F.BindEnv a -> c a -> [CEdge]
 subcEdges bs c =  [(KVar k, Cstr i ) | k  <- V.envKVars bs c]
                ++ [(Cstr i, KVar k') | k' <- V.rhsKVars c ]
@@ -245,10 +234,10 @@
 -- | Eliminated Dependencies
 --------------------------------------------------------------------------------
 {-# SCC elimDeps #-}
-elimDeps :: (F.TaggedC c a) => F.GInfo c a -> [CEdge] -> S.HashSet F.KVar -> S.HashSet F.Symbol -> CDeps
-elimDeps si es nonKutVs ebs = graphDeps si es'
+elimDeps :: (F.TaggedC c a) => F.GInfo c a -> [CEdge] -> S.HashSet F.KVar -> CDeps
+elimDeps si es nonKutVs = graphDeps si es'
   where
-    es'                 = graphElim es nonKutVs ebs
+    es'                 = graphElim es nonKutVs
     _msg                = "graphElim: " ++ show (length es')
 
 {- | `graphElim` "eliminates" a kvar k by replacing every "path"
@@ -259,9 +248,8 @@
 
           ki ------------> c
 -}
-graphElim :: [CEdge] -> S.HashSet F.KVar -> S.HashSet F.Symbol -> [CEdge]
-graphElim es ks _ebs = ikvgEdges $ -- elimEs (S.map EBind ebs) $
-                                  elimKs (S.map KVar ks)   $ edgesIkvg es
+graphElim :: [CEdge] -> S.HashSet F.KVar -> [CEdge]
+graphElim es ks = ikvgEdges $ elimKs (S.map KVar ks)   $ edgesIkvg es
   where
     elimKs      = flip (S.foldl' elimK)
     _elimEs      = flip (S.foldl' elimE)
@@ -307,8 +295,10 @@
 dCut    v = Deps (S.singleton v) S.empty
 
 --------------------------------------------------------------------------------
--- | Compute Dependencies and Cuts ---------------------------------------------
---------------------------------------------------------------------------------
+-- | Compute Dependencies and Cuts
+--
+-- Computes greedily a set of kvars that make the dependency graph acyclic when
+-- removed. Also yields the edges of the dependency graph.
 {-# SCC elimVars #-}
 elimVars :: (F.TaggedC c a) => Config -> F.GInfo c a -> ([CEdge], Elims F.KVar)
 --------------------------------------------------------------------------------
@@ -581,9 +571,8 @@
     nlks          = nonLinearKVars si
     d             = snd $ elimVars cfg si
 
---------------------------------------------------------------------------------
+-- | KVars used more than once in the LHS of some constraint
 nonLinearKVars :: (F.TaggedC c a) => F.GInfo c a -> S.HashSet F.KVar
---------------------------------------------------------------------------------
 nonLinearKVars fi = S.unions $ nlKVarsC bs <$> cs
   where
     bs            = F.bs fi
diff --git a/src/Language/Fixpoint/Graph/Types.hs b/src/Language/Fixpoint/Graph/Types.hs
--- a/src/Language/Fixpoint/Graph/Types.hs
+++ b/src/Language/Fixpoint/Graph/Types.hs
@@ -66,13 +66,11 @@
 
 data CVertex = KVar  !KVar    -- ^ real kvar vertex
              | DKVar !KVar    -- ^ dummy to ensure each kvar has a successor
-             | EBind !F.Symbol  -- ^ existentially bound "ghost paramter" to solve for
              | Cstr  !Integer -- ^ constraint-id which creates a dependency
                deriving (Eq, Ord, Show, Generic)
 
 instance PPrint CVertex where
   pprintTidy _ (KVar k)  = doubleQuotes $ pprint $ kv k
-  pprintTidy _ (EBind s)  = doubleQuotes $ pprint s
   pprintTidy _ (Cstr i)  = text "id_" <-> pprint i
   pprintTidy _ (DKVar k) = pprint k   <-> text "*"
 
@@ -192,8 +190,8 @@
 -- | `SolverInfo` contains all the stuff needed to produce a result, and is the
 --   the essential ingredient of the state needed by solve_
 --------------------------------------------------------------------------------
-data SolverInfo a b = SI
-  { siSol     :: !(F.Sol b F.QBind)             -- ^ the initial solution
+data SolverInfo a = SI
+  { siSol     :: !(F.Sol F.QBind)             -- ^ the initial solution
   , siQuery   :: !(F.SInfo a)                   -- ^ the whole input query
   , siDeps    :: !CDeps                         -- ^ dependencies between constraints/ranks etc.
   , siVars    :: !(S.HashSet F.KVar)            -- ^ set of KVars to actually solve for
diff --git a/src/Language/Fixpoint/Horn/Info.hs b/src/Language/Fixpoint/Horn/Info.hs
--- a/src/Language/Fixpoint/Horn/Info.hs
+++ b/src/Language/Fixpoint/Horn/Info.hs
@@ -5,12 +5,10 @@
     hornFInfo
   ) where
 
-import           Control.Monad (forM)
 import           Data.Ord (Down(..), comparing)
 import qualified Data.HashMap.Strict            as M
 import qualified Data.List                      as L
 import qualified Data.Tuple                     as Tuple
-import           Data.Either                    (partitionEithers)
 import           GHC.Generics                   (Generic)
 import qualified Language.Fixpoint.Misc         as Misc
 import qualified Language.Fixpoint.Types        as F
@@ -22,7 +20,6 @@
 hornFInfo cfg q = mempty
   { F.cm        = cs
   , F.bs        = be2
-  , F.ebinds    = ebs
   , F.ws        = kvEnvWfCs kve
   , F.quals     = H.qQuals q ++ scrapeCstr (F.scrape cfg) hCst
   , F.gLits     = F.fromMapSEnv $ H.qCon q
@@ -30,12 +27,12 @@
   , F.ae        = axEnv cfg q cs
   , F.ddecls    = H.qData q
   , F.hoInfo    = F.cfgHoInfo cfg
-  , F.defns     = H.qDefs q
+  , F.defns     = F.MkDefinedFuns (H.qDefs q)
   }
   where
     be0         = F.emptyBindEnv
     (be1, kve)  = hornWfs   be0     (H.qVars q)
-    (be2, ebs, cs) = hornSubCs be1 kve hCst
+    (be2, cs) = hornSubCs be1 kve hCst
     hCst           = H.qCstr q
 
 
@@ -48,27 +45,26 @@
 
 ----------------------------------------------------------------------------------
 hornSubCs :: F.BindEnv a -> KVEnv a -> H.Cstr a
-          -> (F.BindEnv a, [F.BindId], M.HashMap F.SubcId (F.SubC a))
+          -> (F.BindEnv a, M.HashMap F.SubcId (F.SubC a))
 ----------------------------------------------------------------------------------
-hornSubCs be kve c = (be', ebs, M.fromList (F.addIds cs))
+hornSubCs be kve c = (be', M.fromList (F.addIds cs))
   where
-    (be', ebs, cs) = goS kve F.emptyIBindEnv be c
+    (be', cs) = goS kve F.emptyIBindEnv be c
     -- lhs0           = bindSortedReft kve H.dummyBind
 
 -- | @goS@ recursively traverses the NNF constraint to build up a list
 --   of the vanilla @SubC@ constraints.
 
 goS :: KVEnv a -> F.IBindEnv ->  F.BindEnv a -> H.Cstr a
-    -> (F.BindEnv a, [F.BindId], [F.SubC a])
+    -> (F.BindEnv a, [F.SubC a])
 
-goS kve env be c = (be', mEbs, subcs)
+goS kve env be c = (be', subcs)
   where
-    (be', ecs) = goS' kve env Nothing be c
-    (mEbs, subcs) = partitionEithers ecs
+    (be', subcs) = goS' kve env Nothing be c
 
 goS' :: KVEnv a -> F.IBindEnv -> Maybe F.SortedReft -> F.BindEnv a -> H.Cstr a
-    -> (F.BindEnv a, [Either F.BindId (F.SubC a)])
-goS' kve env lhs be (H.Head p l) = (be, [Right subc])
+    -> (F.BindEnv a, [F.SubC a])
+goS' kve env lhs be (H.Head p l) = (be, [subc])
   where
     subc                        = myMkSubC env lhs rhs Nothing [] l
     rhs                         = updSortedReft kve lhs p
@@ -84,13 +80,6 @@
     bSR                         = bindSortedReft kve b
     env'                        = F.insertsIBindEnv [bId] env
 
-goS' kve env _   be (H.Any b c)  = (be'', Left bId : subcs)
-  where
-    (be'', subcs)               = goS' kve env' (Just bSR) be' c
-    (bId, be')                  = F.insertBindEnv (H.bSym b) bSR (H.bMeta b) be
-    bSR                         = bindSortedReft kve b
-    env'                        = F.insertsIBindEnv [bId] env
-
 myMkSubC :: F.IBindEnv -> Maybe F.SortedReft -> F.SortedReft -> Maybe Integer -> F.Tag -> a -> F.SubC a
 myMkSubC be lhsMb rhs x y z = F.mkSubC be lhs rhs x y z
   where
@@ -114,10 +103,10 @@
     go (H.Var k ys) = kvApp kve k ys
     go (H.PAnd  ps) = F.PAnd (go <$> ps)
 
-kvApp :: KVEnv a -> F.Symbol -> [F.Symbol] -> F.Expr
+kvApp :: KVEnv a -> F.Symbol -> [F.Expr] -> F.Expr
 kvApp kve k ys = F.PKVar (F.KV k) su
   where
-    su         = F.mkSubst (zip params (F.eVar <$> ys))
+    su         = F.mkSubst (zip params ys)
     params     = maybe err1 kvParams (M.lookup k kve)
     err1       = F.panic ("Unknown Horn variable: " ++ F.showpp k)
 
@@ -176,7 +165,6 @@
     go senv (H.Head p _) = scrapePred senv p
     go senv (H.CAnd cs)  = concatMap (go senv) cs
     go senv (H.All b c)  = scrapeBind m senv' b <> go senv' c where senv' = insertBindEnv b senv
-    go senv (H.Any b c)  = scrapeBind m senv' b <> go senv' c where senv' = insertBindEnv b senv
 
 scrapeBind :: F.Scrape -> BindEnv -> H.Bind a -> [F.Qualifier]
 scrapeBind F.Both senv b = scrapePred senv (H.bPred b)
@@ -189,16 +177,17 @@
 
 -- NOTE: Constraints.mkQual will do extra stuff like generalizing the sorts...
 mkQual :: BindEnv -> F.Expr -> [ F.Qualifier ]
-mkQual env e = case qualParams env e of
-  Nothing  -> []
-  Just xts -> [ mkScrapeQual xts' e | xts' <- shiftCycle xts ]
+mkQual env e = [ mkScrapeQual xts' e | xts' <- shiftCycle xts ]
+  where
+    xts = qualParams env e
 
 mkScrapeQual :: [(F.Symbol, F.Sort)] -> F.Expr -> F.Qualifier
-mkScrapeQual xts e = F.mkQual (F.symbol "AUTO") qParams (F.subst su e) (F.dummyPos "")
+mkScrapeQual xts e = F.mkQual (F.symbol "AUTO") qParams body (F.dummyPos "")
   where
     qParams = [ F.QP {F.qpSym = y, F.qpPat = F.PatNone, F.qpSort = t} | (_, y, t) <- xyts ]
     xyts    = zipWith (\i (x, t) -> (x, F.bindSymbol i, t)) [0..] xts
     su      = F.mkSubst [ (x, F.expr y) | (x, y, _) <- xyts ]
+    body    = F.subst su e
 
 
 shiftCycle :: [(F.Symbol, F.Sort)] -> [[(F.Symbol, F.Sort)]]
@@ -221,15 +210,11 @@
   2. Permute the args?
  -}
 
-qualParams :: BindEnv -> F.Expr -> Maybe [(F.Symbol, F.Sort)]
-qualParams env e = do
-    let xs = Misc.nubOrd (F.syms e)
-    ixts <- forM xs $ \x -> do
-              (t, i) <- lookupBindEnv x env
-              return (i, x, t)
-    return [ (x, t) | (_, x, t) <- L.sortBy (comparing Down) ixts ]
-
-    -- ixts = [ (i, x, t) | x <- xs, (i, t) <- lookupBindEnv x env ]
+qualParams :: BindEnv -> F.Expr -> [(F.Symbol, F.Sort)]
+qualParams env e = [ (x, t) | (_, x, t) <- L.sortBy (comparing Down) ixts ]
+  where
+    xs = Misc.nubOrd (F.syms e)
+    ixts = [ (i, x, t) | x <- xs, (t, i) <- Mb.maybeToList (lookupBindEnv x env) ]
 
 -------------------------------------------------------------------------------
 
diff --git a/src/Language/Fixpoint/Horn/Parse.hs b/src/Language/Fixpoint/Horn/Parse.hs
--- a/src/Language/Fixpoint/Horn/Parse.hs
+++ b/src/Language/Fixpoint/Horn/Parse.hs
@@ -84,7 +84,6 @@
   where
     body =  H.CAnd <$> (reserved "and"    *> many hCstrP)
         <|> H.All  <$> (reserved "forall" *> hBindP)      <*> hCstrP
-        <|> H.Any  <$> (reserved "exists" *> hBindP)      <*> hCstrP
         <|> H.Head <$> (reserved "tag"    *> hPredP)      <*> (H.Tag <$> stringLiteral)
         <|> H.Head <$> hPredP                             <*> pure H.NoTag
 
@@ -98,7 +97,7 @@
 -------------------------------------------------------------------------------
 hPredP = parens body
   where
-    body =  H.Var  <$> kvSymP                           <*> some symbolP
+    body =  H.Var  <$> kvSymP                           <*> some predP
         <|> H.PAnd <$> (reserved "and" *> some hPredP)
         <|> H.Reft <$> predP
 
diff --git a/src/Language/Fixpoint/Horn/SMTParse.hs b/src/Language/Fixpoint/Horn/SMTParse.hs
--- a/src/Language/Fixpoint/Horn/SMTParse.hs
+++ b/src/Language/Fixpoint/Horn/SMTParse.hs
@@ -187,7 +187,6 @@
   where
     body =  H.CAnd <$> (reserved "and"    *> many hCstrP)
         <|> H.All  <$> (reserved "forall" *> hBindP)  <*> hCstrP
-        <|> H.Any  <$> (reserved "exists" *> hBindP)  <*> hCstrP
         <|> H.Head <$> (reserved "tag"    *> hPredP)  <*> (H.Tag <$> stringLiteral)
 
 hBindP :: FParser (H.Bind H.Tag)
@@ -200,7 +199,7 @@
 -------------------------------------------------------------------------------
 hPredP = parens body
   where
-    body =  H.Var  <$> kvSymP <*> some symbolP
+    body =  H.Var  <$> kvSymP <*> some exprP
         <|> H.PAnd <$> (reserved "and" *> some hPredP)
         <|> H.Reft <$> exprP
 
@@ -260,6 +259,16 @@
 bindP :: FParser (F.Symbol, F.Sort)
 bindP = sPairP symbolP sortP
 
+-- | We only support lets with a single binder, but we parse this as a "singleton list"
+-- to be forward compatible with multiple binders. Note that the semantics of multiple
+-- binders in smtlib is *simulatenous substitution*, so they cannot be desugared to nested
+-- lets.
+exprBindsP :: FParser (F.Symbol, F.Expr)
+exprBindsP = parens exprBindP
+
+exprBindP :: FParser (F.Symbol, F.Expr)
+exprBindP = sPairP symbolP exprP
+
 defineP :: FParser F.Equation
 defineP = do
   name   <- symbolP
@@ -285,6 +294,7 @@
      <|> try (parens (reserved "func" >> (mkFunc <$> fIntP <*> sMany sortP <*> sortP)))
      <|> try (parens (reserved "list" >> (mkList <$> sortP)))
      <|> parens (F.fAppTC <$> fTyConP <*> many sortP)
+     <|> (F.FNatNum <$> natural)
 
 mkFunc :: Int -> [F.Sort] -> F.Sort -> F.Sort
 mkFunc n ss s = F.mkFFunc n (ss ++ [s])
@@ -304,6 +314,7 @@
 pExprP :: FParser F.Expr
 pExprP
   =   (reserved   "if"     >> (F.EIte   <$> exprP <*> exprP <*> exprP))
+  <|> (reserved   "let"    >> (uncurry F.ELet   <$> exprBindsP <*> exprP))
   <|> (reserved   "lit"    >> (mkLit    <$> stringLiteral <*> sortP))
   <|> (reserved   "cast"   >> (F.ECst   <$> exprP <*> sortP))
   <|> (reserved   "not"    >> (F.PNot   <$> exprP))
@@ -335,7 +346,7 @@
  <|> (sym "-"   >> return F.Minus)
  <|> (sym "*"   >> return F.Times)
  <|> (sym "/"   >> return F.Div)
- <|> (sym "mod" >> return F.Mod)
+ <|> (reserved "mod" >> return F.Mod)
  <|> (sym "*."  >> return F.RTimes)
  <|> (sym "/."  >> return F.RDiv)
 
diff --git a/src/Language/Fixpoint/Horn/Solve.hs b/src/Language/Fixpoint/Horn/Solve.hs
--- a/src/Language/Fixpoint/Horn/Solve.hs
+++ b/src/Language/Fixpoint/Horn/Solve.hs
@@ -30,9 +30,9 @@
 ----------------------------------------------------------------------------------
 solveHorn :: F.Config -> IO ExitCode
 ----------------------------------------------------------------------------------
-solveHorn baseCfg = do
-  q <- parseQuery baseCfg
-
+solveHorn baseCfg0 = do
+  q <- parseQuery baseCfg0
+  let baseCfg = baseCfg0 { F.explicitKvars = True }
   -- If you want to set --eliminate=none, you better make it a pragma
   let cfgElim = if F.eliminate baseCfg == F.None
                   then baseCfg { F.eliminate =  F.Some }
@@ -67,12 +67,12 @@
   saveHornSMT2 cfg q
   saveHornJSON cfg q
 
-saveHornSMT2 :: H.ToHornSMT a => F.Config -> a -> IO ()
+saveHornSMT2 :: F.ToHornSMT a => F.Config -> a -> IO ()
 saveHornSMT2 cfg q = do
   let hq   = F.queryFile Files.HSmt2 cfg
   putStrLn $ "Saving Horn Query: " ++ hq ++ "\n"
   Misc.ensurePath hq
-  writeFile hq $ render ({- F.pprint -} H.toHornSMT q)
+  writeFile hq $ render (F.toHornSMT q)
 
 saveHornJSON :: F.Config -> H.Query H.Tag -> IO ()
 saveHornJSON cfg q = do
diff --git a/src/Language/Fixpoint/Horn/Transformations.hs b/src/Language/Fixpoint/Horn/Transformations.hs
--- a/src/Language/Fixpoint/Horn/Transformations.hs
+++ b/src/Language/Fixpoint/Horn/Transformations.hs
@@ -147,10 +147,12 @@
       (ns, formals, Just defC) -> (ns, formals, defC)
       (_, _, Nothing) -> error $ "pi variable " <> F.showpp k <> " has no defining constraint."
 
+    -- TODO: generalize the `expectVar` business below to handle arbitrary expressions
+    -- https://github.com/ucsd-progsys/liquid-fixpoint/pull/818#discussion_r2643206366
     go :: Cstr a -> ([F.Symbol], [[F.Symbol]], Maybe (Cstr a))
     go (CAnd cs) = (\(as, bs, mcs) -> (concat as, concat bs, cAndMaybes mcs)) $ unzip3 $ go <$> cs
     go (All b@(Bind n _ (Var k' xs) _) c')
-      | k == k' = ([n], [S.toList $ S.fromList xs `S.difference` S.singleton n], Just c')
+      | k == k' = ([n], [S.toList $ S.fromList (expectVar <$> xs) `S.difference` S.singleton n], Just c')
       | otherwise = map3 (fmap (All b)) (go c')
     go (All b c') = map3 (fmap (All b)) (go c')
     go _ = ([], [], Nothing)
@@ -160,6 +162,10 @@
       [] -> Nothing
       cs -> Just $ CAnd cs
 
+expectVar :: F.Expr -> F.Symbol
+expectVar (F.EVar s) = s
+expectVar _ = error "expectVar: expected variable"
+
 map3 :: (c -> d) -> (a, b, c) -> (a, b, d)
 map3 f (x, y, z) = (x, y, f z)
 
@@ -194,7 +200,6 @@
           (eqs' <> eqs'', newVisited')
     go visited (All (Bind _ _ p _) c) = let (eqs, visited') = go visited c in
       (eqs <> collectEqualities p, visited')
-    go _ Any{} = error "exists should not be present in piSols"
 
 ------------------------------------------------------------------------------
 {- | pokec skolemizes the EHC into an HC + side condition
@@ -248,16 +253,7 @@
     go _ (Head c l) = Head c l
     go xs (CAnd c)   = CAnd (go xs <$> c)
     go xs (All b c2) = All b $ go (bSym b : xs) c2
-    go xs (Any b@(Bind x t p ann) c2) = CAnd [All b' $ CAnd [Head p l, go (x:xs) c2], Any b (Head pi' l)]
-      -- TODO: actually use the renamer?
-      where
-        b' = Bind x t pi' ann
-        pi' = piVar x xs
-        l  = cLabel c2
 
-piVar :: F.Symbol -> [F.Symbol] -> Pred
-piVar x xs = Var (piSym x) (x:xs)
-
 piSym :: F.Symbol -> F.Symbol
 piSym s = fromString $ "π" ++ F.symbolString s
 
@@ -332,7 +328,6 @@
   where (nosides, sides) = unzip $ split <$> cs
 split (All b c) = (All b <$> c', All b <$> c'')
     where (c',c'') = split c
-split c@Any{} = (Nothing, Just c)
 split c@Head{} = (Just c, Nothing)
 
 andMaybes :: [Maybe (Cstr a)] -> Maybe (Cstr a)
@@ -407,8 +402,6 @@
   = CAnd $ applyPi k bp <$> cs
 applyPi k bp (All b c)
   = All b (applyPi k bp c)
-applyPi k bp (Any b c)
-  = Any b (applyPi k bp c)
 applyPi k defCstr (Head (Var k' _xs) a)
   | k == k'
   -- what happens when pi's appear inside the defs for other pis?
@@ -475,18 +468,16 @@
   = pure c
 defs x (All _ c) = defs x c
 defs _ (Head _ _) = Nothing
-defs _ (Any _ _) =  error "defs should be run only after noside and poke"
 
 cstrToExpr :: Cstr a -> F.Expr
 cstrToExpr (Head p _) = predToExpr p
 cstrToExpr (CAnd cs) = F.PAnd $ cstrToExpr <$> cs
 cstrToExpr (All (Bind x t p _) c) = F.PAll [(x,t)] $ F.PImp (predToExpr p) $ cstrToExpr c
-cstrToExpr (Any (Bind x t p _) c) = F.PExist [(x,t)] $ F.PImp (predToExpr p) $ cstrToExpr c
 
 predToExpr :: Pred -> F.Expr
 predToExpr (Reft e) = e
 predToExpr (Var k xs) = F.PKVar (F.KV k) (F.Su $ M.fromList su)
-  where su = zip (kargs k) (F.EVar <$> xs)
+  where su = zip (kargs k) xs
 predToExpr (PAnd ps) = F.PAnd $ predToExpr <$> ps
 
 ------------------------------------------------------------------------------
@@ -573,7 +564,6 @@
   foldE v c (CAnd cs) = CAnd <$> mapM (foldE v c) cs
   foldE v c (Head p a) = Head <$> foldE v c p <*> pure a
   foldE v ctx (All (Bind x t p l) c) = All <$> (Bind x t <$> foldE v ctx p <*> pure l) <*> foldE v ctx c
-  foldE v ctx (Any (Bind x t p l) c) = All <$> (Bind x t <$> foldE v ctx p <*> pure l) <*> foldE v ctx c
 
 ------------------------------------------------------------------------------
 -- | Quantifier elimination for use with implicit solver
@@ -700,27 +690,7 @@
 substPiSols piSols (All (Bind x t p l) c)
   | Var k _ <- p = All (Bind x t (M.lookupDefault p k piSols) l) (substPiSols piSols c)
   | otherwise = All (Bind x t p l) (substPiSols piSols c)
-substPiSols piSols (Any (Bind n _ p _) c)
-  | Head (Var pi' _) label <- c, Just sol <- M.lookup pi' piSols =
-    case findSol n sol of
-      Just e -> Head (flatten $ PAnd $ (\predFn -> F.subst1 predFn (n, e)) <$> [p, sol]) label
-      Nothing -> Head (Reft $ F.PAnd []) label
-  | otherwise = error "missing piSol"
 
-findSol :: F.Symbol -> Pred -> Maybe F.Expr
-findSol sym = go
-  where
-    go (Reft e) = findEq e
-    go Var{} = Nothing
-    go (PAnd ps) = case mapMaybe go ps of
-      [] -> Nothing
-      x:_ -> Just x
-
-    findEq (F.PAtom F.Eq left right)
-      | F.EVar y <- left, y == sym = Just right
-      | F.EVar y <- right, y == sym = Just left
-    findEq _ = Nothing
-
 ------------------------------------------------------------------------------
 -- | uniq makes sure each binder has a unique name
 ------------------------------------------------------------------------------
@@ -737,11 +707,6 @@
     c2' <- uniq' c2
     modify $ popName x
     pure $ All b' c2'
-uniq' (Any b@(Bind x _ _ _) c2) = do
-    b' <- uBind b
-    c2' <- uniq' c2
-    modify $ popName x
-    pure $ Any b' c2'
 
 popName :: F.Symbol -> RenameMap -> RenameMap
 popName x m = M.adjust (second tail) x m
@@ -764,18 +729,17 @@
 
 rename :: Pred -> RenameMap -> Pred
 rename e m = substPred (M.mapMaybeWithKey (\k v -> case v of
-                                              (_, n:_) -> Just $ numSym k n
+                                              (_, n:_) -> Just $ F.EVar $ numSym k n
                                               _ -> Nothing) m) e
 
 numSym :: IsString a => F.Symbol -> Integer -> a
 numSym s 0 = fromString $ F.symbolString s
 numSym s i = fromString $ F.symbolString s ++ "#" ++ show i
 
-substPred :: M.HashMap F.Symbol F.Symbol -> Pred -> Pred
-substPred su (Reft e) = Reft $ F.subst (F.Su $ F.EVar <$> su) e
+substPred :: M.HashMap F.Symbol F.Expr -> Pred -> Pred
+substPred su (Reft e) = Reft $ F.subst (F.Su su) e
 substPred su (PAnd ps) = PAnd $ substPred su <$> ps
-substPred su (Var k xs) = Var k $ upd <$> xs
-  where upd x = M.lookupDefault x x su
+substPred su (Var k xs) = Var k $ F.subst (F.Su su) <$> xs
 
 ------------------------------------------------------------------------------
 -- | elim solves all of the KVars in a Cstr (assuming no cycles...)
@@ -814,8 +778,6 @@
     go (Head _ l) = Left l
     go c@(All (Bind _ _ p _) c') =
       if k `S.member` pKVars p then Right c else go c'
-    go Any{} = error "any should not appear after poke"
-
     -- if kvar doesn't appear, then just return the left
     -- if kvar appears in one child, that is the lca
     -- but if kvar appear in multiple chlidren, this is the lca
@@ -850,10 +812,9 @@
 sol1 k (CAnd cs) = sol1 k =<< cs
 sol1 k (All b c) = first (b :) <$> sol1 k c
 sol1 k (Head (Var k' ys) _) | k == k'
-  = [([], zipWith (F.PAtom F.Eq) (F.EVar <$> xs) (F.EVar <$> ys))]
+  = [([], zipWith (F.PAtom F.Eq) (F.EVar <$> xs) ys)]
   where xs = zipWith const (kargs k) ys
 sol1 _ (Head _ _) = []
-sol1 _ (Any _ _) =  error "ebinds don't work with old elim"
 
 kargs :: F.Symbol -> [F.Symbol]
 kargs k = fromString . (("κarg$" ++ F.symbolString k ++ "#") ++) . show <$> [1 :: Integer ..]
@@ -871,9 +832,9 @@
     Right _ -> All (Bind sym' sort' p l) (doelim sym bss cstr)
     Left (kvars, preds) -> demorgan sym' sort' l kvars preds (doelim sym bss cstr) bss
   where
-    demorgan :: F.Symbol -> F.Sort -> a -> [(F.Symbol, [F.Symbol])] -> [Pred] -> Cstr a -> [([Bind a], [F.Expr])] -> Cstr a
+    demorgan :: F.Symbol -> F.Sort -> a -> [(F.Symbol, [F.Expr])] -> [Pred] -> Cstr a -> [([Bind a], [F.Expr])] -> Cstr a
     demorgan x t ann kvars preds cstr' bindExprs = mkAnd $ cubeSol <$> bindExprs
-      where su = F.Su $ M.fromList $ concatMap (\(k, xs) -> zip (kargs k) (F.EVar <$> xs)) kvars
+      where su = F.Su $ M.fromList $ concatMap (\(k, xs) -> zip (kargs k) xs) kvars
             mkAnd [c] = c
             mkAnd cs = CAnd cs
             cubeSol (b:bs, eqs) = All b $ cubeSol (bs, eqs)
@@ -883,14 +844,9 @@
   = Head (Reft F.PTrue) a
 doelim _ _ (Head p a) = Head p a
 
-doelim k bss (Any (Bind x t p l) c) =
-  case findKVarInGuard k p of
-    Right _ -> Any (Bind x t p l) (doelim k bss c)
-    Left (_, rights') -> Any (Bind x t (PAnd rights') l) (doelim k bss c) -- TODO: for now we set the kvar to true. not sure if this is correct
-
 -- If k is in the guard then returns a Left list of that k and the remaining preds in the guard
 -- If k is not in the guard returns a Right of the pred
-findKVarInGuard :: F.Symbol -> Pred -> Either ([(F.Symbol, [F.Symbol])], [Pred]) Pred
+findKVarInGuard :: F.Symbol -> Pred -> Either ([(F.Symbol, [F.Expr])], [Pred]) Pred
 findKVarInGuard k (PAnd ps) =
   if null lefts
     then Right (PAnd ps) -- kvar not found
@@ -923,7 +879,6 @@
 boundKvars (Head p _)           = pKVars p
 boundKvars (CAnd c)             = mconcat $ boundKvars <$> c
 boundKvars (All (Bind _ _ p _) c) = pKVars p <> boundKvars c
-boundKvars (Any (Bind _ _ p _) c) = pKVars p <> boundKvars c
 
 pKVars :: Pred -> S.Set F.Symbol
 pKVars (Var k _) = S.singleton k
@@ -935,7 +890,6 @@
 isNNF Head{} = True
 isNNF (CAnd cs) = all isNNF cs
 isNNF (All _ c) = isNNF c
-isNNF Any{} = False
 
 calculateCuts :: (F.Fixpoint a, F.PPrint a) => F.Config -> Query a -> Cstr a -> S.Set F.Symbol
 calculateCuts cfg q@(Query {}) nnf = convert $ FG.depCuts deps
@@ -949,7 +903,6 @@
 forgetPiVars pis (All (Bind x t p l) c)
   | Var k _ <- p, k `S.member` pis = All (Bind x t (PAnd []) l) $ forgetPiVars pis c
   | otherwise = All (Bind x t p l) $ forgetPiVars pis c
-forgetPiVars _ Any{} = error "shouldn't be present"
 
 -----------------------------------------------------------------------------------
 -- | Cleanup Horn Constraint
@@ -997,7 +950,6 @@
     go (Head p a)          = Just $ Head (flatten p) a
     go (CAnd cs)           = mk . concatMap splitAnd $ mapMaybe flattenCstr cs
     go (All (Bind x t p l) c) = All (Bind x t (flatten p) l) <$> go c
-    go (Any (Bind x t p l) c) = Any (Bind x t (flatten p) l) <$> go c
 
     mk []  = Nothing
     mk [c] = Just c
@@ -1062,7 +1014,6 @@
         splitP kacc pacc [] = (kacc,pacc)
 hornify (Head h a) = Head h a
 hornify (All b c) = All b $ hornify c
-hornify (Any b c) = Any b $ hornify c
 hornify (CAnd cs) = CAnd $ hornify <$> cs
 
 removeDuplicateBinders :: Cstr a -> Cstr a
@@ -1071,7 +1022,6 @@
     go _ c@Head{} = c
     go xs (CAnd cs) = CAnd $ go xs <$> cs
     go xs (All b@(Bind x _ _ _) c) = if x `S.member` xs then go xs c else All b $ go (S.insert x xs) c
-    go xs (Any b c) = Any b $ go xs c
 
 pruneTauts :: Cstr a -> Cstr a
 pruneTauts = fromMaybe (CAnd []) . go
@@ -1084,7 +1034,6 @@
     go (All b c) = do
       c' <- go c
       pure (All b c')
-    go c@Any{} = Just c
 
     goP (Reft e) = if F.isTautoPred e then Nothing else Just $ Reft e
     goP p@Var{} = Just p
diff --git a/src/Language/Fixpoint/Horn/Types.hs b/src/Language/Fixpoint/Horn/Types.hs
--- a/src/Language/Fixpoint/Horn/Types.hs
+++ b/src/Language/Fixpoint/Horn/Types.hs
@@ -10,7 +10,6 @@
 {-# LANGUAGE DeriveTraversable          #-}
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE ViewPatterns #-}
 
 module Language.Fixpoint.Horn.Types
   ( -- * Horn Constraints and their components
@@ -34,8 +33,6 @@
     -- * extract qualifiers
   , quals
 
-    -- * SMTLIB style render
-  , ToHornSMT (..)
   )
   where
 
@@ -63,19 +60,27 @@
   }
   deriving (Eq, Ord, Data, Typeable, Generic, Functor, ToJSON, FromJSON)
 
+
+
 -------------------------------------------------------------------------------
 -- | @HPred@ is a Horn predicate that appears as LHS (body) or RHS (head) of constraints
 -------------------------------------------------------------------------------
 data Pred
   = Reft  !F.Expr                               -- ^ r
-  | Var   !F.Symbol ![F.Symbol]                 -- ^ $k(y1..yn)
+  | Var   !F.Symbol ![F.Expr]                   -- ^ $k(y1..yn)
   | PAnd  ![Pred]                               -- ^ p1 /\ .../\ pn
   deriving (Data, Typeable, Generic, Eq, ToJSON, FromJSON)
 
+instance F.ToHornSMT (Var a) where
+  toHornSMT (HVar k ts _) = P.parens ("var" P.<+> "$" P.<-> F.pprint k P.<+> F.toHornSMT ts)
+instance F.ToHornSMT Pred where
+  toHornSMT (Reft p)   = P.parens (F.toHornSMT p)
+  toHornSMT (Var k xs) = F.toHornMany (F.toHornSMT (F.KV k) : (F.toHornSMT <$> xs))
+  toHornSMT (PAnd ps)  = F.toHornMany ("and" : (F.toHornSMT <$> ps))
 
 instance F.Subable Pred where
   syms (Reft e)   = F.syms e
-  syms (Var _ xs) = xs
+  syms (Var _ xs) = concatMap F.syms xs
   syms (PAnd ps)  = concatMap F.syms ps
 
   substa f (Reft e)   = Reft  (F.substa f      e)
@@ -105,7 +110,6 @@
     go env v (Head p _)  = predQuals env v p
     go env v (CAnd   cs) = concatMap (go env v) cs
     go env _ (All  b c)  = bindQuals env b c
-    go env _ (Any  b c)  = bindQuals env b c
 
 bindQuals  :: F.SEnv F.Sort -> Bind a -> Cstr a -> [F.Qualifier]
 bindQuals env b c = predQuals env' bx (bPred b) ++ cstrQuals env' bx c
@@ -155,6 +159,9 @@
   }
   deriving (Data, Typeable, Generic, Functor, Eq, ToJSON, FromJSON)
 
+instance F.ToHornSMT (Bind a) where
+  toHornSMT (Bind x t p _) = P.parens (F.toHornSMT (x, t) P.<+> F.toHornSMT p)
+
 instance F.Subable (Bind a) where
     syms     (Bind x _ p _) = x : F.syms p
     substa f (Bind v t p a) = Bind (f v) t (F.substa f p) a
@@ -167,9 +174,17 @@
   = Head  !Pred !a                  -- ^ p
   | CAnd  ![Cstr a]                 -- ^ c1 /\ ... /\ cn
   | All   !(Bind a)  !(Cstr a)      -- ^ \all x:t. p => c
-  | Any   !(Bind a)  !(Cstr a)      -- ^ \exi x:t. p /\ c or is it \exi x:t. p => c?
   deriving (Data, Typeable, Generic, Functor, Eq, ToJSON, FromJSON)
 
+instance F.ToHornSMT (Cstr a) where
+  toHornSMT = toHornCstr
+
+toHornCstr :: Cstr a -> P.Doc
+toHornCstr (Head p _) = F.toHornSMT p
+toHornCstr (CAnd cs)  = F.toHornAnd toHornCstr cs
+toHornCstr (All b c)  = P.parens (P.vcat ["forall" P.<+> F.toHornSMT b
+                                         , P.nest 1 (toHornCstr c)])
+
 cLabel :: Cstr a -> a
 cLabel cstr = case go cstr of
   [] -> F.panic "everything is true!!!"
@@ -178,13 +193,11 @@
     go (Head _ l)   = [l]
     go (CAnd cs)    = mconcat $ go <$> cs
     go (All _ c)    = go c
-    go (Any _ c)    = go c
 
 -- We want all valid constraints to start with a binding at the top
 
 okCstr :: Cstr a -> Bool
 okCstr All {} = True
-okCstr Any {} = True
 okCstr _      = False
 
 
@@ -236,7 +249,9 @@
   parseJSON (String t) = pure (Tag (T.unpack t))
   parseJSON invalid    = prependFailure "parsing `Tag` failed, " (typeMismatch "Object" invalid)
 
-
+instance F.ToHornSMT Tag where
+  toHornSMT NoTag   = mempty
+  toHornSMT (Tag s) = P.text s
 
 
 
@@ -284,13 +299,12 @@
 
 instance Show Pred where
   show (Reft p)   = parens $ F.showpp p
-  show (Var x xs) = parens $ "$" ++ unwords (F.symbolString <$> x:xs)
+  show (Var x xs) = parens $ "$" ++ unwords (F.symbolString x : (parens . F.showpp <$> xs))
   show (PAnd ps)  = parens $ unwords $ "and": map show ps
 
 instance Show (Cstr a) where
   show (Head p _) = parens $ show p
   show (All b c)  = parens $ unwords ["forall" , show b , show c]
-  show (Any b c)  = parens $ unwords ["exists" , show b , show c]
   show (CAnd cs)  = parens $ unwords $ "and" : map show cs
 
 instance Show (Bind a) where
@@ -301,7 +315,7 @@
 
 instance F.PPrint Pred where
   pprintPrec k t (Reft p)   = P.parens $ F.pprintPrec k t p
-  pprintPrec _ _ (Var x xs) = P.parens $ P.ptext "$" <> P.hsep (P.ptext . F.symbolString <$> x:xs)
+  pprintPrec k t (Var x xs) = P.parens $ P.ptext "$" <> P.hsep (P.ptext (F.symbolString x) : (P.parens. F.pprintPrec k t <$> xs))
   pprintPrec k t (PAnd ps)  = P.parens $ P.vcat $ P.ptext "and" : map (F.pprintPrec (k+2) t) ps
 
 instance F.PPrint (Cstr a) where
@@ -309,170 +323,25 @@
   pprintPrec k t (All b c)  = P.parens $ P.vcat [ P.ptext "forall" P.<+> F.pprintPrec (k+2) t b
                                                 , F.pprintPrec (k+1) t c
                                                 ]
-  pprintPrec k t (Any b c)  = P.parens $ P.vcat [P.ptext "exists" P.<+> F.pprintPrec (k+2) t b
-                                                , F.pprintPrec (k+1) t c
-                                                ]
   pprintPrec k t (CAnd cs) = P.parens $ P.vcat  $ P.ptext "and" : map (F.pprintPrec (k+2) t) cs
 
 instance F.PPrint (Bind a) where
   pprintPrec _ _ b = P.ptext $ show b
 
-
------------------------------------------------------------------------------------------------------------------
--- Human readable but robustly parseable SMT-LIB format pretty printer
------------------------------------------------------------------------------------------------------------------
-class ToHornSMT a where
-  toHornSMT :: a -> P.Doc
-
-instance ToHornSMT Tag where
-  toHornSMT NoTag   = mempty
-  toHornSMT (Tag s) = P.text s
-
-instance ToHornSMT F.Symbol where
-  toHornSMT s = F.pprint s
-
-instance ToHornSMT (Var a) where
-  toHornSMT (HVar k ts _) = P.parens ("var" P.<+> "$" P.<-> F.pprint k P.<+> toHornSMT ts)
-
-instance ToHornSMT (Query a) where
+instance F.ToHornSMT (Query a) where
   toHornSMT q = P.vcat $ L.intersperse " "
     [ P.vcat   (toHornOpt <$> qOpts q)
     , P.vcat   (toHornNum <$> qNums q)
-    , P.vcat   (toHornSMT <$> qQuals q)
-    , P.vcat   (toHornSMT <$> qVars q)
+    , P.vcat   (F.toHornSMT <$> qQuals q)
+    , P.vcat   (F.toHornSMT <$> qVars q)
     , P.vcat   [toHornCon x t | (x, t) <- M.toList (qCon q)]
-    , P.vcat   (eqnToHornSMT "define"     <$> qEqns q)
-    , P.vcat   (eqnToHornSMT "define_fun" <$> qDefs q)
-    , P.vcat   (toHornSMT <$> qData q)
-    , P.vcat   (toHornSMT <$> qMats q)
-    , P.parens (P.vcat ["constraint", P.nest 1 (toHornSMT (qCstr q))])
+    , P.vcat   (F.eqnToHornSMT "define"     <$> qEqns q)
+    , P.vcat   (F.eqnToHornSMT "define_fun" <$> qDefs q)
+    , P.vcat   (F.toHornSMT <$> qData q)
+    , P.vcat   (F.toHornSMT <$> qMats q)
+    , P.parens (P.vcat ["constraint", P.nest 1 (F.toHornSMT (qCstr q))])
     ]
     where
-      toHornNum x   = toHornMany ["numeric", toHornSMT x]
-      toHornOpt str = toHornMany ["fixpoint", P.text ("\"" ++ str ++ "\"")]
-      toHornCon x t = toHornMany ["constant", toHornSMT x, toHornSMT t]
-
-instance ToHornSMT F.Rewrite where
-  toHornSMT (F.SMeasure f d xs e) =  P.parens ("match" P.<+> toHornSMT f P.<+> toHornSMT (d:xs) P.<+> toHornSMT e)
-
-instance ToHornSMT F.Qualifier where
-  toHornSMT (F.Q n xts p _) =  P.parens ("qualif" P.<+> F.pprint n P.<+> toHornSMT xts P.<+> toHornSMT p)
-
-instance ToHornSMT F.QualParam where
-  toHornSMT qp = toHornSMT (F.qpSym qp, F.qpSort qp)
-
-instance ToHornSMT a => ToHornSMT (F.Symbol, a) where
-  toHornSMT (x, t) = P.parens $ F.pprint x P.<+> toHornSMT t
-
-instance ToHornSMT a => ToHornSMT [a] where
-  toHornSMT = toHornMany . fmap toHornSMT
-
-toHornMany :: [P.Doc] -> P.Doc
-toHornMany = P.parens . P.sep -- Misc.intersperse " "
-
-toHornAnd :: (a -> P.Doc) -> [a] -> P.Doc
-toHornAnd f xs = P.parens (P.vcat ("and" : (P.nest 1 . f <$> xs)))
-
-eqnToHornSMT :: P.Doc -> F.Equation -> P.Doc
-eqnToHornSMT keyword (F.Equ f xs e s _) = P.parens (keyword P.<+> F.pprint f P.<+> toHornSMT xs P.<+> toHornSMT s P.<+> toHornSMT e)
-
-
-instance ToHornSMT F.DataDecl where
-  toHornSMT (F.DDecl tc n ctors) =
-    P.parens $ P.vcat [
-      P.text "datatype" P.<+> P.parens (toHornSMT tc P.<+> P.int n)
-    , P.parens (P.vcat (toHornSMT <$> ctors))
-    ]
-
-instance ToHornSMT F.FTycon where
-  toHornSMT c
-    | c == F.listFTyCon = "list"
-    | otherwise         = toHornSMT (F.symbol c)
-
-instance ToHornSMT a => ToHornSMT (F.Located a) where
-  toHornSMT = toHornSMT . F.val
-instance ToHornSMT F.DataCtor where
-  toHornSMT (F.DCtor x flds) = P.parens (toHornSMT x P.<+> toHornSMT flds)
-
-instance ToHornSMT F.DataField where
-  toHornSMT (F.DField x t) = toHornSMT (F.val x, t)
-
-instance ToHornSMT F.Sort where
-  toHornSMT = toHornSort
-
-toHornSort :: F.Sort -> P.Doc
-toHornSort (F.FVar i)     = "@" P.<-> P.parens (P.int i)
-toHornSort F.FInt         = "Int"
-toHornSort F.FReal        = "Real"
-toHornSort F.FFrac        = "Frac"
-toHornSort (F.FObj x)     = toHornSMT x -- P.parens ("obj" P.<+> toHornSMT x)
-toHornSort F.FNum         = "num"
-toHornSort t@(F.FAbs _ _) = toHornAbsApp t
-toHornSort t@(F.FFunc _ _)= toHornAbsApp t
-toHornSort (F.FTC c)      = toHornSMT c
-toHornSort t@(F.FApp _ _) = toHornFApp (F.unFApp t)
-
-toHornAbsApp :: F.Sort -> P.Doc
-toHornAbsApp (F.functionSort -> Just (vs, ss, s)) = P.parens ("func" P.<+> P.int (length vs) P.<+> toHornSMT ss P.<+> toHornSMT s )
-toHornAbsApp _                                    = error "Unexpected nothing function sort"
-
-toHornFApp     :: [F.Sort] -> P.Doc
-toHornFApp [t] = toHornSMT t
-toHornFApp ts  = toHornSMT ts
-
-instance ToHornSMT F.Subst where
-  toHornSMT (F.Su m) = toHornSMT (Misc.hashMapToAscList m)
-
-instance ToHornSMT (Bind a) where
-  toHornSMT (Bind x t p _) = P.parens (toHornSMT (x, t) P.<+> toHornSMT p)
-
-instance ToHornSMT Pred where
-  toHornSMT (Reft p)   = P.parens (toHornSMT p)
-  toHornSMT (Var k xs) = toHornMany (toHornSMT (F.KV k) : (toHornSMT <$> xs))
-  toHornSMT (PAnd ps)  = toHornMany ("and" : (toHornSMT <$> ps))
-
-instance ToHornSMT F.KVar where
-  toHornSMT (F.KV k) = "$" P.<-> toHornSMT k
-
-instance ToHornSMT F.Expr where
-  toHornSMT = toHornExpr
-
-toHornExpr :: F.Expr -> P.Doc
-toHornExpr (F.ESym c)        = F.pprint c
-toHornExpr (F.ECon c)        = F.pprint c
-toHornExpr (F.EVar s)        = toHornSMT s
-toHornExpr (F.ENeg e)        = P.parens ("-" P.<+> toHornExpr e)
-toHornExpr (F.EApp e1 e2)    = toHornSMT [e1, e2]
-toHornExpr (F.EBin o e1 e2)  = toHornOp   (F.toFix o) [e1, e2]
-toHornExpr (F.EIte e1 e2 e3) = toHornOp "if"  [e1, e2, e3]
-toHornExpr (F.ECst e t)      = toHornMany ["cast", toHornSMT e, toHornSMT t]
-toHornExpr (F.PNot p)        = toHornOp "not"  [p]
-toHornExpr (F.PImp e1 e2)    = toHornOp "=>"   [e1, e2]
-toHornExpr (F.PIff e1 e2)    = toHornOp "<=>"  [e1, e2]
-toHornExpr e@F.PTrue         = F.pprint e
-toHornExpr e@F.PFalse        = F.pprint e
-toHornExpr (F.PAnd es)       = toHornOp "and" es
-toHornExpr (F.POr  es)       = toHornOp "or"  es
-toHornExpr (F.PAtom r e1 e2) = toHornOp (F.toFix r) [e1, e2]
-toHornExpr (F.PAll xts p)    = toHornMany ["forall", toHornSMT xts, toHornSMT p]
-toHornExpr (F.PExist xts p)  = toHornMany ["exists", toHornSMT xts, toHornSMT p]
-toHornExpr (F.ELam b e)      = toHornMany ["lam", toHornSMT b, toHornSMT e]
-toHornExpr (F.ECoerc a t e)  = toHornMany ["coerce", toHornSMT a, toHornSMT t, toHornSMT e]
-toHornExpr (F.PKVar k su)    = toHornMany [toHornSMT k, toHornSMT su]
-toHornExpr (F.ETApp e s)     = toHornMany ["ETApp" , toHornSMT e, toHornSMT s]
-toHornExpr (F.ETAbs e s)     = toHornMany ["ETAbs" , toHornSMT e, toHornSMT s]
-toHornExpr (F.PGrad k _ _ e) = toHornMany ["&&", toHornSMT e, toHornSMT k]
-
-toHornOp :: ToHornSMT a => P.Doc -> [a] -> P.Doc
-toHornOp op es = toHornMany (op : (toHornSMT <$> es))
-
-instance ToHornSMT (Cstr a) where
-  toHornSMT = toHornCstr
-
-toHornCstr :: Cstr a -> P.Doc
-toHornCstr (Head p _) = toHornSMT p
-toHornCstr (CAnd cs)  = toHornAnd toHornCstr cs
-toHornCstr (All b c)  = P.parens (P.vcat ["forall" P.<+> toHornSMT b
-                                         , P.nest 1 (toHornCstr c)])
-toHornCstr (Any b c)  = P.parens (P.vcat ["exists" P.<+> toHornSMT b
-                                         , P.nest 1 (toHornCstr c)])
+      toHornNum x   = F.toHornMany ["numeric", F.toHornSMT x]
+      toHornOpt str = F.toHornMany ["fixpoint", P.text ("\"" ++ str ++ "\"")]
+      toHornCon x t = F.toHornMany ["constant", F.toHornSMT x, F.toHornSMT t]
diff --git a/src/Language/Fixpoint/Minimize.hs b/src/Language/Fixpoint/Minimize.hs
--- a/src/Language/Fixpoint/Minimize.hs
+++ b/src/Language/Fixpoint/Minimize.hs
@@ -21,6 +21,11 @@
 import           Control.DeepSeq
 
 ---------------------------------------------------------------------------
+-- | Top level Solvers ----------------------------------------------------
+---------------------------------------------------------------------------
+type Solver a = Config -> FInfo a -> IO (Result (Integer, a))
+
+---------------------------------------------------------------------------
 -- polymorphic delta debugging implementation
 ---------------------------------------------------------------------------
 deltaDebug :: Bool -> Oracle a c -> Config -> Solver a -> FInfo a -> [c] -> [c] -> IO [c]
diff --git a/src/Language/Fixpoint/Parse.hs b/src/Language/Fixpoint/Parse.hs
--- a/src/Language/Fixpoint/Parse.hs
+++ b/src/Language/Fixpoint/Parse.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP                       #-}
 {-# LANGUAGE FlexibleContexts          #-}
 {-# LANGUAGE FlexibleInstances         #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
@@ -92,6 +91,7 @@
 
   -- * Parsing Function
   , doParse'
+  , doParse''
   , parseTest'
   , parseFromFile
   , parseFromStdIn
@@ -123,9 +123,7 @@
 import qualified Data.IntMap.Strict          as IM
 import qualified Data.HashMap.Strict         as M
 import qualified Data.HashSet                as S
-#if !MIN_VERSION_base(4,20,0)
-import           Data.List                   (foldl')
-#endif
+import qualified Data.List                   as List
 import           Data.List.NonEmpty          (NonEmpty(..))
 import qualified Data.Text                   as T
 import qualified Data.Text.IO                as T
@@ -231,6 +229,7 @@
                      , supply      :: !Integer
                      , layoutStack :: LayoutStack
                      , numTyCons   :: !(S.HashSet Symbol)
+                     , allowExists :: !Bool
                      }
 type PState = PStateV Symbol
 
@@ -476,6 +475,7 @@
   , "func"
   , "autorewrite"
   , "rewrite"
+  , "lit"
 
   -- reserved words used in liquid haskell
   , "forall"
@@ -537,7 +537,7 @@
   , "->"
   , ":="
   , "&", "^", "<<", ">>", "--"
-  , "?", "Bexp"
+  , "Bexp"
   , "'"
   , "_|_"
   , "|"
@@ -677,7 +677,7 @@
 -- * an error message to display if the final check fails.
 --
 condIdR :: ParserV v Char -> (Char -> Bool) -> (String -> Bool) -> String -> ParserV v Symbol
-condIdR initial okChars condition msg = do
+condIdR initial okChars condition msg = try $ do
   s <- (:) <$> initial <*> takeWhileP Nothing okChars
   if condition s
     then pure (symbol s)
@@ -789,28 +789,17 @@
 -- This parser is reused by Liquid Haskell.
 --
 expr0P :: ParseableV v => ParserV v (ExprV v)
-expr0P
-  =  trueP -- constant "true"
- <|> falseP -- constant "false"
- <|> (reservedOp "?" *> predP)
- <|> fastIfP EIte exprP -- "if-then-else", starts with "if"
- <|> coerceP exprP -- coercion, starts with "coerce"
- <|> (ESym <$> symconstP) -- string literal, starts with double-quote
- <|> (ECon <$> constantP) -- numeric literal, starts with a digit
- <|> (reservedOp "_|_" >> return EBot) -- constant bottom, equivalent to "false"
- <|> lamP -- lambda abstraction, starts with backslash
- <|> try tupleP -- tuple expressions, starts with "("
- <|> try (parens exprP) -- parenthesised expression, starts with "("
- <|> try (parens exprCastP) -- explicit type annotation, starts with "(", TODO: should be an operator rather than require parentheses?
- <|> EVar <$> parseV  -- identifier, starts with any letter or underscore
- <|> try (located (brackets (pure ())) >>= emptyListP) -- empty list, start with "["
- <|> try (located (brackets exprP) >>= singletonListP) -- singleton list, starts with "["
- --
- -- Note:
- --
- -- In the parsers above, it is important that *all* parsers starting with "("
- -- are prefixed with "try". This is because expr0P itself is chained with
- -- additional parsers in funAppP ...
+expr0P =
+        botP
+    <|> try (reserved "not") *> fmap PNot appliableExprP -- built-in prefix not
+    <|> funAppP
+    <|> existP
+    <|> fastIfP EIte exprP -- "if-then-else", starts with "if"
+    <|> try (coerceP exprP) -- coercion, starts with "coerce"
+    <|> litP
+    <|> lamP -- lambda abstraction, starts with backslash
+    <|> (reservedOp "&&" >> PAnd <$> predsP) -- built-in prefix and
+    <|> (reservedOp "||" >> POr  <$> predsP) -- built-in prefix or
 
 emptyListP :: Located () -> ParserV v (ExprV v)
 emptyListP lx = do
@@ -836,7 +825,7 @@
 fastIfP :: ParseableV v => (ExprV v -> a -> a -> a) -> ParserV v a -> ParserV v a
 fastIfP f bodyP
   = do reserved "if"
-       p <- predP
+       p <- exprP
        reserved "then"
        b1 <- bodyP
        reserved "else"
@@ -848,35 +837,12 @@
   (s, t) <- parens (pairP sortP (reservedOp "~") sortP)
   ECoerc s t <$> p
 
-
-
-{-
-qmIfP f bodyP
-  = parens $ do
-      p  <- predP
-      reserved "?"
-      b1 <- bodyP
-      colon
-      b2 <- bodyP
-      return $ f p b1 b2
--}
-
--- | Parser for atomic expressions plus function applications.
---
--- Base parser used in 'exprP' which adds in other operators.
---
-expr1P :: ParseableV v => ParserV v (ExprV v)
-expr1P
-  =  try funAppP
- <|> expr0P
-
 -- | Expressions
 
 exprP :: ParseableV v => ParserV v (ExprV v)
-exprP =
-  do
+exprP = do
     table <- gets fixityTable
-    makeExprParser expr1P (flattenOpTable table)
+    makeExprParser expr0P (flattenOpTable table)
 
 data Assoc = AssocNone | AssocLeft | AssocRight
 
@@ -968,7 +934,7 @@
 
 -- | Built-in operator table, parameterised over the composition function.
 bops :: forall v. ParseableV v => Maybe (Located String -> ExprV v) -> OpTable v
-bops cmpFun = foldl' (flip addOperator) initOpTable builtinOps
+bops cmpFun = List.foldl' (flip addOperator) initOpTable builtinOps
   where
     -- Built-in Haskell operators, see https://www.haskell.org/onlinereport/decls.html#fixity
     builtinOps :: [Fixity v]
@@ -980,37 +946,55 @@
                  , FInfix  (Just 5) "mod" (Just $ const $ EBin Mod)   AssocLeft -- Haskell gives mod 7
                  , FInfix  (Just 9) "."   applyCompose        AssocRight
                 --  --
-                --  , FInfix  (Just 4) "<"   (Just $ PAtom Lt)  AssocNone
-                --  , FInfix  (Just 4) "=="  (Just $ PAtom Eq)  AssocNone
-                --  , FInfix  (Just 4) "="   (Just $ PAtom Eq)  AssocNone
-                --  , FInfix  (Just 4) "~~"  (Just $ PAtom Ueq) AssocNone
-                --  , FInfix  (Just 4) "!="  (Just $ PAtom Ne)  AssocNone
-                --  , FInfix  (Just 4) "/="  (Just $ PAtom Ne)  AssocNone
-                --  , FInfix  (Just 4) "!~"  (Just $ PAtom Une) AssocNone
-                --  , FInfix  (Just 4) "<"   (Just $ PAtom Lt)  AssocNone
-                --  , FInfix  (Just 4) "<="  (Just $ PAtom Le)  AssocNone
-                --  , FInfix  (Just 4) ">"   (Just $ PAtom Gt)  AssocNone
-                --  , FInfix  (Just 4) ">="  (Just $ PAtom Ge)  AssocNone
+                 , FInfix  (Just 4) "=="  (Just $ const $ PAtom Eq)  AssocNone
+                 , FInfix  (Just 4) "="   (Just $ const $ PAtom Eq)  AssocNone
+                 , FInfix  (Just 4) "~~"  (Just $ const $ PAtom Ueq) AssocNone
+                 , FInfix  (Just 4) "!="  (Just $ const $ PAtom Ne)  AssocNone
+                 , FInfix  (Just 4) "/="  (Just $ const $ PAtom Ne)  AssocNone
+                 , FInfix  (Just 4) "!~"  (Just $ const $ PAtom Une) AssocNone
+                 , FInfix  (Just 4) "<"   (Just $ const $ PAtom Lt)  AssocNone
+                 , FInfix  (Just 4) "<="  (Just $ const $ PAtom Le)  AssocNone
+                 , FInfix  (Just 4) ">"   (Just $ const $ PAtom Gt)  AssocNone
+                 , FInfix  (Just 4) ">="  (Just $ const $ PAtom Ge)  AssocNone
+
+                 , FInfix  (Just 3) "&&"  (Just $ const $ \x y -> PAnd [x,y]) AssocRight
+                 , FInfix  (Just 2) "||"  (Just $ const $ \x y -> POr [x,y]) AssocRight
+                 , FInfix  (Just 1) "=>"  (Just $ const PImp) AssocRight
+                 , FInfix  (Just 1) "==>" (Just $ const PImp) AssocRight
+                 , FInfix  (Just 1) "<=>" (Just $ const PIff) AssocRight
+                 , FPrefix (Just 9) "~"   (Just $ const PNot)
                  ]
+
     applyCompose :: Maybe (Located String -> ExprV v -> ExprV v -> ExprV v)
     applyCompose = (\f lop x y -> f lop `eApps` [x,y]) <$> cmpFun
 
 -- | Parser for function applications.
---
--- Andres, TODO: Why is this so complicated?
---
 funAppP :: ParseableV v => ParserV v (ExprV v)
-funAppP      =  litP <|> exprFunP <|> simpleAppP
-  where
-    exprFunP = eApps <$> funSymbolP <*> funRhsP
-    funRhsP  =  some expr0P
-            <|> parens innerP
-    innerP   = brackets (sepBy exprP semi)
+funAppP = do
+    f <- appliableExprP
+    foldl EApp f <$> (<|>)
+      (try $ parens $ brackets $ sepBy exprP semi)  -- special form: f ([e1; e2; ...; en])
+      (many appliableExprP)                   -- normal function application: f e1 e2 ... en
 
-    -- TODO:AZ the parens here should be superfluous, but it hits an infinite loop if removed
-    simpleAppP     = EApp <$> parens exprP <*> parens exprP
-    funSymbolP     = EVar <$> parseV
+appliableExprP :: ParseableV v => ParserV v (ExprV v)
+appliableExprP =
+       trueP -- constant "true"
+   <|> falseP -- constant "false"
+   <|> (ESym <$> symconstP) -- string literal, starts with double-quote
+   <|> (ECon <$> constantP) -- numeric literal, starts with a digit
+   <|> botP
+   <|> try tupleP -- tuple expressions, starts with "("
+   <|> try (parens exprP) -- parenthesised expression, starts with "("
+   <|> try (parens exprCastP) -- explicit type annotation, starts with "(", TODO: should be an operator rather than require parentheses?
+   <|> EVar <$> parseV  -- identifier, starts with any letter or underscore
+   <|> try (located (brackets (pure ())) >>= emptyListP) -- empty list, start with "["
+   <|> try (located (brackets exprP) >>= singletonListP) -- singleton list, starts with "["
+   <|> kvarPredP
 
+-- | constant bottom, equivalent to "false"
+botP :: ParserV v (ExprV v)
+botP = reservedOp "_|_" >> return EBot
+
 -- | Parser for tuple expressions (two or more components).
 tupleP :: ParseableV v => ParserV v (ExprV v)
 tupleP = do
@@ -1035,9 +1019,10 @@
        t <- sortP
        reservedOp "->"
        ELam (x, t) <$> exprP
+      <?> "lambda abstraction"
 
 varSortP :: ParserV v Sort
-varSortP  = FVar  <$> parens intP
+varSortP  = FVar  <$> parens (fromInteger <$> integerP)
 
 -- | Parser for function sorts without the "func" keyword.
 funcSortP :: ParserV v Sort
@@ -1072,6 +1057,7 @@
   <|> (fAppTC listFTyCon . pure <$> brackets sortP)
   <|> (fAppTC <$> fTyConP <*> appArgsP)
   <|> (fApp   <$> tvarP   <*> appArgsP)
+  <|> (FNatNum <$> natural)
 
 tvarP :: ParserV v Sort
 tvarP
@@ -1100,31 +1086,6 @@
 -- | Predicates ----------------------------------------------------------------
 --------------------------------------------------------------------------------
 
--- | Parser for "atomic" predicates.
---
--- This parser is reused by Liquid Haskell.
---
-pred0P :: ParseableV v => ParserV v (ExprV v)
-pred0P =  trueP -- constant "true"
-      <|> falseP -- constant "false"
-      <|> (reservedOp "??" >> makeUniquePGrad)
-      <|> kvarPredP
-      <|> fastIfP pIte predP -- "if-then-else", starts with "if"
-      <|> try predrP -- binary relation, starts with anything that an expr can start with
-      <|> parens predP -- parenthesised predicate, starts with "("
-      <|> (reservedOp "?" *> exprP)
-      <|> try funAppP
-      <|> EVar <$> parseV -- identifier, starts with any letter or underscore
-      <|> (reservedOp "&&" >> pGAnds <$> predsP) -- built-in prefix and
-      <|> (reservedOp "||" >> POr  <$> predsP) -- built-in prefix or
-
-makeUniquePGrad :: ParserV v (ExprV v)
-makeUniquePGrad
-  = do uniquePos <- getSourcePos
-       return $ PGrad (KV $ symbol $ show uniquePos) (Su mempty) (srcGradInfo uniquePos) PTrue
-
--- qmP    = reserved "?" <|> reserved "Bexp"
-
 -- | Parser for the reserved constant "true".
 trueP :: ParserV v (ExprV v)
 trueP  = reserved "true"  >> return PTrue
@@ -1150,53 +1111,23 @@
 -- disjunction.
 --
 predsP :: ParseableV v => ParserV v [ExprV v]
-predsP = brackets $ sepBy predP semi
+predsP = brackets $ sepBy exprP semi
 
 -- | Parses a predicate.
 --
--- Unlike for expressions, there is a built-in operator list.
---
 predP  :: ParseableV v => ParserV v (ExprV v)
-predP  = makeExprParser pred0P lops
-  where
-    lops = [ [Prefix (reservedOp "~"    >> return PNot)]
-           , [Prefix (reserved   "not"  >> return PNot)]
-           , [InfixR (reservedOp "&&"   >> return pGAnd)]
-           , [InfixR (reservedOp "||"   >> return (\x y -> POr [x,y]))]
-           , [InfixR (reservedOp "=>"   >> return PImp)]
-           , [InfixR (reservedOp "==>"  >> return PImp)]
-           , [InfixR (reservedOp "="    >> return PIff)]
-           , [InfixR (reservedOp "<=>"  >> return PIff)]
-           , [InfixR (reservedOp "!="   >> return pNotIff)]
-           , [InfixR (reservedOp "/="   >> return pNotIff)]
-           ]
-
-pNotIff :: ExprV v -> ExprV v -> ExprV v
-pNotIff x y = PNot (PIff x y)
-
--- | Parses a relation predicate.
---
--- Binary relations connect expressions and predicates.
---
-predrP :: ParseableV v => ParserV v (ExprV v)
-predrP =
-  (\ e1 r e2 -> r e1 e2) <$> exprP <*> brelP <*> exprP
+predP  = exprP
 
--- | Parses a relation symbol.
---
--- There is a built-in table of available relations.
---
-brelP ::  ParserV v (ExprV v -> ExprV v -> ExprV v)
-brelP =  (reservedOp "==" >> return (PAtom Eq))
-     <|> (reservedOp "="  >> return (PAtom Eq))
-     <|> (reservedOp "~~" >> return (PAtom Ueq))
-     <|> (reservedOp "!=" >> return (PAtom Ne))
-     <|> (reservedOp "/=" >> return (PAtom Ne))
-     <|> (reservedOp "!~" >> return (PAtom Une))
-     <|> (reservedOp "<"  >> return (PAtom Lt))
-     <|> (reservedOp "<=" >> return (PAtom Le))
-     <|> (reservedOp ">"  >> return (PAtom Gt))
-     <|> (reservedOp ">=" >> return (PAtom Ge))
+existP :: ParseableV v => ParserV v (ExprV v)
+existP = do
+    allow <- gets allowExists
+    if allow then do
+      reserved "exists"
+      bs <- brackets $ sepBy ((,) <$> bindP <*> sortP) comma
+      _ <- dot
+      PExist bs <$> exprP
+     else
+      empty
 
 --------------------------------------------------------------------------------
 -- | BareTypes -----------------------------------------------------------------
@@ -1204,8 +1135,8 @@
 
 -- | Refa
 refaP :: ParseableV v => ParserV v (ExprV v)
-refaP =  try (pAnd <$> brackets (sepBy predP semi))
-     <|> predP
+refaP =  try (PAnd <$> brackets (sepBy exprP semi))
+     <|> exprP
 
 
 -- | (Sorted) Refinements with configurable sub-parsers
@@ -1260,8 +1191,7 @@
   pos    <- getSourcePos
   n      <- upperIdP
   params <- parens $ sepBy1 (qualParamP tP) comma
-  _      <- colon
-  body   <- predP
+  body   <- braces exprP
   return  $ mkQual n params body pos
 
 qualParamP :: ParserV v Sort -> ParserV v QualParam
@@ -1302,8 +1232,10 @@
   _          <- spaces
   _          <- reserved "="
   _          <- spaces
-  (lhs, rhs) <- braces $
-      pairP exprP (reserved "=") exprP
+  e <- braces exprP
+  (lhs, rhs) <- case e of
+                  PAtom Eq l r -> return (l, r)
+                  _ -> error "Expected rewrite rule of the form: LHS = RHS"
   return $ AutoRewrite args lhs rhs
 
 
@@ -1312,9 +1244,7 @@
   name   <- symbolP
   params <- parens        $ sepBy (symBindP sortP) comma
   sort   <- colon        *> sortP
-  body   <- reserved "=" *> braces (
-              if sort == boolSort then predP else exprP
-               )
+  body   <- reserved "=" *> braces exprP
   return  $ mkEquation name params body sort
 
 defineLocalP :: Parser (Int, [(Symbol, Expr)])
@@ -1331,7 +1261,7 @@
         return (x, e)
 
 matchP :: Parser Rewrite
-matchP = SMeasure <$> symbolP <*> symbolP <*> many symbolP <*> (reserved "=" >> exprP)
+matchP = SMeasure <$> symbolP <*> symbolP <*> many symbolP <*> braces exprP
 
 pairsP :: Parser a -> Parser b -> Parser [(a, b)]
 pairsP aP bP = brackets $ sepBy (pairP aP (reserved ":") bP) semi
@@ -1444,20 +1374,23 @@
 intP :: ParserV v Int
 intP = fromInteger <$> natural
 
+integerP :: ParserV v Integer
+integerP =
+        (try (char '-') >> negate <$> natural)
+    <|> natural
+
 boolP :: Parser Bool
 boolP = (reserved "True" >> return True)
     <|> (reserved "False" >> return False)
 
 defsFInfo :: [Def a] -> FInfo a
-defsFInfo defs = {- SCC "defsFI" -} Types.FI cm ws bs ebs lts dts kts qs binfo adts mempty mempty ae lrws mempty
+defsFInfo defs = {- SCC "defsFI" -} Types.FI cm ws bs lts dts kts qs binfo adts mempty mempty ae lrws mempty
   where
     cm         = Misc.safeFromList
                    "defs-cm"        [(cid c, c)         | Cst c       <- defs]
     ws         = Misc.safeFromList
                    "defs-ws"        [(i, w)              | Wfc w    <- defs, let i = Misc.thd3 (wrft w)]
-    bs         = bindEnvFromList  $ exBinds ++ [(n,(x,r,a)) | IBind n x r a <- defs]
-    ebs        =                    [ n                  | (n,_) <- exBinds]
-    exBinds    =                    [(n, (x, RR t trueReft, a)) | EBind n x t a <- defs]
+    bs         = bindEnvFromList    [(n,(x,r,a)) | IBind n x r a <- defs]
     lts        = fromListSEnv       [(x, t)             | Con x t     <- defs]
     dts        = fromListSEnv       [(x, t)             | Dis x t     <- defs]
     kts        = KS $ S.fromList    [k                  | Kut k       <- defs]
@@ -1468,7 +1401,7 @@
     rews       =                    [r                  | Mat r       <- defs]
     autoRWs    = M.fromList         [(arId , s)         | AutoRW arId s <- defs]
     rwEntries  =                    [(i, f)             | RWMap fs   <- defs, (i,f) <- fs]
-    rwMap      = foldl' insert (M.fromList []) rwEntries
+    rwMap      = List.foldl' insert (M.fromList []) rwEntries
                  where
                    insert map' (cid', arId) =
                      case M.lookup arId autoRWs of
@@ -1498,28 +1431,6 @@
   msg <- takeWhileP Nothing (const True) -- consume the rest of the input
   return $ Crash [(i, Nothing)] msg
 
-predSolP :: Parser Expr
-predSolP = parens (predP  <* (comma >> iQualP))
-
-iQualP :: Parser [Symbol]
-iQualP = upperIdP >> parens (sepBy symbolP comma)
-
-solution1P :: Parser (KVar, Expr)
-solution1P = do
-  reserved "solution:"
-  k  <- kvP
-  reservedOp ":="
-  ps <- brackets $ sepBy predSolP semi
-  return (k, simplify $ PAnd ps)
-  where
-    kvP = try kvarP <|> (KV <$> symbolP)
-
-solutionP :: Parser (M.HashMap KVar Expr)
-solutionP = M.fromList <$> sepBy solution1P spaces
-
-solutionFileP :: Parser (FixResult Integer, M.HashMap KVar Expr)
-solutionFileP = (,) <$> fixResultP natural <*> solutionP
-
 --------------------------------------------------------------------------------
 
 -- | Parse via the given parser, and obtain the rest of the input
@@ -1547,6 +1458,7 @@
                            , supply      = 0
                            , layoutStack = Empty
                            , numTyCons   = S.empty
+                           , allowExists = False
                            }
 
 -- | Entry point for parsing, for testing.
@@ -1555,8 +1467,11 @@
 -- Fails with an exception on a parse error.
 --
 doParse' :: Parser a -> SourceName -> String -> a
-doParse' parser fileName input =
-  case runParser (evalStateT (spaces *> parser <* eof) (initPState Nothing)) fileName input of
+doParse' = doParse'' False
+
+doParse'' :: Bool -> Parser a -> SourceName -> String -> a
+doParse'' allowEx parser fileName input =
+  case runParser (evalStateT (spaces *> parser <* eof) ((initPState Nothing) { allowExists = allowEx})) fileName input of
     Left peb@(ParseErrorBundle errors posState) -> -- parse errors; we extract the first error from the error bundle
       let
         ((_, pos) :| _, _) = attachSourcePos errorOffset errors posState
@@ -1600,7 +1515,7 @@
  <|> (reserved "push"     >> return Push)
  <|> (reserved "pop"      >> return Pop)
  <|> (reserved "check"    >> return CheckSat)
- <|> (reserved "assert"   >> (Assert Nothing <$> predP))
+ <|> (reserved "assert"   >> (Assert Nothing <$> exprP))
  <|> (reserved "distinct" >> (Distinct <$> brackets (sepBy exprP comma)))
 
 cmdVarP :: Parser Command
@@ -1632,9 +1547,6 @@
 instance Inputable (FixResult Integer) where
   rr' = doParse' $ fixResultP natural
 
-instance Inputable (FixResult Integer, FixSolution) where
-  rr' = doParse' solutionFileP
-
 instance Inputable (FInfo ()) where
   rr' = {- SCC "fInfoP" -} doParse' fInfoP
 
@@ -1647,100 +1559,3 @@
 instance Inputable [Command] where
   rr' = doParse' commandsP
 
-{-
----------------------------------------------------------------
---------------------------- Testing ---------------------------
----------------------------------------------------------------
-
--- A few tricky predicates for parsing
--- myTest1 = "((((v >= 56320) && (v <= 57343)) => (((numchars a o ((i - o) + 1)) == (1 + (numchars a o ((i - o) - 1)))) && (((numchars a o (i - (o -1))) >= 0) && (((i - o) - 1) >= 0)))) && ((not (((v >= 56320) && (v <= 57343)))) => (((numchars a o ((i - o) + 1)) == (1 + (numchars a o (i - o)))) && ((numchars a o (i - o)) >= 0))))"
---
--- myTest2 = "len x = len y - 1"
--- myTest3 = "len x y z = len a b c - 1"
--- myTest4 = "len x y z = len a b (c - 1)"
--- myTest5 = "x >= -1"
--- myTest6 = "(bLength v) = if n > 0 then n else 0"
--- myTest7 = "(bLength v) = (if n > 0 then n else 0)"
--- myTest8 = "(bLength v) = (n > 0 ? n : 0)"
-
-
-sa  = "0"
-sb  = "x"
-sc  = "(x0 + y0 + z0) "
-sd  = "(x+ y * 1)"
-se  = "_|_ "
-sf  = "(1 + x + _|_)"
-sg  = "f(x,y,z)"
-sh  = "(f((x+1), (y * a * b - 1), _|_))"
-si  = "(2 + f((x+1), (y * a * b - 1), _|_))"
-
-s0  = "true"
-s1  = "false"
-s2  = "v > 0"
-s3  = "(0 < v && v < 100)"
-s4  = "(x < v && v < y+10 && v < z)"
-s6  = "[(v > 0)]"
-s6' = "x"
-s7' = "(x <=> y)"
-s8' = "(x <=> a = b)"
-s9' = "(x <=> (a <= b && b < c))"
-
-s7  = "{ v: Int | [(v > 0)] }"
-s8  = "x:{ v: Int | v > 0 } -> {v : Int | v >= x}"
-s9  = "v = x+y"
-s10 = "{v: Int | v = x + y}"
-
-s11 = "x:{v:Int | true } -> {v:Int | true }"
-s12 = "y : {v:Int | true } -> {v:Int | v = x }"
-s13 = "x:{v:Int | true } -> y:{v:Int | true} -> {v:Int | v = x + y}"
-s14 = "x:{v:a  | true} -> y:{v:b | true } -> {v:a | (x < v && v < y) }"
-s15 = "x:Int -> Bool"
-s16 = "x:Int -> y:Int -> {v:Int | v = x + y}"
-s17 = "a"
-s18 = "x:a -> Bool"
-s20 = "forall a . x:Int -> Bool"
-
-s21 = "x:{v : GHC.Prim.Int# | true } -> {v : Int | true }"
-
-r0  = (rr s0) :: Pred
-r0' = (rr s0) :: [Refa]
-r1  = (rr s1) :: [Refa]
-
-
-e1, e2  :: Expr
-e1  = rr "(k_1 + k_2)"
-e2  = rr "k_1"
-
-o1, o2, o3 :: FixResult Integer
-o1  = rr "SAT "
-o2  = rr "UNSAT [1, 2, 9,10]"
-o3  = rr "UNSAT []"
-
--- sol1 = doParse solution1P "solution: k_5 := [0 <= VV_int]"
--- sol2 = doParse solution1P "solution: k_4 := [(0 <= VV_int)]"
-
-b0, b1, b2, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13 :: BareType
-b0  = rr "Int"
-b1  = rr "x:{v:Int | true } -> y:{v:Int | true} -> {v:Int | v = x + y}"
-b2  = rr "x:{v:Int | true } -> y:{v:Int | true} -> {v:Int | v = x - y}"
-b4  = rr "forall a . x : a -> Bool"
-b5  = rr "Int -> Int -> Int"
-b6  = rr "(Int -> Int) -> Int"
-b7  = rr "({v: Int | v > 10} -> Int) -> Int"
-b8  = rr "(x:Int -> {v: Int | v > x}) -> {v: Int | v > 10}"
-b9  = rr "x:Int -> {v: Int | v > x} -> {v: Int | v > 10}"
-b10 = rr "[Int]"
-b11 = rr "x:[Int] -> {v: Int | v > 10}"
-b12 = rr "[Int] -> String"
-b13 = rr "x:(Int, [Bool]) -> [(String, String)]"
-
--- b3 :: BareType
--- b3  = rr "x:Int -> y:Int -> {v:Bool | ((v is True) <=> x = y)}"
-
-m1 = ["len :: [a] -> Int", "len (Nil) = 0", "len (Cons x xs) = 1 + len(xs)"]
-m2 = ["tog :: LL a -> Int", "tog (Nil) = 100", "tog (Cons y ys) = 200"]
-
-me1, me2 :: Measure.Measure BareType Symbol
-me1 = (rr $ intercalate "\n" m1)
-me2 = (rr $ intercalate "\n" m2)
--}
diff --git a/src/Language/Fixpoint/Smt/Interface.hs b/src/Language/Fixpoint/Smt/Interface.hs
--- a/src/Language/Fixpoint/Smt/Interface.hs
+++ b/src/Language/Fixpoint/Smt/Interface.hs
@@ -45,13 +45,14 @@
     , smtDecls
     , smtDefineFunc
     , smtAssert
+    , smtAssertDecl
     , smtFuncDecl
     , smtAssertAxiom
     , smtCheckUnsat
-    , smtCheckSat
     , smtBracket, smtBracketAt
     , smtDistinct
     , smtPush, smtPop
+    , smtComment
 
     -- * Check Validity
     , checkValid
@@ -59,10 +60,12 @@
     , checkValidWithContext
     , checkValids
 
+    , funcSortVars
+
     ) where
 
 import           Language.Fixpoint.Types.Config ( SMTSolver (..), solverFlags
-                                                , Config (solver, smtTimeout, gradual, stringTheory, save))
+                                                , Config (solver, smtTimeout, noStringTheory, save, allowHO))
 import qualified Language.Fixpoint.Misc          as Misc
 import           Language.Fixpoint.Types.Errors
 import           Language.Fixpoint.Utils.Files
@@ -73,6 +76,7 @@
 import           Language.Fixpoint.Smt.Serialize ()
 import           Control.Applicative      ((<|>))
 import           Control.Monad
+import           Control.Monad.State
 import           Control.Exception
 import           Data.ByteString.Builder (Builder)
 import qualified Data.ByteString.Builder as BS
@@ -80,6 +84,7 @@
 import qualified Data.ByteString.Lazy.Char8 as Char8
 import           Data.Char
 import qualified Data.HashMap.Strict      as M
+import           Data.List                (uncons)
 import           Data.Maybe              (fromMaybe)
 import qualified Data.Text                as T
 import qualified Data.Text.Encoding       as TE
@@ -102,6 +107,7 @@
 import qualified SMTLIB.Backends.Process as Process
 import qualified Language.Fixpoint.Conditional.Z3 as Conditional.Z3
 import Control.Concurrent.Async (async)
+import GHC.Stack (HasCallStack)
 
 {-
 runFile f
@@ -117,36 +123,43 @@
        return zs
 -}
 
-checkValidWithContext :: Context -> [(Symbol, Sort)] -> Expr -> Expr -> IO Bool
-checkValidWithContext me xts p q =
-  smtBracket me "checkValidWithContext" $
-    checkValid' me xts p q
+checkValidWithContext
+  :: HasCallStack => [(Symbol, Sort)] -> Expr -> Expr -> SmtM Bool
+checkValidWithContext xts p q =
+  smtBracket "checkValidWithContext" $
+    checkValid' xts p q
 
 -- | type ClosedPred E = {v:Pred | subset (vars v) (keys E) }
 -- checkValid :: e:Env -> ClosedPred e -> ClosedPred e -> IO Bool
-checkValid :: Config -> FilePath -> [(Symbol, Sort)] -> Expr -> Expr -> IO Bool
+checkValid
+  :: HasCallStack
+  => Config -> FilePath -> [(Symbol, Sort)] -> Expr -> Expr -> IO Bool
 checkValid cfg f xts p q = do
   me <- makeContext cfg f
-  checkValid' me xts p q
+  evalStateT (checkValid' xts p q) me
 
-checkValid' :: Context -> [(Symbol, Sort)] -> Expr -> Expr -> IO Bool
-checkValid' me xts p q = do
-  smtDecls me xts
-  smtAssert me $ pAnd [p, PNot q]
-  smtCheckUnsat me
+checkValid' :: HasCallStack => [(Symbol, Sort)] -> Expr -> Expr -> SmtM Bool
+checkValid' xts p q = do
+  smtDecls xts
+  smtAssertDecl $ pAnd [p, PNot q]
+  smtCheckUnsat
 
 -- | If you already HAVE a context, where all the variables have declared types
 --   (e.g. if you want to make MANY repeated Queries)
 
 -- checkValid :: e:Env -> [ClosedPred e] -> IO [Bool]
 checkValids :: Config -> FilePath -> [(Symbol, Sort)] -> [Expr] -> IO [Bool]
-checkValids cfg f xts ps
-  = do me <- makeContext cfg f
-       smtDecls me xts
-       forM ps $ \p ->
-          smtBracket me "checkValids" $
-            smtAssert me (PNot p) >> smtCheckUnsat me
+checkValids cfg f xts ps = do
+  me <- makeContext cfg f
+  evalStateT (checkValids' xts ps) me
 
+checkValids' :: [(Symbol, Sort)] -> [Expr] -> SmtM [Bool]
+checkValids' xts ps = do
+  smtDecls xts
+  forM ps $ \p ->
+     smtBracket "checkValids" $
+       smtAssert (PNot p) >> smtCheckUnsat
+
 -- debugFile :: FilePath
 -- debugFile = "DEBUG.smt2"
 
@@ -154,42 +167,55 @@
 -- | SMT IO --------------------------------------------------------------------
 --------------------------------------------------------------------------------
 
+commandRaw :: Maybe Handle -> SMTLIB.Backends.Solver -> Bool -> Builder -> IO Response
+commandRaw ctxLog ctxSolver ctxVerbose cmdBS = do
+  resp <- SMTLIB.Backends.command ctxSolver cmdBS
+  let respTxt =
+        TE.decodeUtf8With (const $ const $ Just ' ') $
+        LBS.toStrict resp
+  case A.parseOnly responseP respTxt of
+    Left e  -> Misc.errorstar $ "SMTREAD:" ++ e
+    Right r -> do
+      let textResponse = "; SMT Says: " <> T.pack (show r)
+      forM_ ctxLog $ \h ->
+        Data.Text.IO.hPutStrLn h textResponse
+      when ctxVerbose $
+        Data.Text.IO.putStrLn textResponse
+      return r
+
 --------------------------------------------------------------------------------
 {-# SCC command #-}
-command              :: Context -> Command -> IO Response
+command  :: HasCallStack => Command -> SmtM Response
 --------------------------------------------------------------------------------
-command Ctx{..} !cmd       = do
+command !cmd       = do
   -- whenLoud $ do LTIO.appendFile debugFile (s <> "\n")
   --               LTIO.putStrLn ("CMD-RAW:" <> s <> ":CMD-RAW:DONE")
-  forM_ ctxLog $ \h -> do
+  ctxLog <- gets ctxLog
+  ctxSolver <- gets ctxSolver
+  ctxVerbose <- gets ctxVerbose
+  cmdBS <- liftSym $ runSmt2 cmd
+  forM_ ctxLog $ \h -> lift $ do
     BS.hPutBuilder h cmdBS
     LBS.hPutStr h "\n"
-  case cmd of
-    CheckSat   -> commandRaw
-    GetValue _ -> commandRaw
+  lift $ case cmd of
+    CheckSat   -> commandRaw ctxLog ctxSolver ctxVerbose cmdBS
+    GetValue _ -> commandRaw ctxLog ctxSolver ctxVerbose cmdBS
     _          -> SMTLIB.Backends.command_ ctxSolver cmdBS >> return Ok
-  where
-    commandRaw      = do
-      resp <- SMTLIB.Backends.command ctxSolver cmdBS
-      let respTxt =
-            TE.decodeUtf8With (const $ const $ Just ' ') $
-            LBS.toStrict resp
-      parse respTxt
-    cmdBS = {-# SCC "Command-runSmt2" #-} runSmt2 ctxSymEnv cmd
-    parse resp      = do
-      case A.parseOnly responseP resp of
-        Left e  -> Misc.errorstar $ "SMTREAD:" ++ e
-        Right r -> do
-          let textResponse = "; SMT Says: " <> T.pack (show r)
-          forM_ ctxLog $ \h ->
-            Data.Text.IO.hPutStrLn h textResponse
-          when ctxVerbose $
-            Data.Text.IO.putStrLn textResponse
-          return r
 
-smtSetMbqi :: Context -> IO ()
-smtSetMbqi me = interact' me SetMbqi
+-- | A variant of `command` that accepts a pre-built command
+commandB :: Builder -> SmtM Response
+--------------------------------------------------------------------------------
+commandB cmdBS       = do
+  ctxLog <- gets ctxLog
+  ctxSolver <- gets ctxSolver
+  forM_ ctxLog $ \h -> lift $ do
+    BS.hPutBuilder h cmdBS
+    LBS.hPutStr h "\n"
+  lift $ SMTLIB.Backends.command_ ctxSolver cmdBS >> return Ok
 
+smtSetMbqi :: SmtM ()
+smtSetMbqi = interact' SetMbqi
+
 type SmtParser a = Parser T.Text a
 
 responseP :: SmtParser Response
@@ -254,12 +280,11 @@
     where
        smtFile = extFileName Smt2 f
 
-makeContextWithSEnv :: Config -> FilePath -> SymEnv -> [Equation] -> IO Context
+makeContextWithSEnv :: Config -> FilePath -> SymEnv -> DefinedFuns -> IO Context
 makeContextWithSEnv cfg f env defns = do
-  ctx     <- makeContext cfg f
+  ctx      <- makeContext cfg f
   let ctx' = ctx {ctxSymEnv = env, ctxDefines = defns}
-  declare ctx'
-  return ctx'
+  execStateT declare ctx'
 
 makeContextNoLog :: Config -> IO Context
 makeContextNoLog cfg = do
@@ -307,16 +332,22 @@
          Cvc5    -> makeProcess ctxLog $
                       Process.defaultConfig
                              { Process.exe = "cvc5"
-                             , Process.args = ["-L", "smtlib2"] }
+                             , Process.args = ["-L", "smtlib2", "--arrays-exp"] }
        solver <- SMTLIB.Backends.initSolver SMTLIB.Backends.Queuing backend
        loud <- isLoud
        return Ctx { ctxSolver    = solver
-                  , ctxElabF     = solverFlags slv
+                  , ctxElabF     = solverFlags cfg
                   , ctxClose     = closeIO
                   , ctxLog       = ctxLog
                   , ctxVerbose   = loud
                   , ctxSymEnv    = mempty
+                  , ctxIxs       = []
                   , ctxDefines   = mempty
+                  -- This is a heurstic to avoid generating large sequences of unused `lam_arg` symbols
+                  -- when there's no higher-order reasoning. It might require some tuning on larger codebases
+                  -- if `unknown function/constant lam_arg$XXX` errors are encountered.
+                  , ctxLams      = allowHO cfg
+                  , config       = cfg
                   }
 
 -- | Close file handles and release the solver backend's resources.
@@ -333,9 +364,11 @@
   | s == Z3 || s == Z3mem
     = do v <- getZ3Version me
          checkValidStringFlag Z3 v cfg
-         return $ makeMbqi cfg ++ makeTimeout cfg ++ Thy.preamble cfg Z3
+         return $ makeMbqi ++ makeTimeout cfg ++ Thy.preamble cfg Z3
   | otherwise
     = checkValidStringFlag s [] cfg >> return (Thy.preamble cfg s)
+  where
+    makeMbqi = ["\n(set-option :smt.mbqi false)"]
 
 getZ3Version :: Context -> IO [Int]
 getZ3Version me
@@ -361,58 +394,59 @@
 
 noString :: SMTSolver -> [Int] -> Config -> Bool
 noString smt v cfg
-  =  stringTheory cfg
-  && not (smt == Z3 && (v >= [4, 4, 2]))
-
+  =  not (noStringTheory cfg)
+  && not (smt == Cvc5 || (smt == Z3 && (v >= [4, 4, 2])))
 -----------------------------------------------------------------------------
 -- | SMT Commands -----------------------------------------------------------
 -----------------------------------------------------------------------------
 
-smtPush, smtPop   :: Context -> IO ()
-smtPush me        = interact' me Push
-smtPop me         = interact' me Pop
+smtPush, smtPop :: SmtM ()
+smtPush = interact' Push
+smtPop  = interact' Pop
 
-smtDecls :: Context -> [(Symbol, Sort)] -> IO ()
-smtDecls = mapM_ . uncurry . smtDecl
+smtComment :: T.Text -> SmtM ()
+smtComment t = interact' (Comment t)
 
-smtDecl :: Context -> Symbol -> Sort -> IO ()
-smtDecl me x t = interact' me ({- notracepp msg $ -} Declare (symbolSafeText x) ins' out')
+smtDecls :: [(Symbol, Sort)] -> SmtM ()
+smtDecls = mapM_ $ uncurry smtDecl
+
+smtDecl :: Symbol -> Sort -> SmtM ()
+smtDecl x t = do
+  me <- get
+  let env = seData (ctxSymEnv me)
+  let ins' = sortSmtSort False env <$> ins
+  let out' = sortSmtSort False env     out
+  interact' (notracepp _msg $ Declare (symbolSafeText x) ins' out')
   where
-    ins'       = sortSmtSort False env <$> ins
-    out'       = sortSmtSort False env     out
     (ins, out) = deconSort t
-    _msg        = "smtDecl: " ++ showpp (x, t, ins, out)
-    env        = seData (ctxSymEnv me)
+    _msg       = "smtDecl: " ++ showpp (x, t, ins, out)
 
-smtFuncDecl :: Context -> T.Text -> ([SmtSort],  SmtSort) -> IO ()
-smtFuncDecl me x (ts, t) = interact' me (Declare x ts t)
+smtFuncDecl :: T.Text -> ([SmtSort],  SmtSort) -> SmtM ()
+smtFuncDecl x (ts, t) = interact' (Declare x ts t)
 
-smtDataDecl :: Context -> [DataDecl] -> IO ()
-smtDataDecl me ds = interact' me (DeclData ds)
+smtDataDecl :: [DataDecl] -> SmtM ()
+smtDataDecl ds = interact' (DeclData ds)
 
 deconSort :: Sort -> ([Sort], Sort)
 deconSort t = case functionSort t of
                 Just (_, ins, out) -> (ins, out)
                 Nothing            -> ([], t)
 
--- hack now this is used only for checking gradual condition.
-smtCheckSat :: Context -> Expr -> IO Bool
-smtCheckSat me p
- = smtAssert me p >> (ans <$> command me CheckSat)
- where
-   ans Sat = True
-   ans _   = False
+smtAssert :: Expr -> SmtM ()
+smtAssert p = interact' (Assert Nothing p)
 
-smtAssert :: Context -> Expr -> IO ()
-smtAssert me p = interact' me (Assert Nothing p)
+-- the following three functions will emit additional `apply`,
+-- `coerce`, and `lambda` symbols for fresh function sorts as needed
+smtAssertDecl :: HasCallStack => Expr -> SmtM ()
+smtAssertDecl p = interactDecl' (Assert Nothing p)
 
-smtDefineEqn :: Context -> Equation -> IO ()
-smtDefineEqn me Equ {..} = smtDefineFunc me eqName eqArgs eqSort eqBody
+smtDefineEqn :: Equation -> SmtM ()
+smtDefineEqn Equ {..} = smtDefineFunc eqName eqArgs eqSort eqBody
 
-smtDefineFunc :: Context -> Symbol -> [(Symbol, F.Sort)] -> F.Sort -> Expr -> IO ()
-smtDefineFunc me name symList rsort e =
-  let env = seData (ctxSymEnv me)
-  in interact' me $
+smtDefineFunc :: Symbol -> [(Symbol, F.Sort)] -> F.Sort -> Expr -> SmtM ()
+smtDefineFunc name symList rsort e =
+  do env <- gets (seData . ctxSymEnv)
+     interactDecl' $
         DefineFunc
           name
           (map (sortSmtSort False env <$>) symList)
@@ -421,34 +455,60 @@
 
 -----------------------------------------------------------------
 
-smtAssertAxiom :: Context -> Triggered Expr -> IO ()
-smtAssertAxiom me p  = interact' me (AssertAx p)
+smtAssertAxiom :: Triggered Expr -> SmtM ()
+smtAssertAxiom p  = interact' (AssertAx p)
 
-smtDistinct :: Context -> [Expr] -> IO ()
-smtDistinct me az = interact' me (Distinct az)
+smtDistinct :: [Expr] -> SmtM ()
+smtDistinct az = interact' (Distinct az)
 
-smtCheckUnsat :: Context -> IO Bool
-smtCheckUnsat me  = respSat <$> command me CheckSat
+smtCheckUnsat :: HasCallStack => SmtM Bool
+smtCheckUnsat = respSat <$> command CheckSat
 
-smtBracketAt :: SrcSpan -> Context -> String -> IO a -> IO a
-smtBracketAt sp x y z = smtBracket x y z `catch` dieAt sp
+smtBracketAt :: SrcSpan -> String -> SmtM a -> SmtM a
+smtBracketAt sp _msg a =
+  smtBracket _msg a `catchSMT` dieAt sp
 
-smtBracket :: Context -> String -> IO a -> IO a
-smtBracket me _msg a   = do
-  smtPush me
+-- | `smtBracket` adds a new level to the apply stack and saves the last fresh index
+--   on the index stack before the action, and reverts these changes after the action.
+smtBracket :: String -> SmtM a -> SmtM a
+smtBracket msg a = do
+  smtComment (T.pack $ "smtBracket - start: " ++ msg)
+  smtPush
+  modify $ \ctx ->
+    let env = ctxSymEnv ctx in
+    ctx { ctxSymEnv = env { seAppls = pushAppls (seAppls env) }
+        , ctxIxs = seIx env : ctxIxs ctx}
   r <- a
-  smtPop me
+  smtPop
+  smtComment (T.pack $ "smtBracket - end: " ++ msg)
+  modify $ \ctx ->
+    let env = ctxSymEnv ctx
+        (i , is) = fromMaybe (0, []) (uncons $ ctxIxs ctx)
+      in
+    ctx { ctxSymEnv = env {seAppls = popAppls (seAppls env) , seIx = i}
+        , ctxIxs = is}
   return r
 
-respSat :: Response -> Bool
+respSat :: HasCallStack => Response -> Bool
 respSat Unsat   = True
 respSat Sat     = False
 respSat Unknown = False
 respSat r       = die $ err dummySpan $ text ("crash: SMTLIB2 respSat = " ++ show r)
 
-interact' :: Context -> Command -> IO ()
-interact' me cmd  = void $ command me cmd
+interact' :: Command -> SmtM ()
+interact' cmd  = void $ command cmd
 
+-- | a variant of `interact'` which also emits fresh
+--   `apply`, `coerce`, and `lambda` symbols
+interactDecl' :: HasCallStack => Command -> SmtM ()
+interactDecl' cmd  = do
+  cmdBS <- liftSym $ runSmt2 cmd
+  ctx <- get
+  let env = ctxSymEnv ctx
+  let ats = funcSortVars (ctxLams ctx) env
+  forM_ ats $ uncurry smtFuncDecl
+  put (ctx {ctxSymEnv = env {seAppls = mergeTopAppls (seApplsCur env) (seAppls env), seApplsCur = M.empty} })
+  void $ commandB cmdBS
 
 makeTimeout :: Config -> [Builder]
 makeTimeout cfg
@@ -456,36 +516,28 @@
   | otherwise                = [""]
 
 
-makeMbqi :: Config -> [Builder]
-makeMbqi cfg
-  | gradual cfg = [""]
-  | otherwise   = ["\n(set-option :smt.mbqi false)"]
-
-
 --------------------------------------------------------------------------------
-declare :: Context -> IO ()
+declare :: SmtM ()
 --------------------------------------------------------------------------------
-declare me = do
-  forM_ dss    $           smtDataDecl me
-  forM_ thyXTs $ uncurry $ smtDecl     me
-  forM_ qryXTs $ uncurry $ smtDecl     me
-  forM_ ats    $ uncurry $ smtFuncDecl me
-  forM_ defs   $           smtDefineEqn me
-  forM_ ess    $           smtDistinct me
-  forM_ axs    $           smtAssert   me
-  where
-    env        = ctxSymEnv me
-    dss        = dataDeclarations          env
-    lts        = F.toListSEnv . F.seLits $ env
-    ess        = distinctLiterals  lts
-    axs        = Thy.axiomLiterals lts
-    thyXTs     =             [ (x, t) | (x, t) <- xts, symKind env x == Just F.Uninterp ]
-    qryXTs     = fmap tx <$> [ (x, t) | (x, t) <- xts, symKind env x == Nothing ]
-    -- isKind n   = (n ==)  . symKind env . fst
-    xts        = symbolSorts (F.seSort env)
-    tx         = elaborate (ElabParam (ctxElabF me) "declare" env)
-    ats        = funcSortVars env
-    defs       = ctxDefines me
+declare = do
+  me <- get
+  let env        = ctxSymEnv me
+  let xts        = symbolSorts (F.seSort env)
+  let tx         = elaborate (ElabParam (ctxElabF me) "declare" env)
+  let lts        = F.toListSEnv . F.seLits $ env
+  let dss        = dataDeclarations          env
+  let thyXTs     =             [ (x, t) | (x, t) <- xts, symKind env x == Just F.Uninterp ]
+  let qryXTs     = fmap tx <$> [ (x, t) | (x, t) <- xts, symKind env x == Nothing ]
+  -- let isKind n   = (n ==)  . symKind env . fst
+  let MkDefinedFuns defs = ctxDefines me
+  let ess        = distinctLiterals  lts
+  let axs        = Thy.axiomLiterals (config me) lts
+  forM_ dss              smtDataDecl
+  forM_ thyXTs $ uncurry smtDecl
+  forM_ qryXTs $ uncurry smtDecl
+  forM_ defs             smtDefineEqn
+  forM_ ess              smtDistinct
+  forM_ axs              smtAssert
 
 symbolSorts :: F.SEnv F.Sort -> [(F.Symbol, F.Sort)]
 symbolSorts env = [(x, tx t) | (x, t) <- F.toListSEnv env ]
@@ -496,14 +548,24 @@
 dataDeclarations :: SymEnv -> [[DataDecl]]
 dataDeclarations = orderDeclarations . map snd . F.toListSEnv . F.seData
 
-funcSortVars :: F.SymEnv -> [(T.Text, ([F.SmtSort], F.SmtSort))]
-funcSortVars env  = [(var applyName  t       , appSort t) | t <- ts]
-                 ++ [(var coerceName t       , ([t1],t2)) | t@(t1, t2) <- ts]
-                 ++ [(var lambdaName t       , lamSort t) | t <- ts]
-                 ++ [(var (lamArgSymbol i) t , argSort t) | t@(_,F.SInt) <- ts, i <- [1..Thy.maxLamArg] ]
+-- | See 'F.seApplsCur' for explanation.
+funcSortVars :: Bool -> F.SymEnv -> [(T.Text, ([F.SmtSort], F.SmtSort))]
+funcSortVars lams env =
+    concatMap symbolsForTag $ M.toList $ F.seApplsCur env
   where
-    var n         = F.symbolAtSmtName n env ()
-    ts            = M.keys (F.seAppls env)
+    symbolsForTag (t, i) =
+      let applySym  = symbolAtSortIndex applyName i
+          coerceSym = symbolAtSortIndex coerceName i
+          lamSym    = symbolAtSortIndex lambdaName i
+          argSyms   = if lams && snd t == F.SInt
+                        then [ (symbolAtSortIndex (lamArgSymbol j) i, argSort t)
+                             | j <- [1..Thy.maxLamArg] ]
+                        else []
+      in  (applySym, appSort t)
+        : (coerceSym, ([fst t], snd t))
+        : (lamSym, lamSort t)
+        : argSyms
+
     appSort (s,t) = ([F.SInt, s], t)
     lamSort (s,t) = ([s, t], F.SInt)
     argSort (s,_) = ([]    , s)
diff --git a/src/Language/Fixpoint/Smt/Serialize.hs b/src/Language/Fixpoint/Smt/Serialize.hs
--- a/src/Language/Fixpoint/Smt/Serialize.hs
+++ b/src/Language/Fixpoint/Smt/Serialize.hs
@@ -14,6 +14,7 @@
 
 module Language.Fixpoint.Smt.Serialize (smt2SortMono) where
 
+import           Control.Monad.State
 import           Data.ByteString.Builder (Builder)
 import           Language.Fixpoint.SortCheck
 import           Language.Fixpoint.Types
@@ -27,47 +28,65 @@
 -- import Debug.Trace (trace)
 
 instance SMTLIB2 (Symbol, Sort) where
-  smt2 env c@(sym, t) = -- build "({} {})" (smt2 env sym, smt2SortMono c env t)
-                        parenSeqs [smt2 env sym, smt2SortMono c env t]
+  smt2 c@(sym, t) =
+    -- build "({} {})" (smt2 env sym, smt2SortMono c env t)
+    do s <- smt2 sym
+       ss <- smt2SortMono c t
+       pure $ parenSeqs [s , ss]
 
-smt2SortMono, smt2SortPoly :: (PPrint a) => a -> SymEnv -> Sort -> Builder
+instance SMTLIB2 (Symbol, Expr) where
+  smt2 (sym, e) =
+    do s <- smt2 sym
+       ss <- smt2 e
+       pure $ parenSeqs [s, ss]
+
+smt2SortMono, smt2SortPoly :: (PPrint a) => a -> Sort -> SymM Builder
 smt2SortMono = smt2Sort False
 smt2SortPoly = smt2Sort True
 
-smt2Sort :: (PPrint a) => Bool -> a -> SymEnv -> Sort -> Builder
-smt2Sort poly _ env t = smt2 env (Thy.sortSmtSort poly (seData env) t)
+smt2Sort :: (PPrint a) => Bool -> a -> Sort -> SymM Builder
+smt2Sort poly _ t =
+  do env <- get
+     smt2 (Thy.sortSmtSort poly (seData env) t)
 
-smt2data :: SymEnv -> [DataDecl] -> Builder
-smt2data env = smt2data' env . map padDataDecl
+smt2data :: [DataDecl] -> SymM Builder
+smt2data = smt2data' . map padDataDecl
 
-smt2data' :: SymEnv -> [DataDecl] -> Builder
-smt2data' env ds = seqs [ parens $ smt2many (smt2dataname env <$> ds)
-                        , parens $ smt2many (smt2datactors env <$> ds)
-                        ]
+smt2data' :: [DataDecl] -> SymM Builder
+smt2data' ds =
+  do n <- traverse smt2dataname ds
+     d <- traverse smt2datactors ds
+     pure $ seqs [ parens $ smt2many n , parens $ smt2many d ]
 
 
-smt2dataname :: SymEnv -> DataDecl -> Builder
-smt2dataname env (DDecl tc as _) = parenSeqs [name, n]
-  where
-    name  = smt2 env (symbol tc)
-    n     = smt2 env as
+smt2dataname :: DataDecl -> SymM Builder
+smt2dataname (DDecl tc as _) =
+  do name <- smt2 (symbol tc)
+     n    <- smt2 as
+     pure $ parenSeqs [name, n]
 
 
-smt2datactors :: SymEnv -> DataDecl -> Builder
-smt2datactors env (DDecl _ as cs)
-  | as > 0       = parenSeqs ["par", parens tvars, parens ds]
-  | otherwise    =                                 parens ds
+smt2datactors :: DataDecl -> SymM Builder
+smt2datactors (DDecl _ as cs) =
+  do ds <- traverse (smt2ctor as) cs
+     if as > 0
+      then do tvars <- traverse smt2TV [0..(as-1)]
+              pure $ parenSeqs ["par", parens (smt2many tvars), parens (smt2many ds)]
+      else pure $                                               parens (smt2many ds)
   where
-    tvars        = smt2many (smt2TV <$> [0..(as-1)])
-    smt2TV       = smt2 env . SVar
-    ds           = smt2many (smt2ctor env as <$> cs)
-
-smt2ctor :: SymEnv -> Int -> DataCtor -> Builder
+    smt2TV = smt2 . SVar
 
-smt2ctor env as (DCtor c fs)  = parenSeqs (smt2 env c : (smt2field env as <$> fs))
+smt2ctor :: Int -> DataCtor -> SymM Builder
+smt2ctor as (DCtor c fs) =
+  do h <- smt2 c
+     t <- traverse (smt2field as) fs
+     pure $ parenSeqs (h : t)
 
-smt2field :: SymEnv -> Int -> DataField -> Builder
-smt2field env as d@(DField x t) = parenSeqs [smt2 env x, smt2SortPoly d env $ mkPoly as t]
+smt2field :: Int -> DataField -> SymM Builder
+smt2field as d@(DField x t) =
+  do s <- smt2 x
+     ss <- smt2SortPoly d $ mkPoly as t
+     pure $ parenSeqs [s , ss]
 
 -- | SMTLIB/Z3 don't like "unused" type variables; they get pruned away and
 --   cause wierd hassles. See tests/pos/adt_poly_dead.fq for an example.
@@ -95,120 +114,153 @@
     go is _        = is
 
 instance SMTLIB2 Symbol where
-  smt2 env s
-    | Just t <- Thy.smt2Symbol env s = t
-  smt2 _ s                           = symbolBuilder s
-
+  smt2 s = do env <- get
+              case Thy.smt2Symbol env s of
+                Just t  -> pure t
+                Nothing -> pure $ symbolBuilder s
 instance SMTLIB2 Int where
-  smt2 _ = Builder.fromString . show
+  smt2 i = pure $ Builder.fromString $ show i
 
 instance SMTLIB2 LocSymbol where
-  smt2 env = smt2 env . val
+  smt2 = smt2 . val
 
 instance SMTLIB2 SymConst where
-  smt2 env = smt2 env . symbol
+  smt2 = smt2 . symbol
 
 instance SMTLIB2 Constant where
-  smt2 _ (I n)   = bShow n
-  smt2 _ (R d)   = bFloat d
-  smt2 _ (L t _) = fromText t
+  smt2 (I n)   = pure $ bShow n
+  smt2 (R d)   = pure $ bFloat d
+  smt2 (L t s)
+    | isString s = pure $ quotes $ fromText t
+    | otherwise  = pure $ fromText t
 
 instance SMTLIB2 Bop where
-  smt2 _ Plus   = "+"
-  smt2 _ Minus  = "-"
-  smt2 _ Times  = symbolBuilder mulFuncName
-  smt2 _ Div    = symbolBuilder divFuncName
-  smt2 _ RTimes = "*"
-  smt2 _ RDiv   = "/"
-  smt2 _ Mod    = "mod"
+  smt2 Plus   = pure "+"
+  smt2 Minus  = pure "-"
+  smt2 Times  = pure $ symbolBuilder mulFuncName
+  smt2 Div    = pure $ symbolBuilder divFuncName
+  smt2 RTimes = pure "*"
+  smt2 RDiv   = pure "/"
+  smt2 Mod    = pure "mod"
 
 instance SMTLIB2 Brel where
-  smt2 _ Eq    = "="
-  smt2 _ Ueq   = "="
-  smt2 _ Gt    = ">"
-  smt2 _ Ge    = ">="
-  smt2 _ Lt    = "<"
-  smt2 _ Le    = "<="
-  smt2 _ _     = errorstar "SMTLIB2 Brel"
+  smt2 Eq  = pure "="
+  smt2 Ueq = pure "="
+  smt2 Gt  = pure ">"
+  smt2 Ge  = pure ">="
+  smt2 Lt  = pure "<"
+  smt2 Le  = pure "<="
+  smt2 _   = errorstar "SMTLIB2 Brel"
 
 -- NV TODO: change the way EApp is printed
 instance SMTLIB2 Expr where
-  smt2 env (ESym z)         = smt2 env z
-  smt2 env (ECon c)         = smt2 env c
-  smt2 env (EVar x)         = smt2 env x
-  smt2 env e@(EApp _ _)     = smt2App env e
-  smt2 env (ENeg e)         = parenSeqs ["-", smt2 env e]
-  smt2 env (EBin o e1 e2)   = parenSeqs [smt2 env o, smt2 env e1, smt2 env e2]
-  smt2 env (EIte e1 e2 e3)  = parenSeqs ["ite", smt2 env e1, smt2 env e2, smt2 env e3]
-  smt2 env (ECst e t)       = smt2Cast env e t
-  smt2 _   PTrue            = "true"
-  smt2 _   PFalse           = "false"
-  smt2 _   (PAnd [])        = "true"
-  smt2 env (PAnd ps)        = parenSeqs ["and", smt2s env ps]
-  smt2 _   (POr [])         = "false"
-  smt2 env (POr ps)         = parenSeqs ["or", smt2s env ps]
-  smt2 env (PNot p)         = parenSeqs ["not", smt2 env p]
-  smt2 env (PImp p q)       = parenSeqs ["=>", smt2 env p, smt2 env q]
-  smt2 env (PIff p q)       = parenSeqs ["=", smt2 env p, smt2 env q]
-  smt2 env (PExist [] p)    = smt2 env p
-  smt2 env (PExist xs p)    = parenSeqs ["exists", parens (smt2s env xs), smt2 env p]
-  smt2 env (PAll   [] p)    = smt2 env p
-  smt2 env (PAll   xs p)    = parenSeqs ["forall", parens (smt2s env xs), smt2 env p]
-  smt2 env (PAtom r e1 e2)  = mkRel env r e1 e2
-  smt2 env (ELam b e)       = smt2Lam env b e
-  smt2 env (ECoerc t1 t2 e) = smt2Coerc env t1 t2 e
-  smt2 _   e                = panic ("smtlib2 Pred  " ++ show e)
-
-
+  smt2 (ESym z)         = smt2 z
+  smt2 (ECon c)         = smt2 c
+  smt2 (EVar x)         = smt2 x
+  smt2 e@(EApp _ _)     = smt2App e
+  smt2 (ENeg e)         = do s <- smt2 e
+                             pure $ parenSeqs ["-", s]
+  smt2 (EBin o e1 e2)   = do so <- smt2 o
+                             s1 <- smt2 e1
+                             s2 <- smt2 e2
+                             pure $ parenSeqs [so, s1, s2]
+  smt2 (ELet x e1 e2)   = do s1 <- smt2 (x, e1)
+                             s2 <- smt2 e2
+                             pure $ parenSeqs ["let", parens s1, s2]
+  smt2 (EIte e1 e2 e3)  = do s1 <- smt2 e1
+                             s2 <- smt2 e2
+                             s3 <- smt2 e3
+                             pure $ parenSeqs ["ite", s1, s2, s3]
+  smt2 (ECst e t)       = smt2Cast e t
+  smt2 PTrue            = pure "true"
+  smt2 PFalse           = pure "false"
+  smt2 (PAnd [])        = pure "true"
+  smt2 (PAnd ps)        = do s <- smt2s ps
+                             pure $ parenSeqs ["and", s]
+  smt2 (POr [])         = pure "false"
+  smt2 (POr ps)         = do s <- smt2s ps
+                             pure $ parenSeqs ["or", s]
+  smt2 (PNot p)         = do s <- smt2 p
+                             pure $ parenSeqs ["not", s]
+  smt2 (PImp p q)       = do s1 <- smt2 p
+                             s2 <- smt2 q
+                             pure $ parenSeqs ["=>", s1, s2]
+  smt2 (PIff p q)       = do s1 <- smt2 p
+                             s2 <- smt2 q
+                             pure $ parenSeqs ["=", s1, s2]
+  smt2 (PExist [] p)    = smt2 p
+  smt2 (PExist xs p)    = do s <- smt2s xs
+                             s1 <- smt2 p
+                             pure $ parenSeqs ["exists", parens s, s1]
+  smt2 (PAll   [] p)    = smt2 p
+  smt2 (PAll   xs p)    = do s <- smt2s xs
+                             s1 <- smt2 p
+                             pure $ parenSeqs ["forall", parens s, s1]
+  smt2 (PAtom r e1 e2)  = mkRel r e1 e2
+  smt2 (ELam b e)       = smt2Lam b e
+  smt2 (ECoerc t1 t2 e) = smt2Coerc t1 t2 e
+  smt2 e                = panic ("smtlib2 Pred  " ++ show e)
 
 -- | smt2Cast uses the 'as x T' pattern needed for polymorphic ADT constructors
 --   like Nil, see `tests/pos/adt_list_1.fq`
 
-smt2Cast :: SymEnv -> Expr -> Sort -> Builder
-smt2Cast env (EVar x) t = smt2Var env x t
-smt2Cast env e        _ = smt2    env e
-
-smt2Var :: SymEnv -> Symbol -> Sort -> Builder
-smt2Var env x t
-  | isLamArgSymbol x            = smtLamArg env x t
-  | Just s <- symEnvSort x env
-  , isPolyInst s t              = smt2VarAs env x t
-  | otherwise                   = smt2 env x
+smt2Cast :: Expr -> Sort -> SymM Builder
+smt2Cast (EVar x) t = smt2Var x t
+smt2Cast e        _ = smt2    e
 
-smtLamArg :: SymEnv -> Symbol -> Sort -> Builder
-smtLamArg env x t = Builder.fromText $ symbolAtName x env () (FFunc t FInt)
+smt2Var :: Symbol -> Sort -> SymM Builder
+smt2Var x t
+  | isLamArgSymbol x = smtLamArg x t
+  | otherwise        = do env <- get
+                          case symEnvSort x env of
+                            Just s | isPolyInst s t -> smt2VarAs x t
+                            _                       -> smt2 x
 
-smt2VarAs :: SymEnv -> Symbol -> Sort -> Builder
-smt2VarAs env x t = parenSeqs ["as", smt2 env x, smt2SortMono x env t]
+smt2VarAs :: Symbol -> Sort -> SymM Builder
+smt2VarAs x t =
+  do s <- smt2 x
+     s1 <- smt2SortMono x t
+     pure $ parenSeqs ["as", s, s1]
 
-smt2Lam :: SymEnv -> (Symbol, Sort) -> Expr -> Builder
-smt2Lam env (x, xT) full@(ECst _ eT) = parenSeqs [Builder.fromText lambda, x', smt2 env full]
-  where
-    x'     = smtLamArg env x xT
-    lambda = symbolAtName lambdaName env () (FFunc xT eT)
+-- the next four functions (ones containing a call to `symbolAtName`) can trigger
+-- an expansion of the "nursery" tag table ('seApplsCur' in 'SymEnv') when processing
+-- a fresh function sort
+smtLamArg :: Symbol -> Sort -> SymM Builder
+smtLamArg x t =
+  do s <- symbolAtName x (FFunc t FInt)
+     pure $ Builder.fromText s
 
-smt2Lam _ _ e
+smt2Lam :: (Symbol, Sort) -> Expr -> SymM Builder
+smt2Lam (x, xT) full@(ECst _ eT) =
+  do x' <- smtLamArg x xT
+     lambda <- symbolAtName lambdaName (FFunc xT eT)
+     f <- smt2 full
+     pure $ parenSeqs [Builder.fromText lambda, x', f]
+smt2Lam _ e
   = panic ("smtlib2: Cannot serialize unsorted lambda: " ++ showpp e)
 
-smt2App :: SymEnv -> Expr -> Builder
-smt2App env e@(EApp (EApp f e1) e2)
+smt2App :: Expr -> SymM Builder
+smt2App (EApp (EApp f e1) e2)
   | Just t <- unApplyAt f
-  = parenSeqs [Builder.fromText (symbolAtName applyName env e t), smt2s env [e1, e2]]
-smt2App env e
-  | Just b <- Thy.smt2App smt2VarAs env f (smt2 env <$> es)
-  = b
-  | otherwise
-  = parenSeqs [smt2 env f, smt2s env es]
+  = do a <- symbolAtName applyName t
+       s <- smt2s [e1, e2]
+       pure $ parenSeqs [Builder.fromText a, s]
+smt2App e = do s0 <- traverse smt2 es
+               s1 <- Thy.smt2App smt2VarAs f s0
+               case s1 of
+                 Just b -> pure b
+                 Nothing -> do s2 <- smt2 f
+                               s3 <- smt2s es
+                               pure $ parenSeqs [s2, s3]
   where
-    (f, es)   = splitEApp' e
+    (f, es) = splitEApp' e
 
-smt2Coerc :: SymEnv -> Sort -> Sort -> Expr -> Builder
-smt2Coerc env t1 t2 e
-  | t1 == t2  = smt2 env e
-  | otherwise = parenSeqs [Builder.fromText coerceFn , smt2 env e]
-  where
-    coerceFn  = symbolAtName coerceName env (ECoerc t1 t2 e) t
-    t         = FFunc t1 t2
+smt2Coerc :: Sort -> Sort -> Expr -> SymM Builder
+smt2Coerc t1 t2 e
+  | t1 == t2  = smt2 e
+  | otherwise = do coerceFn <- symbolAtName coerceName (FFunc t1 t2)
+                   s <- smt2 e
+                   pure $ parenSeqs [Builder.fromText coerceFn , s]
 
 splitEApp' :: Expr -> (Expr, [Expr])
 splitEApp'            = go []
@@ -217,50 +269,76 @@
   --   go acc (ECst e _) = go acc e
     go acc e          = (e, acc)
 
-mkRel :: SymEnv -> Brel -> Expr -> Expr -> Builder
-mkRel env Ne  e1 e2 = mkNe env e1 e2
-mkRel env Une e1 e2 = mkNe env e1 e2
-mkRel env r   e1 e2 = parenSeqs [smt2 env r, smt2 env e1, smt2 env e2]
-
-mkNe :: SymEnv -> Expr -> Expr -> Builder
-mkNe env e1 e2      = key "not" (parenSeqs ["=",  smt2 env e1, smt2 env e2])
+mkRel :: Brel -> Expr -> Expr -> SymM Builder
+mkRel Ne  e1 e2 = mkNe e1 e2
+mkRel Une e1 e2 = mkNe e1 e2
+mkRel r   e1 e2 = do s <- smt2 r
+                     s1 <- smt2 e1
+                     s2 <- smt2 e2
+                     pure $ parenSeqs [s, s1, s2]
 
+mkNe :: Expr -> Expr -> SymM Builder
+mkNe e1 e2 = do s1 <- smt2 e1
+                s2 <- smt2 e2
+                pure $ key "not" (parenSeqs ["=", s1, s2])
 instance SMTLIB2 Command where
-  smt2 env (DeclData ds)       = key "declare-datatypes" (smt2data env ds)
-  smt2 env (Declare x ts t)    = parenSeqs ["declare-fun", Builder.fromText x, parens (smt2many (smt2 env <$> ts)), smt2 env t]
-  smt2 env c@(Define t)        = key "declare-sort" (smt2SortMono c env t)
-  smt2 env (DefineFunc name paramxs rsort e) =
-    let bParams = [ parenSeqs [smt2 env s, smt2 env t] | (s, t) <- paramxs]
-     in parenSeqs ["define-fun", smt2 env name, parenSeqs bParams, smt2 env rsort, smt2 env e]
-  smt2 env (Assert Nothing p)  = {-# SCC "smt2-assert" #-} key "assert" (smt2 env p)
-  smt2 env (Assert (Just i) p) = {-# SCC "smt2-assert" #-} key "assert" (parens ("!"<+> smt2 env p <+> ":named p-" <> bShow i))
-  smt2 env (Distinct az)
-    | length az < 2            = ""
-    | otherwise                = key "assert" (key "distinct" (smt2s env az))
-  smt2 env (AssertAx t)        = key "assert" (smt2 env t)
-  smt2 _   Push                = "(push 1)"
-  smt2 _   Pop                 = "(pop 1)"
-  smt2 _   CheckSat            = "(check-sat)"
-  smt2 env (GetValue xs)       = key "key-value" (parens (smt2s env xs))
-  smt2 env (CMany cmds)        = smt2many (smt2 env <$> cmds)
-  smt2 _   Exit                = "(exit)"
-  smt2 _   SetMbqi             = "(set-option :smt.mbqi true)"
+  smt2     (DeclData ds)       = do s <- smt2data ds
+                                    pure $ key "declare-datatypes" s
+  smt2     (Declare x ts t)    = do s <- smt2s ts
+                                    s1 <- smt2 t
+                                    pure $ parenSeqs ["declare-fun", Builder.fromText x, parens s, s1]
+  smt2     c@(Define t)        = do s <- smt2SortMono c t
+                                    pure $ key "declare-sort" s
+  smt2     (DefineFunc name paramxs rsort e) =
+    do n <- smt2 name
+       bParams <- traverse (\(s, t) -> do s0 <- smt2 s
+                                          s1 <- smt2 t
+                                          pure $ parenSeqs [s0 , s1]) paramxs
+       r <- smt2 rsort
+       e' <- smt2 e
+       pure $ parenSeqs ["define-fun", n, parenSeqs bParams, r, e']
 
+  smt2     (Assert Nothing p)  = {-# SCC "smt2-assert" #-}
+                                  do s <- smt2 p
+                                     pure $ key "assert" s
+  smt2     (Assert (Just i) p) = {-# SCC "smt2-assert" #-}
+                                  do s <- smt2 p
+                                     pure $ key "assert" (parens ("!"<+> s <+> ":named p-" <> bShow i))
+  smt2     (Distinct az)
+    | length az < 2            = pure ""
+    | otherwise                = do s <- smt2s az
+                                    pure $ key "assert" $ key "distinct" s
+  smt2     (AssertAx t)        = do s <- smt2 t
+                                    pure $ key "assert" s
+  smt2     Push                = pure "(push 1)"
+  smt2     Pop                 = pure "(pop 1)"
+  smt2     CheckSat            = pure "(check-sat)"
+  smt2     (GetValue xs)       = do s <- smt2s xs
+                                    pure $ key "key-value" (parens s)
+  smt2     (CMany cmds)        = smt2s cmds
+  smt2     Exit                = pure "(exit)"
+  smt2     SetMbqi             = pure "(set-option :smt.mbqi true)"
+  smt2     (Comment t)         = pure $ fromText ("; " <> t <> "\n")
+
 instance SMTLIB2 (Triggered Expr) where
-  smt2 env (TR NoTrigger e)       = smt2 env e
-  smt2 env (TR _ (PExist [] p))   = smt2 env p
-  smt2 env t@(TR _ (PExist xs p)) = smtTr env "exists" xs p t
-  smt2 env (TR _ (PAll   [] p))   = smt2 env p
-  smt2 env t@(TR _ (PAll   xs p)) = smtTr env "forall" xs p t
-  smt2 env (TR _ e)               = smt2 env e
+  smt2 (TR NoTrigger e)       = smt2 e
+  smt2 (TR _ (PExist [] p))   = smt2 p
+  smt2 t@(TR _ (PExist xs p)) = smtTr "exists" xs p t
+  smt2 (TR _ (PAll   [] p))   = smt2 p
+  smt2 t@(TR _ (PAll   xs p)) = smtTr "forall" xs p t
+  smt2 (TR _ e)               = smt2 e
 
 {-# INLINE smtTr #-}
-smtTr :: SymEnv -> Builder -> [(Symbol, Sort)] -> Expr -> Triggered Expr -> Builder
-smtTr env q xs p t = key q (parens (smt2s env xs) <+> key "!" (smt2 env p <+> ":pattern" <> parens (smt2s env (makeTriggers t))))
+smtTr :: Builder -> [(Symbol, Sort)] -> Expr -> Triggered Expr -> SymM Builder
+smtTr q xs p t =
+  do s <- smt2s xs
+     s1 <- smt2 p
+     s2 <- smt2s (makeTriggers t)
+     pure $ key q (parens s <+> key "!" (s1 <+> ":pattern" <> parens s2))
 
 {-# INLINE smt2s #-}
-smt2s    :: SMTLIB2 a => SymEnv -> [a] -> Builder
-smt2s env as = smt2many (smt2 env <$> as)
+smt2s :: SMTLIB2 a => [a] -> SymM Builder
+smt2s as = smt2many <$> traverse smt2 as
 
 {-# INLINE smt2many #-}
 smt2many :: [Builder] -> Builder
diff --git a/src/Language/Fixpoint/Smt/Theories.hs b/src/Language/Fixpoint/Smt/Theories.hs
--- a/src/Language/Fixpoint/Smt/Theories.hs
+++ b/src/Language/Fixpoint/Smt/Theories.hs
@@ -3,7 +3,6 @@
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE UndecidableInstances      #-}
-{-# LANGUAGE PatternGuards             #-}
 {-# LANGUAGE ViewPatterns              #-}
 
 {-# OPTIONS_GHC -Wno-orphans           #-}
@@ -32,7 +31,7 @@
      , dataDeclSymbols
 
        -- * Theories
-     , setEmpty, setEmp, setSng, setAdd, setMem
+     , setEmpty, setEmp, setSng, setAdd, setMem, setCard
      , setCom, setCap, setCup, setDif, setSub
 
      , mapDef, mapSel, mapSto
@@ -49,6 +48,9 @@
      , arrConstB, arrStoreB, arrSelectB
      , arrMapPlusB, arrMapLeB, arrMapGtB, arrMapIteB
 
+     -- * CVC5 finite fields
+     , ffVal, ffAdd, ffMul
+
       -- * Query Theories
      , isSmt2App
      , axiomLiterals
@@ -56,6 +58,7 @@
      ) where
 
 import           Prelude hiding (map)
+import           Control.Monad.State
 import           Data.ByteString.Builder (Builder)
 import           Language.Fixpoint.Types.Sorts
 import           Language.Fixpoint.Types.Config
@@ -139,7 +142,8 @@
 mapSel   = "Map_select"
 mapSto   = "Map_store"
 
-setEmpty, setEmp, setCap, setSub, setAdd, setMem, setCom, setCup, setDif, setSng :: (IsString a) => a
+setCard, setEmpty, setEmp, setCap, setSub, setAdd, setMem, setCom, setCup, setDif, setSng :: (IsString a) => a
+setCard  = "Set_card"
 setEmpty = "Set_empty"
 setEmp   = "Set_emp"
 setCap   = "Set_cap"
@@ -195,20 +199,34 @@
 arrMapGtB   = "arr_map_gt"
 arrMapIteB   = "arr_map_ite"
 
-strLen, strSubstr, strConcat :: (IsString a) => a -- Symbol
+-- Finite field operations
+ffVal, ffAdd, ffMul :: (IsString a) => a -- Symbol
+ffVal = "FF_val"
+ffAdd = "FF_add"
+ffMul = "FF_mul"
+
+strLen, strSubstr, strConcat, strConcat', strPrefixOf, strSuffixOf, strContains :: (IsString a) => a -- Symbol
 strLen    = "strLen"
 strSubstr = "subString"
 strConcat = "concatString"
+strConcat' = "strConcat"
+strPrefixOf  = "strPrefixOf"
+strSuffixOf = "strSuffixOf"
+strContains = "strContains"
 
-smtlibStrLen, smtlibStrSubstr, smtlibStrConcat :: Raw
+smtlibStrLen, smtlibStrSubstr, smtlibStrConcat, smtlibStrPrefixOf, smtlibStrSuffixOf, smtlibStrContains :: Raw
 smtlibStrLen    = "str.len"
 smtlibStrSubstr = "str.substr"
 smtlibStrConcat = "str.++"
+smtlibStrPrefixOf = "str.prefixof"
+smtlibStrSuffixOf = "str.suffixof"
+smtlibStrContains = "str.contains"
 
-strLenSort, substrSort, concatstrSort :: Sort
+strLenSort, substrSort, concatstrSort, strCompareSort :: Sort
 strLenSort    = FFunc strSort intSort
 substrSort    = mkFFunc 0 [strSort, intSort, intSort, strSort]
 concatstrSort = mkFFunc 0 [strSort, strSort, strSort]
+strCompareSort = mkFFunc 0 [strSort, strSort, boolSort]
 
 string :: Raw
 string = strConName
@@ -256,6 +274,7 @@
      , (SOnly [Cvc5],       "(set-logic ALL)")
      , (SOnly [Cvc4, Cvc5], "(set-option :incremental true)")
      ]
+  ++ setPreamble cfg
   ++ boolPreamble cfg
   ++ arithPreamble cfg
   ++ stringPreamble cfg
@@ -265,6 +284,10 @@
 data PreambleCondition = SAll | SOnly [SMTSolver]
   deriving (Eq, Show)
 
+setPreamble :: Config -> [Preamble]
+-- Z3 does not support cardinality on sets, which is defined to be uninterpreted function
+setPreamble _
+  = [ (SOnly [Z3, Z3mem],  bFun' "set.card" ["(Array Int Bool)"] "Int") ]
 
 boolPreamble :: Config -> [Preamble]
 boolPreamble _
@@ -277,7 +300,7 @@
  ]
 
 stringPreamble :: Config -> [Preamble]
-stringPreamble cfg | stringTheory cfg
+stringPreamble cfg | not (noStringTheory cfg)
   = [ (SAll, bSort string "String")
     , (SAll, bFun strLen [("s", fromText string)] "Int" (key (fromText smtlibStrLen) "s"))
     , (SAll, bFun strSubstr [("s", fromText string), ("i", "Int"), ("j", "Int")] (fromText string) (key (fromText smtlibStrSubstr) "s i j"))
@@ -298,7 +321,7 @@
 smt2Symbol env x = fromText . tsRaw <$> symEnvTheory x env
 
 instance SMTLIB2 SmtSort where
-  smt2 _ = smt2SmtSort
+  smt2 s = pure $ smt2SmtSort s
 
 smt2SmtSort :: SmtSort -> Builder
 smt2SmtSort SInt         = "Int"
@@ -308,6 +331,7 @@
 smt2SmtSort (SSet a)     = key "Set" (smt2SmtSort a)
 smt2SmtSort (SBag a)     = key "Bag" (smt2SmtSort a)
 smt2SmtSort (SArray a b) = key2 "Array" (smt2SmtSort a) (smt2SmtSort b)
+smt2SmtSort (SFFld n)    = key "_ FiniteField" (bShow n)
 smt2SmtSort (SBitVec n)  = key "_ BitVec" (bShow n)
 smt2SmtSort (SVar n)     = "T" <> bShow n
 smt2SmtSort (SData c []) = symbolBuilder c
@@ -318,37 +342,44 @@
 smt2SmtSorts :: [SmtSort] -> Builder
 smt2SmtSorts = seqs . fmap smt2SmtSort
 
-type VarAs = SymEnv -> Symbol -> Sort -> Builder
+type VarAs = Symbol -> Sort -> SymM Builder
 --------------------------------------------------------------------------------
-smt2App :: VarAs -> SymEnv -> Expr -> [Builder] -> Maybe Builder
+smt2App :: VarAs -> Expr -> [Builder] -> SymM (Maybe Builder)
 --------------------------------------------------------------------------------
-smt2App _ env ex@(dropECst -> EVar f) [d]
-  | f == arrConstS = Just (key (key "as const" (getTarget ex)) d)
-  | f == arrConstB = Just (key (key "as const" (getTarget ex)) d)
-  | f == arrConstM = Just (key (key "as const" (getTarget ex)) d)
-  | f == setEmpty  = Just (key "as set.empty" (getTarget ex))
-  | f == bagEmpty  = Just (key "as bag.empty" (getTarget ex))
+smt2App _ ex@(dropECst -> EVar f) [d]
+  | f == arrConstS || f == arrConstB || f == arrConstM =
+      do env <- get
+         pure $ Just $ key (key "as const" (getTarget env ex)) d
+  | f == setEmpty  =
+      do env <- get
+         pure $ Just $ key "as set.empty" (getTarget env ex)
+  | f == bagEmpty  =
+      do env <- get
+         pure $ Just $ key "as bag.empty" (getTarget env ex)
+  | f == ffVal  =
+      do env <- get
+         pure $ Just $ key ("as ff" <> d) (getTarget env ex)
   where
-    getTarget :: Expr -> Builder
+    getTarget :: SymEnv -> Expr -> Builder
     -- const is a function, but SMT expects only the output sort
-    getTarget (ECst _ t) = smt2SmtSort $ sortSmtSort True (seData env) (ffuncOut t)
-    getTarget e = bShow e
-
-smt2App k env ex (builder:builders)
-  | Just fb <- smt2AppArg k env ex
-  = Just $ key fb (builder <> mconcat [ " " <> d | d <- builders])
-
-smt2App _ _ _ _    = Nothing
+    getTarget env (ECst _ t) = smt2SmtSort $ sortSmtSort True (seData env) (ffuncOut t)
+    getTarget _ e = bShow e
 
-smt2AppArg :: VarAs -> SymEnv -> Expr -> Maybe Builder
-smt2AppArg k env (ECst (dropECst -> EVar f) t)
-  | Just fThy <- symEnvTheory f env
-  = Just $ if isPolyCtor fThy t
-            then k env f (ffuncOut t)
-            else fromText (tsRaw fThy)
+smt2App k ex (builder:builders) =
+  do a <- smt2AppArg k ex
+     pure $ (\fb -> key fb (builder <> mconcat [ " " <> d | d <- builders])) <$> a
+smt2App _ _ [] = pure Nothing
 
-smt2AppArg _ _ _
-  = Nothing
+smt2AppArg :: VarAs -> Expr -> SymM (Maybe Builder)
+smt2AppArg k (ECst (dropECst -> EVar f) t)
+  = do env <- get
+       case symEnvTheory f env of
+         Just fThy -> if isPolyCtor fThy t
+                           then Just <$> k f (ffuncOut t)
+                           else pure $ Just $ fromText (tsRaw fThy)
+         Nothing   -> pure Nothing
+smt2AppArg _ _
+  = pure Nothing
 
 isPolyCtor :: TheorySymbol -> Sort -> Bool
 isPolyCtor fThy t = isPolyInst (tsSort fThy) t && tsInterp fThy == Ctor
@@ -388,9 +419,13 @@
   theorySymbols :: [DataDecl] -> SEnv TheorySymbol
   theorySymbols = fromListSEnv . concatMap dataDeclSymbols
 
+
 instance TheorySymbols [Equation] where
   theorySymbols = fromListSEnv . fmap equationSymbol
 
+instance TheorySymbols DefinedFuns where
+  theorySymbols (MkDefinedFuns eqns) = theorySymbols eqns
+
 equationSymbol :: Equation -> (Symbol, TheorySymbol)
 equationSymbol eq = (sym, Thy sym (symbolRaw sym) sort Defined)
   where
@@ -414,6 +449,7 @@
 
   -- CVC5 sets
 
+  , interpSym setCard  "set.card"       (FAbs 0 $ FFunc (setSort $ FVar 0) intSort)
   , interpSym setEmp   "set.is_empty"   (FAbs 0 $ FFunc (setSort $ FVar 0) boolSort)
   , interpSym setEmpty "set.empty"      (FAbs 0 $ FFunc intSort (setSort $ FVar 0))
   , interpSym setSng   "set.singleton"  (FAbs 0 $ FFunc (FVar 0) (setSort $ FVar 0))
@@ -435,14 +471,15 @@
   , interpSym bagMin   "bag.inter_min"      bagBopSort
   , interpSym bagSub   "bag.subbag"         (FAbs 0 $ FFunc (bagSort $ FVar 0) $ FFunc (bagSort $ FVar 0) boolSort)
 
-  -- , interpSym bvOrName  "bvor"  bvBopSort
-  -- , interpSym bvAndName "bvand" bvBopSort
-  -- , interpSym bvAddName "bvadd" bvBopSort
-  -- , interpSym bvSubName "bvsub" bvBopSort
+  -- Strings
+  , interpSym strLen     strLen    strLenSort
+  , interpSym strSubstr  strSubstr substrSort
+  , interpSym strConcat  strConcat concatstrSort
+  , interpSym strConcat' smtlibStrConcat concatstrSort
+  , interpSym strPrefixOf smtlibStrPrefixOf strCompareSort
+  , interpSym strSuffixOf smtlibStrSuffixOf strCompareSort
+  , interpSym strContains smtlibStrContains strCompareSort
 
-  , interpSym strLen    strLen    strLenSort
-  , interpSym strSubstr strSubstr substrSort
-  , interpSym strConcat strConcat concatstrSort
   , interpSym boolInt   boolInt   (FFunc boolSort intSort)
 
   -- Function mappings for indexed identifier functions
@@ -489,12 +526,18 @@
   , interpBvCmp bvSLeName
   , interpBvCmp bvSGtName
   , interpBvCmp bvSGeName
+
+  -- int to bv Conversions
+
   , interpSym intbv32Name   "(_ int2bv 32)" (FFunc intSort bv32)
   , interpSym intbv64Name   "(_ int2bv 64)" (FFunc intSort bv64)
-  , interpSym bv32intName   (bv2i cfg 32) (FFunc bv32    intSort)
-  , interpSym bv64intName   (bv2i cfg 64) (FFunc bv64    intSort)
-  -- , interpSym bv32intName   "(_ bv2int 32)" (FFunc bv32    intSort)
-  -- , interpSym bv64intName   "(_ bv2int 64)" (FFunc bv64    intSort)
+  , interpSym bv32intName   (bv2i cfg 32)   (FFunc bv32    intSort)
+  , interpSym bv64intName   (bv2i cfg 64)   (FFunc bv64    intSort)
+
+  , interpSym intbv8Name    "(_ int2bv 8)"  (FFunc intSort bv8)
+  , interpSym intbv16Name   "(_ int2bv 16)" (FFunc intSort bv16)
+  , interpSym bv8intName    (bv2i cfg 32)   (FFunc bv8    intSort)
+  , interpSym bv16intName   (bv2i cfg 64)   (FFunc bv16    intSort)
   ]
   ++
   if cfg == Z3 || cfg == Z3mem
@@ -521,13 +564,22 @@
   , interpSym arrMapLeB   "(_ map (<= (Int Int) Bool))"      (FAbs 0 $ FFunc bagArrSort $ FFunc bagArrSort setArrSort)
   , interpSym arrMapGtB   "(_ map (> (Int Int) Bool))"       (FAbs 0 $ FFunc bagArrSort $ FFunc bagArrSort setArrSort)
   , interpSym arrMapIteB  "(_ map (ite (Bool Int Int) Int))" (FAbs 0 $ FFunc setArrSort $ FFunc bagArrSort $ FFunc bagArrSort bagArrSort)
+  ] else if cfg == Cvc5
+  then
+  [
+    -- CVC5 finite fields
+
+    interpSym ffVal ffVal     (FAbs 0 $ FFunc intSort (finfieldSort (FVar 0)))
+  , interpSym ffAdd "ff.add" (FAbs 0 $ FFunc (finfieldSort (FVar 0)) $ FFunc (finfieldSort (FVar 0)) (finfieldSort (FVar 0)))
+  , interpSym ffMul "ff.mul" (FAbs 0 $ FFunc (finfieldSort (FVar 0)) $ FFunc (finfieldSort (FVar 0)) (finfieldSort (FVar 0)))
   ] else []
   where
 
     mapArrSort = arraySort (FVar 0) (FVar 1)
     setArrSort = arraySort (FVar 0) boolSort
     bagArrSort = arraySort (FVar 0) intSort
-    -- (sizedBitVecSort "Size1")
+    bv8        = sizedBitVecSort "Size8"
+    bv16       = sizedBitVecSort "Size16"
     bv32       = sizedBitVecSort "Size32"
     bv64       = sizedBitVecSort "Size64"
     boolInt    = boolToIntName
@@ -630,17 +682,29 @@
 interpSym :: Symbol -> Raw -> Sort -> (Symbol, TheorySymbol)
 interpSym x n t = (x, Thy x n t Theory)
 
--- This variable is uded to generate the lambda names `lam_arg$n` in
+-- This variable is used to generate the lambda names `lam_arg$n` in
 -- `Interface.hs` that will be used during defunctionalization in
--- `Defunctionalize.hs`, is a pretty gross hack as if the user typees in the
--- program or ple generates a term that has more than `maxLamArg` lambda binders
--- one inside the other, the smt will crash complaining that
--- `lam_arg${maxLamArg}` was not declared.
+-- `Defunctionalize.hs`, is a pretty gross hack as if the user types in the
+-- program or PLE generates a term that has more than `maxLamArg` lambda binders
+-- one inside the other, the SMT will crash complaining that
+-- `lam_arg${maxLamArg + k}` was not declared.
 maxLamArg :: Int
 maxLamArg = 20
 
-axiomLiterals :: [(Symbol, Sort)] -> [Expr]
-axiomLiterals lts = catMaybes [ lenAxiom l <$> litLen l | (l, t) <- lts, isString t ]
+axiomLiterals :: Config -> [(Symbol, Sort)] -> [Expr]
+axiomLiterals cfg
+  | noStringTheory cfg = lenAxiomLiterals
+  | otherwise          = strAxiomLiterals
+
+strAxiomLiterals :: [(Symbol, Sort)] -> [Expr]
+strAxiomLiterals lts = catMaybes [ strAxiom l | (l, t) <- lts, isString t ]
+  where
+    strAxiom l = do
+      sym <- unLitSymbol l
+      pure (EEq (expr l) (ECon $ L (symbolText sym) strSort))
+
+lenAxiomLiterals :: [(Symbol, Sort)] -> [Expr]
+lenAxiomLiterals lts = catMaybes [ lenAxiom l <$> litLen l | (l, t) <- lts, isString t ]
   where
     lenAxiom l n  = EEq (EApp (expr (strLen :: Symbol)) (expr l)) (expr n `ECst` intSort)
     litLen        = fmap (Data.Text.length .  symbolText) . unLitSymbol
diff --git a/src/Language/Fixpoint/Smt/Types.hs b/src/Language/Fixpoint/Smt/Types.hs
--- a/src/Language/Fixpoint/Smt/Types.hs
+++ b/src/Language/Fixpoint/Smt/Types.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE FlexibleInstances         #-}
+{-# LANGUAGE TupleSections             #-}
 {-# LANGUAGE FlexibleContexts          #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE OverloadedStrings         #-}
@@ -25,11 +26,18 @@
     -- * SMTLIB2 Process Context
     , Context (..)
 
-    ) where
+    -- * SMT monad
+    , SmtM
+    , liftSym
+    , catchSMT
+    , bracketSMT
 
+    ) where
+import           Control.Exception
+import           Control.Monad.State
 import           Data.ByteString.Builder (Builder)
 import           Language.Fixpoint.Types
-import           Language.Fixpoint.Types.Config (ElabFlags)
+import           Language.Fixpoint.Types.Config (ElabFlags, Config)
 import qualified Data.Text                as T
 import           Text.PrettyPrint.HughesPJ
 import qualified SMTLIB.Backends
@@ -59,6 +67,7 @@
                   | Distinct [Expr] -- {v:[Expr] | 2 <= len v}
                   | GetValue [Symbol]
                   | CMany    [Command]
+                  | Comment T.Text
                   deriving (Eq, Show)
 
 instance PPrint Command where
@@ -81,6 +90,7 @@
 ppCmd Distinct {} = text "Distinct ..."
 ppCmd GetValue {} = text "GetValue ..."
 ppCmd CMany {}    = text "CMany ..."
+ppCmd (Comment t) = text ("; " ++ T.unpack t)
 
 -- | Responses received from SMT engine
 data Response     = Ok
@@ -102,15 +112,44 @@
   , ctxLog     :: !(Maybe Handle)
   , ctxVerbose :: !Bool
   , ctxSymEnv  :: !SymEnv
-  , ctxDefines :: ![Equation]
+  -- | The stack of sort indexes which were fresh at the corresponding level of push/pop stack.
+  , ctxIxs     :: ![Int]
+  , ctxDefines :: DefinedFuns
+  -- | Flag which controls the generation SMT placeholders for lambda arguments
+  --   See also `L.F.Smt.Theories.maxLamArg`
+  , ctxLams    :: !Bool
+  -- | Configuration options
+  , config     :: !Config
   }
 
+-- | SMT monad, used to communicate with the SMT solver backend.
+--   The `SymM` monad embeds into it, as the symbolic state has to be threaded
+--   through for gnerating `apply`s and other function sort symbols.
+type SmtM = StateT Context IO
+
+liftSym :: SymM a -> SmtM a
+liftSym s =
+  do ctx <- get
+     let (a, env') = runState s (ctxSymEnv ctx)
+     put (ctx {ctxSymEnv = env'})
+     pure a
+
+catchSMT :: Exception e => SmtM a -> (e -> IO a) -> SmtM a
+catchSMT action handler = StateT $ \s -> catch (runStateT action s) (fmap (, s) . handler)
+
+bracketSMT :: SmtM a -> (a -> IO b) -> (a -> SmtM c) -> SmtM c
+bracketSMT acquire release use = StateT $ \s ->
+  bracket
+    (runStateT acquire s)
+    (\(resource, _) -> release resource)
+    (\(resource, intermediateState) -> runStateT (use resource) intermediateState)
+
 --------------------------------------------------------------------------------
 -- | AST Conversion: Types that can be serialized ------------------------------
 --------------------------------------------------------------------------------
 
 class SMTLIB2 a where
-  smt2 :: SymEnv -> a -> Builder
+  smt2 :: a -> SymM Builder
 
-runSmt2 :: (SMTLIB2 a) => SymEnv -> a -> Builder
+runSmt2 :: (SMTLIB2 a) => a -> SymM Builder
 runSmt2 = smt2
diff --git a/src/Language/Fixpoint/Solver.hs b/src/Language/Fixpoint/Solver.hs
--- a/src/Language/Fixpoint/Solver.hs
+++ b/src/Language/Fixpoint/Solver.hs
@@ -8,7 +8,7 @@
 
 module Language.Fixpoint.Solver (
     -- * Invoke Solver on an FInfo
-    solve, Solver
+    solve
 
     -- * Invoke Solver on a .fq file
   , solveFQ
@@ -26,26 +26,28 @@
 
 import           Control.Concurrent                 (setNumCapabilities)
 import qualified Data.HashMap.Strict              as HashMap
+import qualified Data.HashSet                     as HashSet
 import qualified Data.Store                       as S
 import           Data.Aeson                         (ToJSON, encode)
-import qualified Data.List as L
 import qualified Data.Text.Lazy.IO                as LT
 import qualified Data.Text.Lazy.Encoding          as LT
 import           System.Exit                        (ExitCode (..))
 import           System.Console.CmdArgs.Verbosity   (whenNormal, whenLoud)
-import           Text.PrettyPrint.HughesPJ          (render)
-import           Control.Monad                      (mplus, when)
-import           Control.Exception                  (catch)
+import           Control.Monad                      (when)
+import           Control.Exception                  (SomeException, catch)
+import           Control.Exception.Compat
+    (ExceptionWithContext(..), displayExceptionContext, wrapExceptionWithContext)
 import           Language.Fixpoint.Solver.EnvironmentReduction
   (reduceEnvironments, simplifyBindings)
 import           Language.Fixpoint.Solver.Sanitize  (symbolEnv, sanitize)
 import           Language.Fixpoint.Solver.UniqifyBinds (renameAll)
 import           Language.Fixpoint.Defunctionalize (defunctionalize)
-import           Language.Fixpoint.SortCheck            (ElabParam (..), Elaborate (..), unElab)
+import           Language.Fixpoint.SortCheck            (ElabParam (..), Elaborate (..), unElab, unElabFSetBagZ3)
 import           Language.Fixpoint.Solver.Extensionality (expand)
 import           Language.Fixpoint.Solver.Prettify (savePrettifiedQuery)
 import           Language.Fixpoint.Solver.UniqifyKVars (wfcUniqify)
 import qualified Language.Fixpoint.Solver.Solve     as Sol
+import qualified Language.Fixpoint.Solver.Solution  as Sol
 import           Language.Fixpoint.Types.Config
 import           Language.Fixpoint.Types.Errors
 import           Language.Fixpoint.Utils.Files            hiding (Result)
@@ -56,10 +58,10 @@
 import           Language.Fixpoint.Types hiding (GInfo(..), fi)
 import qualified Language.Fixpoint.Types as Types (GInfo(..))
 import           Language.Fixpoint.Minimize (minQuery, minQuals, minKvars)
-import           Language.Fixpoint.Solver.Instantiate (instantiate)
 import           Control.DeepSeq
 import qualified Data.ByteString as B
-import Data.Maybe (catMaybes, mapMaybe)
+import Data.Maybe (catMaybes)
+import qualified Text.PrettyPrint.HughesPJ as PJ
 
 ---------------------------------------------------------------------------
 -- | Solve an .fq file ----------------------------------------------------
@@ -86,7 +88,7 @@
     jStr    = LT.decodeUtf8 . encode $ r
     stat    = resStatus $!! r
     eCode   = resultExit . resStatus
-    statStr = render . resultDoc
+    statStr = PJ.render . resultDoc
 
 ignoreQualifiers :: Config -> FInfo a -> FInfo a
 ignoreQualifiers cfg fi
@@ -97,7 +99,9 @@
 --------------------------------------------------------------------------------
 -- | Solve FInfo system of horn-clause constraints -----------------------------
 --------------------------------------------------------------------------------
-solve :: (PPrint a, NFData a, Fixpoint a, Show a, Loc a) => Solver a
+solve
+  :: (PPrint a, NFData a, Fixpoint a, Show a, Loc a)
+  => Config -> FInfo a -> IO (Result (Integer, a))
 --------------------------------------------------------------------------------
 solve cfg q
   | parts cfg      = partition  cfg        $!! q
@@ -107,15 +111,16 @@
   | minimizeKs cfg = minKvars cfg solve'   $!! q
   | otherwise      = solve'     cfg        $!! q
 
-solve' :: (PPrint a, NFData a, Fixpoint a, Show a, Loc a) => Solver a
+
+solve'
+  :: (PPrint a, NFData a, Fixpoint a, Show a, Loc a)
+  => Config -> FInfo a -> IO (Result (Integer, a))
 solve' cfg q = do
   when (save cfg) $ saveQuery   cfg q
-  configSW  cfg     solveNative cfg q
-
-configSW :: (NFData a, Fixpoint a, Show a, Loc a) => Config -> Solver a -> Solver a
-configSW cfg
-  | multicore cfg = solveParWith
-  | otherwise     = solveSeqWith
+  if multicore cfg then
+    solvePar cfg q
+  else
+    solveNative cfg (slice cfg q)
 
 --------------------------------------------------------------------------------
 readFInfo :: FilePath -> IO (FInfo (), [String])
@@ -140,17 +145,11 @@
 --------------------------------------------------------------------------------
 -- | Solve in parallel after partitioning an FInfo to indepdendant parts
 --------------------------------------------------------------------------------
-solveSeqWith :: (Fixpoint a) => Solver a -> Solver a
-solveSeqWith s c fi0 = {- withProgressFI fi $ -} s c fi
-  where
-    fi               = slice c fi0
-
---------------------------------------------------------------------------------
--- | Solve in parallel after partitioning an FInfo to indepdendant parts
---------------------------------------------------------------------------------
-solveParWith :: (Fixpoint a) => Solver a -> Solver a
+solvePar
+  :: (Loc a, NFData a, PPrint a, Show a, Fixpoint a)
+  => Config -> FInfo a -> IO (Result (Integer, a))
 --------------------------------------------------------------------------------
-solveParWith s c fi0 = do
+solvePar c fi0 = do
   -- putStrLn "Using Parallel Solver \n"
   let fi    = slice c fi0
   mci      <- mcInfo c
@@ -161,10 +160,10 @@
   writeLoud $ "maximum part size    : " ++ show (maxPartSize c)
   case fis of
     []        -> errorstar "partiton' returned empty list!"
-    [onePart] -> s c onePart
-    _         -> inParallelUsing (f s c) $ zip [1..] fis
+    [onePart] -> solveNative c onePart
+    _         -> inParallelUsing (f c) $ zip [1..] fis
     where
-      f s' c' (j, fi) = s' (c {srcFile = queryFile (Part j) c'}) fi
+      f c' (j, fi) = solveNative (c {srcFile = queryFile (Part j) c'}) fi
 
 --------------------------------------------------------------------------------
 -- | Solve a list of FInfos using the provided solver function in parallel
@@ -180,26 +179,35 @@
 --------------------------------------------------------------------------------
 -- | Native Haskell Solver -----------------------------------------------------
 --------------------------------------------------------------------------------
-solveNative, solveNative' :: (NFData a, Fixpoint a, Show a, Loc a, PPrint a) => Solver a
+solveNative, solveNative'
+  :: (NFData a, Fixpoint a, Show a, Loc a, PPrint a)
+  => Config -> FInfo a -> IO (Result (Integer, a))
 --------------------------------------------------------------------------------
 solveNative !cfg !fi0 = solveNative' cfg fi0
                           `catch`
-                             (return . crashResult (errorMap fi0))
+                             (return . crashResult (errorMap fi0) . wrapExceptionWithContext)
+                          `catch`
+                             (return . crashResultOther . wrapExceptionWithContext)
 
-crashResult :: (PPrint a) => ErrorMap a -> Error -> Result (Integer, a)
-crashResult m err' = Result res mempty mempty mempty
+crashResult :: (PPrint a) => ErrorMap a -> ExceptionWithContext Error -> Result (Integer, a)
+crashResult m (ExceptionWithContext ectx ex) = Result res mempty mempty mempty
   where
-    res           = Crash es msg
-    es            = catMaybes [ findError m e | e <- ers ]
-    ers           = errs err'
-    msg | null ers = "Sorry, unexpected panic in liquid-fixpoint!"
-        --  {-dbgFalse-} True  = "Sorry, unexpected panic in liquid-fixpoint!\n" ++ crashMessage es
-        | otherwise = showpp err'
+    res = Crash es msg
+    es  = catMaybes [ findError m e | e <- ers ]
+    ers = errs ex
+    msg = displayExceptionContext ectx ++ "\n" ++ msg0
+    msg0 | null ers = "Sorry, unexpected panic in liquid-fixpoint!\n"
+                       ++ showpp ex
+         | otherwise = showpp ex
 
-_crashMessage :: [((Integer, a), Maybe String) ] -> String
-_crashMessage es = L.intercalate "\n" [ msg i s | ((i,_), Just s) <- es ]
+crashResultOther
+  :: ExceptionWithContext SomeException -> Result (Integer, a)
+crashResultOther (ExceptionWithContext ectx ex) =
+    Result res mempty mempty mempty
   where
-    msg i s = "Error in constraint " ++ show i ++ ":\n" ++ s
+    res = Crash [] msg
+    msg = displayExceptionContext ectx ++ "\n" ++ msg0
+    msg0 = "Sorry, unexpected panic in liquid-fixpoint!\n" ++ show ex
 
 -- | Unpleasant hack to save meta-data that can be recovered from SrcSpan
 type ErrorMap a = HashMap.HashMap SrcSpan a
@@ -207,7 +215,7 @@
 findError :: ErrorMap a -> Error1 -> Maybe ((Integer, a), Maybe String)
 findError m e = do
   ann <- HashMap.lookup (errLoc e) m
-  let str = render (errMsg e)
+  let str = PJ.render (errMsg e)
   return ((-1, ann), Just str)
 
 -- The order is important here: we want the "binders" to get the "precedence"
@@ -219,13 +227,13 @@
             ++ [ a | (_, (_,_, a)) <- bindEnvToList (Types.bs fi) ]
 
 loudDump :: (Fixpoint a) => Int -> Config -> SInfo a -> IO ()
-loudDump i cfg si = when False (writeLoud $ msg ++ render (toFixpoint cfg si))
+loudDump i cfg si = when False (writeLoud $ msg ++ PJ.render (toFixpoint cfg si))
   where
     msg           = "fq file after Uniqify & Rename " ++ show i ++ "\n"
 
 {-# SCC simplifyFInfo #-}
 simplifyFInfo :: (NFData a, Fixpoint a, Show a, Loc a)
-               => Config -> FInfo a -> IO (SInfo a)
+               => Config -> FInfo a -> IO (ElabParam, SInfo a)
 simplifyFInfo !cfg !fi0 = do
   -- writeLoud $ "fq file in: \n" ++ render (toFixpoint cfg fi)
   -- rnf fi0 `seq` donePhase Loud "Read Constraints"
@@ -250,13 +258,16 @@
   -- writeLoud $ "fq file after defunc: \n" ++ render (toFixpoint cfg si4)
   -- putStrLn $ "AXIOMS: " ++ showpp (asserts si4)
   loudDump 2 cfg si4
-  let si5  = {- SCC "elaborate" -} elaborate (ElabParam (solverFlags $ solver cfg) (atLoc dummySpan "solver") (symbolEnv cfg si4)) si4
+  let ef = solverFlags cfg
+      elabParam = ElabParam
+                     ef
+                     (atLoc dummySpan "solver")
+                     (coerceEnv ef (symbolEnv cfg si4))
+      si5  = elaborate elabParam si4
   -- writeLoud $ "fq file after elaborate: \n" ++ render (toFixpoint cfg si5)
   loudDump 3 cfg si5
   let si6 = if extensionality cfg then {- SCC "expand" -} expand cfg si5 else si5
-  if rewriteAxioms cfg && noLazyPLE cfg
-    then instantiate cfg si6 $!! Nothing
-    else return si6
+  return (elabParam, si6){- SCC "elaborate" -}
 
 reduceFInfo :: Fixpoint a => Config -> FInfo a -> IO (FInfo a)
 reduceFInfo cfg fi = do
@@ -264,15 +275,15 @@
       reducedFi = {- SCC "reduceEnvironments" -} reduceEnvironments simplifiedFi
   when (save cfg) $
     savePrettifiedQuery cfg reducedFi
-  if noEnvironmentReduction cfg then
+  if noEnvReduction cfg then
     return fi
   else
     return reducedFi
 
 solveNative' !cfg !fi0 = do
-  si6 <- simplifyFInfo cfg fi0
-  res0 <- {- SCC "Sol.solve" -} Sol.solve cfg $!! si6
-  let res = simplifyResult res0
+  (elabParam, si6) <- simplifyFInfo cfg fi0
+  res0 <- {- SCC "Sol.solve" -} Sol.solve cfg elabParam $!! si6
+  let res = simplifyResult cfg res0
   -- rnf soln `seq` donePhase Loud "Solve2"
   --let stat = resStatus res
   -- saveSolution cfg res
@@ -304,119 +315,27 @@
   writeFile f $ unlines $
     [ ""
     , "Solution:"
-    , showpp (resSolution  res)
+    , scopedRender (resSolution  res)
     ] ++
-    ( if gradual cfg then
-        ["", "", showpp $ gresSolution res]
-      else
-        []
-    ) ++
     [ ""
     , ""
     , "Non-cut kvars:"
     , ""
-    , showpp (HashMap.map unElab $ resNonCutsSolution res)
+    , scopedRender (HashMap.map forceDelayed $ resNonCutsSolution res)
     ]
+    where
+      scopedRender = PJ.render . PJ.vcat . map ncDoc . scoped
+      scoped sol = [ (k, scope k, e) | (k, e) <- HashMap.toList sol]
+      scope k = HashMap.lookupDefault [] k $ resSorts res
+      ncDoc (k, xts, e) = PJ.hsep [ pprint k PJ.<> pprint xts, ":=", pprint e ]
 
-simplifyResult :: Result a -> Result a
-simplifyResult res =
+simplifyResult :: Config -> Result a -> Result a
+simplifyResult cfg res =
     res
-      { resSolution = HashMap.map simplifyKVar (resSolution res)
-      , resNonCutsSolution = HashMap.map simplifyKVar (resNonCutsSolution res)
+      { resSolution = HashMap.map simplifyKVar' (resSolution res)
+      , resNonCutsSolution = HashMap.map (fmap simplifyKVar') (resNonCutsSolution res)
       }
-
--- | Simplifies existential expressions with unused or inconsequential bindings.
---
--- For instance, in the following example, "x" is not used at all.
---
--- > simplifyKVar "exists x y. y == z && y == C" == "exists y. y == z && y == C"
---
--- And in the following example, @x@ is used but in a way that doesn't
--- contribute any useful knowledge.
---
--- > simplifyKVar "exists x y. x == C && y == z && y == C"
--- >   ==
--- > "exists y. y == z && y == C"
---
--- We require that relevant variables occur more than once, or that
--- they occur in some other place than as an argument to @==@.
---
-simplifyKVar :: Expr -> Expr
-simplifyKVar (POr es) = POr $ map simplifyKVar es
-simplifyKVar (PExist bs e@(PAnd es)) =
-    let fvs = L.group $ L.sort $ collectFreeVarOccurrences e
-        esv = map (isUniqueEq fvs) es
-        removed = mapMaybe fst esv
-        needed = map head fvs L.\\ removed
-        bs' = filter ((`elem` needed) . fst) bs
-     in
-        PExist bs' $ PAnd $ [ei | (Nothing, ei) <- esv]
   where
-    -- | Determine if the expression is an equality that sets the value of
-    -- a variable that doesn't occur elsewhere.
-    --
-    -- In @isUniqueEq fvs e@, @fvs@ contains the occurrences of the free
-    -- variables, so we can infer if there is more than one occurrence
-    -- of a given free variable, and @e@ is the equality to analyze.
-    --
-    -- Yields @(Just v, e)@ if @v@ doesn't occur elsewhere, and @e@ has
-    -- the form @v == e'@.
-    isUniqueEq :: [[Symbol]] -> Expr -> (Maybe Symbol, Expr)
-    isUniqueEq fvs er = case unElab er of
-      PAtom brel e0 e1
-        | isEqRel brel ->
-          let m = isVarToDrop fvs e0 `mplus` isVarToDrop fvs e1
-           in (m, er)
-      _ ->
-        (Nothing, er)
-
-    -- | Tells if the binary relation is an equality.
-    isEqRel Eq = True
-    isEqRel Ueq = True
-    isEqRel _ = False
-
-    -- | @isVarToDrop fvs s@ yields @Just s@ if the variable @s@ doesn't occur
-    -- elsewhere according to @fvs@.
-    --
-    -- > isVarToDrop fvs (cast_as_int s) == isVarToDrop fvs s
-    --
-    isVarToDrop fvs (EApp (EVar "cast_as_int") ei) = isVarToDrop fvs ei
-    isVarToDrop fvs (EVar s)
-      | elem [s] fvs = Just s
-    isVarToDrop _fvs _ = Nothing
-
-simplifyKVar e = e
-
--- | Produces the free variables of an expressions as many times as they occur.
---
--- There are no guarantees on the order in which the variables are produced. For
--- instance,
---
--- > collectFreeVarOccurrences "z (y x) (y x)" == ["z", "y", "x", "y", "x"]
---
-collectFreeVarOccurrences :: Expr -> [Symbol]
-collectFreeVarOccurrences = go []
-  where
-    go acc e0 = case e0 of
-      ESym _ -> acc
-      ECon _ -> acc
-      EVar v -> v : acc
-      PKVar _ (Su m) -> foldr (flip go) acc $ HashMap.elems m
-      PGrad _ (Su m) _ e -> foldr (flip go) acc $ e : HashMap.elems m
-      ENeg e -> go acc e
-      PNot p -> go acc p
-      ECst e _t -> go acc e
-      PAll _xts p -> go acc p
-      ELam (b, _) e -> go acc e L.\\ [b]
-      ECoerc _a _t e -> go acc e
-      PExist _xts p -> go acc p
-      ETApp e _s -> go acc e
-      ETAbs e _s -> go acc e
-      EApp g e -> go (go acc e) g
-      EBin _o e1 e2 -> go (go acc e2) e1
-      PImp p1 p2 -> go (go acc p2) p1
-      PIff p1 p2 -> go (go acc p2) p1
-      PAtom _r e1 e2 -> go (go acc e2) e1
-      EIte p e1 e2 -> go (go (go acc e2) e1) p
-      PAnd ps -> foldr (flip go) acc ps
-      POr ps -> foldr (flip go) acc ps
+    simplifyKVar' = unElabSets . unElab . Sol.simplifyKVar HashSet.empty
+    sets          = elabSetBag . solverFlags $ cfg
+    unElabSets    = if sets then unElabFSetBagZ3 else id
diff --git a/src/Language/Fixpoint/Solver/Common.hs b/src/Language/Fixpoint/Solver/Common.hs
--- a/src/Language/Fixpoint/Solver/Common.hs
+++ b/src/Language/Fixpoint/Solver/Common.hs
@@ -2,30 +2,39 @@
 
 module Language.Fixpoint.Solver.Common (askSMT, toSMT) where
 
-import Language.Fixpoint.Types.Config (Config, solver, solverFlags)
+import Control.Monad.State
+import Language.Fixpoint.Types.Config (Config, solverFlags)
 import Language.Fixpoint.Smt.Interface (Context(..), checkValidWithContext)
+import Language.Fixpoint.Smt.Types (SmtM)
 import Language.Fixpoint.Types
 import Language.Fixpoint.Types.Visitor (kvarsExpr)
 import Language.Fixpoint.Defunctionalize (defuncAny)
 import Language.Fixpoint.SortCheck (ElabParam(..), elaborate)
+import GHC.Stack (HasCallStack)
 
 mytracepp :: (PPrint a) => String -> a -> a
 mytracepp = notracepp
 
-askSMT :: Config -> Context -> [(Symbol, Sort)] -> Expr -> IO Bool
-askSMT cfg ctx xs e
---   | isContraPred e  = return False
+askSMT
+  :: HasCallStack
+  => Config
+  -> [(Symbol, Sort)] -- ^ symbols already declared in the SMT solver
+  -> [(Symbol, Sort)] -- ^ symbols to declare in the SMT solver
+  -> Expr
+  -> SmtM Bool
+askSMT cfg bsInSMT xs e
   | isTautoPred  e     = return True
-  | null (kvarsExpr e) = checkValidWithContext ctx xs PTrue e'
+  | null (kvarsExpr e) =
+      do ctx <- get
+         let e' = toSMT "askSMT" cfg ctx (xs ++ bsInSMT) e
+         checkValidWithContext xs PTrue e'
   | otherwise          = return False
-  where
-    e' = toSMT "askSMT" cfg ctx xs e
 
-toSMT :: String -> Config -> Context -> [(Symbol, Sort)] -> Expr -> Pred
+toSMT :: HasCallStack => String -> Config -> Context -> [(Symbol, Sort)] -> Expr -> Pred
 toSMT msg cfg ctx xs e =
     defuncAny cfg symenv .
-        elaborate (ElabParam (solverFlags $ solver cfg) (dummyLoc msg) (elabEnv xs)) .
-            mytracepp ("toSMT from " ++ msg ++ showpp e) $
+        elaborate (ElabParam (solverFlags cfg) (dummyLoc msg) (elabEnv xs)) .
+            mytracepp ("toSMT from " ++ msg ++ " > " ++ showpp e) $
                 e
   where
     elabEnv = insertsSymEnv symenv
diff --git a/src/Language/Fixpoint/Solver/Eliminate.hs b/src/Language/Fixpoint/Solver/Eliminate.hs
--- a/src/Language/Fixpoint/Solver/Eliminate.hs
+++ b/src/Language/Fixpoint/Solver/Eliminate.hs
@@ -16,39 +16,61 @@
 import           Language.Fixpoint.Types.Visitor   (kvarsExpr, isConcC)
 import           Language.Fixpoint.Graph
 import           Language.Fixpoint.Misc            (safeLookup, group, errorstar)
-import           Language.Fixpoint.Solver.Sanitize
 
 --------------------------------------------------------------------------------
 -- | `solverInfo` constructs a `SolverInfo` comprising the Solution and various
 --   indices needed by the worklist-based refinement loop
+--
+-- Computes the set of cut and non-cut kvars, computes the hypotheses common
+-- to all of the usage sites of each kvar, then initializes the solutions of
+-- the non-cut KVars (in the sHyp field).
+--
+-- This is part of the implementation of the FUSION algorithm described in:
+--
+-- "Local Refinement Typing", ICFP 2017, https://ranjitjhala.github.io/static/local_refinement_typing.pdf
+--
 --------------------------------------------------------------------------------
 {-# SCC solverInfo #-}
-solverInfo :: Config -> SInfo a -> SolverInfo a b
+solverInfo :: Config -> SInfo a -> SolverInfo a
 --------------------------------------------------------------------------------
 solverInfo cfg sI = SI sHyp sI' cD cKs
   where
-    cD             = elimDeps     sI es nKs ebs
+    cD             = elimDeps     sI es nKs
     sI'            = cutSInfo     sI kI cKs
-    sHyp           = Sol.fromList sE mempty mempty kHyps kS [] sEnv
-    sEnv           = fromListSEnv [ (x, (i, sr_sort sr)) | (i, (x,sr, _)) <- bindEnvToList (bs sI)]
+    sHyp = Sol.Sol
+      { Sol.sMap = mempty
+      , Sol.sHyp = M.fromList kHyps
+      , Sol.sScp = kS
+      }
     kHyps          = nonCutHyps   sI kI nKs
     kI             = kIndex       sI
     (es, cKs, nKs) = kutVars cfg  sI
     kS             = kvScopes     sI es
-    sE             = symbolEnv   cfg sI
-    ebs            = S.fromList [x | i <- ebinds sI, let (x, _, _) = lookupBindEnv i (bs sI) ]
 
-
 --------------------------------------------------------------------------------
+-- | For each KVar, provide the intersection of the binding environments
+--   of all the constraints in which it appears.
+--
+-- See Section 2.4 of "Local Refinement Typing", ICFP 2017, for the motivation
+-- to collect these.
 kvScopes :: SInfo a -> [CEdge] -> M.HashMap KVar IBindEnv
-kvScopes sI es = is2env <$> kiM
+kvScopes sI es = commonBindingsOfConstraints <$> kvarUses
   where
-    is2env = foldr1 intersectionIBindEnv . fmap (senv . getSubC sI)
-    kiM    = group $ [(k, i) | (Cstr i, KVar k) <- es ] ++
-                     [(k, i) | (KVar k, Cstr i) <- es ]
+    -- | The common bindings of a list of constraints
+    commonBindingsOfConstraints :: [Integer] -> IBindEnv
+    commonBindingsOfConstraints =
+      foldr1 intersectionIBindEnv . fmap (senv . getSubC sI)
 
---------------------------------------------------------------------------------
+    -- | The constraints in which each KVar appears
+    kvarUses :: M.HashMap KVar [Integer]
+    kvarUses =
+      group $ [(k, i) | (Cstr i, KVar k) <- es ] ++
+              [(k, i) | (KVar k, Cstr i) <- es ]
 
+--------------------------------------------------------------------------------
+-- | @cutSInfo si kI cKs@ drops well-formed constraints that don't refer to the
+-- KVars in @cKs@. Also drops subtyping constraints that don't refer in their
+-- RHS to any of the KVars in @cKs@ or which aren't concrete.
 cutSInfo :: SInfo a -> KIndex -> S.HashSet KVar -> SInfo a
 cutSInfo si kI cKs = si { ws = ws', cm = cm' }
   where
@@ -57,13 +79,17 @@
     cs    = S.fromList      (concatMap kCs cKs)
     kCs k = M.lookupDefault [] k kI
 
+-- | Compute Dependencies and Cuts
+--
+-- Yields the edges of the dependency graph, then the set of KVars whose removal
+-- makes the graph acyclic (cuts), and finally the rest of the KVars.
 kutVars :: Config -> SInfo a -> ([CEdge], S.HashSet KVar, S.HashSet KVar)
 kutVars cfg si   = (es, depCuts ds, depNonCuts ds)
   where
     (es, ds)     = elimVars cfg si
 
 --------------------------------------------------------------------------------
--- | Map each `KVar` to the list of constraints on which it appears on RHS
+-- | Map each 'KVar' to the list of constraints on which it appears on RHS
 --------------------------------------------------------------------------------
 type KIndex = M.HashMap KVar [Integer]
 
diff --git a/src/Language/Fixpoint/Solver/EnvironmentReduction.hs b/src/Language/Fixpoint/Solver/EnvironmentReduction.hs
--- a/src/Language/Fixpoint/Solver/EnvironmentReduction.hs
+++ b/src/Language/Fixpoint/Solver/EnvironmentReduction.hs
@@ -33,9 +33,9 @@
 import           Data.HashSet (HashSet)
 import qualified Data.HashSet as HashSet
 #if MIN_VERSION_base(4,20,0)
-import           Data.List (nub, partition)
+import           Data.List (partition)
 #else
-import           Data.List (foldl', nub, partition)
+import           Data.List (foldl', partition)
 #endif
 import           Data.Maybe (fromMaybe)
 import           Data.ShareMap (ShareMap)
@@ -153,7 +153,6 @@
      { bs = bs'
      , cm = HashMap.fromList cm'
      , ws = ws'
-     , ebinds = updateEbinds bs' (ebinds finfo)
      , bindInfo = updateBindInfoKeys bs' $ bindInfo finfo
      }
 
@@ -170,9 +169,6 @@
        in
           HashMap.filterWithKey (\bId _ -> memberIBindEnv bId ibindEnv) be
 
-    -- Updates BindIds in an ebinds list
-    updateEbinds be = filter (`HashMap.member` beBinds be)
-
     -- Updates BindId keys in a bindInfos map
     updateBindInfoKeys be oldBindInfos =
       HashMap.intersection oldBindInfos (beBinds be)
@@ -310,10 +306,17 @@
   filter relevantBind env
   where
     allSymbols =
-      reachableSymbols (HashSet.union extraSymbols envSymbols) aenvMap
+      reachableSymbols (HashSet.unions [extraSymbols, envSymbols, withKVars]) aenvMap
     envSymbols =
       HashSet.unions $ map (\(_, _, sr,_) -> sortedReftSymbols sr) env
 
+    -- If there are bindings with KVars, we include them to be conservative.
+    withKVars =
+      HashSet.fromList $
+      map fst $
+      filter (not . HashMap.null . exprKVars . reftPred . sr_reft . snd) $
+      map (\(x, _, sr, _) -> (x, sr)) env
+
     relevantBind (s, _, sr, _)
       | HashSet.member s allSymbols = True
       | otherwise = case reftPred (sr_reft sr) of
@@ -470,15 +473,9 @@
    in finfo
         { bs = bs'
         , cm = cm'
-        , ebinds = updateEbinds oldToNew (ebinds finfo)
         , bindInfo = updateBindInfoKeys oldToNew $ bindInfo finfo
         }
   where
-    updateEbinds :: HashMap BindId [BindId] -> [BindId] -> [BindId]
-    updateEbinds oldToNew ebs =
-      nub $
-      concat [ bId : fromMaybe [] (HashMap.lookup bId oldToNew) | bId <- ebs ]
-
     updateBindInfoKeys
       :: HashMap BindId [BindId] -> HashMap BindId a -> HashMap BindId a
     updateBindInfoKeys oldToNew infoMap =
@@ -516,7 +513,7 @@
 
           mergedEnv = mergeDuplicatedBindings env
           undoANFEnv =
-            if inlineANFBindings cfg then undoANFOnlyModified mergedEnv else HashMap.empty
+            if inlineANFBinds cfg then undoANFOnlyModified mergedEnv else HashMap.empty
           boolSimplEnv =
             simplifyBooleanRefts $ HashMap.union undoANFEnv mergedEnv
 
@@ -749,7 +746,13 @@
 dropLikelyIrrelevantBindings ss env = HashMap.filterWithKey relevant env
   where
     directlyUses = HashMap.map (exprSymbolsSet . reftPred . sr_reft) env
-    relatedSyms = relatedSymbols ss directlyUses
+    relatedSyms = relatedSymbols (HashSet.union ss withKVars) directlyUses
+    -- If there are bindings with KVars, we include them to be conservative.
+    withKVars =
+      HashSet.fromList $
+      map fst $
+      filter (not . HashMap.null . exprKVars . reftPred . sr_reft . snd) $
+      HashMap.toList env
     relevant s _sr =
       (not (capitalizedSym s) || prefixOfSym s /= s) && s `HashSet.member` relatedSyms
     capitalizedSym = Text.all isUpper . Text.take 1 . symbolText
diff --git a/src/Language/Fixpoint/Solver/Extensionality.hs b/src/Language/Fixpoint/Solver/Extensionality.hs
--- a/src/Language/Fixpoint/Solver/Extensionality.hs
+++ b/src/Language/Fixpoint/Solver/Extensionality.hs
@@ -24,7 +24,7 @@
 mytracepp = notracepp
 
 expand :: Config -> SInfo a -> SInfo a
-expand cfg si = evalState (ext si) $ initST (symbolEnv cfg si) (ddecls si) (solverFlags $ solver cfg)
+expand cfg si = evalState (ext si) $ initST (symbolEnv cfg si) (ddecls si) (solverFlags cfg)
   where
     ext :: SInfo a -> Ex a (SInfo a)
     ext = extend
@@ -148,6 +148,8 @@
     go p (PImp p1 p2)    = f p =<< (PImp        <$>  go (negatePos p) p1 <*> go p p2)
     go p (PAnd ps)       = f p . PAnd =<< (go p `traverse` ps)
 
+    go p (ELet x e1 e2)  = f p =<< ELet x <$> go p e1 <*> go p e2
+
     -- The below cannot appear due to normalization
     go p (PNot e)        = f p . PNot =<< go p e
     go p (PIff p1 p2)    = f p =<< (PIff        <$>  go p p1 <*> go p p2            )
@@ -160,7 +162,6 @@
     go p (PExist xts e)  = f p . PExist xts =<< go p e
     go p (ETApp e s)     = f p . (`ETApp` s) =<< go p e
     go p (ETAbs e s)     = f p . (`ETAbs` s) =<< go p e
-    go p (PGrad k s i e) = f p . PGrad k s i =<< go p e
 
 normalize :: Expr -> Expr
 normalize expr' = mytracepp ("normalize: " ++ showpp expr') $ go expr'
@@ -181,12 +182,12 @@
     go (EIte e e1 e2)    = go $ PAnd [PImp e e1, PImp (PNot e) e2]
     go (PAnd ps)         = pAnd (go <$> ps)
     go (POr  ps)         = foldl' (\x y -> PImp (PImp (go x) PFalse) y) PFalse ps
+    go e@ELet{}          = e
     go e@(PAll _ _)      = e -- Cannot appear
     go e@(ELam _ _)      = e -- Cannot appear
     go e@(PExist _ _)    = e -- Cannot appear
     go e@(ETApp _ _)     = e -- Cannot appear
     go e@(ETAbs _ _)     = e -- Cannot appear
-    go e@PGrad{}         = e -- Cannot appear
 
 
 type Ex a = State (ExSt a)
diff --git a/src/Language/Fixpoint/Solver/GradualSolution.hs b/src/Language/Fixpoint/Solver/GradualSolution.hs
deleted file mode 100644
--- a/src/Language/Fixpoint/Solver/GradualSolution.hs
+++ /dev/null
@@ -1,135 +0,0 @@
-{-# LANGUAGE CPP                #-}
-{-# LANGUAGE FlexibleInstances  #-}
-
-module Language.Fixpoint.Solver.GradualSolution
-  ( -- * Create Initial Solution
-    init
-  ) where
-
-import           Control.Parallel.Strategies
-import           Control.Monad.Reader
-import qualified Data.HashMap.Strict            as M
-import qualified Data.List                      as L
-import           Data.Maybe                     (maybeToList, isNothing)
-import           Language.Fixpoint.Types.Config
-import           Language.Fixpoint.Types.PrettyPrint ()
-import qualified Language.Fixpoint.SortCheck          as So
-import           Language.Fixpoint.Misc
-import qualified Language.Fixpoint.Types              as F
-import qualified Language.Fixpoint.Types.Solutions    as Sol
-import qualified Language.Fixpoint.Types.Constraints  as Cons
-import           Prelude                              hiding (init, lookup)
-import           Language.Fixpoint.Solver.Sanitize  (symbolEnv)
-import Language.Fixpoint.SortCheck
-
---------------------------------------------------------------------------------
--- | Initial Gradual Solution (from Qualifiers and WF constraints) -------------
---------------------------------------------------------------------------------
-init :: (F.Fixpoint a) => Config -> F.SInfo a -> [(F.KVar, (F.GWInfo, [F.Expr]))]
---------------------------------------------------------------------------------
-init cfg si = map elab (runReader (traverse (refineG si qs genv) gs) ef) `using` parList rdeepseq
-  where
-    qs         = F.quals si
-    gs         = snd <$> gs0
-    genv       = instConstants si
-
-    gs0        = L.filter (Cons.isGWfc . snd) $ M.toList (F.ws si)
-
-    elab (k, (x,es)) = (k, (x, elaborate (ElabParam ef (F.atLoc F.dummySpan "init") (sEnv (Cons.gsym x) (Cons.gsort x))) <$> es))
-
-    sEnv x s    = isEnv {F.seSort = F.insertSEnv x s (F.seSort isEnv)}
-    isEnv       = symbolEnv cfg si
-    ef          = solverFlags $ solver cfg
-
-
---------------------------------------------------------------------------------
-refineG :: F.SInfo a -> [F.Qualifier] -> F.SEnv F.Sort -> F.WfC a -> ElabM (F.KVar, (F.GWInfo, [F.Expr]))
-refineG fi qs genv w =
-  do (k, qb) <- refine fi qs genv w
-     pure (k, (F.gwInfo w, Sol.qbExprs qb))
-
-refine :: F.SInfo a -> [F.Qualifier] -> F.SEnv F.Sort -> F.WfC a -> ElabM (F.KVar, Sol.QBind)
-refine fi qs genv w = refineK (Cons.allowHOquals fi) env qs $ F.wrft w
-  where
-    env             = wenv <> genv
-    wenv            = F.sr_sort <$> F.fromListSEnv (F.envCs (F.bs fi) (F.wenv w))
-
-instConstants :: F.SInfo a -> F.SEnv F.Sort
-instConstants = F.fromListSEnv . filter notLit . F.toListSEnv . F.gLits
-  where
-    notLit    = not . F.isLitSymbol . fst
-
-
-refineK :: Bool -> F.SEnv F.Sort -> [F.Qualifier] -> (F.Symbol, F.Sort, F.KVar) -> ElabM (F.KVar, Sol.QBind)
-refineK ho env qs (v, t, k) =
-  do eqs' <- Sol.qbFilterM (okInst env v t) eqs
-     pure (k, eqs')
-   where
-    eqs                     = instK ho env v t qs
-
-
---------------------------------------------------------------------------------
-instK :: Bool
-      -> F.SEnv F.Sort
-      -> F.Symbol
-      -> F.Sort
-      -> [F.Qualifier]
-      -> Sol.QBind
---------------------------------------------------------------------------------
-instK ho env v t = Sol.qb . unique . concatMap (instKQ ho env v t)
-  where
-    unique       = L.nubBy ((. Sol.eqPred) . (==) . Sol.eqPred)
-
-instKQ :: Bool
-       -> F.SEnv F.Sort
-       -> F.Symbol
-       -> F.Sort
-       -> F.Qualifier
-       -> [Sol.EQual]
-instKQ ho env v t q =
-  case Cons.qpSort <$> F.qParams q of
-    (qt:qts) -> do
-        (su0, v0) <- candidates senv [(t, [v])] qt
-        xs        <- match senv tyss [v0] (So.apply su0 <$> qts)
-        return     $ Sol.eQual q (reverse xs)
-      where
-        tyss       = instCands ho env
-        senv       = (`F.lookupSEnvWithDistance` env)
-    [] -> error "Empty qpSort of qParams q"
-
-instCands :: Bool -> F.SEnv F.Sort -> [(F.Sort, [F.Symbol])]
-instCands ho env = filter isOk tyss
-  where
-    tyss      = groupList [(t, x) | (x, t) <- xts]
-    isOk      = if ho then const True else isNothing . F.functionSort . fst
-    xts       = F.toListSEnv env
-
-match :: So.Env -> [(F.Sort, [F.Symbol])] -> [F.Symbol] -> [F.Sort] -> [[F.Symbol]]
-match env tyss xs (t : ts)
-  = do (su, x) <- candidates env tyss t
-       match env tyss (x : xs) (So.apply su <$> ts)
-match _   _   xs []
-  = return xs
-
---------------------------------------------------------------------------------
-candidates :: So.Env -> [(F.Sort, [F.Symbol])] -> F.Sort -> [(So.TVSubst, F.Symbol)]
---------------------------------------------------------------------------------
-candidates env tyss tx =
-    [(su, y) | (t, ys) <- tyss
-             , su      <- maybeToList $ So.unifyFast mono env tx t
-             , y       <- ys                                   ]
-  where
-    mono = So.isMono tx
-
---------------------------------------------------------------------------------
-okInst :: F.SEnv F.Sort -> F.Symbol -> F.Sort -> Sol.EQual -> ElabM Bool
---------------------------------------------------------------------------------
-okInst env v t eq =
-  do tc <- So.checkSorted F.dummySpan env sr
-     pure $ isNothing tc
-  where
-    sr            = F.RR t (F.Reft (v, p))
-    p             = Sol.eqPred eq
-
-
-
diff --git a/src/Language/Fixpoint/Solver/GradualSolve.hs b/src/Language/Fixpoint/Solver/GradualSolve.hs
deleted file mode 100644
--- a/src/Language/Fixpoint/Solver/GradualSolve.hs
+++ /dev/null
@@ -1,328 +0,0 @@
-{-# LANGUAGE FlexibleContexts  #-}
-
---------------------------------------------------------------------------------
--- | Solve a system of horn-clause constraints ---------------------------------
---------------------------------------------------------------------------------
-
-module Language.Fixpoint.Solver.GradualSolve (solveGradual) where
-
-{- COMMENTING OUT AS IT DOESNT BUILD!
-import           Control.Monad (when, filterM, foldM)
-import           Control.Monad.State.Strict (lift)
-import           Language.Fixpoint.Misc
-import qualified Language.Fixpoint.Types.Solutions as Sol
-import qualified Language.Fixpoint.SortCheck       as So
-import           Language.Fixpoint.Types.PrettyPrint
-import qualified Language.Fixpoint.Solver.GradualSolution  as S
-import qualified Language.Fixpoint.Solver.Worklist  as W
-import qualified Language.Fixpoint.Solver.Eliminate as E
-import           Language.Fixpoint.Solver.Monad
-import           Language.Fixpoint.Utils.Progress
-import           Language.Fixpoint.Graph
-import           Text.PrettyPrint.HughesPJ
-import           Text.Printf
-import           System.Console.CmdArgs.Verbosity (whenNormal, whenLoud)
-import qualified Data.HashMap.Strict as M
-import qualified Data.HashSet        as S
--}
-
-import           Control.DeepSeq
-import qualified Language.Fixpoint.Types           as F
-import           Language.Fixpoint.Types.Config hiding (stats)
-
-solveGradual :: (NFData a, F.Fixpoint a) => Config -> F.SInfo a -> IO (F.Result (Integer, a))
-solveGradual = undefined
-
-
-
-{- COMMENTING OUT AS IT DOESNT BUILD!
-
---------------------------------------------------------------------------------
--- | Progress Bar
---------------------------------------------------------------------------------
-withProgressFI :: SolverInfo a b -> IO b -> IO b
-withProgressFI = withProgress . fromIntegral . cNumScc . siDeps
---------------------------------------------------------------------------------
-
-printStats :: F.SInfo a ->  W.Worklist a -> Stats -> IO ()
-printStats fi w s = putStrLn "\n" >> ppTs [ ptable fi, ptable s, ptable w ]
-  where
-    ppTs          = putStrLn . showpp . mconcat
-
---------------------------------------------------------------------------------
-solverInfo :: Config -> F.SInfo a -> SolverInfo a b
---------------------------------------------------------------------------------
-solverInfo cfg fI
-  | useElim cfg = E.solverInfo cfg fI
-  | otherwise   = SI mempty fI cD (siKvars fI)
-  where
-    cD          = elimDeps fI (kvEdges fI) mempty
-
-siKvars :: F.SInfo a -> S.HashSet F.KVar
-siKvars = S.fromList . M.keys . F.ws
-
-
---------------------------------------------------------------------------------
--- | tidyResult ensures we replace the temporary kVarArg names introduced to
---   ensure uniqueness with the original names in the given WF constraints.
---------------------------------------------------------------------------------
-tidyResult :: F.Result a -> F.Result a
-tidyResult r = r { F.resSolution  =  tidySolution  (F.resSolution r)
-                 , F.gresSolution =  gtidySolution (F.gresSolution r)
-                 }
-
-tidySolution :: F.FixSolution -> F.FixSolution
-tidySolution = fmap tidyPred
-
-gtidySolution :: F.GFixSolution -> F.GFixSolution
-gtidySolution = fmap tidyPred --  (\(e, es) -> (tidyPred e, tidyPred <$> es))
-
-tidyPred :: F.Expr -> F.Expr
-tidyPred = F.substf (F.eVar . F.tidySymbol)
-
-
-predKs :: F.Expr -> [(F.KVar, F.Subst)]
-predKs (F.PAnd ps)    = concatMap predKs ps
-predKs (F.PKVar k su) = [(k, su)]
-predKs _              = []
-
-
-
---------------------------------------------------------------------------------
-minimizeResult :: Config -> M.HashMap F.KVar F.Expr
-               -> SolveM (M.HashMap F.KVar F.Expr)
---------------------------------------------------------------------------------
-minimizeResult cfg s
-  | minimalSol cfg = mapM minimizeConjuncts s
-  | otherwise      = return s
-
-minimizeConjuncts :: F.Expr -> SolveM F.Expr
-minimizeConjuncts p = F.pAnd <$> go (F.conjuncts p) []
-  where
-    go []     acc   = return acc
-    go (p:ps) acc   = do b <- isValid (F.pAnd (acc ++ ps)) p
-                         if b then go ps acc
-                              else go ps (p:acc)
-
-
-
-showUnsat :: Bool -> Integer -> F.Pred -> F.Pred -> IO ()
-showUnsat u i lP rP = {- when u $ -} do
-  putStrLn $ printf   "UNSAT id %s %s" (show i) (show u)
-  putStrLn $ showpp $ "LHS:" <+> pprint lP
-  putStrLn $ showpp $ "RHS:" <+> pprint rP
-
---------------------------------------------------------------------------------
--- | Predicate corresponding to RHS of constraint in current solution
---------------------------------------------------------------------------------
-rhsPred :: F.SimpC a -> F.Expr
---------------------------------------------------------------------------------
-rhsPred c
-  | isTarget c = F.crhs c
-  | otherwise  = errorstar $ "rhsPred on non-target: " ++ show (F.sid c)
-
-isValid :: F.Expr -> F.Expr -> SolveM Bool
-isValid p q = (not . null) <$> filterValid p [(q, ())]
-
-
--------------------------------------------------------------------------------
--- | solve with edits to allow Gradual types ----------------------------------
--------------------------------------------------------------------------------
-
-solveGradual :: (NFData a, F.Fixpoint a) => Config -> F.SInfo a -> IO (F.Result (Integer, a))
--- solveGradual = undefined
-
-solveGradual cfg fi = do
-    (res, stat) <- withProgressFI sI $ runSolverM cfg sI n act
-    when (solverStats cfg) $ printStats fi wkl stat
-    return res
-  where
-    act  = solveGradual_ cfg fi s0 ks  wkl
-    sI   = solverInfo cfg fi
-    wkl  = W.init sI
-    n    = fromIntegral $ W.wRanks wkl
-    s0   = siSol  sI
-    ks   = siVars sI
-
---------------------------------------------------------------------------------
-solveGradual_ :: (NFData a, F.Fixpoint a)
-       => Config
-       -> F.SInfo a
-       -> Sol.GSolution
-       -> S.HashSet F.KVar
-       -> W.Worklist a
-       -> SolveM (F.Result (Integer, a), Stats)
---------------------------------------------------------------------------------
-solveGradual_ cfg fi s0 ks wkl = do
-  let s1  = mappend s0 $ {- SCC "sol-init" #-} S.init cfg fi ks
-  s2      <- {- SCC "sol-local"  #-} filterLocal s1
-  s       <- {- SCC "sol-refine" #-} refine s2 wkl
-  res     <- {- SCC "sol-result" #-} result cfg wkl s
-  st      <- stats
-  let res' = {- SCC "sol-tidy"   #-} tidyResult res
-  return $!! (res', st)
-
-filterLocal :: Sol.GSolution -> SolveM Sol.GSolution
-filterLocal sol = do
-  gs' <- mapM (initGBind sol) gs
-  return $ Sol.updateGMap sol $ M.fromList gs'
-  where
-    gs = M.toList $ Sol.gMap sol
-
-initGBind :: Sol.GSolution -> (F.KVar, (((F.Symbol, F.Sort), F.Expr), Sol.GBind)) -> SolveM (F.KVar, (((F.Symbol, F.Sort), F.Expr), Sol.GBind))
-initGBind sol (k, (e, gb)) = do
-   elems0  <- filterM (isLocal e) (Sol.gbEquals gb)
-   elems   <- sortEquals elems0
-   lattice <- makeLattice [] (map (:[]) elems) elems
-   return $ ((k,) . (e,) . Sol.equalsGb) lattice
-  where
-    makeLattice acc new elems
-      | null new
-      = return acc
-      | otherwise
-      = do let cands = [e:es |e<-elems, es<-new]
-           localCans <- filterM (isLocal e) cands
-           newElems  <- filterM (notTrivial (new ++ acc)) localCans
-           makeLattice (acc ++ new) newElems elems
-
-    notTrivial [] _     = return True
-    notTrivial (x:xs) p = do v <- isValid (mkPred x) (mkPred p)
-                             if v then return False
-                                  else notTrivial xs p
-
-    mkPred eq = So.elaborate "initBGind.mkPred" (Sol.sEnv sol) (F.pAnd (Sol.eqPred <$> eq))
-    isLocal (v, e) eqs = do
-      let pp = So.elaborate "filterLocal" (Sol.sEnv sol) $ F.PExist [v] $ F.pAnd (e:(Sol.eqPred <$> eqs))
-      isValid mempty pp
-
-    root      = Sol.trueEqual
-    sortEquals xs = (bfs [0]) <$> makeEdges vs [] vs
-      where
-       vs        = zip [0..] (root:(head <$> xs))
-
-       bfs []     _  = []
-       bfs (i:is) es = (snd $ (vs!!i)) : bfs (is++map snd (filter (\(j,k) ->  (j==i && notElem k is)) es)) es
-
-       makeEdges _   acc []    = return acc
-       makeEdges vs acc (x:xs) = do ves  <- concat <$> mapM (makeEdgesOne x) vs
-                                    if any (\(i,j) -> elem (j,i) acc) ves
-                                      then makeEdges (filter ((/= fst x) . fst) vs) (filter (\(i,j) -> ((i /= fst x) && (j /= fst x))) acc) xs
-                                      else makeEdges vs (mergeEdges (ves ++ acc)) xs
-
-    makeEdgesOne (i,_) (j,_) | i == j = return []
-    makeEdgesOne (i,x) (j,y) = do
-      ij <- isValid (mkPred [x]) (mkPred [y])
-      return (if ij then [(j,i)] else [])
-
-    mergeEdges es = filter (\(i,j) -> (not (any (\k -> ((i,k) `elem` es && (k,j) `elem` es)) (fst <$> es)))) es
-
-
---------------------------------------------------------------------------------
-refine :: Sol.GSolution -> W.Worklist a -> SolveM Sol.GSolution
---------------------------------------------------------------------------------
-refine s w
-  | Just (c, w', newScc, rnk) <- W.pop w = do
-     i       <- tickIter newScc
-     (b, s') <- refineC i s c
-     lift $ writeLoud $ refineMsg i c b rnk
-     let w'' = if b then W.push c w' else w'
-     refine s' w''
-  | otherwise = return s
-  where
-    -- DEBUG
-    refineMsg i c b rnk = printf "\niter=%d id=%d change=%s rank=%d\n"
-                            i (F.subcId c) (show b) rnk
-
----------------------------------------------------------------------------
--- | Single Step Refinement -----------------------------------------------
----------------------------------------------------------------------------
-refineC :: Int -> Sol.GSolution -> F.SimpC a -> SolveM (Bool, Sol.GSolution)
----------------------------------------------------------------------------
-refineC _i s c
-  | null rhs  = return (False, s)
-  | otherwise = do be      <- getBinds
-                   let lhss = snd <$> S.lhsPred be s c
-                   kqs     <- filterValidGradual lhss rhs
-                   return   $ S.update s ks kqs
-  where
-    _ci       = F.subcId c
-    (ks, rhs) = rhsCands s c
-    -- msg       = printf "refineC: iter = %d, sid = %s, soln = \n%s\n"
-    --               _i (show (F.sid c)) (showpp s)
-    _msg ks xs ys = printf "refineC: iter = %d, sid = %s, s = %s, rhs = %d, rhs' = %d \n"
-                     _i (show _ci) (showpp ks) (length xs) (length ys)
-
-
-rhsCands :: Sol.GSolution -> F.SimpC a -> ([F.KVar], Sol.Cand (F.KVar, Sol.EQual))
-rhsCands s c    = (fst <$> ks, kqs)
-  where
-    kqs         = [ (p, (k, q)) | (k, su) <- ks, (p, q)  <- cnd k su ]
-    ks          = predKs . F.crhs $ c
-    cnd k su    = Sol.qbPreds msg s su (Sol.lookupQBind s k)
-    msg         = "rhsCands: " ++ show (F.sid c)
-
---------------------------------------------------------------------------------
--- | Gradual Convert Solution into Result ----------------------------------------------
---------------------------------------------------------------------------------
-result :: (F.Fixpoint a) => Config -> W.Worklist a -> Sol.GSolution
-       -> SolveM (F.Result (Integer, a))
---------------------------------------------------------------------------------
-result cfg wkl s = do
-  lift $ writeLoud "Computing Result"
-  stat    <- result_ wkl s
-  lift $ whenNormal $ putStrLn $ "RESULT: " ++ show (F.sid <$> stat)
-  F.Result (ci <$> stat) <$> solResult cfg s <*> solResultGradual wkl cfg s
-  where
-    ci c = (F.subcId c, F.sinfo c)
-
-result_ :: Fixpoint a =>  W.Worklist a -> Sol.GSolution -> SolveM (F.FixResult (F.SimpC a))
-result_  w s = res <$> filterM (isUnsat s) cs
-  where
-    cs       = W.unsatCandidates w
-    res []   = F.Safe
-    res cs'  = F.Unsafe cs'
-
-solResult :: Config -> Sol.GSolution -> SolveM (M.HashMap F.KVar F.Expr)
-solResult cfg
-  = minimizeResult cfg . Sol.result
-
-
-solResultGradual :: W.Worklist a -> Config -> Sol.GSolution -> SolveM F.GFixSolution
-solResultGradual w _cfg sol
-  = F.toGFixSol . Sol.resultGradual <$> updateGradualSolution (W.unsatCandidates w) sol
-
---------------------------------------------------------------------------------
-updateGradualSolution :: [F.SimpC a] -> Sol.GSolution -> SolveM (Sol.GSolution)
---------------------------------------------------------------------------------
-updateGradualSolution cs sol = foldM f (Sol.emptyGMap sol) cs
-  where
-   f s c = do
-    be <- getBinds
-    let lpi = S.lhsPred be sol c
-    let rp  = rhsPred c
-    gbs    <- firstValid rp lpi
-    return $ Sol.updateGMapWithKey gbs s
-
-
-firstValid :: Monoid a =>  F.Expr -> [(a, F.Expr)] -> SolveM a
-firstValid _   [] = return mempty
-firstValid rhs ((y,lhs):xs) = do
-  v <- isValid lhs rhs
-  if v then return y else firstValid rhs xs
-
-
---------------------------------------------------------------------------------
-isUnsat :: Fixpoint a => Sol.GSolution -> F.SimpC a -> SolveM Bool
---------------------------------------------------------------------------------
-isUnsat s c = do
-  -- lift   $ printf "isUnsat %s" (show (F.subcId c))
-  _     <- tickIter True -- newScc
-  be    <- getBinds
-  let lpi = S.lhsPred be s c
-  let rp = rhsPred        c
-  res   <- (not . or) <$> mapM (`isValid` rp) (snd <$> lpi)
-  lift   $ whenLoud $ showUnsat res (F.subcId c) (F.pOr (snd <$> lpi)) rp
-  return res
-
-
--}
diff --git a/src/Language/Fixpoint/Solver/Instantiate.hs b/src/Language/Fixpoint/Solver/Instantiate.hs
deleted file mode 100644
--- a/src/Language/Fixpoint/Solver/Instantiate.hs
+++ /dev/null
@@ -1,816 +0,0 @@
---------------------------------------------------------------------------------
--- | This module implements "Proof by Logical Evaluation" where we
---   unfold function definitions if they *must* be unfolded, to strengthen
---   the environments with function-definition-equalities.
---   The algorithm is discussed at length in:
---
---     1. "Refinement Reflection", POPL 2018, https://arxiv.org/pdf/1711.03842
---     2. "Reasoning about Functions", VMCAI 2018, https://ranjitjhala.github.io/static/reasoning-about-functions.pdf
---------------------------------------------------------------------------------
-
-{-# LANGUAGE OverloadedStrings         #-}
-{-# LANGUAGE PartialTypeSignatures     #-}
-{-# LANGUAGE TupleSections             #-}
-{-# LANGUAGE FlexibleInstances         #-}
-{-# LANGUAGE ViewPatterns              #-}
-{-# LANGUAGE PatternGuards             #-}
-{-# LANGUAGE RecordWildCards           #-}
-{-# LANGUAGE ExistentialQuantification #-}
-
-module Language.Fixpoint.Solver.Instantiate (instantiate) where
-
-import           Language.Fixpoint.Types
-import           Language.Fixpoint.Types.Config  as FC
-import qualified Language.Fixpoint.Types.Visitor as Vis
-import qualified Language.Fixpoint.Misc          as Misc -- (mapFst)
-import qualified Language.Fixpoint.Smt.Interface as SMT
-import           Language.Fixpoint.Defunctionalize
-import qualified Language.Fixpoint.Utils.Trie    as T
-import           Language.Fixpoint.Utils.Progress -- as T
-import           Language.Fixpoint.SortCheck
-import           Language.Fixpoint.Graph.Deps             (isTarget)
-import           Language.Fixpoint.Solver.Sanitize        (symbolEnv)
-import qualified Language.Fixpoint.Solver.PLE as PLE      (instantiate)
-import qualified Language.Fixpoint.Solver.Common as Common (toSMT)
-import           Language.Fixpoint.Solver.Common          (askSMT)
-import           Control.Monad ((>=>), foldM, forM, forM_, join)
-import           Control.Monad.State
-import           Data.Bifunctor (first, second)
-import qualified Data.Text            as T
-import qualified Data.HashMap.Strict  as M
-import qualified Data.HashSet         as S
-import qualified Data.List            as L
-import qualified Data.Maybe           as Mb -- (isNothing, catMaybes, fromMaybe)
-import           Data.Char            (isDigit, isUpper)
--- import           Debug.Trace          (trace)
--- import           Text.Printf (printf)
-
-mytracepp :: (PPrint a) => String -> a -> a
-mytracepp = notracepp
-
---------------------------------------------------------------------------------
--- | Strengthen Constraint Environments via PLE
---------------------------------------------------------------------------------
-instantiate :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (SInfo a)
-instantiate cfg info subcIds
-  | not (oldPLE cfg)
-  = PLE.instantiate cfg info subcIds
-
-  | noIncrPle cfg
-  = instantiate' cfg info subcIds
-
-  | otherwise
-  = incrInstantiate' cfg info subcIds
-
-
--------------------------------------------------------------------------------
--- | New "Incremental" PLE -- see [NOTE:TREE-LIKE]
-
-{- | [NOTE:TREE-LIKE] incremental PLE relies crucially on the SInfo satisfying
-     a "tree like"   invariant:
-       forall constraints c, c'.
-         if i in c and i in c' then
-           forall 0 <= j < i, j in c and j in c'
-
- -}
-
--------------------------------------------------------------------------------
-incrInstantiate' :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (SInfo a)
--------------------------------------------------------------------------------
-incrInstantiate' cfg info subcIds = do
-    let cs = [ (i, c) | (i, c) <- M.toList (cm info), isPleCstr aEnv i c
-                      ,  maybe True (i `L.elem`) subcIds ]
-    let t  = mkCTrie cs                                               -- 1. BUILD the Trie
-    res   <- withProgress (1 + length cs) $
-               withCtx cfg file sEnv (defns info) (pleTrie t . instEnv cfg info cs)  -- 2. TRAVERSE Trie to compute InstRes
-    return $ resSInfo cfg sEnv info res                                 -- 3. STRENGTHEN SInfo using InstRes
-  where
-    file   = srcFile cfg ++ ".evals"
-    sEnv   = symbolEnv cfg info
-    aEnv   = ae info
-
-
-
--------------------------------------------------------------------------------
--- | Step 1a: @instEnv@ sets up the incremental-PLE environment
-instEnv :: (Loc a) => Config -> SInfo a -> [(SubcId, SimpC a)] -> SMT.Context -> InstEnv a
-instEnv cfg info cs ctx = InstEnv cfg ctx bEnv aEnv (M.fromList cs) γ s0
-  where
-    bEnv              = bs info
-    aEnv              = ae info
-    γ                 = knowledge cfg ctx aEnv
-    s0                = EvalEnv 0 [] aEnv (SMT.ctxSymEnv ctx) cfg
-
-----------------------------------------------------------------------------------------------
--- | Step 1b: @mkCTrie@ builds the @Trie@ of constraints indexed by their environments
-mkCTrie :: [(SubcId, SimpC a)] -> CTrie
-mkCTrie ics  = mytracepp  "TRIE" $ T.fromList [ (cBinds c, i) | (i, c) <- ics ]
-  where
-    cBinds   = L.sort . elemsIBindEnv . senv
-
-----------------------------------------------------------------------------------------------
--- | Step 2: @pleTrie@ walks over the @CTrie@ to actually do the incremental-PLE
-pleTrie :: CTrie -> InstEnv a -> IO InstRes
-pleTrie t env = loopT env ctx0 diff0 Nothing res0 t
-  where
-    diff0        = []
-    res0         = M.empty
-    ctx0         = initCtx es0
-    es0          = eqBody <$> L.filter (null . eqArgs) (aenvEqs . ieAenv $ env)
-
-loopT :: InstEnv a -> ICtx -> Diff -> Maybe BindId -> InstRes -> CTrie -> IO InstRes
-loopT env ctx delta i res t = case t of
-  T.Node []  -> return res
-  T.Node [b] -> loopB env ctx delta i res b
-  T.Node bs  -> withAssms env ctx delta Nothing $ \ctx' -> do
-                  (ctx'', res') <- ple1 env ctx' i Nothing res
-                  foldM (loopB env ctx'' [] i) res' bs
-
-loopB :: InstEnv a -> ICtx -> Diff -> Maybe BindId -> InstRes -> CBranch -> IO InstRes
-loopB env ctx delta iMb res b = case b of
-  T.Bind i t -> loopT env ctx (i:delta) (Just i) res t
-  T.Val cid  -> withAssms env ctx delta (Just cid) $ \ctx' -> do
-                  progressTick
-                  snd <$> ple1 env ctx' iMb (Just cid) res
-
-
-withAssms :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> (ICtx -> IO b) -> IO b
-withAssms env@InstEnv{..} ctx delta cidMb act = do
-  let ctx'  = updCtx env ctx delta cidMb
-  let assms = mytracepp  ("ple1-assms: " ++ show (cidMb, delta)) (icAssms ctx')
-  SMT.smtBracket ieSMT  "PLE.evaluate" $ do
-    forM_ assms (SMT.smtAssert ieSMT)
-    act ctx'
-
--- | @ple1@ performs the PLE at a single "node" in the Trie
-ple1 :: InstEnv a -> ICtx -> Maybe BindId -> Maybe SubcId -> InstRes -> IO (ICtx, InstRes)
-ple1 env@InstEnv{..} ctx i cidMb res = do
-  let cands = mytracepp  ("ple1-cands: "  ++ show cidMb) $ S.toList (icCands ctx)
-  -- unfolds  <- evalCands ieKnowl ieEvEnv cands
-  unfolds  <- evalCandsLoop ieCfg ieSMT ieKnowl ieEvEnv cands
-  return    $ updCtxRes env ctx res i cidMb (mytracepp  ("ple1-cands-unfolds: " ++ show cidMb) unfolds)
-
-_evalCands :: Knowledge -> EvalEnv -> [Expr] -> IO [Unfold]
-_evalCands _ _  []    = return []
-_evalCands γ s0 cands = do eqs <- mapM (evalOne γ s0) cands
-                           return $ mkUnfolds (zip (Just <$> cands) eqs)
-
-unfoldPred :: Config -> SMT.Context -> [Unfold] -> Pred
-unfoldPred cfg ctx = toSMT cfg ctx [] . pAnd . concatMap snd
-
-evalCandsLoop :: Config -> SMT.Context -> Knowledge -> EvalEnv -> [Expr] -> IO [Unfold]
-evalCandsLoop cfg ctx γ s0 = go []
-  where
-    go acc []    = return acc
-    go acc cands = do eqss   <- SMT.smtBracket ctx "PLE.evaluate" $ do
-                                  SMT.smtAssert ctx (unfoldPred cfg ctx acc)
-                                  mapM (evalOne γ s0) cands
-                      let us  = zip (Just <$> cands) eqss
-                      case mkUnfolds us of
-                        []  -> return acc
-                        us' -> do let acc'   = acc ++ us'
-                                  let oks    = S.fromList [ e | (Just e, _) <- us' ]
-                                  let cands' = [ e | e <- cands, not (S.member e oks) ]
-                                  go acc' cands'
-
-
-----------------------------------------------------------------------------------------------
--- | Step 3: @resSInfo@ uses incremental PLE result @InstRes@ to produce the strengthened SInfo
-
-resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> SInfo a
-resSInfo cfg env info res = strengthenBinds info res'
-  where
-    res'     = M.fromList $ mytracepp  "ELAB-INST:  " $ zip is ps''
-    ps''     = zipWith (\i -> elaborate (ElabParam (solverFlags $ solver cfg) (atLoc dummySpan ("PLE1 " ++ show i)) env)) is ps'
-    ps'      = defuncAny cfg env ps
-    (is, ps) = unzip (M.toList res)
-
-----------------------------------------------------------------------------------------------
--- | @InstEnv@ has the global information needed to do PLE
-data InstEnv a = InstEnv
-  { ieCfg   :: !Config
-  , ieSMT   :: !SMT.Context
-  , ieBEnv  :: !(BindEnv a)
-  , ieAenv  :: !AxiomEnv
-  , ieCstrs :: !(M.HashMap SubcId (SimpC a))
-  , ieKnowl :: !Knowledge
-  , ieEvEnv :: !EvalEnv
-  }
-
--- | @ICtx@ is the local information -- at each trie node -- obtained by incremental PLE
-data ICtx    = ICtx
-  { icAssms  :: ![Pred]          -- ^ Hypotheses, already converted to SMT format
-  , icCands  :: S.HashSet Expr   -- ^ "Candidates" for unfolding
-  , icEquals :: ![Expr]          -- ^ "Known" equalities
-  , icSolved :: S.HashSet Expr   -- ^ Terms that we have already expanded
-  }
-
--- | @InstRes@ is the final result of PLE; a map from @BindId@ to the equations "known" at that BindId
-type InstRes = M.HashMap BindId Expr
-
--- | @Unfold is the result of running PLE at a single equality;
---     (e, [(e1, e1')...]) is the source @e@ and the (possible empty)
---   list of PLE-generated equalities (e1, e1') ...
--- type Unfold  = (Maybe Expr, [(Expr, Expr)])
-type Unfold  = (Maybe Expr, [Expr])
-type CTrie   = T.Trie   SubcId
-type CBranch = T.Branch SubcId
-type Diff    = [BindId]    -- ^ in "reverse" order
-
-initCtx :: [Expr] -> ICtx
-initCtx es = ICtx
-  { icAssms  = []
-  , icCands  = mempty
-  , icEquals = mytracepp  "INITIAL-STUFF-INCR" es
-  , icSolved = mempty
-  }
-
-equalitiesPred :: [(Expr, Expr)] -> [Expr]
-equalitiesPred eqs = [ EEq e1 e2 | (e1, e2) <- eqs, e1 /= e2 ]
-
-updCtxRes :: InstEnv a -> ICtx -> InstRes -> Maybe BindId -> Maybe SubcId -> [Unfold] -> (ICtx, InstRes)
-updCtxRes env ctx res iMb cidMb us
-                       = -- trace _msg
-                         ( ctx { {- icCands  = cands', -} icSolved = solved', icEquals = mempty}
-                         , res'
-                         )
-  where
-    _msg               = Mb.maybe "nuttin\n" (debugResult env res') cidMb
-    res'               = updRes res iMb (pAnd solvedEqs)
-    _cands'             = (icCands ctx `S.union` newCands) `S.difference` solved'
-    solved'            = S.union (icSolved ctx) solvedCands
-    newCands           = S.fromList (concatMap topApps newEqs)
-    solvedCands        = S.fromList [ e | (Just e, _) <- okUnfolds ]
-    solvedEqs          = icEquals ctx ++ newEqs
-    newEqs             = concatMap snd okUnfolds
-    okUnfolds          = mytracepp  _str [ (eMb, ps)  | (eMb, ps) <- us, {- let ps = equalitiesPred eqs, -} not (null ps) ]
-    _str               = "okUnfolds " ++ showpp (iMb, cidMb)
-    -- cands'             = S.difference (icCands ctx) (S.fromList solvedCands)
-    -- solvedEqs          = icEquals ctx ++ concatMap snd us
-    -- solvedCands        = [ e          | (Just e, _) <- us]
-
-mkUnfolds :: [(a, [(Expr, Expr)])] -> [(a, [Expr])]
-mkUnfolds us = [ (eMb, ps)  | (eMb, eqs) <- us
-                            , let ps = equalitiesPred eqs
-                            , not (null ps)
-               ]
-
-debugResult :: InstEnv a -> InstRes -> SubcId -> String
-debugResult InstEnv{..} res subId = msg
-  where
-    msg                          = "INCR-INSTANTIATE i = " ++ show subId ++ ": " ++ showpp cidEqs
-    cidEqs                       = pAnd [ e | i <- cBinds, e <- Mb.maybeToList $ M.lookup i res ]
-    cBinds                       = L.sort . elemsIBindEnv . senv . getCstr ieCstrs $ subId
-
-
-updRes :: InstRes -> Maybe BindId -> Expr -> InstRes
-updRes res (Just i) e = M.insert i e res
-updRes res  Nothing _ = res
-
--- | @updCtx env ctx delta cidMb@ adds the assumptions and candidates from @delta@ and @cidMb@
---   to the context.
-updCtx :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> ICtx
-updCtx InstEnv {..} ctx delta cidMb
-              = ctx { icAssms  = ctxEqs
-                    , icCands  = cands   <> icCands  ctx
-                    , icEquals = initEqs <> icEquals ctx }
-  where
-    initEqs   = equalitiesPred (initEqualities ieSMT ieAenv bs)
-    cands     = S.fromList (concatMap topApps es0) `S.difference` icSolved ctx
-    ctxEqs    = toSMT ieCfg ieSMT [] <$>
-                  (initEqs ++
-                  [ expr xr
-                  | xr@(_, r) <- bs
-                  , null (Vis.kvarsExpr $ reftPred $ sr_reft r)
-                  ])
-    (bs, es0) = (second unElabSortedReft <$> binds, unElab <$> es)
-    es        = eRhs : (expr <$> binds)
-    eRhs      = maybe PTrue crhs subMb
-    binds     = [ (x, y)  | i <- delta, let (x, y, _) = lookupBindEnv i ieBEnv ]
-    subMb     = getCstr ieCstrs <$> cidMb
-
-getCstr :: M.HashMap SubcId (SimpC a) -> SubcId -> SimpC a
-getCstr env cid = Misc.safeLookup "Instantiate.getCstr" cid env
-
---------------------------------------------------------------------------------
--- | "Old" GLOBAL PLE
---------------------------------------------------------------------------------
-instantiate' :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (SInfo a)
-instantiate' cfg info subcIds = sInfo cfg env info <$> withCtx cfg file env (defns info) act
-  where
-    act ctx         = forM cstrs $ \(i, c) ->
-                        ((i,srcSpan c),) . mytracepp  ("INSTANTIATE i = " ++ show i) <$> instSimpC cfg ctx (bs info) aenv i c
-    cstrs           = [ (i, c) | (i, c) <- M.toList (cm info) , isPleCstr aenv i c
-                               ,  maybe True (i `L.elem`) subcIds ]
-    file            = srcFile cfg ++ ".evals"
-    env             = symbolEnv cfg info
-    aenv            = {- mytracepp  "AXIOM-ENV" -} ae info
-
-sInfo :: Config -> SymEnv -> SInfo a -> [((SubcId, SrcSpan), Expr)] -> SInfo a
-sInfo cfg env info ips = strengthenHyp info (mytracepp  "ELAB-INST:  " $ zip (fst <$> is) ps'')
-  where
-    (is, ps)         = unzip ips
-    ps'              = defuncAny cfg env ps
-    ps''             = zipWith (\(i, sp) -> elaborate (ElabParam (solverFlags $ solver cfg) (atLoc sp ("PLE1 " ++ show i)) env)) is ps'
-
-instSimpC :: Config -> SMT.Context -> BindEnv a -> AxiomEnv -> SubcId -> SimpC a -> IO Expr
-instSimpC cfg ctx bds aenv subId sub
-  | isPleCstr aenv subId sub = do
-    let is0       = mytracepp  "INITIAL-STUFF" $ eqBody <$> L.filter (null . eqArgs) (aenvEqs aenv)
-    let (bs, es0) = cstrExprs bds sub
-    equalities   <- evaluate cfg ctx aenv bs es0 subId
-    let evalEqs   = [ EEq e1 e2 | (e1, e2) <- equalities, e1 /= e2 ]
-    return        $ pAnd (is0 ++ evalEqs)
-  | otherwise     = return PTrue
-
-isPleCstr :: AxiomEnv -> SubcId -> SimpC a -> Bool
-isPleCstr aenv subId c = isTarget c && M.lookupDefault False subId (aenvExpand aenv)
-
-cstrExprs :: BindEnv a -> SimpC a -> ([(Symbol, SortedReft)], [Expr])
-cstrExprs bds sub = (second unElabSortedReft <$> binds, unElab <$> es)
-  where
-    es            = crhs sub : (expr <$> binds)
-    binds         = envCs bds (senv sub)
-
---------------------------------------------------------------------------------
--- | Symbolic Evaluation with SMT
---------------------------------------------------------------------------------
-evaluate :: Config -> SMT.Context -> AxiomEnv -- ^ Definitions
-         -> [(Symbol, SortedReft)]            -- ^ Environment of "true" facts
-         -> [Expr]                            -- ^ Candidates for unfolding
-         -> SubcId                            -- ^ Constraint Id
-         -> IO [(Expr, Expr)]                 -- ^ Newly unfolded equalities
---------------------------------------------------------------------------------
-evaluate cfg ctx aenv facts es subId = do
-  let eqs      = initEqualities ctx aenv facts
-  let γ        = knowledge cfg ctx aenv
-  let cands    = mytracepp  ("evaluate-cands " ++ showpp subId) $ Misc.setNub (concatMap topApps es)
-  let s0       = EvalEnv 0 [] aenv (SMT.ctxSymEnv ctx) cfg
-  let ctxEqs   = [ toSMT cfg ctx [] (EEq e1 e2) | (e1, e2)  <- eqs ]
-              ++ [ toSMT cfg ctx [] (expr xr)   | xr@(_, r) <- facts, null (Vis.kvarsExpr $ reftPred $ sr_reft r) ]
-  eqss        <- _evalLoop cfg ctx γ s0 ctxEqs cands
-  return       $ eqs ++ eqss
-
-
-
-_evalLoop :: Config -> SMT.Context -> Knowledge -> EvalEnv -> [Pred] -> [Expr] -> IO [(Expr, Expr)]
-_evalLoop cfg ctx γ s0 ctxEqs = loop 0 []
-  where
-    loop _ acc []    = return acc
-    loop i acc cands = do let eqp = toSMT cfg ctx [] $ pAnd $ equalitiesPred acc
-                          eqss <- SMT.smtBracket ctx "PLE.evaluate" $ do
-                                    forM_ (eqp : ctxEqs) (SMT.smtAssert ctx)
-                                    mapM (evalOne γ s0) cands
-                          case concat eqss of
-                            []   -> return acc
-                            eqs' -> do let acc'   = acc ++ eqs'
-                                       let oks    = S.fromList (fst <$> eqs')
-                                       let cands' = [ e | e <- cands, not (S.member e oks) ]
-                                       loop (i + 1 :: Integer) acc' cands'
-
-
-
---------------------------------------------------------------------------------
-data EvalEnv = EvalEnv
-  { evId        :: !Int
-  , evSequence  :: [(Expr,Expr)]
-  , _evAEnv     :: !AxiomEnv
-  , evEnv       :: !SymEnv
-  , _evCfg      :: !Config
-  }
-
-type EvalST a = StateT EvalEnv IO a
---------------------------------------------------------------------------------
-
-evalOne :: Knowledge -> EvalEnv -> Expr -> IO [(Expr, Expr)]
-evalOne γ s0 e = do
-  (e', st) <- runStateT (eval γ initCS (mytracepp "evalOne: " e)) s0
-  if e' == e then return [] else return ((e, e') : evSequence st)
-
-{- | [NOTE: Eval-Ite]  We should not be doing any PLE/eval under if-then-else where
-     the guard condition does not provably hold. For example, see issue #387.
-     However, its ok and desirable to `eval` in this case, as long as one is not
-     unfolding recursive functions. To permit this, we track the "call-stack" and
-     whether or not, `eval` is occurring under an unresolved guard: if so, we do not
-     expand under any function that is already on the call-stack.
-  -}
-
-data Recur  = Ok | Stop deriving (Eq, Show)
-type CStack = ([Symbol], Recur)
-
-instance PPrint Recur where
-  pprintTidy _ = Misc.tshow
-
-initCS :: CStack
-initCS = ([], Ok)
-
-pushCS :: CStack -> Symbol -> CStack
-pushCS (fs, r) f = (f:fs, r)
-
-recurCS :: CStack -> Symbol -> Bool
-recurCS (_,  Ok) _ = True
--- recurCS (_,  _ ) _ = False -- not (f `elem` fs)
-recurCS (fs, _) f  = f `notElem` fs
-
-noRecurCS :: CStack -> CStack
-noRecurCS (fs, _) = (fs, Stop)
-
--- Don't evaluate under Lam, App, Ite, or Constants
-topApps :: Expr -> [Expr]
-topApps = go
-  where
-    go (PAnd es)       = concatMap go es
-    go (POr es)        = concatMap go es
-    go (PAtom _ e1 e2) = go e1  ++ go e2
-    go (PIff e1 e2)    = go e1  ++ go e2
-    go (PImp e1 e2)    = go e1  ++ go e2
-    go (EBin  _ e1 e2) = go e1  ++ go e2
-    go (PNot e)        = go e
-    go (ENeg e)        = go e
-    go e@(EApp _ _)    = [e]
-    go _               = []
-
--- makeLam is the adjoint of splitEApp
-makeLam :: Knowledge -> Expr -> Expr
-makeLam γ e = L.foldl' (flip ELam) e (knLams γ)
-
-eval :: Knowledge -> CStack -> Expr -> EvalST Expr
-eval γ stk = go
-  where
-    go (ELam (x,s) e)   = ELam (x, s) <$> eval γ' stk e where γ' = γ { knLams = (x, s) : knLams γ }
-    go e@(EIte b e1 e2) = go b        >>= \b' -> evalIte γ stk e b' e1 e2
-    go (ECoerc s t e)   = ECoerc s t  <$> go e
-    go e@(EApp _ _)     = evalArgs γ stk e >>= evalApp γ stk e
-    go e@(EVar _)       = evalApp  γ stk e (e, [])
-    go (PAtom r e1 e2)  = PAtom r      <$> go e1 <*> go e2
-    go (ENeg e)         = ENeg         <$> go e
-    go (EBin o e1 e2)   = EBin o       <$> go e1 <*> go e2
-    go (ETApp e t)      = flip ETApp t <$> go e
-    go (ETAbs e s)      = flip ETAbs s <$> go e
-    go (PNot e)         = PNot         <$> go e
-    go (PImp e1 e2)     = PImp         <$> go e1 <*> go e2
-    go (PIff e1 e2)     = PIff         <$> go e1 <*> go e2
-    go (PAnd es)        = PAnd         <$> (go `traverse` es)
-    go (POr es)         = POr          <$> (go `traverse` es)
-    go e                = return e
-
--- | `evalArgs` also evaluates all the partial applications for hacky reasons,
---   suppose `foo g = id` then we want `foo g 10 = 10` and for that we need
---   to `eval` the term `foo g` into `id` to tickle the `eval` on `id 10`.
---   This seems a bit of a hack. At any rate, this can lead to divergence.
---   TODO: distill a .fq test from the MOSSAKA-hw3 example.
-
-evalArgs :: Knowledge -> CStack -> Expr -> EvalST (Expr, [Expr])
-evalArgs γ stk = go []
-  where
-    go acc (EApp f e)
-      = do f' <- evalOk γ stk f
-           e' <- eval γ stk e
-           go (e':acc) f'
-    go acc e
-      = (,acc) <$> eval γ stk e
-
--- | Minimal test case illustrating this `evalOk` hack is LH#tests/ple/pos/MossakaBug.hs
---   too tired & baffled to generate simple .fq version. TODO:nuke and rewrite PLE!
-evalOk :: Knowledge -> CStack -> Expr -> EvalST Expr
-evalOk γ stk@(_, Ok) e = eval γ stk e
-evalOk _ _           e = pure e
-
-{-
-evalArgs :: Knowledge -> CStack -> Expr -> EvalST (Expr, [Expr])
-evalArgs
-  | True  = evalArgsOLD
-  | False = evalArgsNEW
-
-evalArgsNEW :: Knowledge -> CStack -> Expr -> EvalST (Expr, [Expr])
-evalArgsNEW γ stk e = do
-    let (e1, es) = splitEApp e
-    e1' <- eval γ stk e1
-    es' <- mapM (eval γ stk) es
-    return (e1', es')
-
--}
-
-evalApp :: Knowledge -> CStack -> Expr -> (Expr, [Expr]) -> EvalST Expr
--- evalApp γ stk e (e1, es) = tracepp ("evalApp:END" ++ showpp (e1,es)) <$> (evalAppAc γ stk e (e1, es))
-evalApp γ stk e (e1, es) = do
-  res     <- evalAppAc γ stk e (e1, es)
-  let diff = res /= eApps e1 es
-  return   $ mytracepp ("evalApp:END:" ++ showpp diff) res
-
-evalAppAc :: Knowledge -> CStack -> Expr -> (Expr, [Expr]) -> EvalST Expr
-
-{- MOSSAKA-}
-evalAppAc γ stk e (EVar f, [ex])
-  | (EVar dc, es) <- splitEApp ex
-  , Just simp <- L.find (\simp -> (smName simp == f) && (smDC simp == dc)) (knSims γ)
-  , length (smArgs simp) == length es
-  = do let msg    = "evalAppAc:ePop: " ++ showpp (f, dc, es)
-       let ePopIf = mytracepp msg $ substPopIf (zip (smArgs simp) es) (smBody simp)
-       e'    <- eval γ stk ePopIf
-       (e, "Rewrite -" ++ showpp f) ~> e'
-
-evalAppAc γ stk _ (EVar f, es)
-  -- we should move the lookupKnowledge stuff here into kmAms γ
-  | Just eq <- L.find (( == f) . eqName) (knAms γ)
-  , Just bd <- getEqBody eq
-  , length (eqArgs eq) == length es
-  , f `notElem` syms bd               -- non-recursive equations << HACK! misses MUTUALLY RECURSIVE definitions!
-  , recurCS stk f
-  = do env   <- gets (seSort . evEnv)
-       let ee = substEq env PopIf eq es bd
-       assertSelectors γ ee
-       eval γ (pushCS stk f) ee
-
-evalAppAc γ stk _e (EVar f, es)
-  | Just eq <- L.find ((== f) . eqName) (knAms γ)
-  , Just bd <- getEqBody eq
-  , length (eqArgs eq) == length es   -- recursive equations
-  , recurCS stk f
-  = do env      <- gets (seSort . evEnv)
-       mytracepp ("EVAL-REC-APP" ++ showpp (stk, _e))
-         <$> evalRecApplication γ (pushCS stk f) (eApps (EVar f) es) (substEq env Normal eq es bd)
-
-evalAppAc _ _ _ (f, es)
-  = return (eApps f es)
-
---------------------------------------------------------------------------------
--- | 'substEq' unfolds or instantiates an equation at a particular list of
---   argument values. We must also substitute the sort-variables that appear
---   as coercions. See tests/proof/ple1.fq
---------------------------------------------------------------------------------
-substEq :: SEnv Sort -> SubstOp -> Equation -> [Expr] -> Expr -> Expr
-substEq env o eq es bd = substEqVal o eq es (substEqCoerce env eq es bd)
-
-data SubstOp = PopIf | Normal
-
-substEqVal :: SubstOp -> Equation -> [Expr] -> Expr -> Expr
-substEqVal o eq es bd = case o of
-    PopIf  -> substPopIf     xes  bd
-    Normal -> subst (mkSubst xes) bd
-  where
-    xes    =  zip xs es
-    xs     =  eqArgNames eq
-
-substEqCoerce :: SEnv Sort -> Equation -> [Expr] -> Expr -> Expr
-substEqCoerce env eq es bd = Vis.applyCoSub coSub bd
-  where
-    ts    = snd    <$> eqArgs eq
-    sp    = panicSpan "mkCoSub"
-    eTs   = sortExpr sp env <$> es
-    coSub = mytracepp ("substEqCoerce" ++ showpp (eqName eq, es, eTs, ts)) $ mkCoSub env eTs ts
-
-mkCoSub :: SEnv Sort -> [Sort] -> [Sort] -> Vis.CoSub
-mkCoSub env eTs xTs = M.fromList [ (x, unite ys) | (x, ys) <- Misc.groupList xys ]
-  where
-    unite ts    = mytracepp ("UNITE: " ++ showpp ts) $ Mb.fromMaybe (uError ts) (unifyTo1 symToSearch ts)
-    symToSearch = mkSearchEnv env
-    uError ts   = panic ("mkCoSub: cannot build CoSub for " ++ showpp xys ++ " cannot unify " ++ showpp ts)
-    xys         = mytracepp "mkCoSubXXX" $ Misc.sortNub $ concat $ zipWith matchSorts _xTs _eTs
-    (_xTs,_eTs) = mytracepp "mkCoSub:MATCH" (xTs, eTs)
-
-matchSorts :: Sort -> Sort -> [(Symbol, Sort)]
-matchSorts sort1 sort2 = mytracepp  ("matchSorts :" ++ showpp (sort1, sort2)) $ go sort1 sort2
-  where
-    go (FObj x)      {-FObj-} y    = [(x, y)]
-    go (FAbs _ t1)   (FAbs _ t2)   = go t1 t2
-    go (FFunc s1 t1) (FFunc s2 t2) = go s1 s2 ++ go t1 t2
-    go (FApp s1 t1)  (FApp s2 t2)  = go s1 s2 ++ go t1 t2
-    go _             _             = []
-
---------------------------------------------------------------------------------
-getEqBody :: Equation -> Maybe Expr
-getEqBody (Equ x xts b _ _)
-  | Just (fxs, e) <- getEqBodyPred b
-  , (EVar f, es)  <- splitEApp fxs
-  , f == x
-  , es == (EVar . fst <$> xts)
-  = Just e
-getEqBody _
-  = Nothing
-
-getEqBodyPred :: Expr -> Maybe (Expr, Expr)
-getEqBodyPred (PAtom Eq fxs e)
-  = Just (fxs, e)
-getEqBodyPred (PAnd ((PAtom Eq fxs e):_))
-  = Just (fxs, e)
-getEqBodyPred _
-  = Nothing
-
-eqArgNames :: Equation -> [Symbol]
-eqArgNames = map fst . eqArgs
-
-substPopIf :: [(Symbol, Expr)] -> Expr -> Expr
-substPopIf xes expr' = L.foldl' go expr' xes
-  where
-    go e (x, EIte b e1 e2) = EIte b (subst1 e (x, e1)) (subst1 e (x, e2))
-    go e (x, ex)           = subst1 e (x, ex)
-
--- see [NOTE:Eval-Ite] the below is wrong; we need to guard other branches too. sigh.
-
-evalRecApplication :: Knowledge -> CStack -> Expr -> Expr -> EvalST Expr
-evalRecApplication γ stk e (EIte b e1 e2) = do
-  contra <- {- mytracepp  ("CONTRA? " ++ showpp e) <$> -} liftIO (isValid γ PFalse)
-  if contra
-    then return e
-    else do b' <- eval γ stk (mytracepp "REC-APP-COND" b) -- <<<<<<<<<<<<<<<<<<<<< MOSSAKA-LOOP?
-            b1 <- liftIO (isValid γ b')
-            if b1
-              then addEquality γ e e1 >>
-                   {- SCC "assertSelectors-1" -} assertSelectors γ e1 >>
-                   eval γ stk (mytracepp ("evalREC-1: " ++ showpp stk) e1) >>=
-                   ((e, "App1: ") ~>)
-              else do
-                   b2 <- liftIO (isValid γ (PNot b'))
-                   if b2
-                      then addEquality γ e e2 >>
-                           {- SCC "assertSelectors-2" -} assertSelectors γ e2 >>
-                           eval γ stk (mytracepp ("evalREC-2: " ++ showpp stk) e2) >>=
-                           ((e, "App2: " ++ showpp stk ) ~>)
-                      else return e
-evalRecApplication _ _ _ e
-  = return e
-
-addEquality :: Knowledge -> Expr -> Expr -> EvalST ()
-addEquality γ e1 e2 =
-  modify (\st -> st{evSequence = (makeLam γ e1, makeLam γ e2):evSequence st})
-
-evalIte :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> EvalST Expr
-evalIte γ stk e b e1 e2 = mytracepp "evalIte:END: " <$>
-                            evalIteAc γ stk e b e1 (mytracepp msg e2)
-  where
-    msg = "evalIte:BEGINS: " ++ showpp (stk, e)
-
-
-evalIteAc :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> EvalST Expr
-evalIteAc γ stk e b e1 e2
-  = join $ evalIte' γ stk e b e1 e2 <$> liftIO (isValid γ b) <*> liftIO (isValid γ (PNot b))
-
-evalIte' :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> Bool -> Bool -> EvalST Expr
-evalIte' γ stk e _ e1 _ b _
-  | b
-  = do e' <- eval γ stk e1
-       (e, "If-True of:" ++ showpp b)  ~> e'
-evalIte' γ stk e _ _ e2 _ b'
-  | b'
-  = do e' <- eval γ stk e2
-       (e, "If-False") ~> e'
-evalIte' γ stk _ b e1 e2 _ _
-  -- see [NOTE:Eval-Ite] #387
-  = EIte b <$> eval γ stk' e1 <*> eval γ stk' e2
-    where stk' = mytracepp "evalIte'" $ noRecurCS stk
-
---------------------------------------------------------------------------------
--- | Knowledge (SMT Interaction)
---------------------------------------------------------------------------------
-data Knowledge = KN
-  { knSims    :: ![Rewrite]           -- ^ Measure info, asserted for each new Ctor ('assertSelectors')
-  , knAms     :: ![Equation]          -- ^ (Recursive) function definitions, used for PLE
-  , knContext :: SMT.Context
-  , knPreds   :: SMT.Context -> [(Symbol, Sort)] -> Expr -> IO Bool
-  , knLams    :: [(Symbol, Sort)]
-  }
-
-isValid :: Knowledge -> Expr -> IO Bool
-isValid γ e = mytracepp ("isValid: " ++ showpp e) <$>
-                knPreds γ (knContext γ) (knLams γ) e
-
-isProof :: (a, SortedReft) -> Bool
-isProof (_, RR s _) = showpp s == "Tuple"
-
-knowledge :: Config -> SMT.Context -> AxiomEnv -> Knowledge
-knowledge cfg ctx aenv = KN
-  { knSims    = aenvSimpl aenv
-  , knAms     = aenvEqs   aenv
-  , knContext = ctx
-  , knPreds   = askSMT    cfg
-  , knLams    = []
-  }
-
--- | This creates the rewrite rule e1 -> e2, applied when:
--- 1. when e2 is a DataCon and can lead to further reductions
--- 2. when size e2 < size e1
-initEqualities :: SMT.Context -> AxiomEnv -> [(Symbol, SortedReft)] -> [(Expr, Expr)]
-initEqualities ctx aenv es = concatMap (makeSimplifications (aenvSimpl aenv)) dcEqs
-  where
-    dcEqs                  = Misc.setNub (Mb.catMaybes [getDCEquality symEnv' e1 e2 | EEq e1 e2 <- atoms])
-    atoms                  = splitPAnd . expr =<< filter isProof es
-    symEnv'                = SMT.ctxSymEnv ctx
-
--- AT: Non-obvious needed invariant: askSMT True is always the
--- totality-effecting one.
--- RJ: What does "totality effecting" mean?
-
-toSMT :: Config -> SMT.Context -> [(Symbol, Sort)] -> Expr -> Pred
-toSMT = Common.toSMT "Instantiate.toSMT"
-
-makeSimplifications :: [Rewrite] -> (Symbol, [Expr], Expr) -> [(Expr, Expr)]
-makeSimplifications sis (dc, es, e)
-     = go =<< sis
- where
-   go (SMeasure f dc' xs bd)
-     | dc == dc', length xs == length es
-     = [(EApp (EVar f) e, subst (mkSubst $ zip xs es) bd)]
-   go _
-     = []
-
-getDCEquality :: SymEnv -> Expr -> Expr -> Maybe (Symbol, [Expr], Expr)
-getDCEquality sEnv e1 e2
-  | Just dc1 <- f1
-  , Just dc2 <- f2
-  = if dc1 == dc2
-      then Nothing
-      else error ("isDCEquality on" ++ showpp e1 ++ "\n" ++ showpp e2)
-  | Just dc1 <- f1
-  = Just (dc1, es1, e2)
-  | Just dc2 <- f2
-  = Just (dc2, es2, e1)
-  | otherwise
-  = Nothing
-  where
-    (f1, es1) = first (getDC sEnv) (splitEApp e1)
-    (f2, es2) = first (getDC sEnv) (splitEApp e2)
-
--- TODO: Stringy hacks
-getDC :: SymEnv -> Expr -> Maybe Symbol
-getDC sEnv (EVar x)
-  | isUpperSymbol x && Mb.isNothing (symEnvTheory x sEnv)
-  = Just x
-getDC _ _
-  = Nothing
-
-isUpperSymbol :: Symbol -> Bool
-isUpperSymbol x = (0 < lengthSym x') && isUpper (headSym x')
-  where
-    x' = dropModuleNames x
-
-dropModuleNames :: Symbol -> Symbol
-dropModuleNames = mungeNames (symbol . last) "."
-  where
-    mungeNames _ _ ""  = ""
-    mungeNames f d s'@(symbolText -> s)
-      | isTupleSymbol s' = s'
-      | otherwise        = f $ T.splitOn d $ stripParens s
-    stripParens t = Mb.fromMaybe t ((T.stripPrefix "(" >=> T.stripSuffix ")") t)
-
-    -- TODO: Remove this code which is LH specific
-    isTupleSymbol :: Symbol -> Bool
-    isTupleSymbol s =
-      let t = symbolText s
-       in T.isPrefixOf "Tuple" t &&
-          T.all isDigit (T.drop 5 t)
-
---------------------------------------------------------------------------------
--- | Creating Measure Info
---------------------------------------------------------------------------------
--- AT@TODO do this for all reflected functions, not just DataCons
-
-{- [NOTE:Datacon-Selectors] The 'assertSelectors' function
-   insert measure information for every constructor that appears
-   in the expression e.
-
-   In theory, this is not required as the SMT ADT encoding takes
-   care of it. However, in practice, some constructors, e.g. from
-   GADTs cannot be directly encoded in SMT due to the lack of SMTLIB
-   support for GADT. Hence, we still need to hang onto this code.
-
-   See tests/proof/ple2.fq for a concrete example.
- -}
-
-assertSelectors :: Knowledge -> Expr -> EvalST ()
-assertSelectors γ expr' = do
-    sims <- gets (aenvSimpl . _evAEnv)
-    -- cfg  <- gets evCfg
-    -- _    <- foldlM (\_ s -> Vis.mapMExpr (go s) e) (mytracepp  "assertSelector" e) sims
-    forM_ sims $ \s -> Vis.mapMExpr (go s) expr'
-  where
-    go :: Rewrite -> Expr -> EvalST Expr
-    go (SMeasure f dc xs bd) e@(EApp _ _)
-      | (EVar dc', es) <- splitEApp e
-      , dc == dc'
-      , length xs == length es
-      = do let e1 = EApp (EVar f) e
-           let e2 = subst (mkSubst $ zip xs es) bd
-           addEquality γ e1 e2
-           return e
-    go _ e
-      = return e
-
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-
-withCtx :: Config -> FilePath -> SymEnv -> [Equation] -> (SMT.Context -> IO a) -> IO a
-withCtx cfg file env defns k = do
-  ctx <- SMT.makeContextWithSEnv cfg file env defns
-  _   <- SMT.smtPush ctx
-  res <- k ctx
-  SMT.cleanupContext ctx
-  return res
-
-infixl 9 ~>
-(~>) :: (Expr, String) -> Expr -> EvalST Expr
-(e, _str) ~> e' = do
-  let msg = "PLE: " ++ _str ++ showpp (e, e')
-  modify (\st -> st {evId = mytracepp msg (evId st) + 1})
-  return e'
diff --git a/src/Language/Fixpoint/Solver/Interpreter.hs b/src/Language/Fixpoint/Solver/Interpreter.hs
--- a/src/Language/Fixpoint/Solver/Interpreter.hs
+++ b/src/Language/Fixpoint/Solver/Interpreter.hs
@@ -57,7 +57,7 @@
 --------------------------------------------------------------------------------
 -- | Strengthen Constraint Environments via PLE
 --------------------------------------------------------------------------------
-instInterpreter :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (SInfo a)
+instInterpreter :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (BindEnv a)
 instInterpreter cfg fi' subcIds = do
     let cs = M.filterWithKey
                (\i c -> isPleCstr aEnv i c && maybe True (i `L.elem`) subcIds)
@@ -178,11 +178,11 @@
 -- | Step 3: @resSInfo@ uses incremental PLE result @InstRes@ to produce the strengthened SInfo
 ----------------------------------------------------------------------------------------------
 
-resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> SInfo a
+resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> BindEnv a
 resSInfo cfg env info res = strengthenBinds info res'
   where
     res'     = M.fromList $ zip is ps''
-    ps''     = zipWith (\i -> elaborate (ElabParam (solverFlags $ solver cfg) (atLoc dummySpan ("PLE1 " ++ show i)) env)) is ps'
+    ps''     = zipWith (\i -> elaborate (ElabParam (solverFlags cfg) (atLoc dummySpan ("PLE1 " ++ show i)) env)) is ps'
     ps'      = defuncAny cfg env ps
     (is, ps) = unzip (M.toList res)
 
@@ -358,6 +358,7 @@
       EIte b _ _ -> go b $ e0 : acc
       ECoerc _ _ e -> go e acc
       ECst e _ -> go e acc
+      ELet{} -> acc
       ESym _ -> acc
       ECon _ -> acc
       EVar _ -> acc
@@ -367,7 +368,6 @@
       PKVar _ _ -> acc
       PAll _ _ -> acc
       PExist _ _ -> acc
-      PGrad{} -> acc
 
 largestApps :: Expr -> [Expr]
 largestApps = flip go []
@@ -388,13 +388,13 @@
       ESym _ -> acc
       ECon _ -> acc
       EVar _ -> e0 : acc
+      ELet{} -> acc
       ELam _ _ -> acc
       ETApp _ _ -> acc
       ETAbs _ _ -> acc
       PKVar _ _ -> acc
       PAll _ _ -> acc
       PExist _ _ -> acc
-      PGrad{} -> acc
 
 fastEval :: ConstMap -> Knowledge -> ICtx -> Expr -> EvalST Expr
 fastEval ienv γ ctx e
@@ -554,9 +554,11 @@
 interpret ie γ ctx env e@(PExist xss e1) = case xss of
   [] -> interpret' ie γ ctx env e1
   _  -> e
-interpret _  _ _   _   e@PGrad{}         = e
 interpret ie γ ctx env (ECoerc s t e)    = let e' = interpret' ie γ ctx env e in
                                              if s == t then e' else ECoerc s t e'
+interpret ie γ ctx env (ELet x e1 e2)    = let e1' = interpret' ie γ ctx env e1
+                                               e2' = interpret' ie γ ctx env e2 in
+                                             ELet x e1' e2'
 
 
 --------------------------------------------------------------------------------
diff --git a/src/Language/Fixpoint/Solver/Monad.hs b/src/Language/Fixpoint/Solver/Monad.hs
--- a/src/Language/Fixpoint/Solver/Monad.hs
+++ b/src/Language/Fixpoint/Solver/Monad.hs
@@ -5,19 +5,16 @@
 module Language.Fixpoint.Solver.Monad
        ( -- * Type
          SolveM
+       , liftSMT
 
          -- * Execution
        , runSolverM
 
-         -- * Get Binds
-       , getBinds
        , getContext
 
          -- * SMT Query
        , filterRequired
        , filterValid
-       , filterValidGradual
-       , checkSat
        , smtEnablembqi
        , sendConcreteBindingsToSMT
 
@@ -27,10 +24,12 @@
        , stats
        , numIter
        , SolverState(..)
+
+       , modifyContext
        )
        where
 
-import           Control.Monad (foldM, forM, forM_, when)
+import           Control.Monad (forM, forM_, when)
 import           Language.Fixpoint.Utils.Progress
 import qualified Language.Fixpoint.Types.Config  as C
 import           Language.Fixpoint.Types.Config  (Config)
@@ -43,18 +42,20 @@
 import           Language.Fixpoint.Smt.Serialize ()
 import           Language.Fixpoint.Types.PrettyPrint ()
 import           Language.Fixpoint.Smt.Interface
+import           Language.Fixpoint.Smt.Types (SmtM)
 -- import qualified Language.Fixpoint.Smt.Theories as Thy
 import           Language.Fixpoint.Solver.Sanitize
 import           Language.Fixpoint.Solver.Stats
 import           Language.Fixpoint.Graph.Types (SolverInfo (..))
 -- import           Language.Fixpoint.Solver.Solution
 -- import           Data.Maybe           (catMaybes)
-import           Data.List            (partition)
 -- import           Data.Char            (isUpper)
+import qualified Control.Monad.State as ST
 import           Control.Monad.State.Strict
 import qualified Data.HashMap.Strict as M
 import           Data.Maybe (catMaybes)
 import           Control.Exception.Base (bracket)
+import Language.Fixpoint.SortCheck (ElabParam)
 
 --------------------------------------------------------------------------------
 -- | Solver Monadic API --------------------------------------------------------
@@ -64,8 +65,8 @@
 
 data SolverState ann = SS
   { ssCtx     :: !Context         -- ^ SMT Solver Context
-  , ssBinds   :: !(F.BindEnv ann) -- ^ All variables and types
   , ssStats   :: !Stats           -- ^ Solver Statistics
+  , ssElabParam :: !ElabParam      -- ^ Elaboration Parameters
   }
 
 stats0    :: F.GInfo c b -> Stats
@@ -74,31 +75,24 @@
     nCs   = M.size $ F.cm fi
 
 --------------------------------------------------------------------------------
-runSolverM :: Config -> SolverInfo ann c -> SolveM ann a -> IO a
+runSolverM :: Config -> SolverInfo ann -> ElabParam -> SolveM ann a -> IO a
 --------------------------------------------------------------------------------
-runSolverM cfg sI act =
+runSolverM cfg sI elabParam act =
   bracket acquire release $ \ctx -> do
     res <- runStateT act' (s0 ctx)
     return (fst res)
   where
-    s0 ctx   = SS ctx be (stats0 fi)
+    s0 ctx   = SS ctx (stats0 fi) elabParam
     act'     = assumesAxioms (F.asserts fi) >> act
     release  = cleanupContext
     acquire  = makeContextWithSEnv cfg file initEnv (F.defns fi)
     initEnv  = symbolEnv cfg fi
-    be       = F.bs fi
     file     = C.srcFile cfg
     -- only linear arithmetic when: linear flag is on or solver /= Z3
     -- lar     = linear cfg || Z3 /= solver cfg
     fi       = (siQuery sI) {F.hoInfo = F.cfgHoInfo cfg }
 
-
 --------------------------------------------------------------------------------
-getBinds :: SolveM ann (F.BindEnv ann)
---------------------------------------------------------------------------------
-getBinds = ssBinds <$> get
-
---------------------------------------------------------------------------------
 getIter :: SolveM ann Int
 --------------------------------------------------------------------------------
 getIter = numIter . ssStats <$> get
@@ -115,8 +109,13 @@
 incChck n = modifyStats $ \s -> s {numChck = n + numChck s}
 incVald n = modifyStats $ \s -> s {numVald = n + numVald s}
 
-withContext :: (Context -> IO a) -> SolveM ann a
-withContext k = (lift . k) =<< getContext
+liftSMT :: SmtM a -> SolveM ann a
+liftSMT k =
+  do es <- get
+     let ctx = ssCtx es
+     (a, ctx') <- lift $ ST.runStateT k ctx
+     put (es {ssCtx = ctx'})
+     pure a
 
 getContext :: SolveM ann Context
 getContext = ssCtx <$> get
@@ -124,6 +123,9 @@
 modifyStats :: (Stats -> Stats) -> SolveM ann ()
 modifyStats f = modify $ \s -> s { ssStats = f (ssStats s) }
 
+modifyContext :: (Context -> Context) -> SolveM ann ()
+modifyContext f = modify $ \s -> s { ssCtx = f (ssCtx s) }
+
 --------------------------------------------------------------------------------
 -- | SMT Interface -------------------------------------------------------------
 --------------------------------------------------------------------------------
@@ -133,9 +135,8 @@
 --
 -- Yields the ids of bindings known to the SMT
 sendConcreteBindingsToSMT
-  :: F.IBindEnv -> (F.IBindEnv -> SolveM ann a) -> SolveM ann a
-sendConcreteBindingsToSMT known act = do
-  be <- getBinds
+  :: F.IBindEnv -> F.BindEnv ann -> (F.IBindEnv -> SolveM ann a) -> SolveM ann a
+sendConcreteBindingsToSMT known be act = do
   let concretePreds =
         [ (i, F.subst1 p (v, F.EVar s))
         | (i, (s, F.RR _ (F.Reft (v, p)),_)) <- F.bindEnvToList be
@@ -144,12 +145,16 @@
         , not (F.memberIBindEnv i known)
         ]
   st <- get
-  (a, st') <- withContext $ \me -> do
-    smtBracket me "" $ do
+  (a, st'') <- liftSMT $
+    smtBracket "sendConcreteBindingsToSMT" $ do
       forM_ concretePreds $ \(i, e) ->
-        smtDefineFunc me (F.bindSymbol (fromIntegral i)) [] F.boolSort e
-      flip runStateT st $ act $ F.unionIBindEnv known $ F.fromListIBindEnv $ map fst concretePreds
-  put st'
+        smtDefineFunc (F.bindSymbol (fromIntegral i)) [] F.boolSort e
+      ctx <- get
+      let st' = st { ssCtx = ctx }
+      (a, st'') <- liftIO $ flip runStateT st' $ act $ F.unionIBindEnv known $ F.fromListIBindEnv $ map fst concretePreds
+      put (ssCtx st'')
+      return (a, st'')
+  modify $ \st''' -> st'' { ssCtx = ssCtx st''' }
   return a
   where
     isShortExpr F.PTrue = True
@@ -170,9 +175,9 @@
 filterValid :: F.SrcSpan -> F.Expr -> F.Cand a -> SolveM ann [a]
 --------------------------------------------------------------------------------
 filterValid sp p qs = do
-  qs' <- withContext $ \me ->
-           smtBracket me "filterValidLHS" $
-             filterValid_ sp p qs me
+  qs' <- liftSMT $
+           smtBracket "filterValidLHS" $
+             filterValid_ sp p qs
   -- stats
   incBrkt
   incChck (length qs)
@@ -180,66 +185,23 @@
   return qs'
 
 {-# SCC filterValid_ #-}
-filterValid_ :: F.SrcSpan -> F.Expr -> F.Cand a -> Context -> IO [a]
-filterValid_ sp p qs me = catMaybes <$> do
-  smtAssert me p
+filterValid_ :: F.SrcSpan -> F.Expr -> F.Cand a -> SmtM [a]
+filterValid_ sp p qs = catMaybes <$> do
+  smtAssertDecl p
   forM qs $ \(q, x) ->
-    smtBracketAt sp me "filterValidRHS" $ do
-      smtAssert me (F.PNot q)
-      valid <- smtCheckUnsat me
+    smtBracketAt sp "filterValidRHS" $ do
+      smtAssertDecl (F.PNot q)
+      valid <- smtCheckUnsat
       return $ if valid then Just x else Nothing
 
---------------------------------------------------------------------------------
--- | `filterValidGradual ps [(x1, q1),...,(xn, qn)]` returns the list `[ xi | p => qi]`
--- | for some p in the list ps
---------------------------------------------------------------------------------
-filterValidGradual :: [F.Expr] -> F.Cand a -> SolveM ann [a]
---------------------------------------------------------------------------------
-filterValidGradual p qs = do
-  qs' <- withContext $ \me ->
-           smtBracket me "filterValidGradualLHS" $
-             filterValidGradual_ p qs me
-  -- stats
-  incBrkt
-  incChck (length qs)
-  incVald (length qs')
-  return qs'
-
-filterValidGradual_ :: [F.Expr] -> F.Cand a -> Context -> IO [a]
-filterValidGradual_ ps qs me
-  = map snd . fst <$> foldM partitionCandidates ([], qs) ps
-  where
-    partitionCandidates :: (F.Cand a, F.Cand a) -> F.Expr -> IO (F.Cand a, F.Cand a)
-    partitionCandidates (ok, candidates) p = do
-      (valids', invalids')  <- partition snd <$> filterValidOne_ p candidates me
-      let (valids, invalids) = (fst <$> valids', fst <$> invalids')
-      return (ok ++ valids, invalids)
-
-filterValidOne_ :: F.Expr -> F.Cand a -> Context -> IO [((F.Expr, a), Bool)]
-filterValidOne_ p qs me = do
-  smtAssert me p
-  forM qs $ \(q, x) ->
-    smtBracket me "filterValidRHS" $ do
-      smtAssert me (F.PNot q)
-      valid <- smtCheckUnsat me
-      return ((q, x), valid)
-
 smtEnablembqi :: SolveM ann ()
 smtEnablembqi
-  = withContext smtSetMbqi
-
---------------------------------------------------------------------------------
-checkSat :: F.Expr -> SolveM ann Bool
---------------------------------------------------------------------------------
-checkSat p
-  = withContext $ \me ->
-      smtBracket me "checkSat" $
-        smtCheckSat me p
+  = liftSMT smtSetMbqi
 
 --------------------------------------------------------------------------------
 assumesAxioms :: [F.Triggered F.Expr] -> SolveM ann ()
 --------------------------------------------------------------------------------
-assumesAxioms es = withContext $ \me -> forM_  es $ smtAssertAxiom me
+assumesAxioms es = liftSMT $ forM_ es smtAssertAxiom
 
 
 ---------------------------------------------------------------------------
diff --git a/src/Language/Fixpoint/Solver/PLE.hs b/src/Language/Fixpoint/Solver/PLE.hs
--- a/src/Language/Fixpoint/Solver/PLE.hs
+++ b/src/Language/Fixpoint/Solver/PLE.hs
@@ -8,6 +8,7 @@
 --     2. "Reasoning about Functions", VMCAI 2018, https://ranjitjhala.github.io/static/reasoning-about-functions.pdf
 --------------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts          #-}
 {-# LANGUAGE OverloadedStrings         #-}
 {-# LANGUAGE PartialTypeSignatures     #-}
 {-# LANGUAGE TupleSections             #-}
@@ -30,10 +31,11 @@
 
 import           Language.Fixpoint.Types hiding (simplify)
 import           Language.Fixpoint.Types.Config  as FC
-import           Language.Fixpoint.Types.Solutions (CMap)
+import           Language.Fixpoint.Types.Solutions (CMap, Solution)
 import qualified Language.Fixpoint.Types.Visitor as Vis
 import qualified Language.Fixpoint.Misc          as Misc
 import qualified Language.Fixpoint.Smt.Interface as SMT
+import           Language.Fixpoint.Smt.Types (SmtM)
 import           Language.Fixpoint.Defunctionalize
 import           Language.Fixpoint.Solver.EnvironmentReduction (inlineInExpr, undoANF)
 import qualified Language.Fixpoint.Utils.Files   as Files
@@ -44,6 +46,7 @@
 import           Language.Fixpoint.Solver.Common          (askSMT, toSMT)
 import           Language.Fixpoint.Solver.Sanitize        (symbolEnv)
 import           Language.Fixpoint.Solver.Simplify
+import           Language.Fixpoint.Solver.Solution (CombinedEnv(..), applyInSortedReft)
 import           Language.Fixpoint.Solver.Rewrite as Rewrite
 
 import Language.REST.OCAlgebra as OC
@@ -51,7 +54,7 @@
 import Language.REST.RuntimeTerm as RT
 import Language.REST.SMT (withZ3, SolverHandle)
 
-import           Control.Monad (filterM, foldM, forM_, when, replicateM)
+import           Control.Monad (filterM, foldM, forM_, when, replicateM, zipWithM)
 import           Control.Monad.State
 import           Control.Monad.Trans.Maybe
 import           Data.Bifunctor (second)
@@ -71,26 +74,31 @@
 
 --------------------------------------------------------------------------------
 -- | Strengthen Constraint Environments via PLE
---------------------------------------------------------------------------------
+--
+-- @instantiate cfg fi subcIds@ yields @F.bs fi@ strengthened with the
+-- unfoldings discovered by PLE on the constraints in @subcIds@ (or all
+-- constraints if @subcIds == Nothing@).
 {-# SCC instantiate #-}
-instantiate :: (Loc a) => Config -> SInfo a -> Maybe [SubcId] -> IO (SInfo a)
-instantiate cfg fi' subcIds = do
+instantiate :: (Loc a) => Config -> SInfo a -> Maybe Solution -> Maybe [SubcId] -> SmtM (BindEnv a)
+instantiate cfg fi' mSol subcIds = do
     let cs = M.filterWithKey
                (\i c -> isPleCstr aEnv i c && maybe True (i `L.elem`) subcIds)
                (cm info)
     let t  = mkCTrie (M.toList cs)                                          -- 1. BUILD the Trie
-    res   <- withRESTSolver $ \solver -> withProgress (1 + M.size cs) $
-               withCtx cfg file sEnv (defns fi') $ \ctx -> do
-                  env <- instEnv cfg info cs solver ctx
-                  pleTrie t env                                             -- 2. TRAVERSE Trie to compute InstRes
-    savePLEEqualities cfg info sEnv res
+    res   <- withRESTSolver $ \solver -> do
+               ctx <- get
+               (res, ctx') <- liftIO $ withProgressM (`runStateT` ctx) (1 + M.size cs) $ do
+                 env <- instEnv cfg info mSol cs solver
+                 pleTrie t env                                              -- 2. TRAVERSE Trie to compute InstRes
+               put ctx'
+               return res
+    liftIO $ savePLEEqualities cfg info sEnv res
     return $ resSInfo cfg sEnv info res                                     -- 3. STRENGTHEN SInfo using InstRes
   where
-    withRESTSolver :: (Maybe SolverHandle -> IO a) -> IO a
+    withRESTSolver :: (Maybe SolverHandle -> SmtM a) -> SmtM a
     withRESTSolver f | all null (M.elems $ aenvAutoRW aEnv) = f Nothing
     withRESTSolver f = withZ3 (f . Just)
 
-    file = srcFile cfg ++ ".evals"
     sEnv = symbolEnv cfg info
     aEnv = ae info
     info = normalize fi'
@@ -107,7 +115,7 @@
   where
     equalitiesPerConstraint (cid, c) =
       (cid, L.sort [ e | i <- elemsIBindEnv (senv c), Just e <- [M.lookup i res] ])
-    elabParam = ElabParam (solverFlags $ solver cfg) "savePLEEqualities" sEnv
+    elabParam = ElabParam (solverFlags cfg) "savePLEEqualities" sEnv
     renderConstraintRewrite (cid, eqs) =
       "constraint id" <+> text (show cid ++ ":")
       $+$ nest 2
@@ -122,10 +130,18 @@
 
 -------------------------------------------------------------------------------
 -- | Step 1a: @instEnv@ sets up the incremental-PLE environment
-instEnv :: (Loc a) => Config -> SInfo a -> CMap (SimpC a) -> Maybe SolverHandle -> SMT.Context -> IO (InstEnv a)
-instEnv cfg info cs restSolver ctx = do
-    refRESTCache <- newIORef mempty
-    refRESTSatCache <- newIORef mempty
+instEnv
+  :: Loc a
+  => Config
+  -> SInfo a
+  -> Maybe Solution
+  -> CMap (SimpC a)
+  -> Maybe SolverHandle
+  -> SmtM (InstEnv a)
+instEnv cfg info s cs restSolver = do
+    ctx <- get
+    refRESTCache <- liftIO $ newIORef mempty
+    refRESTSatCache <- liftIO $ newIORef mempty
     let
         restOrd = FC.restOC cfg
         oc0 = ordConstraints restOrd $ Mb.fromJust restSolver
@@ -145,6 +161,8 @@
         s0 = EvalEnv
               { evEnv = SMT.ctxSymEnv ctx
               , evElabF = ef
+              , evKCtx = ctx
+              , evExScope = []
               , evPendingUnfoldings = mempty
               , evNewEqualities = mempty
               , evSMTCache = mempty
@@ -157,16 +175,16 @@
               }
     return $ InstEnv
        { ieCfg = cfg
-       , ieSMT = ctx
-       , ieBEnv = coerceBindEnv ef (bs info)
+       , ieBEnv = bs info
        , ieAenv = ae info
        , ieCstrs = cs
-       , ieKnowl = knowledge cfg ctx info
+       , ieKnowl = knowledge cfg info
        , ieEvEnv = s0
        , ieLRWs  = lrws info
+       , ieSol  = s
        }
   where
-    ef = solverFlags $ solver cfg
+    ef = solverFlags cfg
 
     cachedNotStrongerThan refRESTCache oc a b = do
       m <- readIORef refRESTCache
@@ -212,7 +230,7 @@
 
 ----------------------------------------------------------------------------------------------
 -- | Step 2: @pleTrie@ walks over the @CTrie@ to actually do the incremental-PLE
-pleTrie :: CTrie -> InstEnv a -> IO InstRes
+pleTrie :: Loc a => CTrie -> InstEnv a -> SmtM InstRes
 pleTrie t env = loopT env ctx0 diff0 Nothing res0 t
   where
     diff0        = []
@@ -225,42 +243,54 @@
       , icSubcId             = Nothing
       , icANFs               = []
       , icLRWs               = mempty
+      , icBindIds            = mempty
       , icEtaBetaFlag        = etabeta        $ ieCfg env
       , icExtensionalityFlag = extensionality $ ieCfg env
       , icLocalRewritesFlag  = localRewrites  $ ieCfg env
+      , icFreshExistentialCounter = 0
+      , icInitialLHSs  = mempty
       }
 
 loopT
-  :: InstEnv a
+  :: Loc a
+  => InstEnv a
   -> ICtx
   -> Diff         -- ^ The longest path suffix without forks in reverse order
   -> Maybe BindId -- ^ bind id of the branch ancestor of the trie if any.
                   --   'Nothing' when this is the top-level trie.
   -> InstRes
   -> CTrie
-  -> IO InstRes
-loopT env ctx delta i res t = case t of
+  -> SmtM InstRes
+loopT env ictx delta i res t = case t of
   T.Node []  -> return res
-  T.Node [b] -> loopB env ctx delta i res b
-  T.Node bs  -> withAssms env ctx delta Nothing $ \ctx' -> do
-                  (ctx'', env'', res') <- ple1 env ctx' i res
-                  foldM (loopB env'' ctx'' [] i) res' bs
+  T.Node [b] -> loopB env ictx delta i res b
+  T.Node bs  -> withAssms env ictx delta Nothing (Just t) $ \ictx' -> do
+                  (ictx'', env'', res') <- ple1 env ictx' i res
+                  foldM (loopB env'' ictx'' [] i) res' bs
 
 loopB
-  :: InstEnv a
+  :: Loc a
+  => InstEnv a
   -> ICtx
   -> Diff         -- ^ The longest path suffix without forks in reverse order
   -> Maybe BindId -- ^ bind id of the branch ancestor of the branch if any.
                   --   'Nothing' when this is a branch of the top-level trie.
   -> InstRes
   -> CBranch
-  -> IO InstRes
-loopB env ctx delta iMb res b = case b of
-  T.Bind i t -> loopT env ctx (i:delta) (Just i) res t
-  T.Val cid  -> withAssms env ctx delta (Just cid) $ \ctx' -> do
-                  progressTick
-                  (\(_, _, r) -> r) <$> ple1 env ctx' iMb res
+  -> SmtM InstRes
+loopB env ictx delta iMb res b = case b of
+  T.Bind i t -> loopT env ictx (i:delta) (Just i) res t
+  T.Val cid  -> withAssms env ictx delta (Just cid) Nothing $ \ictx' -> do
+                  liftIO progressTick
+                  (\(_, _, r) -> r) <$> ple1 env ictx' iMb res
 
+collectConstraints :: CTrie -> [SubcId]
+collectConstraints = go
+  where
+    go (T.Node bs) = concatMap goB bs
+    goB (T.Bind _ t) = go t
+    goB (T.Val cid)  = [cid]
+
 -- | Adds to @ctx@ candidate expressions to unfold from the bindings in @delta@
 -- and the rhs of @cidMb@.
 --
@@ -271,35 +301,59 @@
 -- Pushes assumptions from the modified context to the SMT solver, runs @act@,
 -- and then pops the assumptions.
 --
-withAssms :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> (ICtx -> IO b) -> IO b
-withAssms env@InstEnv{..} ctx delta cidMb act = do
-  let ctx' = updCtx env ctx delta cidMb
-  let assms = icAssms ctx'
+withAssms
+  :: Loc a
+  => InstEnv a
+  -> ICtx
+  -> Diff
+  -> Maybe SubcId
+  -> Maybe CTrie
+  -> (ICtx -> SmtM b)
+  -> SmtM b
+withAssms env ctx delta cidMb mCTrie act = do
+  sctx <- get
+  let cfg = SMT.config sctx
+  let (ictx', bs) = updCtx cfg env sctx ctx delta cidMb mCTrie
+  let assms = icAssms ictx'
 
-  SMT.smtBracket ieSMT "PLE.evaluate" $ do
-    forM_ assms (SMT.smtAssert ieSMT)
-    act ctx' { icAssms = mempty }
+  SMT.smtBracket "PLE.withAssms" $ do
+    -- See Note [Existential quantification when unfolding]
+    SMT.smtDecls $ elabBindings (ieEvEnv env) bs
+    forM_ (S.toList assms) SMT.smtAssertDecl
+    act $ ictx' { icAssms = mempty }
 
+  where
+    elabBindings eenv bs =
+      elaborate (ElabParam (evElabF eenv) "withAssms: PExist Args" (evEnv eenv)) bs
+
 -- | @ple1@ performs the PLE at a single "node" in the Trie
 --
 -- It will generate equalities for all function invocations in the candidates
 -- in @ctx@ for which definitions are known. The function definitions are in
 -- @ieKnowl@.
-ple1 :: InstEnv a -> ICtx -> Maybe BindId -> InstRes -> IO (ICtx, InstEnv a, InstRes)
-ple1 ie@InstEnv{..} ctx i res = do
-  (ctx', env) <- runStateT (evalCandsLoop ieCfg ctx ieSMT ieKnowl) ieEvEnv
-  let pendings = collectPendingUnfoldings env (icSubcId ctx)
-      newEqs = pendings ++ S.toList (S.difference (icEquals ctx') (icEquals ctx))
-  return (ctx', ie { ieEvEnv = env }, updCtxRes res i newEqs)
+ple1 :: InstEnv a -> ICtx -> Maybe BindId -> InstRes -> SmtM (ICtx, InstEnv a, InstRes)
+ple1 ie@InstEnv{..} ictx i res = do
+  ctx <- get
+  (ictx', env) <- liftIO $ runStateT (evalCandsLoop ieCfg ictx ieKnowl) (ieEvEnv { evKCtx = ctx })
+  put $ evKCtx env
+  let pendings = collectPendingUnfoldings env (icSubcId ictx)
+      newEqs =
+        reconstructExistentials
+          (M.intersectionWith S.union (icInitialLHSs ictx) $         -- add original predicates
+           M.map (S.map equalitiesPred) $                            -- construct equalities
+           M.unionWith S.union pendings $                            -- pending unfoldings if any
+           M.unionWith S.difference (icEquals ictx') (icEquals ictx) -- new equalities only
+          )
+  return (ictx', ie { ieEvEnv = env }, updCtxRes res i newEqs)
   where
     -- Pending unfoldings (i.e. with undecided guards) are collected only
     -- when we reach a leaf in the Trie, and only if the user asked for them.
-    collectPendingUnfoldings env (Just _) | pleWithUndecidedGuards ieCfg =
-      M.toList (evPendingUnfoldings env)
-    collectPendingUnfoldings _ _ = []
+    collectPendingUnfoldings env (Just _) | pleUndecGuards ieCfg =
+      M.map (S.fromList . M.toList) (evPendingUnfoldings env)
+    collectPendingUnfoldings _ _ = mempty
 
-evalToSMT :: String -> Config -> SMT.Context -> (Expr, Expr) -> Pred
-evalToSMT msg cfg ctx (e1,e2) = toSMT ("evalToSMT:" ++ msg) cfg ctx [] (EEq e1 e2)
+evalToSMT :: String -> Config -> SMT.Context -> [(Symbol, Sort)] -> (Expr, Expr) -> Pred
+evalToSMT msg cfg ctx bs (e1,e2) = toSMT ("evalToSMT:" ++ msg) cfg ctx bs (EEq e1 e2)
 
 -- | Generate equalities for all function invocations in the candidates
 -- in @ctx@ for which definitions are known. The function definitions are in
@@ -316,44 +370,72 @@
 -- > until no new equalities are discovered
 -- >       or the environment becomes inconsistent
 --
-evalCandsLoop :: Config -> ICtx -> SMT.Context -> Knowledge -> EvalST ICtx
-evalCandsLoop cfg ictx0 ctx γ = go ictx0 0
+evalCandsLoop :: Config -> ICtx -> Knowledge -> EvalST ICtx
+evalCandsLoop cfg ictx0 γ = go ictx0 0
   where
-    go ictx _ | S.null (icCands ictx) = return ictx
+    go :: ICtx -> Int -> EvalST ICtx
+    go ictx _ | all null (icCands ictx) = return ictx
     go ictx i = do
       inconsistentEnv <- testForInconsistentEnvironment
       if inconsistentEnv
         then return ictx
-        else do liftIO $ SMT.smtAssert ctx (pAndNoDedup (S.toList $ icAssms ictx))
+        else do liftSMT $ SMT.smtAssertDecl $ pAndNoDedup $ S.toList $ icAssms ictx
                 let ictx' = ictx { icAssms = mempty }
-                    cands = S.toList $ icCands ictx
-                candss <- mapM (evalOne γ ictx' i) cands
-                us <- gets evNewEqualities
-                modify $ \st -> st { evNewEqualities = mempty }
-                let noCandidateChanged = and (zipWith eqCand candss cands)
-                    unknownEqs = us `S.difference` icEquals ictx
-                if S.null unknownEqs && noCandidateChanged
-                      then return ictx
-                      else do let eqsSMT = evalToSMT "evalCandsLoop" cfg ctx `S.map` unknownEqs
-                              let ictx'' = ictx' { icEquals = icEquals ictx <> unknownEqs
-                                                 , icAssms  = S.filter (not . isTautoPred) eqsSMT }
-                              go (ictx'' { icCands = S.fromList (concat candss) }) (i + 1)
+                    (scopes, candSets) = unzip $ M.toList $ icCands ictx
+                    cands = map S.toList candSets
+                (candss, uss) <- unzip <$> zipWithM (evalCand ictx' i) scopes cands
+                let noCandidateChanged = all and $ zipWith (zipWith eqCand) candss cands
+                    unknownEqs = M.unionWith S.difference (M.fromList (zip scopes uss)) (icEquals ictx)
+                if all null unknownEqs && noCandidateChanged then
+                  return ictx
+                else do
+                  ctx' <- gets evKCtx
+                  let eqsSMT =
+                        S.unions $ M.elems $
+                          M.mapWithKey
+                            (\scope -> S.map $ evalToSMT "evalCandsLoop" cfg ctx' scope)
+                            unknownEqs
+                      ictx'' = ictx
+                        { icEquals = M.unionWith S.union (icEquals ictx) unknownEqs
+                        , icAssms  = S.filter (not . isTautoPred) eqsSMT
+                        }
+                  go (ictx'' { icCands = M.fromList $ zip scopes (map (S.fromList . concat) candss) }) (i + 1)
 
+    testForInconsistentEnvironment :: EvalST Bool
     testForInconsistentEnvironment =
-      liftIO $ knPreds γ (knContext γ) (knLams γ) PFalse
+      knPredsEvalST γ PFalse
 
     eqCand [e0] e1 = e0 == e1
     eqCand _ _ = False
 
+    evalCand :: ICtx -> Int -> ExScope -> [Expr] -> EvalST ([[Expr]], S.HashSet (Expr, Expr))
+    evalCand ictx i scope es = withExScope scope $ mapM (evalOne γ ictx i) es >>= collectEqs
+
+    collectEqs :: [[Expr]] -> EvalST ([[Expr]], S.HashSet (Expr, Expr))
+    collectEqs es = do
+      env <- get
+      let newEqs = evNewEqualities env
+      modify $ \st -> st { evNewEqualities = mempty }
+      return (es, newEqs)
+
+    withExScope :: ExScope -> EvalST a -> EvalST a
+    withExScope s m = do
+      env <- get
+      put $ env { evExScope = s }
+      r <- m
+      modify $ \st -> st { evExScope = evExScope env }
+      return r
+
+
 ----------------------------------------------------------------------------------------------
 -- | Step 3: @resSInfo@ uses incremental PLE result @InstRes@ to produce the strengthened SInfo
 ----------------------------------------------------------------------------------------------
 
-resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> SInfo a
+resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> BindEnv a
 resSInfo cfg env info res = strengthenBinds info res'
   where
     res'     = M.fromList $ zip is ps''
-    ps''     = zipWith (\i -> elaborate (ElabParam (solverFlags $ solver cfg) (atLoc dummySpan ("PLE1 " ++ show i)) env)) is ps'
+    ps''     = zipWith (\i -> elaborate (ElabParam (solverFlags cfg) (atLoc dummySpan ("PLE1 " ++ show i)) env)) is ps'
     ps'      = defuncAny cfg env ps
     (is, ps) = unzip (M.toList res)
 
@@ -363,13 +445,13 @@
 
 data InstEnv a = InstEnv
   { ieCfg   :: !Config
-  , ieSMT   :: !SMT.Context
   , ieBEnv  :: !(BindEnv a)
   , ieAenv  :: !AxiomEnv
   , ieCstrs :: !(CMap (SimpC a))
   , ieKnowl :: !Knowledge
   , ieEvEnv :: !EvalEnv
   , ieLRWs  :: LocalRewritesEnv
+  , ieSol :: Maybe Solution
   }
 
 ----------------------------------------------------------------------------------------------
@@ -378,18 +460,22 @@
 
 data ICtx    = ICtx
   { icAssms              :: S.HashSet Pred           -- ^ Equalities converted to SMT format
-  , icCands              :: S.HashSet Expr           -- ^ "Candidates" for unfolding
-  , icEquals             :: EvEqualities             -- ^ Accumulated equalities
+  , icCands              :: M.HashMap ExScope (S.HashSet Expr)  -- ^ "Candidates" for unfolding
+  , icEquals             :: M.HashMap ExScope EvEqualities      -- ^ Accumulated equalities
   , icSimpl              :: !ConstMap                -- ^ Map of expressions to constants
   , icSubcId             :: Maybe SubcId             -- ^ Current subconstraint ID
   , icANFs               :: [[(Symbol, SortedReft)]] -- Hopefully contain only ANF things
   , icLRWs               :: LocalRewrites            -- ^ Local rewrites
+  , icBindIds            :: IBindEnv                 -- ^ Bind Ids in the current context
   , icEtaBetaFlag        :: Bool                     -- ^ True if the etabeta flag is turned on, needed
                                                      -- for the eta expansion reasoning as its going to
                                                      -- generate ho constraints
                                                      -- See Note [Eta expansion].
   , icExtensionalityFlag :: Bool                     -- ^ True if the extensionality flag is turned on
   , icLocalRewritesFlag  :: Bool                     -- ^ True if the local rewrites flag is turned on
+  , icFreshExistentialCounter :: Int                 -- ^ Counter to generate fresh names for existentials
+  , icInitialLHSs :: M.HashMap ExScope (S.HashSet Expr)
+                                                     -- ^ LHS candidates before any unfoldings
   }
 
 ----------------------------------------------------------------------------------------------
@@ -408,11 +494,13 @@
 type CBranch = T.Branch SubcId
 type Diff    = [BindId]    -- ^ in "reverse" order
 
-equalitiesPred :: [(Expr, Expr)] -> [Expr]
-equalitiesPred eqs = [ EEq e1 e2 | (e1, e2) <- eqs, e1 /= e2 ]
+equalitiesPred :: (Expr, Expr) -> Expr
+equalitiesPred (e1, e2)
+  | e1 /= e2 = EEq e1 e2
+  | otherwise = PTrue
 
-updCtxRes :: InstRes -> Maybe BindId -> [(Expr, Expr)] -> InstRes
-updCtxRes res iMb = updRes res iMb . pAndNoDedup . equalitiesPred
+updCtxRes :: InstRes -> Maybe BindId -> [Expr] -> InstRes
+updCtxRes res iMb = updRes res iMb . pAndNoDedup
 
 
 updRes :: InstRes -> Maybe BindId -> Expr -> InstRes
@@ -422,41 +510,91 @@
 ----------------------------------------------------------------------------------------------
 -- | @updCtx env ctx delta cidMb@ adds the assumptions and candidates from @delta@ and @cidMb@
 --   to the context.
+--
+-- Yields the new context and a list of existential binders found in @delta@.
+-- See Note [Existential quantification when unfolding].
 ----------------------------------------------------------------------------------------------
 
-updCtx :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> ICtx
-updCtx InstEnv{..} ctx delta cidMb
-            = ctx { icAssms  = S.fromList (filter (not . isTautoPred) ctxEqs)
-                  , icCands  = S.fromList deANFedCands <> icCands  ctx
-                  , icSimpl  = icSimpl ctx <> econsts
-                  , icSubcId = cidMb
-                  , icANFs   = anfBinds
-                  , icLRWs   = mconcat $ icLRWs ctx : newLRWs
-                  }
+updCtx
+  :: Loc a
+  => Config
+  -> InstEnv a
+  -> SMT.Context
+  -> ICtx
+  -> Diff
+  -> Maybe SubcId
+  -> Maybe CTrie
+  -> (ICtx, [(Symbol, Sort)])
+updCtx cfg InstEnv{..} ieSMT ictx delta cidMb mCTrie =
+    ( ictx { icAssms  = S.fromList ctxEqs
+           , icCands  = M.unionWith S.union candsPerExScope (icCands ictx)
+           , icSimpl  = icSimpl ictx <> econsts
+           , icSubcId = cidMb
+           , icANFs   = anfBinds
+           , icLRWs   = mconcat $ icLRWs ictx : newLRWs
+           , icBindIds = ibinds
+           , icFreshExistentialCounter = existentialCounter
+           , icInitialLHSs = M.unionWith S.union candsPerExScopeNoRHS (icInitialLHSs ictx)
+           }
+    , ebs
+    )
   where
+    ebs = concat (M.keys candsPerExScope)
+    ibinds = insertsIBindEnv delta (icBindIds ictx)
     cands     = rhs:es
-    anfBinds  = bs : icANFs ctx
+    anfBinds  = bs : icANFs ictx
     econsts   = M.fromList $ findConstants ieKnowl es
-    ctxEqs    = toSMT "updCtx" ieCfg ieSMT [] <$> L.nub
-                  [ c | xr <- bs, c <- conjuncts (expr xr), null (Vis.kvarsExpr c) ]
+    ctxEqs    = toSMT "updCtx" ieCfg ieSMT ebs <$> L.nub
+                  [ c
+                  | (_, s) <- drop 1 deANFedCands
+                  , e <- S.toList s
+                  , c <- conjuncts e
+                  , not (isTautoPred c)
+                  ]
     bs        = second unApplySortedReft <$> binds
     rhs       = unApply eRhs
     es        = expr <$> bs
     eRhs      = maybe PTrue crhs subMb
-    binds     = [ (x, y) | i <- delta, let (x, y, _) = lookupBindEnv i ieBEnv]
+
+    (binds, existentialCounter) = renameExistentialsInSortedRefts binds0 (icFreshExistentialCounter ictx)
+
+    binds0    = [ maybeApplyKVarSolutions (x, y)
+                | i <- delta
+                , let (x, y, _) = lookupBindEnv i ieBEnv
+                ]
     subMb     = getCstr ieCstrs <$> cidMb
     newLRWs   = Mb.mapMaybe (`lookupLocalRewrites` ieLRWs) delta
 
-    deANFedCands =
+    candsPerExScopeNoRHS = M.fromListWith S.union $ ([], S.empty) : drop 1 deANFedCands
+    -- ebs expects all keys to contain disjoint sets of bindings
+    candsPerExScope = M.unionWith S.union candsPerExScopeNoRHS $ M.fromListWith S.union (take 1 deANFedCands)
+
+    deANFedCands = map (second S.singleton . prenexExistentials) $
       -- We only call 'deANF' if necessary.
       if not (null (getAutoRws ieKnowl cidMb))
-         || icExtensionalityFlag ctx
-         || icEtaBetaFlag ctx then
+         || icExtensionalityFlag ictx
+         || icEtaBetaFlag ictx then
         deANF anfBinds cands
       else
         cands
 
+    maybeApplyKVarSolutions xsr =
+      case ieSol of
+        Just sol -> applyInSortedReft cfg g sol xsr
+        Nothing  -> xsr
+      where
+        gCid = case collectConstraints <$> mCTrie of
+          Just (c:_) -> Just c
+          _ -> Nothing
+        g = CEnv
+          { ceCid = gCid
+          , ceBEnv = ieBEnv
+          , ceIEnv = ibinds
+          , ceSpan = maybe dummySpan srcSpan $ gCid >>= (`M.lookup` ieCstrs)
+          , ceBindingsInSmt = emptyIBindEnv
+          }
 
+
 findConstants :: Knowledge -> [Expr] -> [(Expr, Expr)]
 findConstants γ es = [(EVar x, c) | (x,c) <- go [] (concatMap splitPAnd es)]
   where
@@ -481,8 +619,15 @@
 data EvalEnv = EvalEnv
   { evEnv      :: !SymEnv
   , evElabF    :: ElabFlags
-    -- | Equalities where we couldn't evaluate the guards
-  , evPendingUnfoldings :: M.HashMap Expr Expr
+  , evKCtx     :: SMT.Context
+    -- | The current scope of existential variables.
+    -- See Note [Existential quantification when unfolding].
+  , evExScope  :: ExScope
+    -- | Equalities where we couldn't evaluate the guards, in a map which
+    -- uses their existential scope as key.
+    --
+    -- See Note [Existential quantification when unfolding].
+  , evPendingUnfoldings :: M.HashMap ExScope (M.HashMap Expr Expr)
   , evNewEqualities :: EvEqualities -- ^ Equalities discovered during a traversal of
                                     -- an expression
   , evSMTCache :: M.HashMap Expr Bool -- ^ Whether an expression is valid or its negation
@@ -508,6 +653,15 @@
 defFuelCount cfg = FC mempty (fuel cfg)
 
 type EvalST a = StateT EvalEnv IO a
+
+liftSMT :: SmtM a -> EvalST a
+liftSMT k =
+  do es <- get
+     let ctx = evKCtx es
+     (a, ctx') <- lift $ runStateT k ctx
+     put (es {evKCtx = ctx'})
+     pure a
+
 --------------------------------------------------------------------------------
 
 getAutoRws :: Knowledge -> Maybe SubcId -> [AutoRewrite]
@@ -525,7 +679,7 @@
 -- way.
 evalOne :: Knowledge -> ICtx -> Int -> Expr -> EvalST [Expr]
 evalOne γ ctx i e
-  | i > 0 || null (getAutoRws γ (icSubcId ctx)) = (:[]) . fst <$> eval γ ctx NoRW e
+  | i > 0 || null (getAutoRws γ (icSubcId ctx)) = (:[]) <$> eval γ ctx NoRW e
 evalOne γ ctx _ e | isExprRewritable e = do
     env <- get
     let oc :: OCAlgebra OCType RuntimeTerm IO
@@ -555,37 +709,7 @@
   | RWNormal   -- REST: Fully Expand Defs in the context of rewriting (similar to NoRW)
   deriving (Eq)
 
--- Indicates whether or not the evaluation has expanded a function statement
--- into a conditional branch.
--- In this case, rewriting should stop
--- It's unclear whether or not rewriting in either branch makes sense,
--- since one branch could be an ill-formed expression.
-newtype FinalExpand = FE Bool deriving (Show)
 
-noExpand :: FinalExpand
-noExpand = FE False
-
-expand :: FinalExpand
-expand = FE True
-
-mapFE :: (Expr -> Expr) -> (Expr, FinalExpand) -> (Expr, FinalExpand)
-mapFE f (e, fe) = (f e, fe)
-
-feVal :: FinalExpand -> Bool
-feVal (FE f) = f
-
-feAny :: [FinalExpand] -> FinalExpand
-feAny xs = FE $ any feVal xs
-
-infixl 9 <|>
-(<|>) :: FinalExpand -> FinalExpand -> FinalExpand
-(<|>) (FE True) _ = expand
-(<|>) _         f = f
-
-
-feSeq :: [(Expr, FinalExpand)] -> ([Expr], FinalExpand)
-feSeq xs = (map fst xs, feAny (map snd xs))
-
 -- | Unfolds function invocations in expressions.
 --
 -- Also reduces if-then-else when the boolean condition or the negation can be
@@ -594,70 +718,55 @@
 --
 -- Also adds to the monad state all the unfolding equalities that have been
 -- discovered as necessary.
-eval :: Knowledge -> ICtx -> EvalType -> Expr -> EvalST (Expr, FinalExpand)
+eval :: Knowledge -> ICtx -> EvalType -> Expr -> EvalST Expr
 eval γ ctx et = go
   where
     go (ELam (x,s) e)   = evalELam γ ctx et (x, s) e
     go e@EIte{}         = evalIte γ ctx et e
-    go (ECoerc s t e)   = mapFE (ECoerc s t)  <$> go e
+    go (ECoerc s t e)   = ECoerc s t <$> go e
     go e@(EApp _ _)     =
       case splitEAppThroughECst e of
        (f, es) | et == RWNormal ->
           -- Just evaluate the arguments first, to give rewriting a chance to step in
           -- if necessary
           do
-            (es', finalExpand) <- feSeq <$> mapM (eval γ ctx et) es
+            es' <- mapM (eval γ ctx et) es
             if es /= es'
-              then return (eApps f es', finalExpand)
+              then return (eApps f es')
               else do
-                (f', fe) <- case dropECst f of
-                  EVar _ -> pure (f, noExpand)
+                f' <- case dropECst f of
+                  EVar _ -> pure f
                   _      -> go f
-                (me', fe') <- evalApp γ ctx f' es et
-                return (Mb.fromMaybe (eApps f' es') me', fe <|> fe')
+                Mb.fromMaybe (eApps f' es') <$> evalApp γ ctx f' es et
        (f, es) ->
           do
-            (f', fe1) <- case dropECst f of
-              EVar _ -> pure (f, noExpand)
+            f' <- case dropECst f of
+              EVar _ -> pure f
               _      -> go f
-            (es', fe2) <- feSeq <$> mapM (eval γ ctx et) es
-            let fe = fe1 <|> fe2
-            (me', fe') <- evalApp γ ctx f' es' et
-            return (Mb.fromMaybe (eApps f' es') me', fe <|> fe')
+            es' <- mapM (eval γ ctx et) es
+            Mb.fromMaybe (eApps f' es') <$> evalApp γ ctx f' es' et
 
-    go (PAtom r e1 e2) = binOp (PAtom r) e1 e2
-    go (ENeg e)         = do (e', fe)  <- go e
-                             return (ENeg e', fe)
-    go (EBin o e1 e2)   = do (e1', fe1) <- go e1
-                             (e2', fe2) <- go e2
-                             return (EBin o e1' e2', fe1 <|> fe2)
-    go (ETApp e t)      = mapFE (`ETApp` t) <$> go e
-    go (ETAbs e s)      = mapFE (`ETAbs` s) <$> go e
-    go (PNot e')        = mapFE PNot <$> go e'
-    go (PImp e1 e2)     = binOp PImp e1 e2
-    go (PIff e1 e2)     = binOp PIff e1 e2
-    go (PAnd es)        = efAll PAnd (go `traverse` es)
-    go (POr es)         = efAll POr (go `traverse` es)
+    go (PAtom r e1 e2) = PAtom r <$> go e1 <*> go e2
+    go (ENeg e)         = ENeg <$> go e
+    go (EBin o e1 e2)   = EBin o <$> go e1 <*> go e2
+    go (ETApp e t)      = (`ETApp` t) <$> go e
+    go (ETAbs e s)      = (`ETAbs` s) <$> go e
+    go (PNot e')        = PNot <$> go e'
+    go (PImp e1 e2)     = PImp <$> go e1 <*> go e2
+    go (PIff e1 e2)     = PIff <$> go e1 <*> go e2
+    go (PAnd es)        = PAnd <$> traverse go es
+    go (POr es)         = POr <$> traverse go es
     go e | EVar _ <- dropECst e = do
-      (me', fe) <- evalApp γ ctx e [] et
-      return (Mb.fromMaybe e me', fe)
-    go (ECst e t)       = do (e', fe) <- go e
-                             return (ECst e' t, fe)
-    go e                = return (e, noExpand)
+      Mb.fromMaybe e <$> evalApp γ ctx e [] et
+    go (ECst e t)       = (`ECst` t) <$> go e
+    go (ELet x e1 e2)   = ELet x <$> go e1 <*> go e2
 
-    binOp f e1 e2 = do
-      (e1', fe1) <- go e1
-      (e2', fe2) <- go e2
-      return (f e1' e2', fe1 <|> fe2)
+    go e                = return e
 
-    efAll f mes = do
-      xs <- mes
-      let (xs', fe) = feSeq xs
-      return (f xs', fe)
 
--- | 'evalELamb' produces equations that preserve the context of a rewrite
+-- | 'evalELam' produces equations that preserve the context of a rewrite
 -- so equations include any necessary lambda bindings.
-evalELam :: Knowledge -> ICtx -> EvalType -> (Symbol, Sort) -> Expr -> EvalST (Expr, FinalExpand)
+evalELam :: Knowledge -> ICtx -> EvalType -> (Symbol, Sort) -> Expr -> EvalST Expr
 evalELam γ ctx et (x, s) e
   | not $ isEtaSymbol x = do
     -- We need to refresh it as for some reason names bound by lambdas
@@ -686,7 +795,7 @@
     modify $ \st -> st
       { evEnv = insertSymEnv x s $ evEnv st }
 
-    (e', fe) <- eval (γ { knLams = (x, s) : knLams γ }) ctx et e
+    e' <- eval (γ { knLams = (x, s) : knLams γ }) ctx et e
     let e2' = simplify γ ctx e'
         elam = ELam (x, s) e
     -- Discard the old equalities which miss the lambda binding
@@ -696,7 +805,7 @@
       -- Leaving the scope thus we need to get rid of it
       , evEnv = deleteSymEnv x $ evEnv st
       }
-    return (ELam (x, s) e', fe)
+    return (ELam (x, s) e')
 
 data RESTParams oc = RP
   { oc   :: OCAlgebra oc Expr IO
@@ -795,21 +904,22 @@
 evalRESTWithCache cacheRef γ ctx acc rp =
   do
     mexploredTerms <- gets explored
+    ebs <- gets evExScope
     case mexploredTerms of
       Nothing -> return acc
       Just exploredTerms -> do
-        se <- liftIO (shouldExploreTerm exploredTerms exprs)
+        se <- liftIO (shouldExploreTerm ebs exploredTerms exprs)
         if se then do
-          possibleRWs <- getRWs
-          rws <- notVisitedFirst exploredTerms <$> filterM (liftIO . allowed) possibleRWs
+          possibleRWs <- liftSMT (getRWs ebs)
+          rws <- notVisitedFirst exploredTerms <$> filterM (liftIO . allowed ebs) possibleRWs
           oldEqualities <- gets evNewEqualities
           modify $ \st -> st { evNewEqualities = mempty }
 
           -- liftIO $ putStrLn $ (show $ length possibleRWs) ++ " rewrites allowed at path length " ++ (show $ (map snd $ path rp))
-          (e', FE fe) <- do
-            r@(ec, _) <- eval γ ctx FuncNormal exprs
+          e' <- do
+            ec <- eval γ ctx FuncNormal exprs
             if ec /= exprs
-              then return r
+              then return ec
               else eval γ ctx RWNormal exprs
 
           let evalIsNewExpr = e' `L.notElem` pathExprs
@@ -834,8 +944,8 @@
             }
 
           acc'' <- if evalIsNewExpr
-            then if fe && any isRW (path rp)
-              then (:[]) . fst <$> eval γ (addConst (exprs, e')) NoRW e'
+            then if e' /= exprs && any isRW (path rp)
+              then (:[]) <$> eval γ (addConst (exprs, e')) NoRW e'
               else evalRESTWithCache cacheRef γ (addConst (exprs, e')) acc' (rpEval newEqualities e')
             else return acc'
 
@@ -843,17 +953,17 @@
         else
           return acc
   where
-    shouldExploreTerm exploredTerms e | Vis.isConc e =
-      case rwTerminationOpts rwArgs of
+    shouldExploreTerm ebs exploredTerms e | Vis.isConc e =
+      case rwTerminationOpts (rwArgs ebs) of
         RWTerminationCheckDisabled ->
           return $ not $ ExploredTerms.visited (Rewrite.convert e) exploredTerms
         RWTerminationCheckEnabled  ->
           ExploredTerms.shouldExplore (Rewrite.convert e) (c rp) exploredTerms
-    shouldExploreTerm _ _ = return False
+    shouldExploreTerm _ _ _ = return False
 
-    allowed (_, rwE, _) | rwE `elem` pathExprs = return False
-    allowed (_, _, c)   = termCheck c
-    termCheck c = Rewrite.passesTerminationCheck (oc rp) rwArgs c
+    allowed _ebs (_, rwE, _) | rwE `elem` pathExprs = return False
+    allowed ebs (_, _, c)   = termCheck ebs c
+    termCheck ebs c = Rewrite.passesTerminationCheck (oc rp) (rwArgs ebs) c
 
     notVisitedFirst exploredTerms rws =
       let
@@ -879,21 +989,21 @@
     exprs           = last pathExprs
     autorws         = getAutoRws γ (icSubcId ctx)
 
-    rwArgs = RWArgs (isValid cacheRef γ) $ knRWTerminationOpts γ
+    rwArgs ebs = RWArgs (isValid cacheRef ebs γ) $ knRWTerminationOpts γ
 
-    getRWs =
+    getRWs ebs =
       do
         -- Optimization: If we got here via rewriting, then the current constraints
         -- are satisfiable; otherwise double-check that rewriting is still allowed
         ok <-
           if isRW $ last (path rp)
             then return True
-            else liftIO $ termCheck (c rp)
+            else liftIO $ termCheck ebs (c rp)
         if ok
           then
             do
-              let getRW e ar = Rewrite.getRewrite (oc rp) rwArgs (c rp) e ar
-              let getRWs' s  = Mb.catMaybes <$> mapM (liftIO . runMaybeT . getRW s) autorws
+              let getRW e ar = Rewrite.getRewrite (oc rp) (rwArgs ebs) (c rp) e ar
+              let getRWs' s  = Mb.catMaybes <$> mapM (runMaybeT . getRW s) autorws
               concat <$> mapM getRWs' (subExprs exprs)
           else return []
 
@@ -946,7 +1056,7 @@
 
 -- | @evalApp kn ctx e es@ unfolds expressions in @eApps e es@ using rewrites
 -- and equations
-evalApp :: Knowledge -> ICtx -> Expr -> [Expr] -> EvalType -> EvalST (Maybe Expr, FinalExpand)
+evalApp :: Knowledge -> ICtx -> Expr -> [Expr] -> EvalType -> EvalST (Maybe Expr)
 evalApp γ ctx e0 es et
   | EVar f <- dropECst e0
   , Just eq <- Map.lookup f (knAms γ)
@@ -962,7 +1072,7 @@
                     then elaborateExpr "EvalApp unfold full: " newE
                     else pure newE
 
-         (e', fe) <- evalIte γ ctx et newE'        -- TODO:FUEL this is where an "unfolding" happens, CHECK/BUMP counter
+         e' <- evalIte γ ctx et newE'        -- TODO:FUEL this is where an "unfolding" happens, CHECK/BUMP counter
          let e2' = stripPLEUnfold e'
          let e3' = simplify γ ctx (eApps e2' es2)  -- reduces a bit the equations
 
@@ -972,17 +1082,18 @@
            -- If evalIte does any modifications, though, we do unfold in order
            -- to allow analysis of the resulting expression
            modify $ \st -> st
-             { evPendingUnfoldings = M.insert (eApps e0 es) e3' (evPendingUnfoldings st)
+             { evPendingUnfoldings =
+                 M.insertWith M.union (evExScope st) (M.singleton (eApps e0 es) e3') (evPendingUnfoldings st)
              }
-           return (Nothing, noExpand)
+           return Nothing
          else do
            useFuel f
            modify $ \st -> st
              { evNewEqualities = S.insert (eApps e0 es, e3') (evNewEqualities st)
-             , evPendingUnfoldings = M.delete (eApps e0 es) (evPendingUnfoldings st)
+             , evPendingUnfoldings = M.adjust (M.delete (eApps e0 es)) (evExScope st) (evPendingUnfoldings st)
              }
-           return (Just $ eApps e2' es2, fe)
-       else return (Nothing, noExpand)
+           return (Just $ eApps e2' es2)
+       else return Nothing
   where
     -- At the time of writing, any function application wrapping an
     -- if-statement would have the effect of unfolding the invocation.
@@ -1016,7 +1127,7 @@
     when (isUserDataSMeasure == NoUserDataSMeasure) $
       modify $ \st -> st
         { evNewEqualities = S.insert (eApps e0 args, simplify γ ctx newE) (evNewEqualities st) }
-    return (Just newE, noExpand)
+    return (Just newE)
 
 evalApp γ ctx e0 es _et
   | eqs@(_:_) <- noUserDataMeasureEqs γ (eApps e0 es)
@@ -1024,7 +1135,7 @@
        let eqs' = map (second $ simplify γ ctx) eqs
        modify $ \st ->
          st { evNewEqualities = foldr S.insert (evNewEqualities st) eqs' }
-       return (Nothing, noExpand)
+       return Nothing
 
 evalApp γ ctx e0 es et
   | ELam (argName, _) body <- dropECst e0
@@ -1037,13 +1148,13 @@
           useFuel argName
           let argSubst = mkSubst [(argName, lambdaArg)]
           let body' = subst argSubst body
-          (body'', fe) <- evalIte γ ctx et body'
+          body'' <- evalIte γ ctx et body'
           let simpBody = simplify γ ctx (eApps body'' remArgs)
           modify $ \st ->
             st { evNewEqualities = S.insert (eApps e0 es, simpBody) (evNewEqualities st) }
-          return (Just $ eApps body'' remArgs, fe)
+          return (Just $ eApps body'' remArgs)
         else do
-          return (Nothing, noExpand)
+          return Nothing
 
 evalApp _ ctx e0 es _
   | icLocalRewritesFlag ctx
@@ -1054,7 +1165,7 @@
       let expandedTerm = eApps rw es
       modify $ \st -> st
         { evNewEqualities = S.insert (eApps e0 es, expandedTerm) (evNewEqualities st) }
-      return (Just expandedTerm, expand)
+      return (Just expandedTerm)
 
 evalApp _γ ctx e0 es _et
   -- We check the annotation instead of the equations in γ for two reasons.
@@ -1091,7 +1202,7 @@
     -- is already handled by the previous case of evalApp
     modify $ \st -> st
       { evNewEqualities = S.insert (eApps e0 es, etaExpandedTerm) (evNewEqualities st) }
-    return (Just etaExpandedTerm, expand)
+    return (Just etaExpandedTerm)
   where
     unpackFFuncs (FFunc t ts) = t : unpackFFuncs ts
     unpackFFuncs _ = []
@@ -1099,22 +1210,21 @@
     mkLams subject binds = foldr ELam subject binds
 
 evalApp _ _ctx _e0 _es _ = do
-  return (Nothing, noExpand)
+  return Nothing
 
 -- | Evaluates if-then-else statements until they can't be evaluated anymore
 -- or some other expression is found.
-evalIte :: Knowledge -> ICtx -> EvalType -> Expr -> EvalST (Expr, FinalExpand)
+evalIte :: Knowledge -> ICtx -> EvalType -> Expr -> EvalST Expr
 evalIte γ ctx et (ECst e t) = do
-  (e', fe) <- evalIte γ ctx et e
-  return (ECst e' t, fe)
+  (`ECst` t) <$> evalIte γ ctx et e
 evalIte γ ctx et (EIte i e1 e2) = do
-      (b, _) <- eval γ ctx et i
+      b <- eval γ ctx et i
       b'  <- mytracepp ("evalEIt POS " ++ showpp (i, b)) <$> isValidCached γ b
       case b' of
         Just True -> evalIte γ ctx et e1
         Just False -> evalIte γ ctx et e2
-        _ -> return (EIte b e1 e2, expand)
-evalIte _ _ _ e' = return (e', noExpand)
+        _ -> return (EIte b e1 e2)
+evalIte _ _ _ e' = return e'
 
 -- | Creates equations that explain how to rewrite a given constructor
 -- application with all measures that aren't user data measures
@@ -1180,14 +1290,14 @@
   case M.lookup e (evSMTCache env) of
     Nothing -> do
       let isFreeInE (s, _) = not (S.member s (exprSymbolsSet e))
-      b <- liftIO $ knPreds γ (knContext γ) (knLams γ) e
+      b <- knPredsEvalST γ e
       if b
         then do
           when (all isFreeInE (knLams γ)) $
             put (env { evSMTCache = M.insert e True (evSMTCache env) })
           return (Just True)
         else do
-          b2 <- liftIO $ knPreds γ (knContext γ) (knLams γ) (PNot e)
+          b2 <- knPredsEvalST γ (PNot e)
           if b2
             then do
               when (all isFreeInE (knLams γ)) $
@@ -1209,8 +1319,10 @@
     -- user data declaration.
     knSims              :: Map Symbol [(Rewrite, IsUserDataSMeasure)]
   , knAms               :: Map Symbol Equation -- ^ All function definitions
-  , knContext           :: SMT.Context
-  , knPreds             :: SMT.Context -> [(Symbol, Sort)] -> Expr -> IO Bool
+    -- | @knPreds γ bsInSMT xs e@ checks whether @e@ is valid under the
+    -- assumptions that all variables in @bsInSMT@ are in the SMT solver,
+    -- and that all variables in @xs@ need tp be declared in the SMT solver.
+  , knPreds             :: [(Symbol, Sort)] -> [(Symbol, Sort)] -> Expr -> SmtM Bool
   , knLams              :: ![(Symbol, Sort)]
   , knSummary           :: ![(Symbol, Int)]     -- ^ summary of functions to be evaluates (knSims and knAsms) with their arity
   , knDCs               :: !(S.HashSet Symbol)  -- ^ data constructors drawn from Rewrite
@@ -1226,25 +1338,29 @@
 data IsUserDataSMeasure = NoUserDataSMeasure | UserDataSMeasure
   deriving (Eq, Show)
 
-isValid :: IORef (M.HashMap Expr Bool) -> Knowledge -> Expr -> IO Bool
-isValid cacheRef γ e = do
-    smtCache <- readIORef cacheRef
+knPredsEvalST :: Knowledge -> Expr -> EvalST Bool
+knPredsEvalST γ e = do
+    env <- get
+    liftSMT $ knPreds γ (evExScope env) (knLams γ) e
+
+isValid :: IORef (M.HashMap Expr Bool) -> [(Symbol, Sort)] -> Knowledge -> Expr -> SmtM Bool
+isValid cacheRef bs γ e = do
+    smtCache <- liftIO $ readIORef cacheRef
     case M.lookup e smtCache of
       Nothing -> do
-        b <- knPreds γ (knContext γ) (knLams γ) e
+        b <- knPreds γ bs (knLams γ) e
         when b $
-          writeIORef cacheRef (M.insert e True smtCache)
+          liftIO $ writeIORef cacheRef (M.insert e True smtCache)
         return b
-      mb -> return (mb == Just True)
+      Just b -> return b
 
-knowledge :: Config -> SMT.Context -> SInfo a -> Knowledge
-knowledge cfg ctx si = KN
+knowledge :: Config -> SInfo a -> Knowledge
+knowledge cfg si = KN
   { knSims                     = Map.fromListWith (++) $
                                    [ (smDC rw, [(rw, NoUserDataSMeasure)]) | rw <- sims ] ++
                                    [ (smDC rw, [(rw, UserDataSMeasure)]) | rw <- dataSims ]
   , knAms                      = Map.fromList [(eqName eq, eq) | eq <- aenvEqs aenv]
-  , knContext                  = ctx
-  , knPreds                    = askSMT  cfg
+  , knPreds                    = askSMT cfg
   , knLams                     = []
   , knSummary                  =    ((\s -> (smName s, 1)) <$> sims)
                                  ++ ((\s -> (eqName s, length (eqArgs s))) <$> aenvEqs aenv)
@@ -1255,7 +1371,7 @@
   , knConsts                   = Mb.mapMaybe makeCons sims
   , knAutoRWs                  = aenvAutoRW aenv
   , knRWTerminationOpts        =
-      if rwTerminationCheck cfg
+      if rwTermination cfg
       then RWTerminationCheckEnabled
       else RWTerminationCheckDisabled
   }
@@ -1335,15 +1451,6 @@
 --------------------------------------------------------------------------------
 --------------------------------------------------------------------------------
 
-withCtx :: Config -> FilePath -> SymEnv -> [Equation] -> (SMT.Context -> IO a) -> IO a
-withCtx cfg file env defns k = do
-  ctx <- SMT.makeContextWithSEnv cfg file env defns
-  _   <- SMT.smtPush ctx
-  res <- k ctx
-  SMT.cleanupContext ctx
-  return res
-
-
 -- (sel_i, D, i), meaning sel_i (D x1 .. xn) = xi,
 -- i.e., sel_i selects the ith value for the data constructor D
 type SelectorMap = [(Symbol, (Symbol, Int))]
@@ -1466,7 +1573,8 @@
 elaborateExpr :: String -> Expr -> EvalST Expr
 elaborateExpr msg e = do
   let elabSpan = atLoc dummySpan msg
-  symEnv' <- gets evEnv
+  env <- get
+  let symEnv' = insertsSymEnv (evEnv env) (evExScope env)
   ef <- gets evElabF
   pure $ unApply $ elaborate (ElabParam ef elabSpan symEnv') e
 
@@ -1478,3 +1586,128 @@
   case (M.lookup f (fcMap fc), fcMax fc) of
     (Just fk, Just n) -> pure (fk <= n)
     _                 -> pure True
+
+
+-- Note [Existential quantification when unfolding]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+--
+-- After FUSION is performed, some predicates, which previously used kvars, may
+-- contain existential quantifications.
+--
+-- When the unfoldings are searched by PLE in expressions with existentials,
+-- we make sure that the produced unfoldings still have the existential
+-- bindings in scope.
+--
+-- The procedure is as follows:
+-- 1. First, we rename the existential variables in the predicates of the bindings
+--    to make them unique ('renameExistentialsInSortedRefts').
+--
+--    @exists x y. f x y || (exists x. g x y)@
+--
+--    becomes
+--
+--    @exists v0 v1. f v0 v1 || (exists v2. g v2 v1)@
+--
+-- 2. We extract the nested existentials to prenex form, and we store the bodies of
+--    the existentials in a map with the existential binders as keys
+--    ('prenexExistentials' and 'updCtx').
+--
+--    @exists v0 v1. f v0 v1 || (exists v2. g v2 v1)@
+--
+--    produces the map
+--
+--    @[v0, v1, v2] -> f v0 v1 || g v2 v1@
+--
+-- 3. We declare to the SMT solver the existential variables in every scope
+--    (in 'withAssms').
+--
+-- 4. We then look for unfoldings in each of the subexpressions. Whenever
+--    we find an unfolding, we record the scope in which it was found.
+--
+--    @[v0, v1, v3] -> (f v0 v1 = v0 < v1) && (g v2 = v2 > v1)@
+--
+-- 5. When PLE is finished, we create for every scope an existential
+--    quantification whose body contains all the corresponding unfoldings
+--    and the original subexpressions in the scope ('reconstructExistentials').
+--
+--    @exists v0 v1 v0.
+--       (f v0 v 1 = v0 < v1) && (g v2 = v2 > v1) &&
+--       (f v0 v1 || g v2 v1)@
+--
+--    This is the expression that PLE returns.
+
+
+-- | Renames existential variables in the predicates of the given bindings to
+-- make them unique.
+--
+-- Rather than looking for all existential bindings, this function only renames
+-- the superficial existentials which can be introduced by KVar solutions.
+--
+-- These superficial existentials appear in conjunctions, disjunctions and in the
+-- body of other existentials only.
+renameExistentialsInSortedRefts
+  :: [(Symbol, SortedReft)]
+  -> Int
+  -> ([(Symbol, SortedReft)], Int)
+renameExistentialsInSortedRefts binds0 existentialCounter =
+    let
+        binds = [ (x, sr { sr_reft = mapPredReft (const p) (sr_reft sr) }) | ((x, sr), p) <- zip binds0 preds ]
+        (preds, existentialCounter') =
+          renameKVarExistentials (map (reftPred . sr_reft . snd) binds0) existentialCounter
+     in
+        (binds, existentialCounter')
+
+renameKVarExistentials :: [Expr] -> Int -> ([Expr], Int)
+renameKVarExistentials = runState . mapM go
+  where
+    go (POr es) = POr <$> mapM go es
+    go (PAnd es) = PAnd <$> mapM go es
+    go (PExist bs e0) = do
+      i1 <- get
+      let i2 = i1 + length bs
+      put i2
+      let vs = map fst bs
+          vs' = [ existSymbol v (fromIntegral i) | (v, i) <- zip vs [i1..] ]
+          bs' = zip vs' (map snd bs)
+          su = mkSubst $ zip vs (map EVar vs')
+      PExist bs' <$> go (rapierSubstExpr (S.fromList vs') su e0)
+    go e = pure e
+
+-- ^ Scopes of existential binders identifying the location of sub-expressions
+type ExScope = [(Symbol, Sort)]
+
+
+-- | Extracts nested existentials from an expression.
+--
+-- For example, the expression
+--
+-- > exists [x1 : t1]. e1 == e2 &&
+-- > exists [x2 : t2]. e3 == 2 &&
+-- > exists [x3 : t3]. e3 < e4
+--
+-- would be flattened into
+--
+-- > (e1 == e2 && e3 == 2 && e3 < e4, [x1 : t1, x2 : t2, x3 : t3])
+--
+-- Precondition: the existential binding names are unique.
+--
+prenexExistentials :: Expr -> (ExScope, Expr)
+prenexExistentials = go
+  where
+    go :: Expr -> (ExScope, Expr)
+    go (PExist bs e) =
+      let (bs', e') = go e
+      in (bs ++ bs', e')
+    go (PAnd es) =
+      let (bss, es') = unzip (map go es)
+      in (concat bss, PAnd es')
+    go (POr es) =
+      let (bss, es') = unzip (map go es)
+      in (concat bss, POr es')
+    go e = ([], e)
+
+
+-- | Reconstructs expressions with existentials from a map
+-- of existential scopes to their bodies.
+reconstructExistentials :: M.HashMap ExScope (S.HashSet Expr) -> [Expr]
+reconstructExistentials m = [ pExist s (pAndNoDedup $ S.toList es) | (s, es) <- M.toList m, not (null es) ]
diff --git a/src/Language/Fixpoint/Solver/Rewrite.hs b/src/Language/Fixpoint/Solver/Rewrite.hs
--- a/src/Language/Fixpoint/Solver/Rewrite.hs
+++ b/src/Language/Fixpoint/Solver/Rewrite.hs
@@ -30,6 +30,7 @@
 import           Text.PrettyPrint (text)
 import           Language.Fixpoint.Types.Config (RESTOrdering(..))
 import           Language.Fixpoint.Types hiding (simplify)
+import           Language.Fixpoint.Smt.Types (SmtM)
 import           Language.REST
 import           Language.REST.KBO (kbo)
 import           Language.REST.LPO (lpo)
@@ -54,7 +55,7 @@
   | RWTerminationCheckDisabled
 
 data RewriteArgs = RWArgs
- { isRWValid          :: Expr -> IO Bool
+ { isRWValid          :: Expr -> SmtM Bool
  , rwTerminationOpts  :: RWTerminationOpts
  }
 
@@ -128,7 +129,7 @@
   -> oc
   -> SubExpr
   -> AutoRewrite
-  -> MaybeT IO ((Expr, Expr), Expr, oc)
+  -> MaybeT SmtM ((Expr, Expr), Expr, oc)
 getRewrite aoc rwArgs c (subE, toE) (AutoRewrite args lhs rhs) =
   do
     su <- MaybeT $ return $ unify freeVars lhs subE
@@ -145,7 +146,7 @@
           (eqn, expr', c')
       RWTerminationCheckDisabled -> (eqn, expr', c)
   where
-    check :: Expr -> MaybeT IO ()
+    check :: Expr -> MaybeT SmtM ()
     check e = do
       valid <- MaybeT $ Just <$> isRWValid rwArgs e
       guard valid
@@ -225,6 +226,15 @@
 
 subExprs' (POr es) = [ (e, POr . f) | (e, f) <- subs es ]
 
+subExprs' (ELet x e1 e2) = e1'' ++ e2''
+  where
+    e1' = subExprs e1
+    e2' = subExprs e2
+    e1'' :: [SubExpr]
+    e1'' = map (\(e, f) -> (e, \e' -> ELet x (f e') e2)) e1'
+    e2'' :: [SubExpr]
+    e2'' = map (\(e, f) -> (e, \e' -> ELet x e1 (f e'))) e2'
+
 subExprs' _ = []
 
 -- | Computes the subexpressions of a list of expressions.
@@ -304,8 +314,8 @@
       unify freeVars rw seen
     (PExist _ rw, PExist _ seen) ->
       unify freeVars rw seen
-    (PGrad _ _ _ rw, PGrad _ _ _ seen) ->
-      unify freeVars rw seen
     (ECoerc _ _ rw, ECoerc _ _ seen) ->
       unify freeVars rw seen
+    (ELet _ rw1 rw2, ELet _ seen1 seen2) ->
+      unifyAll freeVars [rw1, rw2] [seen1, seen2]
     _ -> Nothing
diff --git a/src/Language/Fixpoint/Solver/Sanitize.hs b/src/Language/Fixpoint/Solver/Sanitize.hs
--- a/src/Language/Fixpoint/Solver/Sanitize.hs
+++ b/src/Language/Fixpoint/Solver/Sanitize.hs
@@ -2,7 +2,6 @@
 --   1. Each binder must be associated with a UNIQUE sort
 {-# LANGUAGE TupleSections     #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE PatternGuards     #-}
 
 module Language.Fixpoint.Solver.Sanitize
   ( -- * Transform FInfo to enforce invariants
@@ -17,7 +16,7 @@
 
 import           Language.Fixpoint.Types.PrettyPrint
 import           Language.Fixpoint.Types.Visitor
-import           Language.Fixpoint.SortCheck     (ElabParam(..), elaborate, applySorts, isFirstOrder)
+import           Language.Fixpoint.SortCheck     (ElabParam(..), theoryEnv, elaborate, applySorts, isFirstOrder)
 -- import           Language.Fixpoint.Defunctionalize
 import           Language.Fixpoint.Misc ((==>))
 import qualified Language.Fixpoint.Misc                            as Misc
@@ -34,7 +33,9 @@
 import qualified Data.Text                                         as T
 import           Data.Maybe          (isNothing, mapMaybe, fromMaybe)
 import           Control.Monad       ((>=>))
+import           GHC.Stack           (HasCallStack)
 import           Text.PrettyPrint.HughesPJ hiding ((<>))
+import qualified Language.Fixpoint.SortCheck as SortCheck
 
 type SanitizeM a = Either E.Error a
 
@@ -45,9 +46,9 @@
          >=> Misc.fM dropFuncSortedShadowedBinders
          >=> Misc.fM sanitizeWfC
          >=> Misc.fM replaceDeadKvars
-         >=> Misc.fM (dropDeadSubsts . restrictKVarDomain)
+         >=> Misc.fM (dropDeadSubsts . restrictKVarDomain cfg)
          >=>         banMixedRhs
-         >=>         banQualifFreeVars
+         >=>         banQualifFreeVars cfg
          >=>         banConstraintFreeVars cfg
          >=> Misc.fM addLiterals
          >=> Misc.fM (eliminateEta cfg)
@@ -88,42 +89,12 @@
 --------------------------------------------------------------------------------
 eliminateEta cfg si
   | Cfg.etaElim cfg
-  , Cfg.oldPLE  cfg
-  = si { F.ae = ae' }
-  | Cfg.etaElim cfg
   = si { F.ae = (ae {F.aenvEqs = etaElimNEW `fmap` F.aenvEqs ae }) }
   | otherwise
   = si
   where
-    ae' = ae {F.aenvEqs = eqs}
     ae = F.ae si
-    eqs = fmap etaElim (F.aenvEqs ae)
 
-    etaElim eq = F.notracepp "Eliminating" $
-                 case body of
-                   F.PAtom F.Eq e0 e1 ->
-                     let (f0, args0) = fapp e0
-                         (f1, args1) = F.notracepp "f1" $ fapp e1 in
-                     if reverse args0 == args
-                     then let commonArgs = F.notracepp "commonArgs" .
-                                           fmap fst .
-                                           takeWhile (uncurry (==)) $
-                                           zip args0 args1
-                              commonLength = length commonArgs
-                              (newArgsAndSorts, elimedArgsAndSorts) =
-                                splitAt (length args - commonLength) argsAndSorts
-                              args0' = F.eVar <$> reverse (drop commonLength args0)
-                              args1' = F.eVar <$> reverse (drop commonLength args1) in
-                       eq { F.eqArgs = newArgsAndSorts
-                          , F.eqSort = foldr F.FFunc sort
-                                       (snd <$> elimedArgsAndSorts)
-                          , F.eqBody = F.PAtom F.Eq (F.eApps f0 args0') (F.eApps f1 args1')}
-                     else eq
-                   _ -> eq
-      where argsAndSorts = F.eqArgs eq
-            args = fst <$> argsAndSorts
-            body = F.eqBody eq
-            sort = F.eqSort eq
     etaElimNEW eq = F.notracepp "Eliminating" $
                   let (f1, args1) = fapp (F.eqBody eq) in
                   let commonArgs = F.notracepp "commonArgs" .
@@ -160,11 +131,6 @@
       | otherwise
       = Nothing
 
-theoryEnv :: Config -> F.GInfo c a -> F.SEnv F.TheorySymbol
-theoryEnv cfg si
-  =  Thy.theorySymbols (Cfg.solver cfg)
-  <> Thy.theorySymbols (F.defns si)
-  <> Thy.theorySymbols (F.ddecls si)
 
 --------------------------------------------------------------------------------
 -- | See issue liquid-fixpoint issue #230. This checks that whenever we have,
@@ -235,10 +201,12 @@
 --   `x` which appear in substitutions of the form `K[x := y]` where `y`
 --   is not in the env.
 --------------------------------------------------------------------------------
-restrictKVarDomain :: F.SInfo a -> F.SInfo a
-restrictKVarDomain si = si { F.ws = M.mapWithKey (restrictWf kvm) (F.ws si) }
+restrictKVarDomain :: Config -> F.SInfo a -> F.SInfo a
+restrictKVarDomain cfg si
+  | Cfg.explicitKvars cfg = si
+  | otherwise             = si { F.ws = M.mapWithKey (restrictWf kvm) (F.ws si) }
   where
-    kvm               = safeKvarEnv si
+    kvm                   = safeKvarEnv si
 
 -- | `restrictWf kve k w` restricts the env of `w` to the parameters in `kve k`.
 restrictWf :: KvDom -> F.KVar -> F.WfC a -> F.WfC a
@@ -248,20 +216,21 @@
     kis            = S.fromList [ i | (_, i) <- F.toListSEnv kEnv ]
     kEnv           = M.lookupDefault mempty k kve
 
+type KvDom     = M.HashMap F.KVar (F.SEnv F.BindId)
+type KvBads    = M.HashMap F.KVar [F.Symbol]
+
 -- | `safeKvarEnv` computes the "real" domain of each kvar, which is
 --   a SUBSET of the input domain, in which we KILL the parameters
 --   `x` which appear in substitutions of the form `K[x := y]`
 --   where `y` is not in the env.
 
-type KvDom     = M.HashMap F.KVar (F.SEnv F.BindId)
-type KvBads    = M.HashMap F.KVar [F.Symbol]
-
 safeKvarEnv :: F.SInfo a -> KvDom
 safeKvarEnv si = L.foldl' (dropKvarEnv si) env0 cs
   where
     cs         = M.elems  (F.cm si)
     env0       = initKvarEnv si
 
+
 dropKvarEnv :: F.SInfo a -> KvDom -> F.SimpC a -> KvDom
 dropKvarEnv si kve c = M.mapWithKey (dropBadParams kBads) kve
   where
@@ -353,15 +322,13 @@
 --------------------------------------------------------------------------------
 -- | check that no qualifier has free variables
 --------------------------------------------------------------------------------
-banQualifFreeVars :: F.SInfo a -> SanitizeM (F.SInfo a)
+banQualifFreeVars :: Config -> F.SInfo a -> SanitizeM (F.SInfo a)
 --------------------------------------------------------------------------------
-banQualifFreeVars fi = Misc.applyNonNull (Right fi) (Left . badQuals) bads
+banQualifFreeVars cfg fi = Misc.applyNonNull (Right fi) (Left . badQuals) bads
   where
     bads    = [ (q, xs) | q <- F.quals fi, let xs = free q, not (null xs) ]
-    free q  = filter (not . isLit) (F.syms q)
-    isLit x = F.memberSEnv x (F.gLits fi)
-    -- lits    = fst <$> F.toListSEnv (F.gLits fi)
-    -- free q  = S.toList $ F.syms (F.qBody q) `nubDiff` (lits ++ F.prims ++ F.syms (F.qpSym <$> F.qParams q))
+    free q  = filter (not . isGlobal) (F.syms q)
+    isGlobal x = F.memberSEnv x (SortCheck.globalEnv cfg fi)
 
 badQuals     :: Misc.ListNE (F.Qualifier, Misc.ListNE F.Symbol) -> E.Error
 badQuals bqs = E.catErrors [ E.errFreeVarInQual q xs | (q, xs) <- bqs]
@@ -393,7 +360,7 @@
 --   function definitions inside the `AxiomEnv` which cannot be elaborated as
 --   it makes it hard to actually find the fundefs within (breaking PLE.)
 --------------------------------------------------------------------------------
-symbolEnv :: Config -> F.SInfo a -> F.SymEnv
+symbolEnv :: HasCallStack => Config -> F.SInfo a -> F.SymEnv
 symbolEnv cfg si = F.symEnv sEnv thyEnv ds lits (ts ++ ts')
   where
     ts'          = applySorts ae'
@@ -403,8 +370,7 @@
     ds           = F.ddecls si
     ts           = Misc.setNub (applySorts si ++ [t | (_, t) <- F.toListSEnv sEnv])
     sEnv         = F.coerceSortEnv ef $ (F.tsSort <$> thyEnv) `mappend` F.fromListSEnv xts
-    slv          = Cfg.solver cfg
-    ef           = solverFlags slv
+    ef           = solverFlags cfg
     xts          = symbolSorts cfg si ++ alits
     lits         = F.dLits si `F.unionSEnv'` F.fromListSEnv alits
     alits        = litsAEnv $ F.ae si
@@ -412,7 +378,7 @@
 litsAEnv :: F.AxiomEnv -> [(F.Symbol, F.Sort)]
 litsAEnv ae = zip (F.symbol <$> symConsts ae) (repeat F.strSort)
 
-symbolSorts :: Config -> F.GInfo c a -> [(F.Symbol, F.Sort)]
+symbolSorts :: HasCallStack => Config -> F.GInfo c a -> [(F.Symbol, F.Sort)]
 symbolSorts cfg fi = either E.die id $ symbolSorts' cfg fi
 
 symbolSorts' :: Config -> F.GInfo c a -> SanitizeM [(F.Symbol, F.Sort)]
diff --git a/src/Language/Fixpoint/Solver/Solution.hs b/src/Language/Fixpoint/Solver/Solution.hs
--- a/src/Language/Fixpoint/Solver/Solution.hs
+++ b/src/Language/Fixpoint/Solver/Solution.hs
@@ -1,7 +1,8 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE PatternGuards     #-}
+{-# LANGUAGE TupleSections #-}
+{-# OPTIONS_GHC -Wwarn #-}
 
 module Language.Fixpoint.Solver.Solution
   ( -- * Create Initial Solution
@@ -10,21 +11,27 @@
     -- * Update Solution
   , Sol.update
 
+    -- * Apply Solution
+  , applyInSortedReft
+  , CombinedEnv(..)
+
     -- * Lookup Solution
   , lhsPred
 
   , nonCutsResult
+
+    -- * Exported for Testing
+  , simplifyKVar
+  , alphaEq
   ) where
 
-import           Control.Parallel.Strategies
 import           Control.Arrow (second, (***))
-import           Control.Monad (void)
+import           Control.Monad                  (guard, mplus)
 import           Control.Monad.Reader
 import qualified Data.HashSet                   as S
 import qualified Data.HashMap.Strict            as M
-import qualified Data.List                      as L
-import           Data.Maybe                     (fromMaybe, maybeToList, isNothing)
-import qualified Data.Bifunctor                 as Bifunctor (second)
+import qualified Data.List                      as List
+import           Data.Maybe                     (maybeToList, isJust, isNothing)
 import           Language.Fixpoint.Types.PrettyPrint ()
 import           Language.Fixpoint.Types.Visitor      as V
 import           Language.Fixpoint.SortCheck          (ElabM)
@@ -32,35 +39,30 @@
 import qualified Language.Fixpoint.Misc               as Misc
 import           Language.Fixpoint.Types.Config
 import qualified Language.Fixpoint.Types              as F
-import           Language.Fixpoint.Types                 ((&.&))
 import qualified Language.Fixpoint.Types.Solutions    as Sol
 import           Language.Fixpoint.Types.Constraints  hiding (ws, bs)
 import           Prelude                              hiding (init, lookup)
-import           Language.Fixpoint.Solver.Sanitize
 
--- DEBUG
-import Text.Printf (printf)
--- import Debug.Trace (trace)
 
-
 --------------------------------------------------------------------------------
 -- | Initial Solution (from Qualifiers and WF constraints) ---------------------
 --------------------------------------------------------------------------------
-init :: (F.Fixpoint a) => Config -> F.SInfo a -> S.HashSet F.KVar -> Sol.Solution
+init :: (F.Fixpoint a) => Config -> F.SInfo a -> S.HashSet F.KVar -> M.HashMap F.KVar Sol.QBind
 --------------------------------------------------------------------------------
-init cfg si ks_ = Sol.fromList symEnv mempty keqs [] mempty ebs xEnv
+init cfg si ks =
+    runReader (traverse (refine si qcs genv) ws) (solverFlags cfg)
   where
-    keqs       = runReader (traverse (refine si qcs genv) ws) (solverFlags $ solver cfg) `using` parList rdeepseq
-    qcs        = {- trace ("init-qs-size " ++ show (length ws, length qs_, M.keys qcs_)) $ -} qcs_
-    qcs_       = mkQCluster qs_
-    qs_        = F.quals si
-    ws         = [ w | (k, w) <- M.toList (F.ws si), not (isGWfc w), k `S.member` ks ]
-    ks         = {- trace ("init-ks-size" ++ show (S.size ks_)) $ -} ks_
-    genv       = instConstants si
-    symEnv     = symbolEnv cfg si
-    ebs        = ebindInfo si
-    xEnv       = F.fromListSEnv [ (x, (i, F.sr_sort sr)) | (i,(x,sr,_)) <- F.bindEnvToList (F.bs si)]
+    qcs = mkQCluster (F.quals si)
+    ws = M.intersection (F.ws si) (S.toMap ks)
+    genv = initQualifierEnv cfg si
 
+initQualifierEnv :: (F.Fixpoint a) => Config -> F.SInfo a -> F.SEnv F.Sort
+initQualifierEnv cfg si
+  | scraping  = So.globalEnv cfg si <> instConstants si
+  | otherwise = instConstants si
+  where
+    scraping = scrape cfg /= No
+
 --------------------------------------------------------------------------------
 -- | [NOTE:qual-cluster] It is wasteful to perform instantiation *individually*
 --   on each qualifier, as many qualifiers have "equivalent" parameters, and
@@ -82,7 +84,7 @@
 
 --------------------------------------------------------------------------------
 
-refine :: F.SInfo a -> QCluster -> F.SEnv F.Sort -> F.WfC a -> ElabM (F.KVar, Sol.QBind)
+refine :: F.SInfo a -> QCluster -> F.SEnv F.Sort -> F.WfC a -> ElabM Sol.QBind
 refine info qs genv w = refineK (allowHOquals info) env qs (F.wrft w)
   where
     env             = wenvSort <> genv
@@ -94,14 +96,10 @@
     notLit    = not . F.isLitSymbol . fst
 
 
-refineK :: Bool -> F.SEnv F.Sort -> QCluster -> (F.Symbol, F.Sort, F.KVar) -> ElabM (F.KVar, Sol.QBind)
-refineK ho env qs (v, t, k) =
-  do eqs' <- Sol.qbFilterM (okInst env v t) eqs
-     pure $ F.notracepp _msg (k, eqs')
+refineK :: Bool -> F.SEnv F.Sort -> QCluster -> (F.Symbol, F.Sort, F.KVar) -> ElabM Sol.QBind
+refineK ho env qs (v, t, _k) = Sol.qbFilterM (okInst env v t) eqs
    where
-    eqs                     = instK ho env v t qs
-
-    _msg                    = printf "\n\nrefineK: k = %s, eqs = %s" (F.showpp k) (F.showpp eqs)
+    eqs = instK ho env v t qs
 
 --------------------------------------------------------------------------------
 instK :: Bool
@@ -253,14 +251,15 @@
 {-# SCC lhsPred #-}
 lhsPred
   :: (F.Loc a)
-  => F.IBindEnv
+  => Config
+  -> F.IBindEnv
   -> F.BindEnv a
   -> Sol.Solution
   -> F.SimpC a
-  -> ElabM F.Expr
-lhsPred bindingsInSmt be s c =
-  do ap <- apply g s bs
-     pure $ F.notracepp _msg $ fst ap
+  -> F.Expr
+lhsPred cfg bindingsInSmt be s c =
+    let ap = apply cfg g s bs
+     in F.notracepp _msg $ fst ap
   where
     g          = CEnv ci be bs (F.srcSpan c) bindingsInSmt
     bs         = F.senv c
@@ -278,91 +277,67 @@
   , ceBindingsInSmt :: !F.IBindEnv
   }
 
-instance F.Loc (CombinedEnv a) where
-  srcSpan = ceSpan
-
 type Cid         = Maybe Integer
 type ExprInfo    = (F.Expr, KInfo)
 
-apply :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.IBindEnv -> ElabM ExprInfo
-apply g s bs      =
-  -- Clear the "known" bindings for applyKVars, since it depends on
-  -- using the fully expanded representation of the predicates to bind their
-  -- variables with quantifiers.
-  do (ps,  ks, _) <- envConcKVars g s bs
-     (pks, kI) <- applyKVars g {ceBindingsInSmt = F.emptyIBindEnv} s ks
-     pure (F.conj (pks:ps), kI)   -- see [NOTE: pAnd-SLOW]
+apply :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.IBindEnv -> ExprInfo
+apply cfg g s bs =
+    -- Clear the "known" bindings for applyKVars, since it depends on
+    -- using the fully expanded representation of the predicates to bind their
+    -- variables with quantifiers.
+    let xrs = map (lookupBindEnvExt g) (F.elemsIBindEnv bs)
+        (ps,  ks) = envConcKVars xrs
+        (pks, kI) = applyKVars cfg g {ceBindingsInSmt = F.emptyIBindEnv} s ks
+     in (F.conj (pks:ps), kI)   -- see [NOTE: pAnd-SLOW]
 
+-- | @applyInSortedReft@ applies the solution to a single sorted reft
+applyInSortedReft
+  :: Config
+  -> CombinedEnv ann
+  -> Sol.Sol Sol.QBind
+  -> (F.Symbol, F.SortedReft)
+  -> (F.Symbol, F.SortedReft)
+applyInSortedReft cfg g s xsr@(x, sr) =
+    let (ps,  ks) = envConcKVars [xsr]
+        (pks, _) = applyKVars cfg g {ceBindingsInSmt = F.emptyIBindEnv} s ks
+     in (x, sr { F.sr_reft = F.Reft (x, F.conj (pks : ps)) })
 
-envConcKVars :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.IBindEnv -> ElabM ([F.Expr], [F.KVSub], [F.KVSub])
-envConcKVars g s bs =
-  do xrs <- traverse (lookupBindEnvExt g s) is
-     let (pss, kss, gss) = unzip3 [ F.notracepp ("sortedReftConcKVars" ++ F.showpp sr) $ F.sortedReftConcKVars x sr | (x, sr) <- xrs ]
-     pure (concat pss, concat kss, L.nubBy (\x y -> F.ksuKVar x == F.ksuKVar y) $ concat gss)
-  where
-    is = F.elemsIBindEnv bs
+-- | Produces conjuncts of each sorted reft in the IBindEnv, separated
+-- into concrete conjuncts and kvars.
+envConcKVars :: [(F.Symbol, F.SortedReft)] -> ([F.Expr], [F.KVSub])
+envConcKVars xrs =
+  let (pss, kss) = unzip [ F.sortedReftConcKVars x sr | (x, sr) <- xrs ]
+   in (concat pss, concat kss)
 
-lookupBindEnvExt :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.BindId -> ElabM (F.Symbol, F.SortedReft)
-lookupBindEnvExt g s i =
-  do msol <- ebSol (g {ceBindingsInSmt = F.emptyIBindEnv}) s i
-     pure (x, case msol of
-                Just p -> sr { F.sr_reft = F.Reft (x, p) }
-                Nothing -> if F.memberIBindEnv i (ceBindingsInSmt g)
-                              then sr { F.sr_reft = F.Reft (x, F.EVar (F.bindSymbol (fromIntegral i)))}
-                              else sr)
+lookupBindEnvExt
+  :: CombinedEnv ann -> F.BindId -> (F.Symbol, F.SortedReft)
+lookupBindEnvExt g i =
+     (,) x $
+       if F.memberIBindEnv i (ceBindingsInSmt g)
+       then sr { F.sr_reft = F.Reft (x, F.EVar (F.bindSymbol (fromIntegral i)))}
+       else sr
    where
       (x, sr, _)              = F.lookupBindEnv i (ceBEnv g)
 
-ebSol :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.BindId -> ElabM (Maybe F.Expr)
-ebSol g sol bindId = case M.lookup bindId sebds of
-  Just (Sol.EbSol p)    -> pure $ Just p
-  Just (Sol.EbDef cs _) ->
-    do let cSol c = if sid c == ceCid g
-                       then pure F.PFalse
-                       else do p <- ebindReft g s' c
-                               pure $ exElim (Sol.sxEnv s') (senv c) bindId p
-       exps <- traverse cSol cs
-       pure $ Just $ F.PAnd exps
-  _                     -> pure Nothing
-  where
-    sebds = Sol.sEbd sol
-    s' = sol { Sol.sEbd = M.insert bindId Sol.EbIncr sebds }
-
-ebindReft :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.SimpC () -> ElabM F.Pred
-ebindReft g s c =
-  do a <- apply g' s bs
-     pure $ F.pAnd [ fst a , F.crhs c ]
-  where
-    g'          = g { ceCid = sid c, ceIEnv = bs }
-    bs          = F.senv c
-
-exElim :: F.SEnv (F.BindId, F.Sort) -> F.IBindEnv -> F.BindId -> F.Pred -> F.Pred
-exElim env ienv xi p = F.notracepp msg (F.pExist yts p)
-  where
-    msg         = "exElim" -- printf "exElim: ix = %d, p = %s" xi (F.showpp p)
-    yts         = [ (y, yt) | y        <- F.syms p
-                            , (yi, yt) <- maybeToList (F.lookupSEnv y env)
-                            , xi < yi
-                            , yi `F.memberIBindEnv` ienv                  ]
-
-applyKVars :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> [F.KVSub] -> ElabM ExprInfo
-applyKVars g s ks =
-  mrExprInfosM (applyKVar g s) F.pAndNoDedup mconcat ks
+applyKVars :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> [F.KVSub] -> ExprInfo
+applyKVars cfg g s ks =
+  let bcs = map (applyKVar cfg g s) ks
+      (es, is) = unzip bcs
+   in (F.pAndNoDedup es, mconcat is)
 
-applyKVar :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVSub -> ElabM ExprInfo
-applyKVar g s ksu = case Sol.lookup s (F.ksuKVar ksu) of
-  Left cs   -> hypPred g s ksu cs
-  Right eqs -> do qbp <- Sol.qbPreds msg s (F.ksuSubst ksu) eqs
-                  pure (F.pAndNoDedup $ fst <$> qbp, mempty) -- TODO: don't initialize kvars that have a hyp solution
-  where
-    msg     = "applyKVar: " ++ show (ceCid g)
+applyKVar :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.KVSub -> ExprInfo
+applyKVar cfg  g s ksu = case Sol.lookup s (F.ksuKVar ksu) of
+  Left cs   -> hypPred cfg g s ksu cs
+  Right eqs -> let qbp = Sol.qbPreds (F.ksuSubst ksu) eqs
+                in (F.pAndNoDedup $ fst <$> qbp, mempty) -- TODO: don't initialize kvars that have a hyp solution
 
-mkNonCutsExpr :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVar -> Sol.Hyp -> ElabM F.Expr
-mkNonCutsExpr ce s k cs = do bcps <- traverse (bareCubePred ce s k) cs
-                             pure $ F.pOr bcps
+mkNonCutsExpr :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.KVar -> Sol.Hyp -> F.Expr
+mkNonCutsExpr cfg ce s k cs =
+  let bcps = map (bareCubePred cfg ce s k) cs
+   in F.pOr bcps
 
-nonCutsResult :: F.BindEnv ann -> Sol.Sol a Sol.QBind -> ElabM (M.HashMap F.KVar F.Expr)
-nonCutsResult be s = M.traverseWithKey (mkNonCutsExpr g s) $ Sol.sHyp s
+nonCutsResult :: Config -> F.BindEnv ann -> Sol.Sol Sol.QBind -> FixDelayedSolution
+nonCutsResult cfg be s = M.mapWithKey (\k -> Delayed . mkNonCutsExpr cfg g s k) $ Sol.sHyp s
   where
     g = CEnv Nothing be F.emptyIBindEnv F.dummySpan F.emptyIBindEnv
 
@@ -373,35 +348,57 @@
 -- differences since the result of 'cubePred' is fed to the verification
 -- pipeline and @bareCubePred@ is meant for human inspection.
 --
--- 1) Only one existential quantifier is introduced at the top of the
---    expression.
--- 2) @bareCubePred@ doesn't elaborate the expression, so it avoids calling
---    'elabExist'. 'apply' is invoked to eliminate other kvars though, and
---    apply will invoke 'elabExist', so 'Liquid.Fixpoint.SortCheck.unElab'
---    might need to be called on the output to remove the elaboration.
--- 3) The expression is created from its defining constraints only, while
---    @cubePred@ does expect the caller to supply the substitution at a
---    particular use of the KVar. Thus @cubePred@ produces a different
---    expression for every use site of the kvar, while here we produce one
---    expression for all the uses.
-bareCubePred :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVar -> Sol.Cube -> ElabM F.Expr
-bareCubePred g s k c =
-  do (xts, psu) <- substElim (Sol.sEnv s) sEnv g' k su
-     (p, _kI) <- apply g' s bs'
-     pure $ F.pExist (xts ++ yts) (psu &.& p)
+-- The expression is created from its defining constraints only, while
+-- @cubePred@ does expect the caller to supply the substitution at a
+-- particular use of the KVar. Thus @cubePred@ produces a different
+-- expression for every use site of the kvar, while here we produce one
+-- expression for all the uses.
+--
+-- Where the cube rhs is @k[params:=xts]@, we keep the parameters free in the
+-- final predicate. e.g. @params == xts && exists yts . ...@
+-- That is, we only quantify out the `yts` as we want to make
+-- explicit what equalities those parameters have in each cube.
+--
+-- Issue https://github.com/ucsd-progsys/liquid-fixpoint/issues/808 discusses
+-- an example where the equalities are essential to keep.
+
+bareCubePred :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.KVar -> Sol.Cube -> F.Expr
+bareCubePred cfg g s k c =
+    let psu = F.pAnd [ F.EEq (F.expr x) e | (x, e) <- M.toList m ]
+        (p, _kI) = apply cfg g' s bs
+     in F.pExist yts (p F.&.& psu)
   where
     bs = Sol.cuBinds c
-    su = Sol.cuSubst c
+    F.Su m = dropUnsortedExprs cfg g' (Sol.cuSubst c)
     g' = addCEnv  g bs
-    bs' = delCEnv s k bs
+    bs' = F.diffIBindEnv bs (Misc.safeLookup "sScp" k (Sol.sScp s))
     yts = symSorts g bs'
-    sEnv = F.seSort (Sol.sEnv s)
 
-hypPred :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVSub -> Sol.Hyp -> ElabM ExprInfo
-hypPred g s ksu hyp =
-  do cs <- traverse (cubePred g s ksu) hyp
-     pure $ F.pOr *** mconcatPlus $ unzip cs
+-- | At the moment, the liquid-fixpoint implementation allows for unsorted
+-- expressions in substitutions. See the discussion in
+-- https://github.com/ucsd-progsys/liquid-fixpoint/issues/800
+-- The `explicitKvars` flag is meant for Horn-style constraints, which must
+-- have well-formed (expressions) as arguments, and so we *disable* the
+-- filtering of unsorted expressions when that flag is set.
+dropUnsortedExprs :: Config -> CombinedEnv ann -> F.Subst -> F.Subst
+dropUnsortedExprs cfg g su@(F.Su m)
+  | explicitKvars cfg = su
+  | otherwise         = F.Su $
+    M.filter
+      (\e -> isJust $ do
+         t <- So.checkSortExpr sp env e
+         guard (not (isClass t))
+      )
+      m
+  where
+    sp  = ceSpan g
+    env = combinedSEnv g
 
+hypPred :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.KVSub -> Sol.Hyp -> ExprInfo
+hypPred cfg g s ksu hyp =
+  let cs = map (cubePred cfg g s ksu) hyp
+   in F.pOr *** mconcatPlus $ unzip cs
+
 {- | `cubePred g s k su c` returns the predicate for
 
         (k . su)
@@ -410,120 +407,48 @@
 
         c := [b1,...,bn] |- (k . su')
 
-      in the binder environment `g`.
-
-        bs' := the subset of "extra" binders in [b1...bn] that are *not* in `g`
-        p'  := the predicate corresponding to the "extra" binders
-
+      in the binder environment `g`. The binders in `sScp s k` are not included
+      in the final predicate. They are considered redundant conjuncts as per
+      section 2.4 of "Local Refinement Typing", ICFP 2017.
  -}
-
-elabExist :: F.SrcSpan -> Sol.Sol a Sol.QBind -> [(F.Symbol, F.Sort)] -> F.Expr -> ElabM F.Expr
-elabExist sp s xts p =
-  do ef <- ask
-     let elab = So.elaborate (So.ElabParam ef (F.atLoc sp "elabExist") env)
-     let xts' = [ (x, elab t) | (x, t) <- xts]
-     pure $ F.pExist xts' p
-  where
-    env = Sol.sEnv s
-
-cubePred :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVSub -> Sol.Cube -> ElabM ExprInfo
-cubePred g s ksu c    =
-  do ((xts,psu,p), kI) <- cubePredExc g s ksu c bs'
-     e <- F.notracepp "cubePred" <$> elabExist sp s xts (psu &.& p)
-     pure (e , kI)
+cubePred :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> F.KVSub -> Sol.Cube -> ExprInfo
+cubePred cfg g s ksu c    =
+    let (p, kI) = cubePredExc cfg g s c bs'
+        -- Free variables in p should not colide with those generated by
+        -- the rapier substitution. If that were the case, perhaps we would
+        -- need to include @combinedSEnv g@ in the scope set.
+     in (F.rapierSubstExpr (F.substSymbolsSet su) su p, kI)
   where
-    sp  = F.srcSpan g
-    bs' = delCEnv s k bs
+    bs' = F.diffIBindEnv bs (Misc.safeLookup "sScp" k (Sol.sScp s))
     bs  = Sol.cuBinds c
     k   = F.ksuKVar ksu
-
-type Binders = [(F.Symbol, F.Sort)]
+    su = dropUnsortedExprs cfg g (F.ksuSubst  ksu)
 
 -- | @cubePredExc@ computes the predicate for the subset of binders bs'.
---   The output is a tuple, `(xts, psu, p, kI)` such that the actual predicate
---   we want is `Exists xts. (psu /\ p)`.
-
-cubePredExc :: CombinedEnv ann -> Sol.Sol a Sol.QBind -> F.KVSub -> Sol.Cube -> F.IBindEnv
-            -> ElabM ((Binders, F.Pred, F.Pred), KInfo)
-cubePredExc g s ksu c bs' =
-  do (xts, psu)  <- substElim (Sol.sEnv s) sEnv g  k su
-     (_  , psu') <- substElim (Sol.sEnv s) sEnv g' k su'
-     (p', kI)    <- apply g' s bs'
-     cubeE       <- elabExist sp s yts' (F.pAndNoDedup [p', psu'])
-     let cubeP = (xts, psu, cubeE)
-     pure (cubeP, extendKInfo kI (Sol.cuTag c))
-  where
-
-    sp              = F.srcSpan g
-    yts'            = symSorts g bs'
-    g'              = addCEnv  g bs
-    su'             = Sol.cuSubst c
-    bs              = Sol.cuBinds c
-    k               = F.ksuKVar   ksu
-    su              = F.ksuSubst  ksu
-    sEnv            = F.insertSEnv (F.ksuVV ksu) (F.ksuSort ksu) (F.seSort $ Sol.sEnv s)
-
--- TODO: SUPER SLOW! Decorate all substitutions with Sorts in a SINGLE pass.
-
-{- | @substElim@ returns the binders that must be existentially quantified,
-     and the equality predicate relating the kvar-"parameters" and their
-     actual values. i.e. given
-
-        K[x1 := e1]...[xn := en]
-
-     where e1 ... en have types t1 ... tn
-     we want to quantify out
-
-       x1:t1 ... xn:tn
-
-     and generate the equality predicate && [x1 ~~ e1, ... , xn ~~ en]
-     we use ~~ because the param and value may have different sorts, see:
-
-        tests/pos/kvar-param-poly-00.hs
-
-     Finally, we filter out binders if they are
-
-     1. "free" in e1...en i.e. in the outer environment.
-        (Hmm, that shouldn't happen...?)
-
-     2. are binders corresponding to sorts (e.g. `a : num`, currently used
-        to hack typeclasses current.)
- -}
-substElim :: F.SymEnv -> F.SEnv F.Sort -> CombinedEnv a -> F.KVar -> F.Subst -> ElabM ([(F.Symbol, F.Sort)], F.Pred)
-substElim syEnv sEnv g _ (F.Su m) =
-    do p <- traverse (\(x, e ,t) -> mkSubst sp syEnv x (substSort sEnv x) e t) xets
-       pure (xts, F.pAnd p)
-  where
-    xts    = [ (x, t)    | (x, _, t) <- xets, not (S.member x frees) ]
-    xets   = [ (x, e, t) | (x, e)    <- xes, t <- sortOf e, not (isClass t)]
-    frees  = S.fromList (concatMap (F.syms . snd) xes)
-    sortOf = maybeToList . So.checkSortExpr sp env
-    sp     = F.srcSpan g
-    xes    = M.toList m
-    env    = combinedSEnv g
-
-substSort :: F.SEnv F.Sort -> F.Symbol -> F.Sort
-substSort sEnv sym = fromMaybe (err sym) $ F.lookupSEnv sym sEnv
-  where
-    err x = error $ "Solution.substSort: unknown binder " ++ F.showpp x
-
-
--- LH #1091
-mkSubst :: F.SrcSpan -> F.SymEnv -> F.Symbol -> F.Sort -> F.Expr -> F.Sort -> ElabM F.Expr
-mkSubst sp env x tx ey ty
-  | tx == ty    = pure $ F.EEq ex ey
-  | otherwise   = do ex' <- elabToInt sp env ex tx
-                     ey' <- elabToInt sp env ey ty
-                     pure $ {- F.tracepp _msg $ -} F.EEq ex' ey'
+--
+-- Schematically, the result is
+--
+-- > Exists (bindsOf bs'). (pAnd (predicatesOf bs'))[Sol.cuSubst c]
+--
+-- but we also preserve the information about which variables are being
+-- substituted:
+--
+-- > Exists (bindsOf bs'). pAnd (predicatesOf bs') && x1=e1 && ... && xn=en
+--
+-- where @Sol.cuSubst c = [x1:=e1;...;xn:=en]@.
+--
+cubePredExc :: Config -> CombinedEnv ann -> Sol.Sol Sol.QBind -> Sol.Cube -> F.IBindEnv
+            -> (F.Pred, KInfo)
+cubePredExc cfg g s c bs' =
+    let psu' = F.pAnd [ F.EEq (F.expr x) e | (x, e) <- M.toList m ]
+        (p', kI) = apply cfg g' s bs'
+        cubeE = F.pExist yts' (F.pAndNoDedup [p', psu'])
+     in (cubeE, extendKInfo kI (Sol.cuTag c))
   where
-    -- _msg        = "mkSubst-DIFF: tx = " ++ F.showpp tx ++ " ty = " ++ F.showpp ty
-    --                                     ++ " ex' = " ++ F.showpp ex' ++ " ey' = " ++ F.showpp ey'
-    ex          = F.expr x
-
-elabToInt :: F.SrcSpan -> F.SymEnv -> F.Expr -> F.Sort -> ElabM F.Expr
-elabToInt sp env e s =
-  do ef <- ask
-     pure $ So.elaborate (So.ElabParam ef (F.atLoc sp "elabToInt") env) (So.toInt env e s)
+    yts' = symSorts g bs'
+    g' = addCEnv  g bs
+    F.Su m = dropUnsortedExprs cfg g' (Sol.cuSubst c)
+    bs = Sol.cuBinds c
 
 isClass :: F.Sort -> Bool
 isClass F.FNum  = True
@@ -539,12 +464,6 @@
 addCEnv :: CombinedEnv a -> F.IBindEnv -> CombinedEnv a
 addCEnv g bs' = g { ceIEnv = F.unionIBindEnv (ceIEnv g) bs' }
 
-
-delCEnv :: Sol.Sol a Sol.QBind -> F.KVar -> F.IBindEnv -> F.IBindEnv
-delCEnv s k bs = F.diffIBindEnv bs _kbs
-  where
-    _kbs       = Misc.safeLookup "delCEnv" k (Sol.sScp s)
-
 symSorts :: CombinedEnv a -> F.IBindEnv -> [(F.Symbol, F.Sort)]
 symSorts g bs = second F.sr_sort <$> F.envCs (ceBEnv g) bs
 
@@ -570,11 +489,11 @@
   mempty  = KI [] 0 1
   mappend = (<>)
 
-mplus :: KInfo -> KInfo -> KInfo
-mplus ki ki' = (mappend ki ki') { kiCubes = kiCubes ki + kiCubes ki'}
+mplusKInfo :: KInfo -> KInfo -> KInfo
+mplusKInfo ki ki' = (mappend ki ki') { kiCubes = kiCubes ki + kiCubes ki'}
 
 mconcatPlus :: [KInfo] -> KInfo
-mconcatPlus = foldr mplus mempty
+mconcatPlus = foldr mplusKInfo mempty
 
 appendTags :: [Tag] -> [Tag] -> [Tag]
 appendTags ts ts' = Misc.sortNub (ts ++ ts')
@@ -583,48 +502,145 @@
 extendKInfo ki t = ki { kiTags  = appendTags [t] (kiTags  ki)
                       , kiDepth = 1  +            kiDepth ki }
 
-mrExprInfosM :: Monad m => (a -> m (b, c)) -> ([b] -> b1) -> ([c] -> c1) -> [a] -> m (b1, c1)
-mrExprInfosM mF erF irF xs =
-  do bcs <- traverse mF xs
-     let (es, is) = unzip bcs
-     pure (erF es, irF is)
+-- | Simplifies existential expressions with unused or inconsequential bindings.
+--
+-- Simplification is helpful for human readability of solutions. It makes easier
+-- reporting errors. Sometimes it can be useful for debugging if run on queries
+-- sent to the SMT solver. We don't do that by default because some benchmarks
+-- show a slowdown in some cases.
+--
+-- For instance, in the following example, "x" is not used at all.
+--
+-- > simplifyKVar "exists x y. y == z && y == C"
+-- >   ==
+-- > "exists y. y == z && y == C"
+--
+-- And in the following example, @x@ is used but in a way that doesn't
+-- contribute any useful knowledge.
+--
+-- > simplifyKVar "exists x y. x == C && y == z && y == C"
+-- >   ==
+-- > "exists y. y == z && y == C"
+--
+-- Therefore we eliminate variables that appear in equalities via substitutions.
+--
+-- > simplifyKVar "exists x y. x == C && P && Q y"
+-- >   ==
+-- > "exists y. (P && Q y)[x:=C]"
+--
+-- The first parameter is the set of symbols that can appear free in the input
+-- expression. At the moment, this only needs to include the free variables that
+-- start with the @subst$@ prefix.
+--
+simplifyKVar :: S.HashSet F.Symbol -> F.Expr -> F.Expr
+simplifyKVar s0 = F.conj . dedupByAlphaEq s0 . floatPExistConjuncts . go s0
+  where
+    go s (F.POr es) = disj $ map (F.conj . floatPExistConjuncts . go s) es
+    go s (F.PAnd es) = F.conj $ dedupByAlphaEq S.empty $ concatMap (floatPExistConjuncts . go s) es
+    go s (F.PExist bs e0) =
+      let es = concatMap (floatPExistConjuncts . go (S.union s $ S.fromList $ map fst bs)) (F.conjuncts e0)
+       in elimExistentialBinds (F.PExist bs (F.conj es))
+    go _ e = e
 
---------------------------------------------------------------------------------
--- | `ebindInfo` constructs the information about the "ebind-definitions".
---------------------------------------------------------------------------------
-ebindInfo :: F.SInfo a -> [(F.BindId, Sol.EbindSol)]
-ebindInfo si = group [((bid, x), cons cid) | (bid, cid, x) <- ebindDefs si]
-  where cons cid = void (Misc.safeLookup "ebindInfo" cid cs)
-        cs = F.cm si
-        cmpByFst x y = fst ( fst x ) == fst ( fst y )
-        group xs = (\ys -> Bifunctor.second (Sol.EbDef (snd <$> ys)) (fst $ head ys))
-                    <$> L.groupBy cmpByFst xs
+    dedupByAlphaEq :: S.HashSet F.Symbol -> [F.Expr] -> [F.Expr]
+    dedupByAlphaEq s = List.nubBy (\e1 e2 -> alphaEq s e1 e2)
 
-ebindDefs :: F.SInfo a -> [(F.BindId, F.SubcId, F.Symbol)]
-ebindDefs si = [ (bid, cid, x) | (cid, x) <- cDefs
-                               , bid      <- maybeToList (M.lookup x ebSyms)]
-  where
-    ebSyms   = ebindSyms si
-    cDefs    = cstrDefs  si
+    disj :: [F.Expr] -> F.Expr
+    disj [] = F.PFalse
+    disj [e] = e
+    disj es = F.POr es
 
-ebindSyms :: F.SInfo a -> M.HashMap F.Symbol F.BindId
-ebindSyms si = M.fromList [ (xi, bi) | bi        <- ebinds si
-                                     , let (xi,_,_) = F.lookupBindEnv bi be ]
-  where
-    be       = F.bs si
+    elimExistentialBinds (F.PExist bs0 (F.PExist bs1 p)) =
+      let bs0' = filter (\(x,_) -> x `notElem` map fst bs1) bs0
+       in elimExistentialBinds (F.PExist (bs0' ++ bs1) p)
+    elimExistentialBinds (F.PExist bs e0) =
+      let es = F.conjuncts e0
+          esv = map (isVarEq (map fst bs)) es
+          -- Eliminating multiple variables at once can be difficult if the
+          -- equalities define cyclic dependencies, so we only eliminate one
+          -- variable at a time.
+          esvElim = take 1 [ (x, v) | (Just (x, v), _) <- esv ]
+          esvKeep =
+            let (xs, ys) = break (isJust . fst) esv
+             in map snd (xs ++ drop 1 ys)
+          su = F.mkSubst esvElim
+          e' = F.rapierSubstExpr (F.substSymbolsSet su) su $ F.conj esvKeep
+          bs' = filter ((`S.member` F.exprSymbolsSet e') . fst) bs
+          e'' = F.pExist bs' e'
+       in
+          if null esvElim then e'' else elimExistentialBinds e''
+    elimExistentialBinds e = e
 
-cstrDefs :: F.SInfo a -> [(F.SubcId, F.Symbol)]
-cstrDefs si = [(cid, x) | (cid, c) <- M.toList (cm si)
-                        , x <- maybeToList (cstrDef be c) ]
+    -- | Float out conjuncts from an existential expression that does not
+    -- depend on the existentially bound variables.
+    floatPExistConjuncts :: F.Expr -> [F.Expr]
+    floatPExistConjuncts e0@(F.PExist bs es0) =
+      let es = F.conjuncts es0
+          (floatable, nonFloatable) =
+           List.partition (isFloatableConjunct (S.fromList (map fst bs))) es
+       in
+          if null floatable then
+            [e0]
+          else
+            elimExistentialBinds (F.pExist bs (F.conj nonFloatable)) : floatable
+      where
+        isFloatableConjunct :: S.HashSet F.Symbol -> F.Expr -> Bool
+        isFloatableConjunct s e = S.null $ S.intersection (F.exprSymbolsSet e) s
+    floatPExistConjuncts e = [e]
+
+-- | Determine if two expressions are alpha-equivalent.
+--
+-- Takes as first parameter the set of variables that might appear free
+-- in the expressions to compare.
+--
+-- Doesn't handle all cases, just enough for simplifying KVars which requires
+-- alpha-equivalence checking of existentially quantified expressions.
+alphaEq :: S.HashSet F.Symbol -> F.Expr -> F.Expr -> Bool
+alphaEq s0 = go s0 (F.mkSubst [])
   where
-    be      = F.bs si
+    go :: S.HashSet F.Symbol -> F.Subst -> F.Expr -> F.Expr -> Bool
+    go s su (F.PExist bs1 x1) (F.PExist bs2 x2) =
+      let su' =
+            List.foldl'
+              (\su1 (v1, v2) -> F.extendSubst su1 v1 (F.EVar v2))
+              su
+              (zip (map fst bs1) (map fst bs2))
+       in go (S.union s (S.fromList $ map fst bs2)) su' x1 x2
+    go s su (F.PAnd es1) (F.PAnd es2) =
+      length es1 == length es2 && and (zipWith (go s su) es1 es2)
+    go s su (F.POr es1) (F.POr es2) =
+      length es1 == length es2 && and (zipWith (go s su) es1 es2)
+    go s su e1 e2 =
+      F.rapierSubstExpr s su e1 == e2
 
-cstrDef :: F.BindEnv a -> F.SimpC a -> Maybe F.Symbol
-cstrDef be c
-  | Just (F.EVar x) <- e = Just x
-  | otherwise            = Nothing
+-- | Determine if the expression is an equality that sets the value of
+-- a variable in the given set.
+--
+-- @isVarEq fvs e@ yields @(Just (v, e'), e)@ if @v@ is in @fvs@, and @e@ has
+-- the form @v == e'@.
+isVarEq :: [F.Symbol] -> F.Expr -> (Maybe (F.Symbol, F.Expr), F.Expr)
+isVarEq fvs ei0 = case ei0 of
+  F.PAtom brel e0 e1
+    | isEqRel brel ->
+      let m :: Maybe (F.Symbol, F.Expr)
+          m = do
+            (v, ei) <- ((,e1) <$> isVarIn e0 fvs) `mplus`
+                       ((,e0) <$> isVarIn e1 fvs)
+            () <- guard (not (S.member v (F.exprSymbolsSet ei)))
+            return (v, ei)
+       in (m, ei0)
+  _ ->
+    (Nothing, ei0)
   where
-    (v,_,_)              = F.lookupBindEnv (cbind c) be
-    e                    = F.notracepp _msg $ F.isSingletonExpr v rhs
-    _msg                 = "cstrDef: " ++ show (stag c) ++ " crhs = " ++ F.showpp rhs
-    rhs                  = V.stripCasts (crhs c)
+    -- | Tells if the binary relation is an equality.
+    isEqRel :: F.Brel -> Bool
+    isEqRel F.Eq = True
+    isEqRel F.Ueq = True
+    isEqRel _ = False
+
+    -- | @isVarIn s fvs@ yields @Just s@ if @s@ is a variable and it is in
+    -- @fvs@.
+    isVarIn :: F.Expr -> [F.Symbol] -> Maybe F.Symbol
+    isVarIn (F.EVar s) vs
+      | elem s vs = Just s
+    isVarIn _ _vs = Nothing
diff --git a/src/Language/Fixpoint/Solver/Solve.hs b/src/Language/Fixpoint/Solver/Solve.hs
--- a/src/Language/Fixpoint/Solver/Solve.hs
+++ b/src/Language/Fixpoint/Solver/Solve.hs
@@ -2,6 +2,8 @@
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections     #-}
 
 {-# OPTIONS_GHC -Wno-name-shadowing #-}
 
@@ -9,18 +11,18 @@
 -- | Solve a system of horn-clause constraints ---------------------------------
 --------------------------------------------------------------------------------
 
-module Language.Fixpoint.Solver.Solve (solve, solverInfo) where
+module Language.Fixpoint.Solver.Solve (solve) where
 
-import           Control.Monad (when, filterM)
+import           Control.Monad (forM, when, filterM)
 import           Control.Monad.Reader
-import           Control.Monad.State.Strict (modify)
 import           Language.Fixpoint.Misc
 import qualified Language.Fixpoint.Misc            as Misc
 import qualified Language.Fixpoint.Types           as F
 import qualified Language.Fixpoint.Types.Solutions as Sol
 import           Language.Fixpoint.Types.PrettyPrint
 import           Language.Fixpoint.Types.Config hiding (stats)
-import           Language.Fixpoint.SortCheck          (ElabM)
+import           Language.Fixpoint.SortCheck          (ElabParam(..), elaborate)
+import           Language.Fixpoint.Solver.Sanitize (symbolEnv)
 import qualified Language.Fixpoint.Solver.Solution  as S
 import qualified Language.Fixpoint.Smt.Types as T
 import qualified Language.Fixpoint.Solver.Worklist  as W
@@ -37,37 +39,67 @@
 -- import qualified Data.Maybe          as Mb
 import qualified Data.List           as L
 import Language.Fixpoint.Types (resStatus, FixResult(Unsafe))
-import qualified Language.Fixpoint.Types.Config as C
+import Language.Fixpoint.Smt.Interface (smtComment)
 import Language.Fixpoint.Solver.Interpreter (instInterpreter)
-import Language.Fixpoint.Solver.Instantiate (instantiate)
---import Debug.Trace                      (trace)
+import qualified Language.Fixpoint.Solver.PLE as PLE      (instantiate)
+import Data.Maybe (maybeToList)
 
 mytrace :: String -> a -> a
-mytrace _ x = {- trace s -} x
+mytrace
+  -- s x = trace s x
+  _ x = x
+{-
+solve_ :: (NFData a, F.Fixpoint a, F.Loc a)
+       => Config
+       -> F.SInfo a
+       -> Sol.Solution
+       -> W.Worklist a
+       -> SolveM a (F.Result (Integer, a), Stats)
+       -}
+--------------------------------------------------------------------------------
 
 --------------------------------------------------------------------------------
-solve :: (NFData a, F.Fixpoint a, Show a, F.Loc a) => Config -> F.SInfo a -> IO (F.Result (Integer, a))
+solve
+  :: forall a. (NFData a, F.Fixpoint a, Show a, F.Loc a)
+  => Config -> ElabParam -> F.SInfo a -> IO (F.Result (Integer, a))
 --------------------------------------------------------------------------------
 
-solve cfg fi = do
+solve cfg elabParam fi = do
     whenLoud $ donePhase Misc.Loud "Worklist Initialize"
     vb <- getVerbosity
-    (res, stat) <- (if Quiet == vb || gradual cfg then id else withProgressFI sI) $ runSolverM cfg sI act
+    (res, stat) <- (if Quiet == vb then id else withProgressFI sI) $ runSolverM cfg sI elabParam act
     when (solverStats cfg) $ printStats fi wkl stat
     -- print (numIter stat)
     return res
   where
-    act = solve_ cfg fi s0 ks  wkl
+    act :: SolveM a (F.Result (Integer, a), Stats)
+    act = do
+      ctx <- getContext
+      let sEnv = symbolEnv cfg fi
+          s1 = s0{Sol.sMap = M.map (elabQBind ctx "solve" sEnv) (Sol.sMap s0)}
+      solve_ cfg fi s1 wkl
+    -- solverInfo computes the set of cut and non-cut kvars, then initializes
+    -- the solutions of the non-cut KVars (in the sHyp field)
+    --
+    -- S.init provides an initial solution for the cut KVars
     sI  = solverInfo cfg fi
     wkl = W.init sI
-    s0  = siSol  sI
+    s0  = (siSol sI) { Sol.sMap = S.init cfg fi ks }
     ks  = siVars sI
+    elabQBind ctx msg env (Sol.QB xs) = Sol.QB (map elabEQual xs)
+      where
+        elabEQual eq =
+          eq { Sol.eqPred =
+                elaborate
+                 (ElabParam (T.ctxElabF ctx) (F.atLoc F.dummySpan msg) env)
+                 (Sol.eqPred eq)
+             }
 
 
 --------------------------------------------------------------------------------
 -- | Progress Bar
 --------------------------------------------------------------------------------
-withProgressFI :: SolverInfo a b -> IO b -> IO b
+withProgressFI :: SolverInfo a -> IO b -> IO b
 withProgressFI = withProgress . (+ 1) . fromIntegral . cNumScc . siDeps
 --------------------------------------------------------------------------------
 
@@ -77,38 +109,19 @@
     ppTs          = putStrLn . showpp . mconcat
 
 --------------------------------------------------------------------------------
-solverInfo :: Config -> F.SInfo a -> SolverInfo a b
+solverInfo :: Config -> F.SInfo a -> SolverInfo a
 --------------------------------------------------------------------------------
 solverInfo cfg fI
   | useElim cfg = E.solverInfo cfg fI
   | otherwise   = SI mempty fI cD (siKvars fI)
   where
-    cD          = elimDeps fI (kvEdges fI) mempty mempty
+    cD          = elimDeps fI (kvEdges fI) mempty
 
 siKvars :: F.SInfo a -> S.HashSet F.KVar
 siKvars = S.fromList . M.keys . F.ws
 
-doInterpret :: (F.Loc a) =>  Config -> F.SInfo a -> [F.SubcId] -> SolveM a (F.SInfo a)
-doInterpret cfg fi0 subcIds = do
-  fi <- liftIO $ instInterpreter cfg fi0 (Just subcIds)
-  modify $ update' fi
-  return fi
-  where
-    update' fi ss = ss{ssBinds = F.bs fi'}
-      where
-        fi' = (siQuery sI) {F.hoInfo = F.HOI (C.allowHO cfg) (C.allowHOqs cfg)}
-        sI  = solverInfo cfg fi
-
-{-# SCC doPLE #-}
-doPLE :: (F.Loc a) =>  Config -> F.SInfo a -> [F.SubcId] -> SolveM a ()
-doPLE cfg fi0 subcIds = do
-  fi <- liftIO $ instantiate cfg fi0 (Just subcIds)
-  modify $ update' fi
-  where
-    update' fi ss = ss{ssBinds = F.bs fi'}
-      where
-        fi' = (siQuery sI) {F.hoInfo = F.HOI (C.allowHO cfg) (C.allowHOqs cfg)}
-        sI  = solverInfo cfg fi
+doInterpret :: (F.Loc a) =>  Config -> F.SInfo a -> [F.SubcId] -> SolveM a (F.BindEnv a)
+doInterpret cfg fi subcIds = liftIO $ instInterpreter cfg fi (Just subcIds)
 
 --------------------------------------------------------------------------------
 {-# SCC solve_ #-}
@@ -116,39 +129,43 @@
        => Config
        -> F.SInfo a
        -> Sol.Solution
-       -> S.HashSet F.KVar
        -> W.Worklist a
        -> SolveM a (F.Result (Integer, a), Stats)
 --------------------------------------------------------------------------------
-solve_ cfg fi s0 ks wkl = do
-  let s1   = F.notracepp "solve_ " $ {-# SCC "sol-init" #-} S.init cfg fi ks
-  let s2   = mappend s0 s1
-  (s3, res0) <- sendConcreteBindingsToSMT F.emptyIBindEnv $ \bindingsInSmt -> do
+solve_ cfg fi s2 wkl = do
+  liftSMT $ smtComment "solve: start"
+  (s3, res0) <- sendConcreteBindingsToSMT F.emptyIBindEnv (F.bs fi) $ \bindingsInSmt -> do
     -- let s3   = solveEbinds fi s2
-    s3       <- {- SCC "sol-refine" -} refine bindingsInSmt s2 wkl
-    res0     <- {- SCC "sol-result" -} result bindingsInSmt cfg wkl s3
+    s3       <- {- SCC "sol-refine" -} refine bindingsInSmt (F.bs fi) s2 wkl
+    res0     <- {- SCC "sol-result" -} result bindingsInSmt cfg fi (W.unsatCandidates wkl) s3
     return (s3, res0)
 
-  (fi1, s4, res1) <- case resStatus res0 of  {- first run the interpreter -}
-    Unsafe _ bads | not (noLazyPLE cfg) && rewriteAxioms cfg && interpreter cfg -> do
-      fi1 <- doInterpret cfg fi (map fst $ mytrace ("before the Interpreter " ++ show (length bads) ++ " constraints remain") bads)
-      (s4, res1) <-  sendConcreteBindingsToSMT F.emptyIBindEnv $ \bindingsInSmt -> do
-        s4    <- {- SCC "sol-refine" -} refine bindingsInSmt s3 wkl
-        res1  <- {- SCC "sol-result" -} result bindingsInSmt cfg wkl s4
-        return (s4, res1)
-      return (fi1, s4, res1)
-    _ -> return  (fi, s3, mytrace "all checked before interpreter" res0)
+  (fi1, res1) <- case resStatus res0 of  {- first run the interpreter -}
+    Unsafe _ bads | rewriteAxioms cfg && interpreter cfg -> do
+      liftSMT $ smtComment "solve: interpreter"
+      bs <- doInterpret cfg fi (map fst $ mytrace ("before the Interpreter " ++ show (length bads) ++ " constraints remain") bads)
+      let fi1 = fi { F.bs = bs }
+          badCs = lookupCMap (F.cm fi) <$> map fst bads
+      liftSMT $ smtComment "solve: pos-interpreter check"
+      fmap (fi1,) $ sendConcreteBindingsToSMT F.emptyIBindEnv bs $ \bindingsInSmt ->
+        result bindingsInSmt cfg fi1 badCs s3
+    _ -> return  (fi, mytrace "all checked before interpreter" res0)
 
   res2  <- case resStatus res1 of  {- then run normal PLE on remaining unsolved constraints -}
-    Unsafe _ bads2 | not (noLazyPLE cfg) && rewriteAxioms cfg -> do
-      doPLE cfg fi1 (map fst $ mytrace ("before z3 PLE " ++ show (length bads2) ++ " constraints remain") bads2)
-      sendConcreteBindingsToSMT F.emptyIBindEnv $ \bindingsInSmt -> do
-        s5    <- {- SCC "sol-refine" -} refine bindingsInSmt s4 wkl
-        result bindingsInSmt cfg wkl s5
+    Unsafe _ bads2 | rewriteAxioms cfg -> do
+      liftSMT $ smtComment "solve: ple"
+      bs <- liftSMT $ PLE.instantiate cfg fi1 (Just s3) (Just $ map fst bads2)
+      -- Check the constraints one last time after PLE
+      let fi2 = fi { F.bs = bs }
+          badsCs2 = lookupCMap (F.cm fi) <$> map fst bads2
+      liftSMT $ smtComment "solve: pos-ple check"
+      sendConcreteBindingsToSMT F.emptyIBindEnv bs $ \bindingsInSmt ->
+        result bindingsInSmt cfg fi2 badsCs2 s3
     _ -> return $ mytrace "all checked with interpreter" res1
 
+  liftSMT $ smtComment "solve: finished"
   st      <- stats
-  let res3 = {- SCC "sol-tidy" -} tidyResult res2
+  let res3 = {- SCC "sol-tidy" -} tidyResult cfg res2
   return $!! (res3, st)
 
 
@@ -156,78 +173,103 @@
 -- | tidyResult ensures we replace the temporary kVarArg names introduced to
 --   ensure uniqueness with the original names in the given WF constraints.
 --------------------------------------------------------------------------------
-tidyResult :: F.Result a -> F.Result a
-tidyResult r = r
+tidyResult :: Config -> F.Result a -> F.Result a
+tidyResult _ r = r
   { F.resSolution = tidySolution (F.resSolution r)
-  , F.resNonCutsSolution = tidySolution (F.resNonCutsSolution r)
+  , F.resNonCutsSolution = M.map (fmap tidyPred) (F.resNonCutsSolution r)
+  , F.resSorts = fmap tidyBind <$>  F.resSorts r
   }
 
 tidySolution :: F.FixSolution -> F.FixSolution
 tidySolution = fmap tidyPred
 
+tidyBind :: (F.Symbol, F.Sort) -> (F.Symbol, F.Sort)
+tidyBind (x, t) = (F.tidySymbol x, t)
+
 tidyPred :: F.Expr -> F.Expr
-tidyPred = F.substf (F.eVar . F.tidySymbol)
+tidyPred =  go
+  where
+    ts = F.tidySymbol
+    tb = tidyBind
+    go (F.EApp s e)      = F.EApp (go s) (go e)
+    go (F.ELam (x,t) e)  = F.ELam (ts x, t) (go e)
+    go (F.ECoerc a t e)  = F.ECoerc a t (go e)
+    go (F.ENeg e)        = F.ENeg (go e)
+    go (F.EBin op e1 e2) = F.EBin op (go e1) (go e2)
+    go (F.ELet x e1 e2)  = F.ELet (ts x) (go e1) (go e2)
+    go (F.EIte p e1 e2)  = F.EIte (go p) (go e1) (go e2)
+    go (F.ECst e so)     = F.ECst (go e) so
+    go (F.EVar x)        = F.EVar (ts x)
+    go (F.PAnd ps)       = F.PAnd $ map go ps
+    go (F.POr  ps)       = F.POr  $ map go ps
+    go (F.PNot p)        = F.PNot $ go p
+    go (F.PImp p1 p2)    = F.PImp (go p1) (go p2)
+    go (F.PIff p1 p2)    = F.PIff (go p1) (go p2)
+    go (F.PAtom r e1 e2) = F.PAtom r (go e1) (go e2)
+    go (F.PExist xts e)  = F.PExist (tb <$> xts) (go e)
+    go (F.PAll xts e)    = F.PAll   (tb <$> xts) (go e)
+    go  p                = p
 
 --------------------------------------------------------------------------------
 {-# SCC refine #-}
+-- | Implementation of the inference algorithm from:
+--
+-- "Liquid Types", PLDI 2008, https://ranjitjhala.github.io/static/liquid_types.pdf
+--
 refine
-  :: (F.Loc a)
+  :: forall a. F.Loc a
   => F.IBindEnv
+  -> F.BindEnv a
   -> Sol.Solution
   -> W.Worklist a
   -> SolveM a Sol.Solution
 --------------------------------------------------------------------------------
-refine bindingsInSmt s w
-  | Just (c, w', newScc, rnk) <- W.pop w = do
-     i       <- tickIter newScc
-     (b, s') <- refineC bindingsInSmt i s c
-     lift $ writeLoud $ refineMsg i c b rnk (showpp s')
-     let w'' = if b then W.push c w' else w'
-     refine bindingsInSmt s' w''
-  | otherwise = return s
+refine bindingsInSmt be0 s0 w0 = go be0 s0 w0
   where
-    -- DEBUG
-    refineMsg i c b rnk s = printf "\niter=%d id=%d change=%s rank=%d s=%s\n"
-                             i (F.subcId c) (show b) rnk s
+    go :: F.BindEnv a -> Sol.Solution -> W.Worklist a -> SolveM a Sol.Solution
+    go be s w
+      | Just (c, w', newScc, rnk) <- W.pop w = do
+         i       <- tickIter newScc
+         (b, s') <- refineC bindingsInSmt be i s c
+         lift $ writeLoud $ refineMsg i c b rnk (showpp s')
+         let w'' = if b then W.push c w' else w'
+         go be s' w''
+      | otherwise = return s
+      where
+        -- DEBUG
+        refineMsg i c b rnk s = printf "\niter=%d id=%d change=%s rank=%d s=%s\n"
+                                 i (F.subcId c) (show b) rnk s
 
 ---------------------------------------------------------------------------
 -- | Single Step Refinement -----------------------------------------------
 ---------------------------------------------------------------------------
 {-# SCC refineC #-}
 refineC
-  :: (F.Loc a)
+  :: forall a. (F.Loc a)
   => F.IBindEnv
+  -> F.BindEnv a
   -> Int
   -> Sol.Solution
   -> F.SimpC a
   -> SolveM a (Bool, Sol.Solution)
 ---------------------------------------------------------------------------
-refineC bindingsInSmt _i s c =
-  do ef <- T.ctxElabF <$> getContext
-     let (ks, rhs) = runReader (rhsCands s c) ef
-     if null rhs
+refineC bindingsInSmt be _i s c =
+  do let krhs = rhsCands s
+     cfg <- T.config <$> getContext
+     if all (null . snd) krhs
         then return (False, s)
-        else do be     <- getBinds
-                let lhs = runReader (S.lhsPred bindingsInSmt (F.coerceBindEnv ef be) s c) ef
-                kqs    <- filterValid (cstrSpan c) lhs rhs
-                return  $ S.update s ks kqs
-  where
-    _ci       = F.subcId c
-    -- msg       = printf "refineC: iter = %d, sid = %s, soln = \n%s\n"
-    --               _i (show (F.sid c)) (showpp s)
-    _msg ks xs ys = printf "refineC: iter = %d, sid = %s, s = %s, rhs = %d, rhs' = %d \n"
-                     _i (show _ci) (showpp ks) (length xs) (length ys)
-
-rhsCands :: Sol.Solution -> F.SimpC a -> ElabM ([F.KVar], Sol.Cand (F.KVar, Sol.EQual))
-rhsCands s c    =
-  do pq <- traverse cnd ks
-     pure (fst <$> ks, concat pq)
+        else do
+          let lhs = S.lhsPred cfg bindingsInSmt be s c
+          kqs <- forM krhs $ \(k, rhs) ->
+            (,) k . Sol.QB <$> filterValid (cstrSpan c) lhs rhs
+          return $ S.update s kqs
   where
-    cnd :: (F.KVar, F.Subst) -> ElabM [(F.Pred, (F.KVar, Sol.EQual))]
-    cnd (k, su) = map (\(p , q) -> (p , (k , q))) <$> Sol.qbPreds msg s su (Sol.lookupQBind s k)
-    ks          = predKs . F.crhs $ c
-
-    msg         = "rhsCands: " ++ show (F.sid c)
+    rhsCands :: Sol.Solution -> [(F.KVar, Sol.Cand Sol.EQual)]
+    rhsCands s = M.toList $ M.fromList $ map cnd ks
+      where
+        ks          = predKs . F.crhs $ c
+        cnd :: (F.KVar, F.Subst) -> (F.KVar , Sol.Cand Sol.EQual)
+        cnd (k, su) = (k, Sol.qbPreds su (Sol.lookupQBind s k))
 
 predKs :: F.Expr -> [(F.KVar, F.Subst)]
 predKs (F.PAnd ps)    = concatMap predKs ps
@@ -242,42 +284,59 @@
   :: (F.Fixpoint a, F.Loc a, NFData a)
   => F.IBindEnv
   -> Config
-  -> W.Worklist a
+  -> F.SInfo a
+  -> [F.SimpC a]
   -> Sol.Solution
   -> SolveM a (F.Result (Integer, a))
 --------------------------------------------------------------------------------
-result bindingsInSmt cfg wkl s =
-  sendConcreteBindingsToSMT bindingsInSmt $ \bindingsInSmt2 -> do
-    lift $ writeLoud "Computing Result"
-    stat    <- result_ bindingsInSmt2 cfg wkl s
-    lift $ whenLoud $ putStrLn $ "RESULT: " ++ show (F.sid <$> stat)
-
-    F.Result (ci <$> stat) <$> solResult cfg s <*> solNonCutsResult s <*> return mempty
+result bindingsInSmt cfg fi cs s =
+  sendConcreteBindingsToSMT bindingsInSmt be $ \bindingsInSmt2 -> do
+    lift       $ writeLoud "Computing Result"
+    stat      <- result_ bindingsInSmt2 be cfg cs s
+    lift       $ whenLoud $ putStrLn $ "RESULT: " ++ show (F.sid <$> stat)
+    resCut    <- solResult cfg s
+    let resNonCut = S.nonCutsResult cfg be s
+        resSorts = resultSorts fi (M.keys resCut ++ M.keys resNonCut) be
+    return     $ F.Result (ci <$> stat) resCut resNonCut resSorts
   where
     ci c = (F.subcId c, F.sinfo c)
+    be = F.bs fi
 
+resultSorts :: F.SInfo a -> [F.KVar] -> F.BindEnv a -> F.ResultSorts
+resultSorts fi ks be = M.fromList
+  [(k, xts)
+    | k <- ks
+    , xts <- maybeToList (kvarScope fi be k) ]
+
+kvarScope :: F.SInfo a -> F.BindEnv a -> F.KVar -> Maybe [(F.Symbol, F.Sort)]
+kvarScope fi be k = do
+  w <- M.lookup k (F.ws fi)
+  let bs = F.wenv w
+  let (v, t, _) = F.wrft w
+  return $ (v, t) : [ bindInfo be i | i <- L.sort (F.elemsIBindEnv bs) ]
+
+bindInfo :: F.BindEnv a -> F.BindId -> (F.Symbol, F.Sort)
+bindInfo be i = (x, F.sr_sort sr)
+  where
+    (x, sr, _) = F.lookupBindEnv i be
+
 solResult :: Config -> Sol.Solution -> SolveM ann (M.HashMap F.KVar F.Expr)
 solResult cfg = minimizeResult cfg . Sol.result
 
-solNonCutsResult :: Sol.Solution -> SolveM ann (M.HashMap F.KVar F.Expr)
-solNonCutsResult s = do
-  be <- getBinds
-  ef <- T.ctxElabF <$> getContext
-  pure $ runReader (S.nonCutsResult be s) ef
-
 result_
   :: (F.Loc a, NFData a)
   => F.IBindEnv
+  -> F.BindEnv a
   -> Config
-  -> W.Worklist a
+  -> [F.SimpC a]
   -> Sol.Solution
   -> SolveM a (F.FixResult (F.SimpC a))
-result_ bindingsInSmt cfg w s = do
-  filtered <- filterM (isUnsat bindingsInSmt s) cs
+result_ bindingsInSmt be cfg cs0 s = do
+  unsatisfiedConstraints <- filterM (isUnsat bindingsInSmt be s) cs
   sts      <- stats
-  pure $ res sts filtered
+  pure $ res sts unsatisfiedConstraints
   where
-    cs          = isChecked cfg (W.unsatCandidates w)
+    cs          = isChecked cfg cs0
     res sts []  = F.Safe sts
     res sts cs' = F.Unsafe sts cs'
 
@@ -314,15 +373,14 @@
 
 --------------------------------------------------------------------------------
 isUnsat
-  :: (F.Loc a, NFData a) => F.IBindEnv -> Sol.Solution -> F.SimpC a -> SolveM a Bool
+  :: (F.Loc a, NFData a) => F.IBindEnv -> F.BindEnv a -> Sol.Solution -> F.SimpC a -> SolveM a Bool
 --------------------------------------------------------------------------------
-isUnsat bindingsInSmt s c = do
+isUnsat bindingsInSmt be s c = do
   -- lift   $ printf "isUnsat %s" (show (F.subcId c))
   _     <- tickIter True -- newScc
-  be    <- getBinds
-  ef <- T.ctxElabF <$> getContext
-  let lp = runReader (S.lhsPred bindingsInSmt (F.coerceBindEnv ef be) s c) ef
-  let rp = rhsPred        c
+  cfg <- T.config <$> getContext
+  let lp = S.lhsPred cfg bindingsInSmt be s c
+      rp = rhsPred c
   res   <- not <$> isValid (cstrSpan c) lp rp
   lift   $ whenLoud $ showUnsat res (F.subcId c) lp rp
   return res
@@ -359,49 +417,3 @@
   putBlankLn
   donePhase Loud msg
 -}
-
-
--- NV TODO Move to a new file
--------------------------------------------------------------------------------
--- | Interaction with the user when Solving -----------------------------------
--------------------------------------------------------------------------------
-
-_iMergePartitions :: [(Int, F.SInfo a)] -> IO [(Int, F.SInfo a)]
-_iMergePartitions ifis = do
-  putStrLn "Current Partitions are: "
-  putStrLn $ unlines (partitionInfo <$> ifis)
-  putStrLn "Merge Partitions? Y/N"
-  c <- getChar
-  if c == 'N'
-    then do putStrLn "Solving Partitions"
-            return ifis
-    else do
-      (i, j) <- getMergePartition (length ifis)
-      _iMergePartitions (mergePartitions i j ifis)
-
-getMergePartition :: Int -> IO (Int, Int)
-getMergePartition n = do
-  putStrLn "Which two partition to merge? (i, j)"
-  ic <- getLine
-  let (i,j) = read ic :: (Int, Int)
-  if i < 1 || n < i || j < 1 || n < j
-    then do putStrLn ("Invalid Partition numbers, write (i,j) with 1 <= i <= " ++ show n)
-            getMergePartition n
-    else return (i,j)
-
-mergePartitions :: Int -> Int -> [(Int, F.SInfo a)] -> [(Int, F.SInfo a)]
-mergePartitions i j fis
-  = zip [1..] ((takei i `mappend` (takei j){F.bs = mempty}):rest)
-  where
-    takei i = snd (fis L.!! (i - 1))
-    rest = snd <$> filter (\(k,_) -> k /= i && k /= j) fis
-
-partitionInfo :: (Int, F.SInfo a) -> String
-partitionInfo (i, fi)
-  = "Partition number " ++ show i ++ "\n" ++
-    "Defined ?? " ++ show defs    ++ "\n" ++
-    "Used ?? "    ++ show uses
-  where
-    gs   = F.wloc . snd <$> L.filter (F.isGWfc . snd) (M.toList (F.ws fi))
-    defs = L.nub (F.gsrc <$> gs)
-    uses = L.nub (F.gused <$> gs)
diff --git a/src/Language/Fixpoint/Solver/UniqifyKVars.hs b/src/Language/Fixpoint/Solver/UniqifyKVars.hs
--- a/src/Language/Fixpoint/Solver/UniqifyKVars.hs
+++ b/src/Language/Fixpoint/Solver/UniqifyKVars.hs
@@ -39,6 +39,7 @@
 import           Language.Fixpoint.Types
 import           Language.Fixpoint.Types.Visitor (mapKVarSubsts)
 import qualified Data.HashMap.Strict as M
+import qualified Data.List as L
 #if !MIN_VERSION_base(4,20,0)
 import           Data.Foldable       (foldl')
 #endif
@@ -78,13 +79,11 @@
 updateWfc :: SInfo a -> WfC a -> SInfo a
 updateWfc fi w    = fi'' { ws = M.insert k w' (ws fi) }
   where
-    w'            = updateWfCExpr (subst su) w''
-    w''           = w { wenv = insertsIBindEnv newIds mempty, wrft = (v', t, k) }
+    w'           = w { wenv = insertsIBindEnv newIds mempty, wrft = (v', t, k) }
     (_, fi'')     = newTopBind v' (trueSortedReft t) a fi'
-    (fi', newIds) = foldl' (accumBindsIfValid k a) (fi, []) (elemsIBindEnv $ wenv w)
+    (fi', newIds) = foldl' (accumBindsIfValid k a) (fi, []) (L.sort $ elemsIBindEnv $ wenv w)
     (v, t, k)     = wrft w
     v'            = kArgSymbol v (kv k)
-    su            = mkSubst ((v, EVar v'):[(x, eVar $ kArgSymbol x (kv k)) | x <- kvarDomain fi k])
     a             = winfo w
 
 accumBindsIfValid :: KVar -> a -> (SInfo a, [BindId]) -> BindId -> (SInfo a, [BindId])
@@ -118,5 +117,6 @@
 isValidInRefinements (FVar _)    = True
 isValidInRefinements (FFunc _ _) = True -- False
 isValidInRefinements (FAbs  _ t) = isValidInRefinements t
-isValidInRefinements (FTC _)     = True --TODO is this true? seems to be required for e.g. ResolvePred.hs
+isValidInRefinements (FTC _)     = True -- TODO is this true? seems to be required for e.g. ResolvePred.hs
 isValidInRefinements (FApp _ _)  = True
+isValidInRefinements (FNatNum _) = True -- TODO probably?
diff --git a/src/Language/Fixpoint/Solver/Worklist.hs b/src/Language/Fixpoint/Solver/Worklist.hs
--- a/src/Language/Fixpoint/Solver/Worklist.hs
+++ b/src/Language/Fixpoint/Solver/Worklist.hs
@@ -24,8 +24,8 @@
 import           Prelude hiding (init)
 import           Language.Fixpoint.Types.PrettyPrint
 import qualified Language.Fixpoint.Types   as F
+import           Language.Fixpoint.Types.Visitor (isConcC)
 import           Language.Fixpoint.Graph.Types
-import           Language.Fixpoint.Graph   (isTarget)
 
 import           Control.Arrow             (first)
 import qualified Data.HashMap.Strict       as M
@@ -87,7 +87,7 @@
 --------------------------------------------------------------------------------
 -- | Initialize worklist and slice out irrelevant constraints ------------------
 --------------------------------------------------------------------------------
-init :: SolverInfo a b -> Worklist a
+init :: SolverInfo a -> Worklist a
 --------------------------------------------------------------------------------
 init sI    = WL { wCs     = items
                 , wPend   = addPends M.empty kvarCs
@@ -104,9 +104,11 @@
     cd        = siDeps sI
     rankm     = cRank cd
     items     = S.fromList $ workItemsAt rankm 0 <$> kvarCs
-    concCs    = fst <$> ics
+    concCs    = fst <$> filter (isNonTriv . snd) ics
     kvarCs    = fst <$> iks
-    (ics,iks) = L.partition (isTarget . snd) (M.toList cm)
+    (ics,iks) = L.partition (isConcC . snd) (M.toList cm)
+
+    isNonTriv = not .  F.isTautoPred . F.crhs
 
 ---------------------------------------------------------------------------
 -- | Candidate Constraints to be checked AFTER computing Fixpoint ---------
diff --git a/src/Language/Fixpoint/SortCheck.hs b/src/Language/Fixpoint/SortCheck.hs
--- a/src/Language/Fixpoint/SortCheck.hs
+++ b/src/Language/Fixpoint/SortCheck.hs
@@ -8,6 +8,7 @@
 {-# LANGUAGE PatternGuards         #-}
 {-# LANGUAGE BangPatterns          #-}
 {-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE InstanceSigs #-}
 
 -- | This module has the functions that perform sort-checking, and related
 -- operations on Fixpoint expressions and predicates.
@@ -17,6 +18,8 @@
     TVSubst
   , Env
   , mkSearchEnv
+  , globalEnv
+  , theoryEnv
 
   -- * Checking Well-Formedness
   , checkSorted
@@ -55,6 +58,7 @@
   , elabNumeric
   , unApply
   , unElab
+  , unElabFSetBagZ3
   , unElabSortedReft
   , unApplySortedReft
   , unApplyAt
@@ -72,13 +76,13 @@
 import           Control.Monad
 import           Control.Monad.Reader
 
-import           Data.Bifunctor (first)
+import           Data.Bifunctor (first, second)
 import qualified Data.IntMap.Strict       as M
 import qualified Data.HashSet              as S
 import           Data.IORef
 import qualified Data.List                 as L
 import           Data.Maybe                (mapMaybe, fromMaybe, isJust)
-
+import qualified Data.HashMap.Strict       as HashMap
 import           Language.Fixpoint.Types.PrettyPrint
 import           Language.Fixpoint.Misc
 import           Language.Fixpoint.Types hiding   (subst, GInfo(..), senv)
@@ -91,6 +95,7 @@
 import           GHC.Stack
 import qualified Language.Fixpoint.Types as F
 import           System.IO.Unsafe (unsafePerformIO)
+import Language.Fixpoint.Types.Config (ElabFlags(elabExplicitKvars))
 
 --import Debug.Trace as Debug
 
@@ -133,14 +138,17 @@
   }
 
 class Elaborate a where
-  elaborate :: ElabParam -> a -> a
+  elaborate :: HasCallStack => ElabParam -> a -> a
 
 
 instance (Loc a) => Elaborate (SInfo a) where
   elaborate ep si = si
     { F.cm      = elaborate ep <$> F.cm      si
     , F.bs      = elaborate ep  $  F.bs      si
+    , F.gLits   = coerceSort (epFlags ep) <$> F.gLits   si
+    , F.dLits   = coerceSort (epFlags ep) <$> F.dLits   si
     , F.asserts = elaborate ep <$> F.asserts si
+    , F.defns   = elaborate ep  $ F.defns    si
     , F.ddecls  = coerceDataDecl (epFlags ep) <$> F.ddecls si
     }
 
@@ -152,7 +160,7 @@
   elaborate ep t = elaborate ep <$> t
 
 instance Elaborate Sort where
-  elaborate _ = go
+  elaborate ep =  coerceSort (epFlags ep) . go
    where
       go s | isString s = strSort
       go (FAbs i s)    = FAbs i  (go s)
@@ -173,19 +181,34 @@
     where
       ep' = ep { epEnv = insertsSymEnv (epEnv ep) undefined }
 
+
 instance Elaborate Equation where
   elaborate ep eq = eq { eqBody = skipElabExpr ep' (eqBody eq) }
     where
       ep' = ep { epEnv = insertsSymEnv (epEnv ep) (eqArgs eq) }
 
+
+instance Elaborate DefinedFuns where
+  elaborate ep (MkDefinedFuns eqs) = MkDefinedFuns (elabDefinedEqn ep <$> eqs)
+
+elabDefinedEqn :: ElabParam -> Equation -> Equation
+elabDefinedEqn ep eq = eq { eqBody = elaborateExpr ep' (eqBody eq) (Just t')
+                          , eqArgs = [(x, tx t) | (x, t) <- eqArgs eq ]
+                          , eqSort = t'
+                          }
+  where
+    ep' = ep { epEnv = insertsSymEnv (epEnv ep) (eqArgs eq) }
+    tx  = coerceSort (epFlags ep)
+    t'  = tx (eqSort eq)
+
 instance Elaborate Expr where
   elaborate p e = elaborateExpr p e Nothing
 
-elaborateExpr :: ElabParam -> Expr -> Maybe Sort -> Expr
+elaborateExpr :: HasCallStack => ElabParam -> Expr -> Maybe Sort -> Expr
 elaborateExpr (ElabParam ef msg env) e t =
-    elabNumeric . elabApply env' . elabExpr (ElabParam ef msg env') t . elabFMap . (if Cfg.elabSetBag ef then elabFSetBagZ3 else id) $ e
-      where
-        env' = coerceEnv ef env
+  elabNumeric . elabApply env' . elabExpr (ElabParam ef msg env') t .  elabSorts ef . elabFMap . (if Cfg.elabSetBag ef then elabFSetBagZ3 else id) $ e
+    where
+      env' = coerceEnv ef env
 
 skipElabExpr :: ElabParam -> Expr -> Expr
 skipElabExpr ep e = case elabExprE ep Nothing e of
@@ -213,7 +236,7 @@
       = e
 
 instance Elaborate SortedReft where
-  elaborate ep (RR s (Reft (v, e))) = RR s (Reft (v, e'))
+  elaborate ep (RR s (Reft (v, e))) = RR (coerceSort (epFlags ep) s) (Reft (v, e'))
     where
       e'   = elaborateExpr ep' e (Just boolSort) -- check that a SortedReft is in fact a bool
       ep' = ep { epEnv = insertSymEnv v s (epEnv ep) }
@@ -248,6 +271,7 @@
 elabFMap (EApp e1 e2)      = EApp (elabFMap e1) (elabFMap e2)
 elabFMap (ENeg e)          = ENeg (elabFMap e)
 elabFMap (EBin b e1 e2)    = EBin b (elabFMap e1) (elabFMap e2)
+elabFMap (ELet x e1 e2)    = ELet x (elabFMap e1) (elabFMap e2)
 elabFMap (EIte e1 e2 e3)   = EIte (elabFMap e1) (elabFMap e2) (elabFMap e3)
 elabFMap (ECst e t)        = ECst (elabFMap e) t
 elabFMap (ELam b e)        = ELam b (elabFMap e)
@@ -261,79 +285,184 @@
 elabFMap (PAtom r e1 e2)   = PAtom r (elabFMap e1) (elabFMap e2)
 elabFMap (PAll   bs e)     = PAll bs (elabFMap e)
 elabFMap (PExist bs e)     = PExist bs (elabFMap e)
-elabFMap (PGrad  k su i e) = PGrad k su i (elabFMap e)
 elabFMap (ECoerc a t e)    = ECoerc a t (elabFMap e)
+elabFMap (PKVar k (Su m))  = PKVar k (Su (elabFMap <$> m))
 elabFMap e                 = e
 
+
 elabFSetBagZ3 :: Expr -> Expr
-elabFSetBagZ3 (EApp h@(EVar f) e)
-  | f == Thy.setEmpty         = EApp (EVar Thy.arrConstS) PFalse
-  | f == Thy.setEmp           = PAtom Eq (EApp (EVar Thy.arrConstS) PFalse) (elabFSetBagZ3 e)
-  | f == Thy.setSng           = EApp (EApp (EApp (EVar Thy.arrStoreS) (EApp (EVar Thy.arrConstS) PFalse)) (elabFSetBagZ3 e)) PTrue
-  | f == Thy.setCom           = EApp (EVar Thy.arrMapNotS) (elabFSetBagZ3 e)
-  | f == Thy.bagEmpty         = EApp (EVar Thy.arrConstB) (ECon (I 0))
-  | otherwise                 = EApp (elabFSetBagZ3 h) (elabFSetBagZ3 e)
-elabFSetBagZ3 (EApp (EApp h@(EVar f) e1) e2)
-  | f == Thy.setMem           = EApp (EApp (EVar Thy.arrSelectS) (elabFSetBagZ3 e2)) (elabFSetBagZ3 e1)
-  | f == Thy.setCup           = EApp (EApp (EVar Thy.arrMapOrS) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | f == Thy.setCap           = EApp (EApp (EVar Thy.arrMapAndS) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | f == Thy.setAdd           = EApp (EApp (EApp (EVar Thy.arrStoreS) (elabFSetBagZ3 e2)) (elabFSetBagZ3 e1)) PTrue
-  -- A \ B == A /\ ~B == ~(A => B)
-  | f == Thy.setDif           = EApp (EApp (EVar Thy.arrMapAndS) (elabFSetBagZ3 e1)) (EApp (EVar Thy.arrMapNotS) (elabFSetBagZ3 e2))
-  | f == Thy.setSub           = PAtom Eq (EApp (EVar Thy.arrConstS) PTrue) (EApp (EApp (EVar Thy.arrMapImpS) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2))
-  | f == Thy.bagCount         = EApp (EApp (EVar Thy.arrSelectB) (elabFSetBagZ3 e2)) (elabFSetBagZ3 e1)
-  | f == Thy.bagSng           = EApp (EApp (EApp (EVar Thy.arrStoreB) (EApp (EVar Thy.arrConstB) (ECon (I 0)))) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | f == Thy.bagCup           = EApp (EApp (EVar Thy.arrMapPlusB) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | f == Thy.bagSub           = PAtom Eq (EApp (EVar Thy.arrConstS) PTrue) (EApp (EApp (EVar Thy.arrMapLeB) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2))
-  | f == Thy.bagMax           = EApp (EApp (EApp (EVar Thy.arrMapIteB) (EApp (EApp (EVar Thy.arrMapGtB) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2))) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | f == Thy.bagMin           = EApp (EApp (EApp (EVar Thy.arrMapIteB) (EApp (EApp (EVar Thy.arrMapLeB) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2))) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-  | otherwise                 = EApp (EApp (elabFSetBagZ3 h) (elabFSetBagZ3 e1)) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (EApp e1 e2)      = EApp (elabFSetBagZ3 e1) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (ENeg e)          = ENeg (elabFSetBagZ3 e)
-elabFSetBagZ3 (EBin b e1 e2)    = EBin b (elabFSetBagZ3 e1) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (EIte e1 e2 e3)   = EIte (elabFSetBagZ3 e1) (elabFSetBagZ3 e2) (elabFSetBagZ3 e3)
-elabFSetBagZ3 (ECst e t)        = ECst (elabFSetBagZ3 e) t
-elabFSetBagZ3 (ELam b e)        = ELam b (elabFSetBagZ3 e)
-elabFSetBagZ3 (ETApp e t)       = ETApp (elabFSetBagZ3 e) t
-elabFSetBagZ3 (ETAbs e t)       = ETAbs (elabFSetBagZ3 e) t
-elabFSetBagZ3 (PAnd es)         = PAnd (elabFSetBagZ3 <$> es)
-elabFSetBagZ3 (POr es)          = POr (elabFSetBagZ3 <$> es)
-elabFSetBagZ3 (PNot e)          = PNot (elabFSetBagZ3 e)
-elabFSetBagZ3 (PImp e1 e2)      = PImp (elabFSetBagZ3 e1) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (PIff e1 e2)      = PIff (elabFSetBagZ3 e1) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (PAtom r e1 e2)   = PAtom r (elabFSetBagZ3 e1) (elabFSetBagZ3 e2)
-elabFSetBagZ3 (PAll   bs e)     = PAll bs (elabFSetBagZ3 e)
-elabFSetBagZ3 (PExist bs e)     = PExist bs (elabFSetBagZ3 e)
-elabFSetBagZ3 (PGrad  k su i e) = PGrad k su i (elabFSetBagZ3 e)
-elabFSetBagZ3 (ECoerc a t e)    = ECoerc a t (elabFSetBagZ3 e)
-elabFSetBagZ3 e                 = e
+elabFSetBagZ3 = go
+  where
+    go (EApp h@(EVar f) e)
+      | f == Thy.setEmpty = EApp (EVar Thy.arrConstS) PFalse
+      | f == Thy.setEmp   = PAtom Eq (EApp (EVar Thy.arrConstS) PFalse) (go e)
+      | f == Thy.setSng   = EApp (EApp (EApp (EVar Thy.arrStoreS) (EApp (EVar Thy.arrConstS) PFalse)) (go e)) PTrue
+      | f == Thy.setCom   = EApp (EVar Thy.arrMapNotS) (go e)
+      | f == Thy.bagEmpty = EApp (EVar Thy.arrConstB) (ECon (I 0))
+      | otherwise         = EApp (go h) (go e)
+    go (EApp (EApp h@(EVar f) e1) e2)
+      | f == Thy.setMem   = EApp (EApp (EVar Thy.arrSelectS) (go e2)) (go e1)
+      | f == Thy.setCup   = EApp (EApp (EVar Thy.arrMapOrS) (go e1)) (go e2)
+      | f == Thy.setCap   = EApp (EApp (EVar Thy.arrMapAndS) (go e1)) (go e2)
+      | f == Thy.setAdd   = EApp (EApp (EApp (EVar Thy.arrStoreS) (go e2)) (go e1)) PTrue
+      -- A \ B == A /\ ~B == ~(A => B)
+      | f == Thy.setDif   = EApp (EApp (EVar Thy.arrMapAndS) (go e1)) (EApp (EVar Thy.arrMapNotS) (go e2))
+      | f == Thy.setSub   = PAtom Eq (EApp (EVar Thy.arrConstS) PTrue) (EApp (EApp (EVar Thy.arrMapImpS) (go e1)) (go e2))
+      | f == Thy.bagCount = EApp (EApp (EVar Thy.arrSelectB) (go e2)) (go e1)
+      | f == Thy.bagSng   = EApp (EApp (EApp (EVar Thy.arrStoreB) (EApp (EVar Thy.arrConstB) (ECon (I 0)))) (go e1)) (go e2)
+      | f == Thy.bagCup   = EApp (EApp (EVar Thy.arrMapPlusB) (go e1)) (go e2)
+      | f == Thy.bagSub   = PAtom Eq (EApp (EVar Thy.arrConstS) PTrue) (EApp (EApp (EVar Thy.arrMapLeB) (go e1)) (go e2))
+      | f == Thy.bagMax   = EApp (EApp (EApp (EVar Thy.arrMapIteB) (EApp (EApp (EVar Thy.arrMapGtB) (go e1)) (go e2))) (go e1)) (go e2)
+      | f == Thy.bagMin   = EApp (EApp (EApp (EVar Thy.arrMapIteB) (EApp (EApp (EVar Thy.arrMapLeB) (go e1)) (go e2))) (go e1)) (go e2)
+      | otherwise         = EApp (EApp (go h) (go e1)) (go e2)
+    go (EApp e1 e2)       = EApp   (go e1) (go e2)
+    go (ENeg e)           = ENeg   (go e)
+    go (EBin b e1 e2)     = EBin b (go e1) (go e2)
+    go (ELet x e1 e2)     = ELet x (go e1) (go e2)
+    go (EIte e1 e2 e3)    = EIte   (go e1) (go e2) (go e3)
+    go (ECst e t)         = ECst   (go e) t
+    go (ELam b e)         = ELam b (go e)
+    go (ETApp e t)        = ETApp  (go e) t
+    go (ETAbs e t)        = ETAbs  (go e) t
+    go (PAnd es)          = PAnd   (go <$> es)
+    go (POr es)           = POr    (go <$> es)
+    go (PNot e)           = PNot   (go e)
+    go (PImp e1 e2)       = PImp   (go e1) (go e2)
+    go (PIff e1 e2)       = PIff   (go e1) (go e2)
+    go (PAtom r e1 e2)    = PAtom r (go e1) (go e2)
+    go (PAll   bs e)      = PAll bs (go e)
+    go (PExist bs e)      = PExist bs (go e)
+    go (ECoerc a t e)     = ECoerc a t (go e)
+    go (PKVar k (Su m))   = PKVar k (Su (go <$> m))
+    go e                  = e
 
+-- | Reverse transformation of elabFSetBagZ3: converts array representations back to set/bag operations
+unElabFSetBagZ3 :: Expr -> Expr
+unElabFSetBagZ3 = go
+  where
+    -- arr_const_s false -> Set_empty
+    go (EApp (EVar f) PFalse)
+      | f == Thy.arrConstS = EVar Thy.setEmpty
+    -- arr_const_s false == e -> Set_emp e
+    go (PAtom Eq (EApp (EVar f) PFalse) e)
+      | f == Thy.arrConstS = EApp (EVar Thy.setEmp) (go e)
+    -- arr_store_s (arr_const_s false) e true -> Set_sng e
+    go (EApp (EApp (EApp (EVar f1) (EApp (EVar f2) PFalse)) e) PTrue)
+      | f1 == Thy.arrStoreS && f2 == Thy.arrConstS = EApp (EVar Thy.setSng) (go e)
+    -- arr_map_not_s e -> Set_com e
+    go (EApp (EVar f) e)
+      | f == Thy.arrMapNotS = EApp (EVar Thy.setCom) (go e)
+    -- arr_const_b 0 -> Bag_empty
+    go (EApp (EVar f) (ECon (I 0)))
+      | f == Thy.arrConstB = EVar Thy.bagEmpty
+    -- arr_select_s e2 e1 -> Set_mem e1 e2
+    go (EApp (EApp (EVar f) e2) e1)
+      | f == Thy.arrSelectS = EApp (EApp (EVar Thy.setMem) (go e1)) (go e2)
+    -- arr_map_or_s e1 e2 -> Set_cup e1 e2
+    go (EApp (EApp (EVar f) e1) e2)
+      | f == Thy.arrMapOrS = EApp (EApp (EVar Thy.setCup) (go e1)) (go e2)
+    -- arr_map_and_s e1 e2 -> Set_cap e1 e2
+    go (EApp (EApp (EVar f) e1) e2)
+      | f == Thy.arrMapAndS = EApp (EApp (EVar Thy.setCap) (go e1)) (go e2)
+    -- arr_store_s e2 e1 true -> Set_add e1 e2
+    go (EApp (EApp (EApp (EVar f) e2) e1) PTrue)
+      | f == Thy.arrStoreS = EApp (EApp (EVar Thy.setAdd) (go e1)) (go e2)
+    -- arr_map_and_s e1 (arr_map_not_s e2) -> Set_dif e1 e2
+    go (EApp (EApp (EVar f1) e1) (EApp (EVar f2) e2))
+      | f1 == Thy.arrMapAndS && f2 == Thy.arrMapNotS = EApp (EApp (EVar Thy.setDif) (go e1)) (go e2)
+    -- arr_const_s true == arr_map_imp_s e1 e2 -> Set_sub e1 e2
+    go (PAtom Eq (EApp (EVar f1) PTrue) (EApp (EApp (EVar f2) e1) e2))
+      | f1 == Thy.arrConstS && f2 == Thy.arrMapImpS = EApp (EApp (EVar Thy.setSub) (go e1)) (go e2)
+    -- arr_select_b e2 e1 -> Bag_count e1 e2
+    go (EApp (EApp (EVar f) e2) e1)
+      | f == Thy.arrSelectB = EApp (EApp (EVar Thy.bagCount) (go e1)) (go e2)
+    -- arr_store_b (arr_const_b 0) e1 e2 -> Bag_sng e1 e2
+    go (EApp (EApp (EApp (EVar f1) (EApp (EVar f2) (ECon (I 0)))) e1) e2)
+      | f1 == Thy.arrStoreB && f2 == Thy.arrConstB = EApp (EApp (EVar Thy.bagSng) (go e1)) (go e2)
+    -- arr_map_plus_b e1 e2 -> Bag_cup e1 e2
+    go (EApp (EApp (EVar f) e1) e2)
+      | f == Thy.arrMapPlusB = EApp (EApp (EVar Thy.bagCup) (go e1)) (go e2)
+    -- arr_const_s true == arr_map_le_b e1 e2 -> Bag_sub e1 e2
+    go (PAtom Eq (EApp (EVar f1) PTrue) (EApp (EApp (EVar f2) e1) e2))
+      | f1 == Thy.arrConstS && f2 == Thy.arrMapLeB = EApp (EApp (EVar Thy.bagSub) (go e1)) (go e2)
+    -- arr_map_ite_b (arr_map_gt_b e1 e2) e1 e2 -> Bag_max e1 e2
+    go (EApp (EApp (EApp (EVar f1) (EApp (EApp (EVar f2) e1a) e2a)) e1b) e2b)
+      | f1 == Thy.arrMapIteB && f2 == Thy.arrMapGtB && e1a == e1b && e2a == e2b
+      = EApp (EApp (EVar Thy.bagMax) (go e1a)) (go e2a)
+    -- arr_map_ite_b (arr_map_le_b e1 e2) e1 e2 -> Bag_min e1 e2
+    go (EApp (EApp (EApp (EVar f1) (EApp (EApp (EVar f2) e1a) e2a)) e1b) e2b)
+      | f1 == Thy.arrMapIteB && f2 == Thy.arrMapLeB && e1a == e1b && e2a == e2b
+      = EApp (EApp (EVar Thy.bagMin) (go e1a)) (go e2a)
+    -- Recursive cases
+    go (EApp e1 e2)       = EApp   (go e1) (go e2)
+    go (ENeg e)           = ENeg   (go e)
+    go (EBin b e1 e2)     = EBin b (go e1) (go e2)
+    go (ELet x e1 e2)     = ELet x (go e1) (go e2)
+    go (EIte e1 e2 e3)    = EIte   (go e1) (go e2) (go e3)
+    go (ECst e t)         = ECst   (go e) t
+    go (ELam b e)         = ELam b (go e)
+    go (ETApp e t)        = ETApp  (go e) t
+    go (ETAbs e t)        = ETAbs  (go e) t
+    go (PAnd es)          = PAnd   (go <$> es)
+    go (POr es)           = POr    (go <$> es)
+    go (PNot e)           = PNot   (go e)
+    go (PImp e1 e2)       = PImp   (go e1) (go e2)
+    go (PIff e1 e2)       = PIff   (go e1) (go e2)
+    go (PAtom r e1 e2)    = PAtom r (go e1) (go e2)
+    go (PAll   bs e)      = PAll bs (go e)
+    go (PExist bs e)      = PExist bs (go e)
+    go (ECoerc a t e)     = ECoerc a t (go e)
+    go (PKVar k (Su m))   = PKVar k (Su (go <$> m))
+    go e                  = e
+
+
+elabSorts :: Cfg.ElabFlags -> Expr -> Expr
+elabSorts ef (EApp e1 e2)      = EApp (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (ENeg e)          = ENeg (elabSorts ef e)
+elabSorts ef (EBin b e1 e2)    = EBin b (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (ELet x e1 e2)    = ELet x (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (EIte e1 e2 e3)   = EIte (elabSorts ef e1) (elabSorts ef e2) (elabSorts ef e3)
+elabSorts ef (ECst e s)        = ECst (elabSorts ef e) (coerceSort ef s)
+elabSorts ef (ELam b e)        = ELam b (elabSorts ef e)
+elabSorts ef (ETApp e s)       = ETApp (elabSorts ef e) (coerceSort ef s)
+elabSorts ef (ETAbs e t)       = ETAbs (elabSorts ef e) t
+elabSorts ef (PAnd es)         = PAnd (elabSorts ef <$> es)
+elabSorts ef (POr es)          = POr (elabSorts ef <$> es)
+elabSorts ef (PNot e)          = PNot (elabSorts ef e)
+elabSorts ef (PImp e1 e2)      = PImp (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (PIff e1 e2)      = PIff (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (PAtom r e1 e2)   = PAtom r (elabSorts ef e1) (elabSorts ef e2)
+elabSorts ef (PAll   bs e)     = PAll bs (elabSorts ef e)
+elabSorts ef (PExist bs e)     = PExist bs (elabSorts ef e)
+elabSorts ef (ECoerc s1 s2 e)  = ECoerc (coerceSort ef s1) (coerceSort ef s2) (elabSorts ef e)
+elabSorts ef (PKVar k (Su m))  = PKVar k (Su (elabSorts ef <$> m))
+elabSorts _ e                 = e
+
 --------------------------------------------------------------------------------
 -- | 'elabExpr' adds "casts" to decorate polymorphic instantiation sites.
 --------------------------------------------------------------------------------
-elabExpr :: ElabParam -> Maybe Sort -> Expr ->  Expr
+elabExpr :: HasCallStack => ElabParam -> Maybe Sort -> Expr -> Expr
 elabExpr ep t e = case elabExprE ep t e of
   Left ex  -> die ex
   Right e' -> F.notracepp ("elabExp " ++ showpp e) e'
 
-validateSort :: Sort -> Maybe Sort -> CheckM ()
-validateSort t (Just t')
-  | t == t'            = return ()
-  | otherwise          = throwErrorAt $ printf "unexpected sort: got `%s` but expected `%s`" (showpp t) (showpp t')
-validateSort _ Nothing = return ()
+validateSort :: Env -> Sort -> Maybe Sort -> CheckM ()
+-- validateSort f t (Just t') = void (unifys f (tracepp ("validateSort" ++ show (t, t')) Nothing) [t] [t'])
+validateSort f t (Just t') = void (unifys f Nothing [t] [t'])
+validateSort _ _ Nothing   = return ()
 
 elabExprE :: ElabParam -> Maybe Sort -> Expr -> Either Error Expr
 elabExprE (ElabParam ef msg env) t e =
   case runCM0 (srcSpan msg) (Just ef) $ do
     (!e', eSort) <- elab (env, envLookup) e
-    validateSort eSort t
+    validateSort envLookup eSort t
     finalThetaRef <- asks chTVSubst
     finalTheta <- liftIO $ readIORef finalThetaRef
     return (applyExpr finalTheta e') of
     Left (ChError f') ->
       let e' = f' ()
       in Left $ err (srcSpan e') (d (val e'))
-    Right s  -> Right s
+    Right elab_e -> Right elab_e
   where
     sEnv = seSort env
     envLookup = (`lookupSEnvWithDistance` sEnv)
@@ -359,6 +488,7 @@
     step (POr [])         = PFalse
     step (ENeg e)         = ENeg (go  e)
     step (EBin o e1 e2)   = EBin o (go e1) (go e2)
+    step (ELet x e1 e2)   = ELet x (go e1) (go e2)
     step (EIte e1 e2 e3)  = EIte (go e1) (go e2) (go e3)
     step (ECst e t)       = ECst (go e) t
     step (PAnd ps)        = PAnd (go <$> ps)
@@ -372,8 +502,7 @@
     step e@EApp {}        = go e
     step (ELam b e)       = ELam b       (go e)
     step (ECoerc a t e)   = ECoerc a t   (go e)
-    step (PGrad k su i e) = PGrad k su i (go e)
-    step e@PKVar{}        = e
+    step (PKVar k (Su m)) = PKVar k (Su (go <$> m))
     step e@ESym{}         = e
     step e@ECon{}         = e
     step e@EVar{}         = e
@@ -465,7 +594,7 @@
 runCM0 :: SrcSpan -> Maybe Cfg.ElabFlags -> CheckM a -> Either ChError a
 runCM0 sp mef act = unsafePerformIO $ do
   ref <- newIORef Nothing
-  try (runReaderT act (ChS varCounterRef sp (fromMaybe (Cfg.ElabFlags False) mef) ref))
+  try (runReaderT act (ChS varCounterRef sp (fromMaybe (Cfg.ElabFlags False False) mef) ref))
 
 fresh :: CheckM Int
 fresh = do
@@ -568,6 +697,7 @@
 checkExpr f (EVar x)        = checkSym f x
 checkExpr f (ENeg e)        = checkNeg f e
 checkExpr f (EBin o e1 e2)  = checkOp f e1 o e2
+checkExpr f (ELet x e1 e2)  = checkLet f x e1 e2
 checkExpr f (EIte p e1 e2)  = checkIte f p e1 e2
 checkExpr f (ECst e t)      = checkCst f t e
 checkExpr f (EApp g e)      = checkApp f Nothing g e
@@ -578,7 +708,6 @@
 checkExpr f (POr ps)        = mapM_ (checkPred f) ps >> return boolSort
 checkExpr f (PAtom r e e')  = checkRel f r e e' >> return boolSort
 checkExpr _ PKVar{}         = return boolSort
-checkExpr f (PGrad _ _ _ e) = checkPred f e >> return boolSort
 
 checkExpr f (PAll  bs e )   = checkExpr (addEnv f bs) e
 checkExpr f (PExist bs e)   = checkExpr (addEnv f bs) e
@@ -606,6 +735,10 @@
   let !result = EBin o (eCst e1' s1) (eCst e2' s2)
   return (result, s)
 
+elab !f (ECst (EApp !e1 !e2) t) = do
+   ee <- elabAppAs f t e1 e2
+   return (eCst ee t, t)
+
 elab !f (EApp !e1 !e2) = do
   (!e1', !s1, !e2', !s2, !s) <- elabEApp f e1 e2
   let !e = eAppC s (eCst e1' s1) (eCst e2' s2)
@@ -623,12 +756,19 @@
 elab !_ e@(ECon (L _ !s)) =
   return (e, s)
 
-elab !_ e@(PKVar _ _) =
-  return (e, boolSort)
-
-elab !f (PGrad !k !su !i !e) = do
-  (!e', !_) <- elab f e
-  return (PGrad k su i e', boolSort)
+-- TODO: the guard below is because some LH tests generate PKVar with ill-sorted substitutions.
+-- However, a cleaner solution could be to modify `Sanitize.restrictKVarDomain` to simply
+-- those ill-sorted substitutions right up at the outset.
+elab !f e@(PKVar k (Su m)) = do
+  expKvars <- asks (elabExplicitKvars . chElabF)
+  if expKvars
+    then do
+      xargs' <- forM (HashMap.toList m) $ \(x, arg) -> do
+        (arg', _) <- elab f arg
+        return (x, arg')
+      return (PKVar k (Su (HashMap.fromList xargs')), boolSort)
+    else
+      return (e, boolSort)
 
 elab (!_, !f) e@(EVar !x) = do
   !cs <- checkSym f x
@@ -653,6 +793,11 @@
   !s          <- checkIteTy g p e1' e2' s1 s2
   return (EIte p' (eCst e1' s) (eCst e2' s), s)
 
+elab f (ELet !x !e1 !e2) = do
+  (!e1', !t1) <- elab f e1
+  (!e2', !t2) <- elab (elabAddEnv f [(x, t1)]) e2
+  return (ELet x e1' e2', t2)
+
 elab !f (ECst !e !t) = do
   (!e', !_) <- elab f e
   return (eCst e' t, t)
@@ -729,11 +874,11 @@
 
 -- | 'eCstAtom' is to support tests like `tests/pos/undef00.fq`
 eCstAtom :: ElabEnv -> Expr -> Sort -> CheckM Expr
-eCstAtom f@(sym,g) (EVar x) t
+eCstAtom f@(sym,g) (ECst (EVar x) _) t
   | Found s <- g x
   , isUndef s
-  , not (isNum sym t) = (`ECst` t) <$> elabAs f t (EApp (eVar tyCastName) (eVar x))
-eCstAtom _ e t = return (ECst e t)
+  , not (isNum sym t) = (`eCst` t) <$> elabAppAs f t (eVar tyCastName) (eVar x)
+eCstAtom _ e t = return (eCst e t)
 
 isUndef :: Sort -> Bool
 isUndef s = case bkAbs s of
@@ -747,8 +892,8 @@
 elabAs f t e = notracepp _msg <$> go e
   where
     _msg  = "elabAs: t = " ++ showpp t ++ "; e = " ++ showpp e
-    go (EApp e1 e2) = elabAppAs f t e1 e2
-    go e'           = fst <$> elab f e'
+    go (EApp e1 e2)    = elabAppAs f t e1 e2
+    go e'              = fst <$> elab f (eCst e' t)
 
 -- DUPLICATION with `checkApp'`
 elabAppAs :: ElabEnv -> Sort -> Expr -> Expr -> CheckM Expr
@@ -762,7 +907,7 @@
   g'       <- elabAs env tg g
   let te    = apply su eT
   e'       <- elabAs env te e
-  pure     $ EApp (ECst g' tg) (ECst e' te)
+  pure     $ EApp (eCst g' tg) (eCst e' te)
 
 elabEApp  :: ElabEnv -> Expr -> Expr -> CheckM (Expr, Sort, Expr, Sort, Sort)
 elabEApp f@(_, g) e1 e2 = do
@@ -845,6 +990,7 @@
     go (ECst (EApp (EApp f e1) e2) _)
       | Just _ <- unApplyAt f = EApp e1 e2
     go (ELam (x,s) e)         = ELam (x, Vis.mapSort go' s) e
+    go (PExist bs e)          = PExist (map (second (Vis.mapSort go')) bs) e
     go e                      = e
 
     go' (FApp (FApp fs t1) t2) | fs == funcSort
@@ -1048,6 +1194,12 @@
     negSort (FApp s1 s2)     = negSort s1 `S.union` negSort s2
     negSort _                = S.empty
 
+-- | Helper for checking let expressions
+checkLet :: Env -> Symbol -> Expr -> Expr -> CheckM Sort
+checkLet f x e1 e2 = do
+  t <- checkExpr f e1
+  checkExpr (addEnv f [(x, t)]) e2
+
 -- | Helper for checking if-then-else expressions
 checkIte :: Env -> Expr -> Expr -> Expr -> CheckM Sort
 checkIte f p e1 e2 = do
@@ -1212,6 +1364,19 @@
 checkRelTy f e Ne t1 t2      = void (unifys f (Just e) [t1] [t2] `withError` errRel e t1 t2)
 checkRelTy _ e _  t1 t2      = unless (t1 == t2) (throwErrorAt $ errRel e t1 t2)
 
+-- | @a ~~ b@ is translated to @(= a b)@ when producing SMTLIB.
+-- But this is only valid if @a@ and @b@ have the same sort in SMTLIB.
+-- It turns out that most types are represented with sort Int, so comparing
+-- values of different types is not rejected in general by SMT solvers.
+--
+-- There are at least two exceptions though. The first of them is the type
+-- Bool, which is represented with the sort Bool. Therefore, @a ~~ b@ is fine
+-- if both arguments have Bool sort, or if neither of them has.
+--
+-- The other exception is functions, which have a function sort in SMTLIB.
+-- But at the moment no @~~@ equalities are produced with function sorts, so
+-- that case isn't considered in this function.
+--
 checkURel :: Expr -> Sort -> Sort -> CheckM ()
 checkURel e s1 s2 = unless (b1 == b2) (throwErrorAt $ errRel e s1 s2)
   where
@@ -1544,3 +1709,14 @@
 
 errBoolSort :: Expr -> Sort -> String
 errBoolSort     e s  = printf "Expressions %s should have bool sort, but has %s" (showpp e) (showpp s)
+
+globalEnv :: Cfg.Config -> F.GInfo c a -> SEnv Sort
+globalEnv cfg finfo = F.gLits finfo <> dataEnv
+  where
+    dataEnv = F.tsSort <$> theoryEnv cfg finfo
+
+theoryEnv :: Cfg.Config -> F.GInfo c a -> F.SEnv F.TheorySymbol
+theoryEnv cfg si
+  =  Thy.theorySymbols (Cfg.solver cfg)
+  <> Thy.theorySymbols (F.defns si)
+  <> Thy.theorySymbols (F.ddecls si)
diff --git a/src/Language/Fixpoint/Types.hs b/src/Language/Fixpoint/Types.hs
--- a/src/Language/Fixpoint/Types.hs
+++ b/src/Language/Fixpoint/Types.hs
@@ -5,6 +5,7 @@
 
 module Language.Fixpoint.Types (module X) where
 
+import Language.Fixpoint.Types.SMTPrint      as X
 import Language.Fixpoint.Types.PrettyPrint      as X
 import Language.Fixpoint.Types.Names            as X
 import Language.Fixpoint.Types.Errors           as X
diff --git a/src/Language/Fixpoint/Types/Config.hs b/src/Language/Fixpoint/Types/Config.hs
--- a/src/Language/Fixpoint/Types/Config.hs
+++ b/src/Language/Fixpoint/Types/Config.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE UndecidableInstances      #-}
 {-# LANGUAGE DeriveGeneric             #-}
+{-# LANGUAGE TemplateHaskell           #-}
 
 module Language.Fixpoint.Types.Config (
     Config  (..)
@@ -13,7 +14,7 @@
 
   -- * SMT Solver options
   , SMTSolver (..)
-  , solverFlags
+  , solverFlags, mkElabFlags
   , ElabFlags (..)
 
   -- REST Options
@@ -45,7 +46,9 @@
 
 import qualified Language.Fixpoint.Conditional.Z3 as Conditional.Z3
 import Language.Fixpoint.Utils.Files
-
+import Development.GitRev (gitHash)
+import Data.Version (showVersion)
+import Paths_liquid_fixpoint (version)
 
 --------------------------------------------------------------------------------
 withPragmas :: Config -> [String] -> IO Config
@@ -75,7 +78,6 @@
   , maxPartSize :: Int                 -- ^ Maximum size of a partition. Overrides minPartSize
   , solver      :: SMTSolver           -- ^ which SMT solver to use
   , linear      :: Bool                -- ^ not interpret div and mul in SMT
-  , stringTheory :: Bool               -- ^ interpretation of string theory by SMT
   , defunction  :: Bool                -- ^ defunctionalize (use 'apply' for all uninterpreted applications)
   , allowHO     :: Bool                -- ^ allow higher order binders in the logic environment
   , allowHOqs   :: Bool                -- ^ allow higher order qualifiers
@@ -94,30 +96,27 @@
   , minimizeKs  :: Bool                -- ^ min .fq by delta debug (sat with min kvars)
   , minimalSol  :: Bool                -- ^ shrink final solution by pruning redundant qualfiers from fixpoint
   , etaElim     :: Bool                -- ^ eta eliminate function definitions
-  , gradual     :: Bool                -- ^ solve "gradual" constraints
-  , ginteractive :: Bool                -- ^ interactive gradual solving
   , autoKuts         :: Bool           -- ^ ignore given kut variables
   , nonLinCuts       :: Bool           -- ^ Treat non-linear vars as cuts
   , noslice          :: Bool           -- ^ Disable non-concrete KVar slicing
   , rewriteAxioms    :: Bool           -- ^ Allow axiom instantiation via rewriting
-  , pleWithUndecidedGuards :: Bool     -- ^ Unfold invocations with undecided guards in PLE
+  , pleUndecGuards   :: Bool           -- ^ Unfold invocations with undecided guards in PLE
   , etabeta          :: Bool           -- ^ Eta expand and beta reduce terms to aid PLE
   , localRewrites    :: Bool           -- ^ Eta expand and beta reduce terms to aid PLE
   , interpreter      :: Bool           -- ^ Do not use the interpreter to assist PLE
-  , oldPLE           :: Bool           -- ^ Use old version of PLE
-  , noIncrPle        :: Bool           -- ^ Use incremental PLE
-  , noEnvironmentReduction :: Bool     -- ^ Don't use environment reduction
-  , inlineANFBindings :: Bool          -- ^ Inline ANF bindings.
+  , noEnvReduction   :: Bool     -- ^ Don't use environment reduction
+  , inlineANFBinds   :: Bool          -- ^ Inline ANF bindings.
                                        -- Sometimes improves performance and sometimes worsens it.
   , checkCstr        :: [Integer]      -- ^ Only check these specific constraints
   , extensionality   :: Bool           -- ^ Enable extensional interpretation of function equality
-  , rwTerminationCheck  :: Bool        -- ^ Enable termination checking for rewriting
+  , rwTermination    :: Bool        -- ^ Enable termination checking for rewriting
   , stdin               :: Bool        -- ^ Read input query from stdin
   , json                :: Bool        -- ^ Render output in JSON format
-  , noLazyPLE           :: Bool
   , fuel                :: Maybe Int   -- ^ Maximum PLE "fuel" (unfold depth) (default=infinite)
   , restOrdering        :: String      -- ^ Term ordering for use in REST
   , noSmtHorn           :: Bool        -- ^ Do not use (new) SMTLIB horn parser
+  , noStringTheory :: Bool             -- ^ disable interpretation of string theory by SMT
+  , explicitKvars  :: Bool             -- ^ use explicitly declared kvars (horn style) which disables several "defensive simplifications"
   } deriving (Eq,Data,Typeable,Show,Generic)
 
 instance Default Config where
@@ -151,13 +150,18 @@
 data SMTSolver = Z3 | Z3mem | Cvc4 | Cvc5 | Mathsat
                  deriving (Eq, Data, Typeable, Generic)
 
-newtype ElabFlags = ElabFlags { elabSetBag :: Bool }
+data ElabFlags = ElabFlags { elabSetBag :: Bool, elabExplicitKvars :: Bool }
 
-solverFlags :: SMTSolver -> ElabFlags
-solverFlags Z3    = ElabFlags True
-solverFlags Z3mem = ElabFlags True
-solverFlags _     = ElabFlags False
+mkElabFlags :: SMTSolver -> Bool -> ElabFlags
+mkElabFlags slv expKvars = ElabFlags (setBag slv) expKvars
+  where
+    setBag Z3    = True
+    setBag Z3mem = True
+    setBag _     = False
 
+solverFlags :: Config -> ElabFlags
+solverFlags cfg = mkElabFlags (solver cfg) (explicitKvars cfg)
+
 instance Default SMTSolver where
   def = if Conditional.Z3.builtWithZ3AsALibrary then Z3mem else Z3
 
@@ -172,9 +176,9 @@
 
 ---------------------------------------------------------------------------------------
 -- | `Scrape` describes which (Horn) constraints to scrape qualifiers from
---   None = do not scrape, only use the supplied qualifiers
+--   No   = do not scrape, only use the supplied qualifiers
 --   Head = scrape only from the constraint heads (i.e. "rhs")
---   All  = scrape all concrete predicates (i.e. "rhs" + "lhs")
+--   Both = scrape all concrete predicates (i.e. "rhs" + "lhs")
 
 data Scrape = No | Head | Both
   deriving (Eq, Data, Typeable, Generic)
@@ -234,7 +238,7 @@
   , defunction               = False   &= help "Allow higher order binders into fixpoint environment"
   , solver                   = def     &= help "Name of SMT Solver"
   , linear                   = False   &= help "Use uninterpreted integer multiplication and division"
-  , stringTheory             = False   &= help "Interpretation of String Theory by SMT"
+  , noStringTheory           = False   &= help "Disable use of string theory by SMT"
   , allowHO                  = False   &= help "Allow higher order binders into fixpoint environment"
   , allowHOqs                = False   &= help "Allow higher order qualifiers"
   , eliminate                = None    &= help "Eliminate KVars [none = quals for all-kvars, cuts = quals for cut-kvars, all = eliminate all-kvars (TRUE for cuts)]"
@@ -255,13 +259,11 @@
   , minimizeQs               = False &= help "Delta debug to minimize fq file (sat with min qualifiers)"
   , minimizeKs               = False &= help "Delta debug to minimize fq file (sat with max kvars replaced by True)"
   , minimalSol               = False &= help "Shrink fixpoint by removing implied qualifiers"
-  , gradual                  = False &= help "Solve gradual-refinement typing constraints"
-  , ginteractive             = False &= help "Interactive Gradual Solving"
   , autoKuts                 = False &= help "Ignore given Kut vars, compute from scratch"
   , nonLinCuts               = False &= help "Treat non-linear kvars as cuts"
   , noslice                  = False &= help "Disable non-concrete KVar slicing"
   , rewriteAxioms            = False &= name "ple" &= help "Allow axiom instantiation via rewriting (PLE)"
-  , pleWithUndecidedGuards   =
+  , pleUndecGuards   =
       False
         &= name "ple-with-undecided-guards"
         &= help "Unfold invocations with undecided guards in PLE"
@@ -270,44 +272,36 @@
       False
         &= name "interpreter"
         &= help "Use the interpreter to assist PLE"
-  , oldPLE                   = False &= help "Use old version of PLE"
   , etabeta                  = False &= help "Use eta expansion and beta reduction to aid PLE"
-  , localRewrites            = False &= name "local-rewrites" &= help "Perform local rewrites inside PLE"
-  , noIncrPle                = False &= help "Don't use incremental PLE"
-  , noEnvironmentReduction   =
-      False
-        &= name "no-environment-reduction"
-        &= help "Don't perform environment reduction"
-  , inlineANFBindings        =
-      False
-        &= name "inline-anf-bindings"
-        &= help (unwords
+  , localRewrites            = False &= help "Perform local rewrites inside PLE"
+  , noEnvReduction           = False &= help "Don't perform environment reduction"
+  , inlineANFBinds           = False &= help (unwords
           [ "Inline ANF bindings."
           , "Sometimes improves performance and sometimes worsens it."
-          , "Disabled by --no-environment-reduction"
+          , "Disabled by --noenvreduction"
           ])
   , checkCstr                = []    &= help "Only check these specific constraint-ids"
   , extensionality           = False &= help "Allow extensional interpretation of extensionality"
-  , rwTerminationCheck       = False   &= help "Enable rewrite divergence checker"
+  , rwTermination       = False   &= help "Enable rewrite divergence checker"
   , stdin                    = False   &= help "Read input query from stdin"
   , json                     = False   &= help "Render result in JSON"
-  , noLazyPLE                = False   &= help "Don't use lazy PLE"
   , fuel                     = Nothing &= help "Maximum fuel (per-function unfoldings) for PLE"
-  , restOrdering             = "rpo"
-        &= name "rest-ordering"
-        &= help "Ordering Constraint Algebra to use for REST"
+  , restOrdering             = "rpo"   &= help "Ordering Constraint Algebra to use for REST"
   , noSmtHorn                = False &= help "Do not use SMTLIB horn format"
+  , explicitKvars            = False &= help "Use explicitly declared kvars (horn style) which disables several defensive simplifications"
   }
   &= verbosity
   &= program "fixpoint"
   &= help    "Predicate Abstraction Based Horn-Clause Solver"
-  &= summary "fixpoint Copyright 2009-15 Regents of the University of California."
+  &= summary summaryInfo
   &= details [ "Predicate Abstraction Based Horn-Clause Solver"
              , ""
              , "To check a file foo.fq type:"
              , "  fixpoint foo.fq"
              ]
 
+summaryInfo :: String
+summaryInfo = "fixpoint " ++ showVersion version ++ " " ++ "("  ++ $(gitHash) ++ ")"
 config :: Mode (CmdArgs Config)
 config = cmdArgsMode defConfig
 
@@ -318,7 +312,7 @@
   return md
 
 banner :: String
-banner =  "\n\nLiquid-Fixpoint Copyright 2013-21 Regents of the University of California.\n"
+banner =  "\n\nLiquid-Fixpoint Copyright 2009-25 Regents of the University of California.\n"
        ++ "All Rights Reserved.\n"
 
 restOC :: Config -> RESTOrdering
diff --git a/src/Language/Fixpoint/Types/Constraints.hs b/src/Language/Fixpoint/Types/Constraints.hs
--- a/src/Language/Fixpoint/Types/Constraints.hs
+++ b/src/Language/Fixpoint/Types/Constraints.hs
@@ -13,6 +13,7 @@
 {-# LANGUAGE PatternGuards              #-}
 
 {-# OPTIONS_GHC -Wno-name-shadowing     #-}
+{-# LANGUAGE RecordWildCards #-}
 
 -- | This module contains the top-level QUERY data types and elements,
 --   including (Horn) implication & well-formedness constraints and sets.
@@ -22,18 +23,18 @@
     FInfo, SInfo, GInfo (..), FInfoWithOpts(..)
   , convertFormat
   , sinfoToFInfo
-  , Solver
 
    -- * Serializing
   , toFixpoint
   , writeFInfo
   , saveQuery
+  , saveSInfo
 
    -- * Constructing Queries
   , fi
 
   -- * Constraints
-  , WfC (..), isGWfc, updateWfCExpr
+  , WfC (..)
   , SubC, SubcId
   , mkSubC, subcId, sid, senv, updateSEnv, slhs, srhs, stag, subC, wfC
   , SimpC (..)
@@ -47,7 +48,6 @@
   , addIds
   , sinfo
   , shiftVV
-  , gwInfo, GWInfo (..)
 
   -- * Qualifiers
   , Qualifier
@@ -63,8 +63,9 @@
 
   -- * Results
   , FixSolution
-  , GFixSolution, toGFixSol
-  , Result (..)
+  , FixDelayedSolution
+  , Delayed (..)
+  , Result (..), ResultSorts
   , unsafe, isUnsafe, isSafe ,safe
 
   -- * Cut KVars
@@ -80,6 +81,7 @@
   -- * Axioms
   , AxiomEnv (..)
   , Equation
+  , DefinedFuns (..)
   , EquationV (..)
   , mkEquation
   , Rewrite  (..)
@@ -90,11 +92,13 @@
   , lookupRewrite
   , lookupLocalRewrites
   , insertRewrites
+  , eqnToHornSMT
 
   -- * Misc  [should be elsewhere but here due to dependencies]
   , substVars
   , sortVars
   , gSorts
+
   ) where
 
 import qualified Data.Store as S
@@ -109,6 +113,7 @@
 import           Control.DeepSeq
 import           Control.Monad             (when, void)
 import           Language.Fixpoint.Types.PrettyPrint
+import           Language.Fixpoint.Types.SMTPrint
 import qualified Language.Fixpoint.Types.Config as C
 import           Language.Fixpoint.Types.Triggers
 import           Language.Fixpoint.Types.Names
@@ -142,38 +147,8 @@
                     , wrft  :: (Symbol, Sort, KVar)
                     , winfo :: !a
                     }
-             | GWfC { wenv  :: !IBindEnv
-                    , wrft  :: !(Symbol, Sort, KVar)
-                    , winfo :: !a
-                    , wexpr :: !Expr
-                    , wloc  :: !GradInfo
-                    }
               deriving (Eq, Generic, Functor)
 
-data GWInfo = GWInfo { gsym  :: Symbol
-                     , gsort :: Sort
-                     , gexpr :: Expr
-                     , ginfo :: GradInfo
-                     }
-              deriving (Eq, Generic)
-
-gwInfo :: WfC a -> GWInfo
-gwInfo (GWfC _ (x,s,_) _ e i)
-  = GWInfo x s e i
-gwInfo _
-  = errorstar "gwInfo"
-
-updateWfCExpr :: (Expr -> Expr) -> WfC a -> WfC a
-updateWfCExpr _ w@WfC{}  = w
-updateWfCExpr f w@GWfC{} = w{wexpr = f (wexpr w)}
-
-isGWfc :: WfC a -> Bool
-isGWfc GWfC{} = True
-isGWfc WfC{}  = False
-
-instance HasGradual (WfC a) where
-  isGradual = isGWfc
-
 type SubcId = Integer
 
 data SubC a = SubC
@@ -199,7 +174,7 @@
 instance Loc a => Loc (SimpC a) where
   srcSpan = srcSpan . _cinfo
 
-strengthenHyp :: SInfo a -> [(Integer, Expr)] -> SInfo a
+strengthenHyp :: SInfo a -> [(Integer, Expr)] -> BindEnv a
 strengthenHyp si ies = strengthenBinds si bindExprs
   where
     bindExprs        = safeFromList "strengthenHyp" [ (subcBind si i, e) | (i, e) <- ies ]
@@ -212,8 +187,8 @@
   = errorstar $ "Unknown subcId in subcBind: " ++ show i
 
 
-strengthenBinds :: SInfo a -> M.HashMap BindId Expr -> SInfo a
-strengthenBinds si m = si { bs = mapBindEnv f (bs si) }
+strengthenBinds :: SInfo a -> M.HashMap BindId Expr -> BindEnv a
+strengthenBinds si m = mapBindEnv f (bs si)
   where
     f i (x, sr, l)   = case M.lookup i m of
                          Nothing -> (x, sr, l)
@@ -272,37 +247,87 @@
 -- | Solutions and Results
 ---------------------------------------------------------------------------
 
-type GFixSolution = GFixSol Expr
+-- | Since some solutions are expensive to compute, we wrap them in a
+-- "Delayed" type to compute them only if needed.
+{- HLINT ignore Delayed "Use newtype instead of data" -}
+data Delayed a = Delayed
+  { forceDelayed  :: a
+  }
+  deriving (Generic, Show, Functor)
 
-type FixSolution  = M.HashMap KVar Expr
+instance (NFData a) => NFData (Delayed a)
 
-newtype GFixSol e = GSol (M.HashMap KVar (e, [e]))
-  deriving (Generic, Semigroup, Monoid, Functor)
 
-toGFixSol :: M.HashMap KVar (e, [e]) -> GFixSol e
-toGFixSol = GSol
-
+type FixSolution  = M.HashMap KVar Expr
+type FixDelayedSolution  = M.HashMap KVar (Delayed Expr)
 
 data Result a = Result
   { resStatus    :: !(FixResult a)
   , resSolution  :: !FixSolution
-  , resNonCutsSolution :: !FixSolution
-  , gresSolution :: !GFixSolution
+  , resNonCutsSolution :: !FixDelayedSolution
+  , resSorts     :: !ResultSorts
   }
   deriving (Generic, Show, Functor)
 
+type ResultSorts = M.HashMap KVar [(Symbol, Sort)]
 
+data ScopedResult = MkScopedResult
+  { scCuts    :: KVarMap ScopedExpr
+  , scNonCuts :: KVarMap ScopedExpr
+  }
+  deriving (Generic, Show)
 
+newtype KVarMap a = MkKVarMap { unKVarMap :: M.HashMap KVar a }
+  deriving (Generic, Show)
+
+newtype KVarBind a = MkKVarBind { unKVarBind :: (KVar, a) }
+  deriving (Generic, Show)
+data ScopedExpr = MkScopedExpr
+  { seParams :: [(Symbol, Sort)]
+  , seBody :: !Expr
+  }
+  deriving (Generic, Show)
+
+instance ToHornSMT ScopedExpr where
+  toHornSMT (MkScopedExpr xts p) = toHornWithBinders "lambda" xts p
+
+
+scopedResult :: Result a -> ScopedResult
+scopedResult res = MkScopedResult cuts  nonCuts
+  where
+    cuts = scoped $ resSolution res
+    nonCuts = scoped $ M.map forceDelayed $ resNonCutsSolution res
+    scoped sol = MkKVarMap $ M.fromList [ (k, MkScopedExpr (scope k) e) | (k, e) <- M.toList sol]
+    scope k = M.lookupDefault [] k $ resSorts res
+
 instance ToJSON a => ToJSON (Result a) where
-  toJSON = toJSON . resStatus
+  toJSON r@(Result {..}) = object
+    [ "status"            .= resStatus
+    , "solution"          .= scCuts scopedSolution
+    , "nonCutsSolution"   .= scNonCuts scopedSolution
+    ]
+    where
+      scopedSolution = scopedResult r
 
+instance ToJSON a => ToJSON (KVarBind a) where
+  toJSON (MkKVarBind (k, v)) = object
+    [ "kvar" .= k
+    , "val"  .= v
+    ]
+
+instance ToJSON a => ToJSON (KVarMap a) where
+  toJSON = toJSON . map MkKVarBind . M.toList . unKVarMap
+
+instance ToJSON ScopedExpr where
+  toJSON = toJSON . render . toHornSMT
+
 instance Semigroup (Result a) where
-  r1 <> r2  = Result stat soln nonCutsSoln gsoln
+  r1 <> r2  = Result stat soln nonCutsSoln sorts
     where
       stat  = resStatus r1    <> resStatus r2
       soln  = resSolution r1  <> resSolution r2
       nonCutsSoln = resNonCutsSolution r1 <> resNonCutsSolution r2
-      gsoln = gresSolution r1 <> gresSolution r2
+      sorts = M.unionWith L.union (resSorts r1) (resSorts r2)
 
 instance Monoid (Result a) where
   mempty        = Result mempty mempty mempty mempty
@@ -370,7 +395,6 @@
               -- NOTE: this next line is printed this way for compatability with the OCAML solver
               $+$ text "reft" <+> toFix (RR t (Reft (v, PKVar k mempty)))
               $+$ toFixMeta (text "wf") (toFix (winfo w))
-              $+$ if isGWfc w then toFixMeta (text "expr") (toFix (wexpr w)) else mempty
           (v, t, k) = wrft w
 
 toFixMeta :: Doc -> Doc -> Doc
@@ -380,31 +404,12 @@
 pprId (Just i)  = "id" <+> tshow i
 pprId _         = ""
 
-instance PPrint GFixSolution where
-  pprintTidy k (GSol xs) = vcat $ punctuate "\n\n" (pprintTidyGradual k <$> M.toList xs)
-
-pprintTidyGradual :: Tidy -> (KVar, (Expr, [Expr])) -> Doc
-pprintTidyGradual _ (x, (e, es)) = ppLocOfKVar x <+> text ":=" <+> (ppNonTauto " && " e <-> pprint es)
-
-ppLocOfKVar :: KVar -> Doc
-ppLocOfKVar = text. dropWhile (/='(') . symbolString .kv
-
-ppNonTauto :: Doc -> Expr -> Doc
-ppNonTauto d e
-  | isTautoPred e = mempty
-  | otherwise     = pprint e <-> d
-
-instance Show   GFixSolution where
-  show = showpp
-
 ----------------------------------------------------------------
 instance S.Store QualPattern
 instance S.Store QualParam
 instance S.Store Qualifier
 instance S.Store Kuts
 instance S.Store HOInfo
-instance S.Store GWInfo
-instance S.Store GFixSolution
 instance (S.Store a) => S.Store (SubC a)
 instance (S.Store a) => S.Store (WfC a)
 instance (S.Store a) => S.Store (SimpC a)
@@ -415,8 +420,6 @@
 instance NFData v => NFData (QualifierV v)
 instance NFData Kuts
 instance NFData HOInfo
-instance NFData GFixSolution
-instance NFData GWInfo
 
 instance (NFData a) => NFData (SubC a)
 instance (NFData a) => NFData (WfC a)
@@ -443,22 +446,16 @@
                  -- NV TO RJ This tests fails with [LT:=GHC.Types.LT][EQ:=GHC.Types.EQ][GT:=GHC.Types.GT]]
                  -- NV TO RJ looks like a resolution issue
                 then [WfC be (v, sr_sort sr, k) x      | k         <- ks ]
-                  ++ [GWfC be (v, sr_sort sr, k) x e i | (k, e, i) <- gs ]
                 else errorstar msg
   where
-    msg             = "wfKvar: malformed wfC " ++ show sr ++ "\n" ++ show (sus ++ gsus)
+    msg             = "wfKvar: malformed wfC " ++ show sr ++ "\n" ++ show sus
     Reft (v, ras)   = sr_reft sr
     (ks, sus)       = unzip $ go ras
-    (gs, gsus)      = unzip $ go' ras
 
     go (PKVar k su) = [(k, su)]
     go (PAnd es)    = [(k, su) | PKVar k su <- es]
     go _            = []
 
-    go' (PGrad k su i e) = [((k, e, i), su)]
-    go' (PAnd es)      = concatMap go' es
-    go' _              = []
-
 mkSubC :: IBindEnv -> SortedReft -> SortedReft -> Maybe Integer -> Tag -> a -> SubC a
 mkSubC = SubC
 
@@ -508,6 +505,10 @@
   }
   deriving (Eq, Ord, Show, Data, Typeable, Generic)
 
+instance ToHornSMT QualParam where
+  toHornSMT qp = toHornSMT (qpSym qp, qpSort qp)
+
+
 data QualPattern
   = PatNone                 -- ^ match everything
   | PatPrefix !Symbol !Int  -- ^ str . $i  i.e. match prefix 'str' with suffix bound to $i
@@ -526,6 +527,11 @@
 instance ToJSON   Rewrite     where
 instance FromJSON Rewrite     where
 
+instance ToHornSMT Qualifier where
+  toHornSMT (Q n qps p _) =  toHornWithBinders name xts p
+    where
+      name = "qualif" <+> pprint n
+      xts =  [(qpSym qp, qpSort qp) | qp <- qps]
 
 trueQual :: Qualifier
 trueQual = Q (symbol ("QTrue" :: String)) [] PTrue (dummyPos "trueQual")
@@ -564,11 +570,15 @@
 instance Fixpoint Qualifier where
   toFix = pprQual
 
-instance PPrint (QualifierV v) where
-  pprintTidy k q = "qualif" <+> pprintTidy k (qName q) <+> "defined at" <+> pprintTidy k (qPos q)
+instance (Ord v, Fixpoint v, PPrint v) => PPrint (QualifierV v) where
+  pprintTidy k q =
+    "qualif" <+> pprintTidy k (qName q) <+>
+     parens (hsep $ punctuate comma (pprintTidy k <$> qParams q)) <+>
+     braces (pprintTidy k (qBody q)) <+> "//defined at" <+> pprintTidy k (qPos q)
 
+
 pprQual :: Qualifier -> Doc
-pprQual (Q n xts p l) = text "qualif" <+> text (symbolString n) <-> parens args <-> colon <+> parens (toFix p) <+> text "//" <+> toFix l
+pprQual (Q n xts p l) = text "qualif" <+> text (symbolString n) <-> parens args <-> braces (toFix p) <+> text "//" <+> toFix l
   where
     args              = intersperse comma (toFix <$> xts)
 
@@ -681,12 +691,11 @@
    -> [Triggered Expr]
    -> AxiomEnv
    -> [DataDecl]
-   -> [BindId]
    -> GInfo SubC a
-fi cs ws binds ls ds ks qs bi aHO aHOq es axe adts ebs
+fi cs ws binds ls ds ks qs bi aHO aHOq es axe adts
   = FI { cm       = M.fromList $ addIds cs
        , ws       = M.fromListWith err [(k, w) | w <- ws, let (_, _, k) = wrft w]
-       , bs       = foldr (adjustBindEnv stripReft) binds ebs
+       , bs       = binds
        , gLits    = ls
        , dLits    = ds
        , kuts     = ks
@@ -696,14 +705,12 @@
        , asserts  = es
        , ae       = axe
        , ddecls   = adts
-       , ebinds   = ebs
        , lrws     = mempty
        , defns    = mempty
        }
   where
     --TODO handle duplicates gracefully instead (merge envs by intersect?)
     err = errorstar "multiple WfCs with same kvar"
-    stripReft (sym, reft) = (sym, reft { sr_reft = trueReft })
 
 ------------------------------------------------------------------------
 -- | Top-level Queries
@@ -733,8 +740,7 @@
 data GInfo c a = FI
   { cm       :: !(M.HashMap SubcId (c a))  -- ^ cst id |-> Horn Constraint
   , ws       :: !(M.HashMap KVar (WfC a))  -- ^ Kvar  |-> WfC defining its scope/args
-  , bs       :: !(BindEnv a)               -- ^ Bind  |-> (Symbol, SortedReft)
-  , ebinds   :: ![BindId]                  -- ^ Subset of existential binders
+  , bs       :: !(BindEnv a)               -- ^ BindId  |-> (Symbol, SortedReft)
   , gLits    :: !(SEnv Sort)               -- ^ Global Constant symbols
   , dLits    :: !(SEnv Sort)               -- ^ Distinct Constant symbols
   , kuts     :: !Kuts                      -- ^ Set of KVars *not* to eliminate
@@ -745,13 +751,10 @@
   , asserts  :: ![Triggered Expr]          -- ^ TODO: what is this?
   , ae       :: AxiomEnv                   -- ^ Information about reflected function defs
   , lrws     :: LocalRewritesEnv           -- ^ Local rewrites
-  , defns    :: ![Equation]                -- ^ `define_fun` definitions to be passed to SMT
+  , defns    :: DefinedFuns                -- ^ `define_fun` definitions to be passed to SMT
   }
   deriving (Eq, Show, Functor, Generic)
 
-instance HasGradual (GInfo c a) where
-  isGradual info = any isGradual (M.elems $ ws info)
-
 instance Semigroup HOInfo where
   i1 <> i2 = HOI { hoBinds = hoBinds i1 || hoBinds i2
                  , hoQuals = hoQuals i1 || hoQuals i2
@@ -764,7 +767,6 @@
   i1 <> i2 = FI { cm       = cm i1       <> cm i2
                 , ws       = ws i1       <> ws i2
                 , bs       = bs i1       <> bs i2
-                , ebinds   = ebinds i1   <> ebinds i2
                 , gLits    = gLits i1    <> gLits i2
                 , dLits    = dLits i1    <> dLits i2
                 , kuts     = kuts i1     <> kuts i2
@@ -783,7 +785,6 @@
   mempty        = FI { cm       = M.empty
                      , ws       = mempty
                      , bs       = mempty
-                     , ebinds   = mempty
                      , gLits    = mempty
                      , dLits    = mempty
                      , kuts     = mempty
@@ -830,9 +831,7 @@
     kutsDoc       = toFix    . kuts
     -- packsDoc      = toFix    . packs
     declsDoc      = vcat     . map ((text "data" <+>) . toFix) . L.sort . ddecls
-    (ubs, ebs)    = splitByQuantifiers (bs x') (ebinds x')
-    bindsDoc      = toFix    ubs
-               $++$ toFix    ebs
+    bindsDoc      = toFix (bs x')
     qualsDoc      = vcat     . map toFix . L.sort . quals
     aeDoc         = toFix    . ae
     lrwsDoc       = toFix    . lrws
@@ -909,11 +908,6 @@
   where
     (b, sr, _) = lookupBindEnv (cbind s) env
 
----------------------------------------------------------------------------
--- | Top level Solvers ----------------------------------------------------
----------------------------------------------------------------------------
-type Solver a = C.Config -> FInfo a -> IO (Result (Integer, a))
-
 --------------------------------------------------------------------------------
 saveQuery :: (Fixpoint a) => C.Config -> FInfo a -> IO ()
 --------------------------------------------------------------------------------
@@ -936,6 +930,26 @@
   ensurePath fq
   T.writeFile fq $ T.pack $ render (toFixpoint cfg fi)
 
+-- | Used for debugging to inspect intermediate 'SInfo' files.
+--
+-- Takes a suffix to put in the name of the written file, whose name
+-- is still derived from the input file name in `cfg`.
+--
+-- Usage example:
+--
+-- > when (save cfg) $
+-- >   saveSInfo cfg ".sinfo" si
+--
+-- This will write a file like `.liquid/Test.hs.sinfo.fq` when the
+-- `--save` flag is used.
+--
+saveSInfo :: Fixpoint a => C.Config -> String -> SInfo a -> IO ()
+saveSInfo cfg sfx si = do
+  let fq = Files.tempFileName (C.srcFile cfg ++ sfx ++ ".fq")
+  putStrLn $ "Saving Text Query: "   ++ fq ++ "\n"
+  ensurePath fq
+  T.writeFile fq $ T.pack $ render (toFixpoint cfg si)
+
 ---------------------------------------------------------------------------
 -- | Axiom Instantiation Information --------------------------------------
 ---------------------------------------------------------------------------
@@ -966,10 +980,12 @@
 instance S.Store AxiomEnv
 instance S.Store Rewrite
 instance S.Store Equation
+instance S.Store DefinedFuns
 instance NFData AutoRewrite
 instance NFData AxiomEnv
 instance NFData Rewrite
 instance NFData Equation
+instance NFData DefinedFuns
 
 dedupAutoRewrites :: M.HashMap SubcId [AutoRewrite] -> [AutoRewrite]
 dedupAutoRewrites = Set.toList . Set.unions . map Set.fromList . M.elems
@@ -989,6 +1005,19 @@
 instance PPrint AxiomEnv where
   pprintTidy _ = text . show
 
+
+newtype DefinedFuns = MkDefinedFuns [Equation]
+  deriving (Data, Eq, Ord, Show, Generic)
+
+instance Semigroup DefinedFuns where
+  MkDefinedFuns eq1 <> MkDefinedFuns eq2 = MkDefinedFuns (eq1 <> eq2)
+
+instance Monoid DefinedFuns where
+  mempty = MkDefinedFuns []
+
+instance PPrint DefinedFuns where
+  pprintTidy k (MkDefinedFuns eqs) = pprintTidy k eqs
+
 type Equation = EquationV Symbol
 data EquationV v = Equ
   { eqName :: !Symbol           -- ^ name of reflected function
@@ -999,6 +1028,10 @@
   }
   deriving (Data, Eq, Ord, Show, Generic, Functor)
 
+eqnToHornSMT :: Doc -> Equation -> Doc
+eqnToHornSMT keyword (Equ f xs e s _) = parens (keyword <+> pprint f <+> toHornSMT xs <+> toHornSMT s <+> toHornSMT e)
+
+
 mkEquation :: Symbol -> [(Symbol, Sort)] -> Expr -> Sort -> Equation
 mkEquation f xts e out = Equ f xts e out (f `elem` syms e)
 
@@ -1062,6 +1095,11 @@
   }
   deriving (Data, Eq, Ord, Show, Generic)
 
+instance ToHornSMT Rewrite where
+  toHornSMT (SMeasure f d xs e) =  parens ("match" <+> toHornSMT f <+> toHornSMT (d:xs) <+> toHornSMT e)
+
+
+
 instance Fixpoint AxiomEnv where
   toFix axe = vcat ((toFix <$> L.sort (aenvEqs axe)) ++ (toFix <$> L.sort (aenvSimpl axe)))
               $+$ renderExpand (pairdoc <$> L.sort (M.toList $ aenvExpand axe))
@@ -1086,8 +1124,7 @@
     = text "match"
    <+> toFix f
    <+> toFix d <+> hsep (toFix <$> xs)
-   <+> text " = "
-   <+> parens (toFix e)
+   <+> braces (toFix e)
 
 instance PPrint Rewrite where
   pprintTidy _ = toFix
diff --git a/src/Language/Fixpoint/Types/Environments.hs b/src/Language/Fixpoint/Types/Environments.hs
--- a/src/Language/Fixpoint/Types/Environments.hs
+++ b/src/Language/Fixpoint/Types/Environments.hs
@@ -318,7 +318,7 @@
   mappend = (<>)
 
 envCs :: BindEnv a -> IBindEnv -> [(Symbol, SortedReft)]
-envCs be env = [(x, y) | i <- elemsIBindEnv env, let (x, y, _) = lookupBindEnv i be]
+envCs be (FB s) = [(x, y) | (x, y, _) <- M.elems (M.intersection (beBinds be) (S.toMap s))]
 
 instance Fixpoint IBindEnv where
   toFix (FB ids) = text "env" <+> toFix ids
diff --git a/src/Language/Fixpoint/Types/Errors.hs b/src/Language/Fixpoint/Types/Errors.hs
--- a/src/Language/Fixpoint/Types/Errors.hs
+++ b/src/Language/Fixpoint/Types/Errors.hs
@@ -62,6 +62,7 @@
 -- import           Data.Hashable
 import qualified Data.Store                   as S
 import           GHC.Generics                  (Generic)
+import           GHC.Stack                     (HasCallStack)
 import           Language.Fixpoint.Types.PrettyPrint
 import           Language.Fixpoint.Types.Spans
 import           Language.Fixpoint.Misc
@@ -137,7 +138,7 @@
 err sp d = Error [Error1 sp d]
 
 ---------------------------------------------------------------------
-panic :: String -> a
+panic :: HasCallStack => String -> a
 ---------------------------------------------------------------------
 panic = die . err dummySpan . text . (panicMsg ++)
 
@@ -145,7 +146,7 @@
 panicMsg = "PANIC: Please file an issue at https://github.com/ucsd-progsys/liquid-fixpoint \n"
 
 ---------------------------------------------------------------------
-die :: Error -> a
+die :: HasCallStack => Error -> a
 ---------------------------------------------------------------------
 die = throw
 
diff --git a/src/Language/Fixpoint/Types/Graduals.hs b/src/Language/Fixpoint/Types/Graduals.hs
deleted file mode 100644
--- a/src/Language/Fixpoint/Types/Graduals.hs
+++ /dev/null
@@ -1,259 +0,0 @@
-{-# LANGUAGE CPP                        #-}
-{-# LANGUAGE FlexibleContexts           #-}
-{-# LANGUAGE FlexibleInstances          #-}
-{-# LANGUAGE NoMonomorphismRestriction  #-}
-{-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE UndecidableInstances       #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE GADTs                      #-}
-{-# LANGUAGE TupleSections              #-}
-
-{-# OPTIONS_GHC -Wno-name-shadowing     #-}
-
--- | This module contains the top-level SOLUTION data types,
---   including various indices used for solving.
-
-module Language.Fixpoint.Types.Graduals (
-  uniquify,
-
-  makeSolutions,
-
-  GSol,
-
-  Gradual (..)
-  ) where
-
-import Language.Fixpoint.Types.Refinements
-import Language.Fixpoint.Types.Constraints
-import Language.Fixpoint.Types.Config
-import Language.Fixpoint.Types.PrettyPrint
-import Language.Fixpoint.Types.Environments
-import Language.Fixpoint.Types.Substitutions
-import Language.Fixpoint.Types.Visitor
-import Language.Fixpoint.Types.Spans
-import Language.Fixpoint.Types.Theories
-import Language.Fixpoint.Types.Names        (gradIntSymbol, tidySymbol)
-import Language.Fixpoint.Misc               (allCombinations, errorstar)
-
-import Control.DeepSeq
-
-import qualified Data.HashMap.Strict       as M
-import qualified Data.List                 as L
-
-import Control.Monad.State.Lazy
-import Data.Maybe (fromMaybe)
-import qualified Language.Fixpoint.SortCheck       as So
-import Language.Fixpoint.Solver.Sanitize (symbolEnv)
-
-
-data GSol = GSol !SymEnv !(M.HashMap KVar (Expr, GradInfo))
-
-instance Semigroup GSol where
-  (GSol e1 m1) <> (GSol e2 m2) = GSol (e1 <> e2) (m1 <> m2)
-
-instance Monoid GSol where
-  mempty = GSol mempty mempty
-
-instance Show GSol where
-  show (GSol _ m) = "GSOL = \n" ++ unlines ((\(k,(e, i)) -> showpp k ++ showInfo i ++  " |-> " ++ showpp (tx e)) <$> M.toList m)
-    where
-      tx e = subst (mkSubst $ [(x, EVar $ tidySymbol x) | x <- syms e]) e
-      showInfo i = show i
-
-
-makeSolutions :: (NFData a, Fixpoint a, Show a)
-              => Config -> SInfo a
-              -> [(KVar, (GWInfo, [[Expr]]))]
-              -> Maybe [GSol]
-
-makeSolutions _ _ []
-  = Nothing
-makeSolutions cfg fi kes
-  = Just $ map (GSol env . M.fromList) (allCombinations (go  <$> kes))
-  where
-    go (k, (i, es)) = [(k, (pAnd (gexpr i:e'), ginfo i)) | e' <- es]
-    env = symbolEnv cfg fi
-
-
--------------------------------------------------------------------------------
--- |  Make each gradual appearence unique -------------------------------------
--------------------------------------------------------------------------------
-uniquify :: (NFData a, Fixpoint a, Loc a) => SInfo a -> SInfo a
-
-uniquify fi = fi{cm = cm', ws = ws', bs = bs'}
-  where
-  (cm', km, bs') = uniquifyCS (bs fi) (cm fi)
-  ws'            = expandWF km (ws fi)
-
-uniquifyCS :: (NFData a, Fixpoint a, Loc a)
-           => BindEnv a
-           -> M.HashMap SubcId (SimpC a)
-           -> (M.HashMap SubcId (SimpC a), M.HashMap KVar [(KVar, Maybe SrcSpan)], BindEnv a)
-uniquifyCS bs cs
-  = (x, km, benv st)
-  where
-    (x, st) = runState (uniq cs) (initUniqueST bs)
-    km      = kmap st
-
-
-class Unique ann a where
-   uniq :: a -> UniqueM ann a
-
-instance Unique ann a => Unique ann (M.HashMap SubcId a) where
-  uniq m = M.fromList <$> mapM (\(i,x) -> (i,) <$> uniq x) (M.toList m)
-
-instance Loc a => Unique a (SimpC a) where
-  uniq cs = do
-    updateLoc $ srcSpan $ _cinfo cs
-    rhs <- uniq (_crhs cs)
-    env <- uniq (_cenv cs)
-    return cs{_crhs = rhs, _cenv = env}
-
-instance Unique ann IBindEnv where
-  uniq env = withCache (fromListIBindEnv <$> mapM uniq (elemsIBindEnv env))
-
-instance Unique ann BindId where
-  uniq i = do
-    bs <- benv <$> get
-    let (x, t, ann) = lookupBindEnv i bs
-    resetChange
-    t' <- uniq t
-    hasChanged <- change <$> get
-    if hasChanged
-      then do let (i', bs') = insertBindEnv x t' ann bs
-              updateBEnv i bs'
-              return i'
-      else return i
-
-instance Unique ann SortedReft where
-  uniq (RR s r) = RR s <$> uniq r
-
-instance Unique ann Reft where
-  uniq (Reft (x,e)) = Reft . (x,) <$> uniq e
-
-instance Unique ann Expr where
-  uniq = mapMExpr go
-   where
-    go (PGrad k su i e) = do
-      k'  <- freshK k
-      src <- uloc <$> get
-      return $ PGrad k' su (i{gused = src}) e
-    go e              = return e
-
--------------------------------------------------------------------------------
--- | The Unique Monad ---------------------------------------------------------
--------------------------------------------------------------------------------
-
-type UniqueM ann = State (UniqueST ann)
-data UniqueST a
-  = UniqueST { freshId :: Integer
-             , kmap    :: M.HashMap KVar [(KVar, Maybe SrcSpan)]
-             , change  :: Bool
-             , cache   :: M.HashMap KVar KVar
-             , uloc    :: Maybe SrcSpan
-             , ubs     :: [BindId]
-             , benv    :: BindEnv a
-             }
-
-updateLoc :: SrcSpan -> UniqueM ann ()
-updateLoc x = modify $ \s -> s{uloc = Just x}
-
-withCache :: UniqueM ann a -> UniqueM ann a
-withCache act = do
-  emptyCache
-  a <- act
-  emptyCache
-  return a
-
-emptyCache :: UniqueM ann ()
-emptyCache = modify $ \s -> s{cache = mempty}
-
-addCache :: KVar -> KVar -> UniqueM ann ()
-addCache k k' = modify $ \s -> s{cache = M.insert k k' (cache s)}
-
-updateBEnv :: BindId -> BindEnv a -> UniqueM a ()
-updateBEnv i bs = modify $ \s -> s{benv = bs, ubs = i : ubs s}
-
-setChange :: UniqueM ann ()
-setChange = modify $ \s -> s{change = True}
-
-resetChange :: UniqueM ann ()
-resetChange = modify $ \s -> s{change = False}
-
-initUniqueST :: BindEnv a ->  UniqueST a
-initUniqueST = UniqueST 0 mempty False mempty Nothing mempty
-
-freshK, freshK' :: KVar -> UniqueM ann KVar
-freshK k  = do
-  setChange
-  cached <- cache <$> get
-  case M.lookup k cached of
-    {- OPTIMIZATION: Only create one fresh occurence of ? per constraint environment. -}
-    Just k' -> return  k'
-    Nothing -> freshK' k
-
-freshK' k = do
-  i <- freshId <$> get
-  modify (\s -> s{freshId = i + 1})
-  let k' = KV $ gradIntSymbol i
-  addK k k'
-  addCache k k'
-  return k'
-
-addK :: KVar -> KVar -> UniqueM ann ()
-addK key val =
-  modify (\s -> s{kmap = M.insertWith (++) key [(val, uloc s)] (kmap s)})
-
--------------------------------------------------------------------------------
--- | expandWF -----------------------------------------------------------------
--------------------------------------------------------------------------------
-
-expandWF :: (NFData a, Fixpoint a)
-         => M.HashMap KVar [(KVar, Maybe SrcSpan)]
-         -> M.HashMap KVar (WfC a)
-         -> M.HashMap KVar (WfC a)
-expandWF km ws
-  = M.fromList
-       ([(k, updateKVar k src w) | (i, w) <- gws, (kw, ks) <- km', kw == i, (k, src) <- ks]
-        ++ kws)
-  where
-    (gws, kws)       = L.partition (isGWfc . snd) $ M.toList ws
-    km'              = M.toList km
-
-    updateKVar k src wfc = let wrft' = (\(v,s,_) -> (v,s,k)) $ wrft wfc in
-      case wfc of
-        GWfC{} -> wfc { wrft = wrft', wloc = (wloc wfc){gused = src} }
-        WfC{}  -> wfc { wrft = wrft' }
-
--------------------------------------------------------------------------------
--- |  Substitute Gradual Solution ---------------------------------------------
--------------------------------------------------------------------------------
-
-class Gradual a where
-  gsubst :: ElabFlags -> GSol -> a -> a
-
-instance Gradual Expr where
-  gsubst ef (GSol env m) e   = mapGVars' (\(k, _) -> Just (fromMaybe (err k) (mknew k))) e
-    where
-      mknew k = So.elaborate (So.ElabParam ef "initBGind.mkPred" env) $ fst <$> M.lookup k m
-      err   k = errorstar ("gradual substitution: Cannot find " ++ showpp k)
-
-instance Gradual Reft where
-  gsubst ef su (Reft (x, e)) = Reft (x, gsubst ef su e)
-
-instance Gradual SortedReft where
-  gsubst ef su r = r {sr_reft = gsubst ef su (sr_reft r)}
-
-instance Gradual (SimpC a) where
-  gsubst ef su c = c {_crhs = gsubst ef su (_crhs c)}
-
-instance Gradual (BindEnv a) where
-  gsubst ef su = mapBindEnv (\_ (x, r, l) -> (x, gsubst ef su r, l))
-
-instance Gradual v => Gradual (M.HashMap k v) where
-  gsubst ef su = M.map (gsubst ef su)
-
-instance Gradual (SInfo a) where
-  gsubst ef su fi = fi { bs = gsubst ef su (bs fi)
-                        , cm = gsubst ef su (cm fi)
-                        }
diff --git a/src/Language/Fixpoint/Types/Names.hs b/src/Language/Fixpoint/Types/Names.hs
--- a/src/Language/Fixpoint/Types/Names.hs
+++ b/src/Language/Fixpoint/Types/Names.hs
@@ -72,7 +72,6 @@
   , dummySymbol
   , intSymbol
   , tempSymbol
-  , gradIntSymbol
   , appendSymbolText
   , hvarArgSymbol
 
@@ -81,6 +80,7 @@
   , bindSymbol
   , testSymbol
   , renameSymbol
+  , renameSubstSymbol
   , kArgSymbol
   , existSymbol
   , suffixSymbol
@@ -101,6 +101,7 @@
   , mapConName
   , bagConName
   , arrayConName
+  , ffldConName
   , strConName
   , charConName
   , nilName
@@ -108,8 +109,8 @@
   , vvName
   , sizeName
   , bitVecName
-  -- , bvAndName, bvOrName, bvSubName, bvAddName
   , intbv32Name, intbv64Name, bv32intName, bv64intName
+  , intbv8Name, intbv16Name, bv8intName, bv16intName
   , propConName
 
   -- HKT , tyAppName
@@ -407,9 +408,6 @@
 unconsSym :: Symbol -> Maybe (Char, Symbol)
 unconsSym (symbolText -> s) = second symbol <$> T.uncons s
 
--- singletonSym :: Char -> Symbol -- Yuck
--- singletonSym = (`consSym` "")
-
 lengthSym :: Symbol -> Int
 lengthSym (symbolText -> t) = T.length t
 
@@ -443,7 +441,6 @@
 suffixSymbolText  x y = x <> symSepName <> y
 
 vv                  :: Maybe Integer -> Symbol
--- vv (Just i)         = symbol $ symbolSafeText vvName `T.snoc` symSepName `mappend` T.pack (show i)
 vv (Just i)         = intSymbol vvName i
 vv Nothing          = vvName
 
@@ -454,12 +451,6 @@
 vvCon       = vvName `suffixSymbol` "F"
 dummySymbol = dummyName
 
--- ctorSymbol :: Symbol -> Symbol
--- ctorSymbol s = ctorPrefix `mappendSym` s
-
--- isCtorSymbol :: Symbol -> Bool
--- isCtorSymbol = isPrefixOfSym ctorPrefix
-
 -- | 'testSymbol c' creates the `is-c` symbol for the adt-constructor named 'c'.
 testSymbol :: Symbol -> Symbol
 testSymbol s = testPrefix `mappendSym` s
@@ -485,6 +476,9 @@
 tempSymbol :: Symbol -> Integer -> Symbol
 tempSymbol prefix = intSymbol (tempPrefix `mappendSym` prefix)
 
+renameSubstSymbol :: Symbol -> Int -> Symbol
+renameSubstSymbol prefix = intSymbol (substPrefix `mappendSym` prefix)
+
 renameSymbol :: Symbol -> Int -> Symbol
 renameSymbol prefix = intSymbol (renamePrefix `mappendSym` prefix)
 
@@ -494,9 +488,6 @@
 existSymbol :: Symbol -> Integer -> Symbol
 existSymbol prefix = intSymbol (existPrefix `mappendSym` prefix)
 
-gradIntSymbol :: Integer -> Symbol
-gradIntSymbol = intSymbol gradPrefix
-
 hvarArgSymbol :: Symbol -> Int -> Symbol
 hvarArgSymbol s i = intSymbol (suffixSymbol hvarPrefix s) i
 
@@ -507,20 +498,17 @@
 bindSymbol :: Integer -> Symbol
 bindSymbol = intSymbol bindPrefix
 
-tempPrefix, anfPrefix, renamePrefix, litPrefix, gradPrefix, bindPrefix :: Symbol
+tempPrefix, anfPrefix, renamePrefix, substPrefix, litPrefix, bindPrefix :: Symbol
 tempPrefix   = "lq_tmp$"
 anfPrefix    = "lq_anf$"
 renamePrefix = "lq_rnm$"
+substPrefix = "subst$"
 litPrefix    = "lit$"
-gradPrefix   = "grad$"
 bindPrefix   = "b$"
 
 testPrefix  :: Symbol
 testPrefix   = "is$"
 
--- ctorPrefix  :: Symbol
--- ctorPrefix   = "mk$"
-
 kArgPrefix, existPrefix, hvarPrefix :: Symbol
 kArgPrefix  = "lq_karg$"
 existPrefix = "lq_ext$"
@@ -534,26 +522,42 @@
 unKArgSymbol :: Symbol -> Symbol
 unKArgSymbol = unSuffixSymbol . unSuffixSymbol . unPrefixSymbol kArgPrefix
 
--- | 'tidySymbol' is used to prettify the names of parameters of kvars appearing in solutions.(*)
---   For example, if you have a kvar $k0 with two parameters, you may have a solution that looks like
---       0 <  lq_karg$nnf_arg$##k0##0##k0
+-- | @tidySymbol@ is used to prettify the names of parameters of kvars appearing
+-- in solutions.  For example, if you have a kvar $k0 with two parameters, you
+-- may have a solution that looks like
+--
+-- > 0 <  lq_karg$nnf_arg$##k0##0##k0
+--
 --   where we know it is a kvar-arg because of the
---      - `kArgPrefix` (`lq_arg`)
---      - `hvarArgPrefix` (`nnf_arg`)
---      - `k0` the name of the kvar
---      - `0`  the parameter index
---      - `k0` again (IDK why?!)
---    all of which are separated by `##`
---   So `tidySymbol` tests if indeed it is a `kArgPrefix`-ed symbol and if so converts
---      `lq_karg$nnf_arg$##k0##0##k0` ----> `$k0##0`
+--      - @kArgPrefix@ (@lq_arg@)
+--      - @hvarArgPrefix@ (@nnf_arg@)
+--      - @k0@ the name of the kvar
+--      - @0@  the parameter index
+--      - @k0@ again (IDK why?!)
+--    all of which are separated by @##@
+--
+--   So @tidySymbol@ tests if indeed it is a @kArgPrefix@-ed symbol and if so
+--   converts
+--
+-- > lq_karg$nnf_arg$##k0##0##k0 ----> $k0##0
+--
+--  KArgs from Liquid Haskell come in the form @k_##0@ instead, and parameters
+--  are like @lq_karg$param_name##0##k_##0@. In this case, tidySymbol will
+--  convert
+--
+--  > lq_karg$param_name##0##k_##0  ----> $param_name##0##k_
 
 tidySymbol :: Symbol -> Symbol
 tidySymbol s
   | s == s'   = s
   | otherwise = s''
   where
-    s'        = unPrefixSymbol kArgPrefix s
-    s''       = consSym '$' . unPrefixSymbol symSepName . unSuffixSymbol . unPrefixSymbol hvarPrefix $ s'
+    s' = unPrefixSymbol kArgPrefix s
+    s'' =
+      consSym '$' $
+      unPrefixSymbol symSepName $
+      unSuffixSymbol $
+      unPrefixSymbol hvarPrefix s'
 
 unPrefixSymbol :: Symbol -> Symbol -> Symbol
 unPrefixSymbol p s = fromMaybe s (stripPrefix p s)
@@ -562,10 +566,6 @@
 unSuffixSymbol s@(symbolText -> t)
   = maybe s symbol $ T.stripSuffix symSepName $ fst $ T.breakOnEnd symSepName t
 
--- takeWhileSym :: (Char -> Bool) -> Symbol -> Symbol
--- takeWhileSym p (symbolText -> t) = symbol $ T.takeWhile p t
-
-
 nonSymbol :: Symbol
 nonSymbol = ""
 
@@ -652,48 +652,41 @@
 preludeName  = "Prelude"
 dummyName    = "LIQUID$dummy"
 boolConName  = "Bool"
-boolLConName  = "bool"
+boolLConName = "bool"
 funConName   = "->"
 
 
-listConName, listLConName, propConName, _hpropConName, vvName, setConName, mapConName, bagConName, arrayConName:: Symbol
-listConName  = "[]"
-listLConName = "List"
-setConName   = "Set_Set"
-mapConName   = "Map_t"
-bagConName   = "Bag_t"
-arrayConName = "Array_t"
-vvName       = "VV"
-propConName  = "Prop"
+listConName, listLConName, propConName, _hpropConName, vvName, setConName, mapConName, bagConName, arrayConName, ffldConName :: Symbol
+listConName   = "[]"
+listLConName  = "List"
+setConName    = "Set_Set"
+mapConName    = "Map_t"
+bagConName    = "Bag_t"
+arrayConName  = "Array_t"
+ffldConName   = "FFld_t"
+vvName        = "VV"
+propConName   = "Prop"
 _hpropConName = "HProp"
 
 strConName, charConName :: (IsString a) => a
 strConName   = "Str"
 charConName  = "Char"
--- symSepName   :: Char
--- symSepName   = '#' -- DO NOT EVER CHANGE THIS
 
 symSepName   :: (IsString a) => a
 symSepName   = "##"
 
--- nilName, consName, size32Name, size64Name, bitVecName :: Symbol
--- nilName      = "nil"
--- consName     = "cons"
--- size32Name   = "Size32"
--- size64Name   = "Size64"
--- bitVecName   = "BitVec"
-
--- bvOrName, bvAndName, bvSubName, bvAddName,
 intbv32Name, intbv64Name, bv32intName, bv64intName :: Symbol
--- bvOrName    = "bvor"
--- bvAndName   = "bvand"
--- bvSubName   = "bvsub"
--- bvAddName   = "bvadd"
 intbv32Name = "int_to_bv32"
 intbv64Name = "int_to_bv64"
 bv32intName = "bv32_to_int"
 bv64intName = "bv64_to_int"
 
+intbv8Name, intbv16Name, bv8intName, bv16intName :: Symbol
+intbv8Name  = "int_to_bv8"
+intbv16Name = "int_to_bv16"
+bv8intName  = "bv8_to_int"
+bv16intName = "bv16_to_int"
+
 nilName, consName, sizeName, bitVecName :: Symbol
 nilName       = "nil"
 consName      = "cons"
@@ -717,8 +710,6 @@
   , "List"
   , "[]"
   , "bool"
-  -- , "int"
-  -- , "real"
   , setConName
   , charConName
   , "Set_sng"
diff --git a/src/Language/Fixpoint/Types/Refinements.hs b/src/Language/Fixpoint/Types/Refinements.hs
--- a/src/Language/Fixpoint/Types/Refinements.hs
+++ b/src/Language/Fixpoint/Types/Refinements.hs
@@ -28,7 +28,6 @@
   , Brel (..)
   , ExprV (..), Pred
   , Expr
-  , GradInfo (..)
   , pattern PTrue, pattern PTop, pattern PFalse, pattern EBot
   , pattern ETimes, pattern ERTimes, pattern EDiv, pattern ERDiv
   , pattern EEq
@@ -104,11 +103,6 @@
 
   , debruijnIndex
 
-  -- * Gradual Type Manipulation
-  , pGAnds, pGAnd
-  , HasGradual (..)
-  , srcGradInfo
-
   ) where
 
 import           Prelude hiding ((<>))
@@ -122,6 +116,7 @@
 import           Data.HashSet              (HashSet)
 import qualified Data.HashSet              as HashSet
 import           GHC.Generics              (Generic)
+import           GHC.Stack                 (HasCallStack)
 #if MIN_VERSION_base(4,20,0)
 import           Data.List                 (partition)
 #else
@@ -148,7 +143,6 @@
 
 instance NFData KVar
 instance NFData v => NFData (SubstV v)
-instance NFData GradInfo
 instance NFData Constant
 instance NFData SymConst
 instance NFData Brel
@@ -163,7 +157,6 @@
 
 instance S.Store KVar
 instance S.Store Subst
-instance S.Store GradInfo
 instance S.Store Constant
 instance S.Store SymConst
 instance S.Store Brel
@@ -175,7 +168,6 @@
 instance B.Binary SymConst
 instance B.Binary Constant
 instance B.Binary Bop
-instance B.Binary GradInfo
 instance B.Binary Brel
 instance B.Binary KVar
 instance (Hashable a, Eq a, B.Binary a) => B.Binary (HashSet a) where
@@ -194,54 +186,18 @@
 reftConjuncts (Reft (v, ra)) = [Reft (v, ra') | ra' <- ras']
   where
     ras'                     = if null ps then ks else conj ps : ks  -- see [NOTE:pAnd-SLOW]
-    (ps, ks)                 = partition isConc (refaConjuncts ra)
+    (ps, ks)                 = partition isConc (conjuncts ra)
 
 isConc :: Expr -> Bool
-isConc p = not (isKvar p || isGradual p)
+isConc p = not (isKvar p)
 
 concConjuncts :: Expr -> [Expr]
-concConjuncts e = filter isConc (refaConjuncts e)
+concConjuncts e = filter isConc (conjuncts e)
 
 isKvar :: Expr -> Bool
 isKvar (PKVar _ _) = True
 isKvar _           = False
 
-class HasGradual a where
-  isGradual :: a -> Bool
-  gVars     :: a -> [KVar]
-  gVars _ = []
-  ungrad    :: a -> a
-  ungrad x = x
-
-instance HasGradual Expr where
-  isGradual PGrad{} = True
-  isGradual (PAnd xs)  = any isGradual xs
-  isGradual _          = False
-
-  gVars (PGrad k _ _ _) = [k]
-  gVars (PAnd xs)       = concatMap gVars xs
-  gVars _               = []
-
-  ungrad PGrad{} = PTrue
-  ungrad (PAnd xs)  = PAnd (ungrad <$> xs )
-  ungrad e          = e
-
-
-instance HasGradual Reft where
-  isGradual (Reft (_,r)) = isGradual r
-  gVars (Reft (_,r))     = gVars r
-  ungrad (Reft (x,r))    = Reft(x, ungrad r)
-
-instance HasGradual SortedReft where
-  isGradual = isGradual . sr_reft
-  gVars     = gVars . sr_reft
-  ungrad r  = r {sr_reft = ungrad (sr_reft r)}
-
-refaConjuncts :: Expr -> [Expr]
-refaConjuncts p = [p' | p' <- conjuncts p, not $ isTautoPred p']
-
-
-
 --------------------------------------------------------------------------------
 -- | Kvars ---------------------------------------------------------------------
 --------------------------------------------------------------------------------
@@ -249,6 +205,8 @@
 newtype KVar = KV { kv :: Symbol }
                deriving (Eq, Ord, Data, Typeable, Generic, IsString, ToJSON, FromJSON)
 
+instance ToJSONKey KVar
+
 intKvar :: Integer -> KVar
 intKvar = KV . intSymbol "k_"
 
@@ -260,7 +218,6 @@
 instance Hashable Bop
 instance Hashable SymConst
 instance Hashable Constant
-instance Hashable GradInfo
 instance Hashable v => Hashable (SubstV v)
 instance Hashable v => Hashable (ExprV v)
 instance Hashable v => Hashable (ReftV v)
@@ -337,6 +294,7 @@
           | EApp !(ExprV v) !(ExprV v)
           | ENeg !(ExprV v)
           | EBin !Bop !(ExprV v) !(ExprV v)
+          | ELet !Symbol !(ExprV v) !(ExprV v)
           | EIte !(ExprV v) !(ExprV v) !(ExprV v)
           | ECst !(ExprV v) !Sort
           | ELam !(Symbol, Sort)   !(ExprV v)
@@ -351,7 +309,6 @@
           | PKVar  !KVar !(SubstV v)
           | PAll   ![(Symbol, Sort)] !(ExprV v)
           | PExist ![(Symbol, Sort)] !(ExprV v)
-          | PGrad  !KVar !(SubstV v) !GradInfo !(ExprV v)
           | ECoerc !Sort !Sort !(ExprV v)
           deriving (Eq, Show, Ord, Data, Typeable, Generic, Functor, Foldable, Traversable)
 
@@ -405,6 +362,7 @@
     go (ECoerc _ _ e)     = go e
     go (ENeg e)           = go e
     go (EBin _ e1 e2)     = gos [e1, e2]
+    go (ELet x e1 e2)     = HashSet.union (go e1) (HashSet.delete x $ go e2)
     go (EIte p e1 e2)     = gos [p, e1, e2]
     go (ECst e _)         = go e
     go (PAnd ps)          = gos ps
@@ -451,6 +409,7 @@
     go (ECoerc _ _ e)     = go e
     go (ENeg e)           = go e
     go (EBin _ e1 e2)     = gos [e1, e2]
+    go (ELet _ e1 e2)     = gos [e1, e2]
     go (EIte p e1 e2)     = gos [p, e1, e2]
     go (ECst e _)         = go e
     go (PAnd ps)          = gos ps
@@ -465,15 +424,6 @@
     go (PExist _xts p)     = go p
     go _                  = HashMap.empty
 
-data GradInfo = GradInfo {gsrc :: SrcSpan, gused :: Maybe SrcSpan}
-          deriving (Eq, Ord, Show, Data, Typeable, Generic)
-
-instance ToJSON   GradInfo
-instance FromJSON GradInfo
-
-srcGradInfo :: SourcePos -> GradInfo
-srcGradInfo src = GradInfo (SS src src) Nothing
-
 mkEApp :: LocSymbol -> [Expr] -> Expr
 mkEApp = eApps . EVar . val
 
@@ -520,6 +470,7 @@
     go (EVar _)        = 1
     go (ENeg e)        = go e
     go (EBin _ e1 e2)  = go e1 + go e2
+    go (ELet _ e1 e2)  = 1 + go e1 + go e2
     go (EIte e e1 e2)  = go e + go e1 + go e2
     go (ETAbs e _)     = go e
     go (ETApp e _)     = go e
@@ -532,7 +483,6 @@
     go (PAll _ e)      = go e
     go (PExist _ e)    = go e
     go (PKVar _ _)     = 1
-    go (PGrad _ _ _ e) = go e
     go (ECoerc _ _ e)  = go e
 
 type Reft = ReftV Symbol
@@ -574,9 +524,6 @@
 encodeSymConst        :: SymConst -> Symbol
 encodeSymConst (SL s) = litSymbol $ symbol s
 
--- _decodeSymConst :: Symbol -> Maybe SymConst
--- _decodeSymConst = fmap (SL . symbolText) . unLitSymbol
-
 instance Fixpoint SymConst where
   toFix (SL t) = text (show t)
 
@@ -609,6 +556,7 @@
   toFix e@(EApp _ _)   = parens $ hcat $ punctuate " " $ toFix <$> (f:es) where (f, es) = splitEApp e
   toFix (ENeg e)       = parens $ text "-"  <+> parens (toFix e)
   toFix (EBin o e1 e2) = parens $ sep [toFix e1  <+> toFix o, nest 2 (toFix e2)]
+  toFix (ELet x e1 e2) = parens $ sep [text "let" <+> toFix x <+> text "=" <+> toFix e1 <+> text "in", nest 2 (toFix e2)]
   toFix (EIte p e1 e2) = parens $ sep [text "if" <+> toFix p <+> text "then", nest 2 (toFix e1), text "else", nest 2 (toFix e2)]
   -- toFix (ECst e _so)   = toFix e
   toFix (ECst e so)    = parens $ toFix e   <+> text " : " <+> toFix so
@@ -623,15 +571,14 @@
   toFix (POr  ps)      = text "||" <+> toFix ps
   toFix (PAtom r e1 e2)  = parens $ sep [ toFix e1 <+> toFix r, nest 2 (toFix e2)]
   toFix (PKVar k su)     = toFix k <-> toFix su
-  toFix (PAll xts p)     = "forall" <+> (toFix xts
+  toFix (PAll xts p)     = parens $ "forall" <+> (toFix xts
                                         $+$ ("." <+> toFix p))
-  toFix (PExist xts p)   = "exists" <+> (toFix xts
+  toFix (PExist xts p)   = parens $ "exists" <+> (toFix xts
                                         $+$ ("." <+> toFix p))
   toFix (ETApp e s)      = text "tapp" <+> toFix e <+> toFix s
   toFix (ETAbs e s)      = text "tabs" <+> toFix e <+> toFix s
-  toFix (PGrad k _ _ e)  = toFix e <+> text "&&" <+> toFix k -- text "??" -- <+> toFix k <+> toFix su
   toFix (ECoerc a t e)   = parens (text "coerce" <+> toFix a <+> text "~" <+> toFix t <+> text "in" <+> toFix e)
-  toFix (ELam (x,s) e)   = text "lam" <+> toFix x <+> ":" <+> toFix s <+> "." <+> toFix e
+  toFix (ELam (x,s) e)   = parens (char '\\' <+> toFix x <+> ":" <+> toFix s <+> "->" <+> toFix e)
 
   simplify = simplifyExpr dedup
     where
@@ -664,10 +611,6 @@
           else if sq == PFalse then PNot sp
           else PIff sp sq
 
-    go (PGrad k su i e)
-      | isContraPred e      = PFalse
-      | otherwise           = PGrad k su i (go e)
-
     go (PAnd ps)
       | any isContraPred ps = PFalse
                            -- Note: Performance of some tests is very sensitive to this code. See #480
@@ -789,6 +732,10 @@
                                    pprintTidy k o         <+>
                                    pprintPrec (zo+1) k e2
     where zo = opPrec o
+  pprintPrec _ k (ELet x e1 e2)  = parens
+                                   "let"  <+> toFix x <+> "=" <+> pprintTidy  k e1  <+>
+                                   "in"   <+> pprintTidy k e2
+
   pprintPrec z k (EIte p e1 e2)  = parensIf (z > zi) $
                                    "if"   <+> pprintPrec (zi+1) k p  <+>
                                    "then" <+> pprintPrec (zi+1) k e1 <+>
@@ -824,19 +771,18 @@
                                    pprintTidy k r         <+>
                                    pprintPrec (za+1) k e2
     where za = 4
-  pprintPrec _ k (PAll xts p)    = pprintQuant k "forall" xts p
-  pprintPrec _ k (PExist xts p)  = pprintQuant k "exists" xts p
+  pprintPrec z k (PAll xts p)    = parensIf (z > 0) $ pprintQuant k "forall" xts p
+  pprintPrec z k (PExist xts p)  = parensIf (z > 0) $ pprintQuant k "exists" xts p
   pprintPrec _ k (ELam (x,t) e)  = "lam" <+> toFix x <+> ":" <+> toFix t <+> text "." <+> pprintTidy k e
   pprintPrec _ k (ECoerc a t e)  = parens $ "coerce" <+> toFix a <+> "~" <+> toFix t <+> text "in" <+> pprintTidy k e
   pprintPrec _ _ p@PKVar{}    = toFix p
   pprintPrec _ _ (ETApp e s)     = "ETApp" <+> toFix e <+> toFix s
   pprintPrec _ _ (ETAbs e s)     = "ETAbs" <+> toFix e <+> toFix s
-  pprintPrec z k (PGrad x _ _ e) = pprintPrec z k e <+> "&&" <+> toFix x -- "??"
 
 pprintQuant
   :: (Ord v, Fixpoint v, PPrint v)
   => Tidy -> Doc -> [(Symbol, Sort)] -> ExprV v -> Doc
-pprintQuant k d xts p = (d <+> toFix xts)
+pprintQuant k d xts p = (d <+> pprintTidy k xts)
                         $+$
                         ("  ." <+> pprintTidy k p)
 
@@ -930,7 +876,7 @@
 
 -- | 'conj' is a fast version of 'pAnd' needed for the ebind tests
 conj :: [Pred] -> Pred
-conj []  = PFalse
+conj []  = PTrue
 conj [p] = p
 conj ps  = PAnd ps
 
@@ -1011,17 +957,6 @@
 reftBind (Reft (x, _)) = x
 
 ------------------------------------------------------------
--- | Gradual Type Manipulation  ----------------------------
-------------------------------------------------------------
-pGAnds :: (Fixpoint v, Ord v) => [ExprV v] -> ExprV v
-pGAnds = foldl' pGAnd PTrue
-
-pGAnd :: (Fixpoint v, Ord v) => ExprV v -> ExprV v -> ExprV v
-pGAnd (PGrad k su i p) q = PGrad k su i (pAnd [p, q])
-pGAnd p (PGrad k su i q) = PGrad k su i (pAnd [p, q])
-pGAnd p q              = pAnd [p,q]
-
-------------------------------------------------------------
 -- | Generally Useful Refinements --------------------------
 ------------------------------------------------------------
 
@@ -1079,7 +1014,7 @@
   -- substa f  = substf (EVar . f)
 
   substf :: (Symbol -> Expr) -> a -> a
-  subst  :: Subst -> a -> a
+  subst  :: HasCallStack => Subst -> a -> a
   subst1 :: a -> (Symbol, Expr) -> a
   subst1 y (x, e) = subst (Su $ M.fromList [(x,e)]) y
 
diff --git a/src/Language/Fixpoint/Types/SMTPrint.hs b/src/Language/Fixpoint/Types/SMTPrint.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Fixpoint/Types/SMTPrint.hs
@@ -0,0 +1,130 @@
+{-# LANGUAGE CPP                #-}
+{-# LANGUAGE FlexibleContexts   #-}
+{-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module Language.Fixpoint.Types.SMTPrint where
+
+
+import qualified Language.Fixpoint.Misc  as Misc
+import qualified Text.PrettyPrint.HughesPJ.Compat as P
+import qualified Language.Fixpoint.Types.PrettyPrint as F
+import qualified Language.Fixpoint.Types.Names as F
+import qualified Language.Fixpoint.Types.Sorts as F
+import qualified Language.Fixpoint.Types.Spans as F
+import qualified Language.Fixpoint.Types.Refinements as F
+-- import qualified Language.Fixpoint.Types.Constraints as F
+
+-----------------------------------------------------------------------------------------------------------------
+-- Human readable but robustly parseable SMT-LIB format pretty printer
+-----------------------------------------------------------------------------------------------------------------
+class ToHornSMT a where
+  toHornSMT :: a -> P.Doc
+
+
+instance ToHornSMT F.Symbol where
+  toHornSMT s = F.pprint s
+
+
+
+toHornWithBinders :: (ToHornSMT a, ToHornSMT t) => P.Doc -> [(F.Symbol, t)] -> a -> P.Doc
+toHornWithBinders name xts p =  P.parens (name P.<+> toHornSMT xts P.<+> toHornSMT p)
+
+instance ToHornSMT a => ToHornSMT (F.Symbol, a) where
+  toHornSMT (x, t) = P.parens $ F.pprint x P.<+> toHornSMT t
+
+instance ToHornSMT a => ToHornSMT [a] where
+  toHornSMT = toHornMany . fmap toHornSMT
+
+toHornMany :: [P.Doc] -> P.Doc
+toHornMany = P.parens . P.sep
+
+toHornAnd :: (a -> P.Doc) -> [a] -> P.Doc
+toHornAnd f xs = P.parens (P.vcat ("and" : (P.nest 1 . f <$> xs)))
+
+
+instance ToHornSMT F.DataDecl where
+  toHornSMT (F.DDecl tc n ctors) =
+    P.parens $ P.vcat [
+      P.text "datatype" P.<+> P.parens (toHornSMT tc P.<+> P.int n)
+    , P.parens (P.vcat (toHornSMT <$> ctors))
+    ]
+
+instance ToHornSMT F.FTycon where
+  toHornSMT c
+    | c == F.listFTyCon = "list"
+    | otherwise         = toHornSMT (F.symbol c)
+
+instance ToHornSMT a => ToHornSMT (F.Located a) where
+  toHornSMT = toHornSMT . F.val
+
+instance ToHornSMT F.DataCtor where
+  toHornSMT (F.DCtor x flds) = P.parens (toHornSMT x P.<+> toHornSMT flds)
+
+instance ToHornSMT F.DataField where
+  toHornSMT (F.DField x t) = toHornSMT (F.val x, t)
+
+instance ToHornSMT F.Sort where
+  toHornSMT = toHornSort
+
+toHornSort :: F.Sort -> P.Doc
+toHornSort (F.FVar i)     = "@" P.<-> P.parens (P.int i)
+toHornSort F.FInt         = "Int"
+toHornSort F.FReal        = "Real"
+toHornSort F.FFrac        = "Frac"
+toHornSort (F.FObj x)     = toHornSMT x -- P.parens ("obj" P.<+> toHornSMT x)
+toHornSort F.FNum         = "num"
+toHornSort t@(F.FAbs _ _) = toHornAbsApp t
+toHornSort t@(F.FFunc _ _)= toHornAbsApp t
+toHornSort (F.FTC c)      = toHornSMT c
+toHornSort t@(F.FApp _ _) = toHornFApp (F.unFApp t)
+toHornSort (F.FNatNum x)  = P.integer x
+
+toHornAbsApp :: F.Sort -> P.Doc
+toHornAbsApp (F.functionSort -> Just (vs, ss, s)) = P.parens ("func" P.<+> P.int (length vs) P.<+> toHornSMT ss P.<+> toHornSMT s )
+toHornAbsApp _                                    = error "Unexpected nothing function sort"
+
+toHornFApp     :: [F.Sort] -> P.Doc
+toHornFApp [t] = toHornSMT t
+toHornFApp ts  = toHornSMT ts
+
+instance ToHornSMT F.Subst where
+  toHornSMT (F.Su m) = toHornSMT (Misc.hashMapToAscList m)
+
+
+
+instance ToHornSMT F.KVar where
+  toHornSMT (F.KV k) = "$" P.<-> toHornSMT k
+
+instance ToHornSMT F.Expr where
+  toHornSMT = toHornExpr
+
+toHornExpr :: F.Expr -> P.Doc
+toHornExpr (F.ESym c)        = F.pprint c
+toHornExpr (F.ECon c)        = F.pprint c
+toHornExpr (F.EVar s)        = toHornSMT s
+toHornExpr (F.ENeg e)        = P.parens ("-" P.<+> toHornExpr e)
+toHornExpr (F.EApp e1 e2)    = toHornSMT [e1, e2]
+toHornExpr (F.EBin o e1 e2)  = toHornOp   (F.toFix o) [e1, e2]
+toHornExpr (F.ELet x e1 e2)  = toHornMany ["let", toHornSMT [(x, e1)], toHornSMT e2]
+toHornExpr (F.EIte e1 e2 e3) = toHornOp "if"  [e1, e2, e3]
+toHornExpr (F.ECst e t)      = toHornMany ["cast", toHornSMT e, toHornSMT t]
+toHornExpr (F.PNot p)        = toHornOp "not"  [p]
+toHornExpr (F.PImp e1 e2)    = toHornOp "=>"   [e1, e2]
+toHornExpr (F.PIff e1 e2)    = toHornOp "<=>"  [e1, e2]
+toHornExpr e@F.PTrue         = F.pprint e
+toHornExpr e@F.PFalse        = F.pprint e
+toHornExpr (F.PAnd es)       = toHornOp "and" es
+toHornExpr (F.POr  es)       = toHornOp "or"  es
+toHornExpr (F.PAtom r e1 e2) = toHornOp (F.toFix r) [e1, e2]
+toHornExpr (F.PAll xts p)    = toHornMany ["forall", toHornSMT xts, toHornSMT p]
+toHornExpr (F.PExist xts p)  = toHornMany ["exists", toHornSMT xts, toHornSMT p]
+toHornExpr (F.ELam b e)      = toHornMany ["lam", toHornSMT b, toHornSMT e]
+toHornExpr (F.ECoerc a t e)  = toHornMany ["coerce", toHornSMT a, toHornSMT t, toHornSMT e]
+toHornExpr (F.PKVar k su)    = toHornMany [toHornSMT k, toHornSMT su]
+toHornExpr (F.ETApp e s)     = toHornMany ["ETApp" , toHornSMT e, toHornSMT s]
+toHornExpr (F.ETAbs e s)     = toHornMany ["ETAbs" , toHornSMT e, toHornSMT s]
+
+toHornOp :: ToHornSMT a => P.Doc -> [a] -> P.Doc
+toHornOp op es = toHornMany (op : (toHornSMT <$> es))
diff --git a/src/Language/Fixpoint/Types/Solutions.hs b/src/Language/Fixpoint/Types/Solutions.hs
--- a/src/Language/Fixpoint/Types/Solutions.hs
+++ b/src/Language/Fixpoint/Types/Solutions.hs
@@ -7,13 +7,11 @@
 {-# LANGUAGE MultiParamTypeClasses      #-}
 {-# LANGUAGE TypeOperators              #-}
 {-# LANGUAGE GADTs                      #-}
-{-# LANGUAGE BangPatterns               #-}
 {-# LANGUAGE PatternGuards              #-}
 {-# LANGUAGE DeriveGeneric              #-}
 {-# LANGUAGE DeriveAnyClass             #-}
 {-# LANGUAGE StandaloneDeriving         #-}
 {-# LANGUAGE DeriveDataTypeable         #-}
-{-# LANGUAGE TupleSections              #-}
 
 {-# OPTIONS_GHC -Wno-name-shadowing     #-}
 
@@ -23,49 +21,39 @@
 module Language.Fixpoint.Types.Solutions (
 
   -- * Solution tables
-    Solution, GSolution
-  , Sol (gMap, sEnv, sEbd, sxEnv)
-  , updateGMap, updateGMapWithKey
-  , sHyp
-  , sScp
+    Solution
+  , Sol (..)
   , CMap
 
   -- * Solution elements
-  , Hyp, Cube (..), QBind, GBind
+  , Hyp, Cube (..), QBind (..)
   , EQual (..)
-  , EbindSol (..)
 
   -- * Equal elements
   , eQual
   , trueEqual
 
-  -- * Gradual Solution elements
-  , qbToGb, gbToQbs, gbEquals, equalsGb, emptyGMap, qbExprs
+  , qbExprs
 
   -- * Solution Candidates (move to SolverMonad?)
   , Cand
 
-  -- * Constructor
-  , fromList
-
   -- * Update
   , update
-  , updateEbind
 
   -- * Lookup
   , lookupQBind
-  , lookup, glookup
+  , lookup
 
   -- * Manipulating QBind
   , qb
   , qbPreds
   , qbFilter
   , qbFilterM
-  , gbFilterM
 
 
   -- * Conversion for client
-  , result, resultGradual
+  , result 
 
   -- * "Fast" Solver (DEPRECATED as unsound)
   , Index  (..)
@@ -77,7 +65,6 @@
 import           Prelude hiding (lookup)
 import           GHC.Generics
 import           Control.DeepSeq
-import           Control.Monad.Reader
 import           Data.Hashable
 import qualified Data.Maybe                 as Mb
 import qualified Data.HashMap.Strict        as M
@@ -87,62 +74,41 @@
 import           Control.Monad (filterM)
 import           Language.Fixpoint.Misc
 import           Language.Fixpoint.Types.PrettyPrint
--- import           Language.Fixpoint.Types.Config  as Cfg
 import           Language.Fixpoint.Types.Spans
 import           Language.Fixpoint.Types.Names
 import           Language.Fixpoint.Types.Sorts
-import           Language.Fixpoint.Types.Theories
 import           Language.Fixpoint.Types.Refinements
 import           Language.Fixpoint.Types.Environments
 import           Language.Fixpoint.Types.Constraints
 import           Language.Fixpoint.Types.Substitutions
-import           Language.Fixpoint.SortCheck (ElabM, ElabParam(..), elaborate)
 import           Text.PrettyPrint.HughesPJ.Compat
 
 --------------------------------------------------------------------------------
--- | Update Solution -----------------------------------------------------------
---------------------------------------------------------------------------------
-update :: Sol a QBind -> [KVar] -> [(KVar, EQual)] -> (Bool, Sol a QBind)
+-- | Update Solution
+--
+-- @update s kqs@ sets in @s@ each KVar in @kqs@ to the corresponding QBind.
+--
+-- Yields a pair @(b, s')@ where @b@ is true if the mapping of any KVar was
+-- changed.
+--
+-- Precondition: @kqs@ contains no duplicate KVars.
+--
+update :: Sol QBind -> [(KVar, QBind)] -> (Bool, Sol QBind)
 --------------------------------------------------------------------------------
-update s ks kqs = {- tracepp msg -} (or bs, s')
-  where
-    kqss        = groupKs ks kqs
-    (bs, s')    = folds update1 s kqss
-    -- msg      = printf "ks = %s, s = %s" (showpp ks) (showpp s)
-
-folds   :: (a -> b -> (c, a)) -> a -> [b] -> ([c], a)
-folds f b = L.foldl' step ([], b)
-  where
-     step (cs, acc) x = (c:cs, x')
-       where
-         (c, x')      = f acc x
-
-groupKs :: [KVar] -> [(KVar, EQual)] -> [(KVar, QBind)]
-groupKs ks kqs = [ (k, QB eqs) | (k, eqs) <- M.toList $ groupBase m0 kqs ]
-  where
-    m0         = M.fromList $ (,[]) <$> ks
-
-update1 :: Sol a QBind -> (KVar, QBind) -> (Bool, Sol a QBind)
-update1 s (k, qs) = (change, updateK k qs s)
+update s kqs = L.foldl' step (False, s) kqs
   where
-    oldQs         = lookupQBind s k
-    change        = qbSize oldQs /= qbSize qs
-
+    step :: (Bool, Sol QBind) -> (KVar, QBind) -> (Bool, Sol QBind)
+    step (changed, s) (k, qs) = (changed || distinctSizes, updateK k qs s)
+      where
+        oldQs = lookupQBind s k
+        distinctSizes = qbSize oldQs /= qbSize qs
 
 --------------------------------------------------------------------------------
 -- | The `Solution` data type --------------------------------------------------
 --------------------------------------------------------------------------------
-type Solution  = Sol () QBind
-type GSolution = Sol (((Symbol, Sort), Expr), GBind) QBind
+type Solution  = Sol QBind
 newtype QBind  = QB [EQual]   deriving (Show, Data, Typeable, Generic, Eq)
-newtype GBind  = GB [[EQual]] deriving (Show, Data, Typeable, Generic)
 
-emptyGMap :: GSolution -> GSolution
-emptyGMap sol = mapGMap sol (\(x,_) -> (x, GB []))
-
-updateGMapWithKey :: [(KVar, QBind)] -> GSolution -> GSolution
-updateGMapWithKey kqs sol = sol {gMap = L.foldl' (\m (k, QB eq) -> M.adjust (\(x, GB eqs) -> (x, GB (if eq `elem` eqs then eqs else eq:eqs))) k m) (gMap sol) kqs }
-
 qb :: [EQual] -> QBind
 qb = QB
 
@@ -152,22 +118,6 @@
 qbExprs :: QBind -> [Expr]
 qbExprs (QB xs) = eqPred <$> xs
 
-qbToGb :: QBind -> GBind
-qbToGb (QB xs) = GB $ map (:[]) xs
-
-gbToQbs :: GBind -> [QBind]
-gbToQbs (GB [])  = [QB [trueEqual]]
-gbToQbs (GB ess) = QB <$> ess
-
-gbEquals :: GBind -> [[EQual]]
-gbEquals (GB eqs) = eqs
-
-equalsGb :: [[EQual]] -> GBind
-equalsGb = GB
-
-gbFilterM :: Monad m => ([EQual] -> m Bool) -> GBind -> m GBind
-gbFilterM f (GB eqs) = GB <$> filterM f eqs
-
 qbSize :: QBind -> Int
 qbSize = length . qbEQuals
 
@@ -178,87 +128,41 @@
 qbFilterM f (QB eqs) = QB <$> filterM f eqs
 
 instance NFData QBind
-instance NFData GBind
 
 instance PPrint QBind where
   pprintTidy k = pprintTidy k . qbEQuals
 
 --------------------------------------------------------------------------------
--- | An `EbindSol` contains the relevant information for an existential-binder;
---   (See tests/pos/ebind-*.fq for examples.) This is either
---   1. the constraint whose HEAD is a singleton that defines the binder, OR
---   2. the solved out TERM that we should use in place of the ebind at USES.
---------------------------------------------------------------------------------
-data EbindSol
-  = EbDef [SimpC ()] Symbol -- ^ The constraint whose HEAD "defines" the Ebind
-                             -- and the @Symbol@ for that EBind
-  | EbSol Expr             -- ^ The solved out term that should be used at USES.
-  | EbIncr                 -- ^ EBinds not to be solved for (because they're currently being solved for)
-   deriving (Show, Generic, NFData)
-
-instance PPrint EbindSol where
-  pprintTidy k (EbDef i x) = "EbDef:" <+> pprintTidy k i <+> pprintTidy k x
-  pprintTidy k (EbSol e)   = "EbSol:" <+> pprintTidy k e
-  pprintTidy _ EbIncr    = "EbIncr"
-
---------------------------------------------------------------------------------
-updateEbind :: Sol a b -> BindId -> Pred -> Sol a b
---------------------------------------------------------------------------------
-updateEbind s i !e = case M.lookup i (sEbd s) of
-  Nothing         -> errorstar $ "updateEBind: Unknown ebind " ++ show i
-  Just (EbSol e0) -> errorstar $ "updateEBind: Re-assigning ebind " ++ show i ++ " with solution: " ++ show e0
-  Just _          -> s { sEbd = M.insert i (EbSol e) (sEbd s) }
-
---------------------------------------------------------------------------------
 -- | A `Sol` contains the various indices needed to compute a solution,
 --   in particular, to compute `lhsPred` for any given constraint.
 --------------------------------------------------------------------------------
-data Sol b a = Sol
-  { sEnv :: !SymEnv                      -- ^ Environment used to elaborate solutions
-  , sMap :: !(M.HashMap KVar a)          -- ^ Actual solution (for cut kvar)
-  , gMap :: !(M.HashMap KVar b)          -- ^ Solution for gradual variables
+data Sol a = Sol
+  { sMap :: !(M.HashMap KVar a)          -- ^ Actual solution (for cut kvar)
   , sHyp :: !(M.HashMap KVar Hyp)        -- ^ Defining cubes  (for non-cut kvar)
-  , sScp :: !(M.HashMap KVar IBindEnv)   -- ^ Set of allowed binders for kvar
-  , sEbd :: !(M.HashMap BindId EbindSol) -- ^ EbindSol for each existential binder
-  , sxEnv :: !(SEnv (BindId, Sort))      --   TODO: merge with sEnv? used for sorts of ebinds to solve ebinds in lhsPred
+  , sScp :: !(M.HashMap KVar IBindEnv)   -- ^ Set of binders which are in scope for every
+                                         -- occurrence of the kvar
   } deriving (Generic)
 
-deriving instance (NFData b, NFData a) => NFData (Sol b a)
-
-updateGMap :: Sol b a -> M.HashMap KVar b -> Sol b a
-updateGMap sol gmap = sol {gMap = gmap}
-
-mapGMap :: Sol b a -> (b -> b) -> Sol b a
-mapGMap sol f = sol {gMap = M.map f (gMap sol)}
+deriving instance NFData a => NFData (Sol a)
 
-instance Semigroup (Sol a b) where
-  s1 <> s2 = Sol { sEnv  = sEnv s1  <> sEnv s2
-                 , sMap  = sMap s1  <> sMap s2
-                 , gMap  = gMap s1  <> gMap s2
+instance Semigroup (Sol a) where
+  s1 <> s2 = Sol { sMap  = sMap s1  <> sMap s2
                  , sHyp  = sHyp s1  <> sHyp s2
                  , sScp  = sScp s1  <> sScp s2
-                 , sEbd  = sEbd s1  <> sEbd s2
-                 , sxEnv = sxEnv s1 <> sxEnv s2
                  }
 
-instance Monoid (Sol a b) where
-  mempty = Sol { sEnv = mempty
-               , sMap = mempty
-               , gMap = mempty
+instance Monoid (Sol a) where
+  mempty = Sol { sMap = mempty
                , sHyp = mempty
                , sScp = mempty
-               , sEbd = mempty
-               , sxEnv = mempty
                }
   mappend = (<>)
 
-instance Functor (Sol a) where
-  fmap f (Sol e s m1 m2 m3 m4 m5) = Sol e (f <$> s) m1 m2 m3 m4 m5
+instance Functor Sol where
+  fmap f (Sol s m1 m2) = Sol (f <$> s) m1 m2
 
-instance (PPrint a, PPrint b) => PPrint (Sol a b) where
-  pprintTidy k s = vcat [ "sMap :=" <+> pprintTidy k (sMap s)
-                        , "sEbd :=" <+> pprintTidy k (sEbd s)
-                        ]
+instance PPrint a => PPrint (Sol a) where
+  pprintTidy k s = vcat [ "sMap :=" <+> pprintTidy k (sMap s) ]
 
 --------------------------------------------------------------------------------
 -- | A `Cube` is a single constraint defining a KVar ---------------------------
@@ -278,78 +182,27 @@
 instance Show Cube where
   show = showpp
 --------------------------------------------------------------------------------
-result :: Sol a QBind -> M.HashMap KVar Expr
---------------------------------------------------------------------------------
-result s = sMap $ pAnd . fmap eqPred . qbEQuals <$> s
-
-
---------------------------------------------------------------------------------
-resultGradual :: GSolution -> M.HashMap KVar (Expr, [Expr])
+result :: Sol QBind -> M.HashMap KVar Expr
 --------------------------------------------------------------------------------
-resultGradual s = fmap go' (gMap s)
-  where
-    go' ((_,e), GB eqss)
-     = (e, [PAnd $ fmap eqPred eqs | eqs <- eqss])
+result s = pAnd . fmap eqPred . qbEQuals <$> sMap s
 
 
 --------------------------------------------------------------------------------
--- | Create a Solution ---------------------------------------------------------
---------------------------------------------------------------------------------
-fromList :: SymEnv
-         -> [(KVar, a)]
-         -> [(KVar, b)]
-         -> [(KVar, Hyp)]
-         -> M.HashMap KVar IBindEnv
-         -> [(BindId, EbindSol)]
-         -> SEnv (BindId, Sort)
-         -> Sol a b
-fromList env kGs kXs kYs z ebs xbs
-        = Sol env kXm kGm kYm z ebm xbs
-  where
-    kXm = M.fromList kXs
-    kYm = M.fromList kYs
-    kGm = M.fromList kGs
-    ebm = M.fromList ebs
-
---------------------------------------------------------------------------------
-qbPreds :: String -> Sol a QBind -> Subst -> QBind -> ElabM [(Pred, EQual)]
+qbPreds :: Subst -> QBind -> [(Pred, EQual)]
 --------------------------------------------------------------------------------
-qbPreds msg s su (QB eqs) =
-  do ef <- ask
-     pure [ (elabPred ef eq, eq) | eq <- eqs ]
-  where
-    elabPred ef eq = elaborate (ElabParam ef (atLoc eq $ "qbPreds:" ++ msg) env)
-                   . subst su
-                   . eqPred
-                   $ eq
-    env            = sEnv s
+qbPreds su (QB eqs) =  [ (subst su $ eqPred eq, eq) | eq <- eqs ]
 
 --------------------------------------------------------------------------------
 -- | Read / Write Solution at KVar ---------------------------------------------
 --------------------------------------------------------------------------------
-lookupQBind :: Sol a QBind -> KVar -> QBind
+lookupQBind :: Sol QBind -> KVar -> QBind
 --------------------------------------------------------------------------------
 lookupQBind s k = {- tracepp _msg $ -} Mb.fromMaybe (QB []) (lookupElab s k)
   where
     _msg        = "lookupQB: k = " ++ show k
 
 --------------------------------------------------------------------------------
-glookup :: GSolution -> KVar -> Either Hyp (Either QBind (((Symbol, Sort), Expr), GBind))
---------------------------------------------------------------------------------
-glookup s k
-  | Just gbs <- M.lookup k (gMap s)
-  = Right (Right gbs)
-  | Just cs  <- M.lookup k (sHyp s) -- non-cut variable, return its cubes
-  = Left cs
-  | Just eqs <- lookupElab s k
-  = Right (Left eqs)                 -- TODO: don't initialize kvars that have a hyp solution
-  | otherwise
-  = errorstar $ "solLookup: Unknown kvar " ++ show k
-
-
-
---------------------------------------------------------------------------------
-lookup :: Sol a QBind -> KVar -> Either Hyp QBind
+lookup :: Sol QBind -> KVar -> Either Hyp QBind
 --------------------------------------------------------------------------------
 lookup s k
   | Just cs  <- M.lookup k (sHyp s) -- non-cut variable, return its cubes
@@ -359,11 +212,11 @@
   | otherwise
   = errorstar $ "solLookup: Unknown kvar " ++ show k
 
-lookupElab :: Sol b QBind -> KVar -> Maybe QBind
+lookupElab :: Sol QBind -> KVar -> Maybe QBind
 lookupElab s k = M.lookup k (sMap s)
 
 --------------------------------------------------------------------------------
-updateK :: KVar -> a -> Sol b a -> Sol b a
+updateK :: KVar -> a -> Sol a -> Sol a
 --------------------------------------------------------------------------------
 updateK k qs s = s { sMap = M.insert k qs (sMap s)
 --                 , sBot = M.delete k    (sBot s)
@@ -381,8 +234,8 @@
 --------------------------------------------------------------------------------
 data EQual = EQL
   { eqQual :: !Qualifier
-  , eqPred  :: !Expr
-  , _eqArgs :: ![Expr]
+  , eqPred  :: !Expr      -- ^ predicate obtained by instantiating the qualifier
+  , _eqArgs :: ![Expr]    -- ^ actual arguments used to instantiate the qualifier
   } deriving (Eq, Show, Data, Typeable, Generic)
 
 instance Loc EQual where
@@ -396,7 +249,7 @@
 
 instance NFData EQual
 
-{- EQL :: q:_ -> p:_ -> ListX F.Expr {q_params q} -> _ @-}
+-- | @eQual q xs@ instantiates @q@ with the arguments in @xs@
 eQual :: Qualifier -> [Symbol] -> EQual
 eQual q xs = {- tracepp "eQual" $ -} EQL q p es
   where
diff --git a/src/Language/Fixpoint/Types/Sorts.hs b/src/Language/Fixpoint/Types/Sorts.hs
--- a/src/Language/Fixpoint/Types/Sorts.hs
+++ b/src/Language/Fixpoint/Types/Sorts.hs
@@ -34,11 +34,13 @@
   , strFTyCon
   , setFTyCon
   , mapFTyCon -- TODO: hide these
+  , ffldFTyCon
   , mapFVar
   , basicSorts, intSort, realSort, boolSort, strSort, funcSort
   -- , bitVec32Sort, bitVec64Sort
   , setSort, bitVecSort, bagSort
   , arraySort
+  , finfieldSort
   , sizedBitVecSort
   , mapSort, charSort
   , listFTyCon
@@ -66,7 +68,7 @@
   , sortSymbols
   , substSort
 
-  , isBool, isNumeric, isReal, isString, isSet, isMap, isBag, isArray, isPolyInst
+  , isBool, isNumeric, isReal, isString, isSet, isMap, isBag, isArray, isFinfield, isPolyInst
 
   -- * User-defined ADTs
   , DataField (..)
@@ -152,7 +154,7 @@
 defStrInfo  = False
 
 charFTyCon, intFTyCon, boolFTyCon, realFTyCon, funcFTyCon, numFTyCon :: FTycon
-strFTyCon, listFTyCon, mapFTyCon, bagFTyCon, setFTyCon :: FTycon
+strFTyCon, listFTyCon, mapFTyCon, bagFTyCon, setFTyCon, ffldFTyCon :: FTycon
 intFTyCon  = TC (dummyLoc "int"       ) numTcInfo
 boolFTyCon = TC (dummyLoc boolLConName) defTcInfo
 realFTyCon = TC (dummyLoc "real"      ) realTcInfo
@@ -164,6 +166,7 @@
 setFTyCon  = TC (dummyLoc setConName  ) defTcInfo
 mapFTyCon  = TC (dummyLoc mapConName  ) defTcInfo
 bagFTyCon  = TC (dummyLoc bagConName  ) defTcInfo
+ffldFTyCon = TC (dummyLoc ffldConName ) defTcInfo
 
 isListConName :: LocSymbol -> Bool
 isListConName x = c == listConName || c == listLConName --"List"
@@ -205,6 +208,14 @@
 isArrayTC :: FTycon -> Bool
 isArrayTC (TC z _) = isArrayConName z
 
+isFinfieldConName :: LocSymbol -> Bool
+isFinfieldConName x = c == ffldConName
+  where
+    c           = val x
+
+isFinfieldTC :: FTycon -> Bool
+isFinfieldTC (TC z _) = isFinfieldConName z
+
 sizeBv :: FTycon -> Maybe Int
 sizeBv tc = do
   let s = val $ fTyconSymbol tc
@@ -293,6 +304,7 @@
         go t@FReal       = t
         go t@FNum        = t
         go t@FFrac       = t
+        go t@(FNatNum _) = t
 
 --------------------------------------------------------------------------------
 -- | Sorts ---------------------------------------------------------------------
@@ -301,12 +313,13 @@
           | FReal
           | FNum                 -- ^ numeric kind for Num tyvars
           | FFrac                -- ^ numeric kind for Fractional tyvars
-          | FObj  !Symbol        -- ^ uninterpreted type
-          | FVar  !Int           -- ^ fixpoint type variable
-          | FFunc !Sort !Sort    -- ^ function
-          | FAbs  !Int !Sort     -- ^ type-abstraction
-          | FTC   !FTycon
-          | FApp  !Sort !Sort    -- ^ constructed type
+          | FObj    !Symbol      -- ^ uninterpreted type
+          | FVar    !Int         -- ^ fixpoint type variable
+          | FFunc   !Sort !Sort  -- ^ function
+          | FAbs    !Int !Sort   -- ^ type-abstraction
+          | FTC     !FTycon
+          | FApp    !Sort !Sort  -- ^ constructed type
+          | FNatNum !Integer     -- ^ typelevel natural numeral
             deriving (Eq, Ord, Show, Data, Typeable, Generic, ToJSON, FromJSON)
 
 instance PPrint Sort where
@@ -423,6 +436,10 @@
 isArray (FTC c) = isArrayTC c
 isArray _       = False
 
+isFinfield :: Sort -> Bool
+isFinfield (FTC c) = isFinfieldTC c
+isFinfield _       = False
+
 isChar :: Sort -> Bool
 isChar (FTC c) = c == charFTyCon
 isChar _       = False
@@ -490,6 +507,7 @@
 toFixSort t@(FFunc _ _)= toFixAbsApp t
 toFixSort (FTC c)      = toFix c
 toFixSort t@(FApp _ _) = toFixFApp (unFApp t)
+toFixSort (FNatNum x)  = toFix x
 
 toFixAbsApp :: Sort -> Doc
 toFixAbsApp (functionSort -> Just (vs, ss, s)) =
@@ -571,6 +589,9 @@
 
 arraySort :: Sort -> Sort -> Sort
 arraySort = FApp . FApp (FTC (symbolFTycon' arrayConName))
+
+finfieldSort :: Sort -> Sort
+finfieldSort = FApp (FTC ffldFTyCon)
 
 symbolFTycon' :: Symbol -> FTycon
 symbolFTycon' = symbolFTycon . dummyLoc
diff --git a/src/Language/Fixpoint/Types/Substitutions.hs b/src/Language/Fixpoint/Types/Substitutions.hs
--- a/src/Language/Fixpoint/Types/Substitutions.hs
+++ b/src/Language/Fixpoint/Types/Substitutions.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE FlexibleInstances #-}
 
 {-# OPTIONS_GHC -Wno-orphans   #-}
+{-# LANGUAGE InstanceSigs #-}
 
 -- | This module contains the various instances for Subable,
 --   which (should) depend on the visitors, and hence cannot
@@ -12,14 +13,18 @@
   , substExcept
   , substfExcept
   , subst1Except
+  , substSymbolsSet
+  , rapierSubstExpr
   , targetSubstSyms
   , filterSubst
   , catSubst
   , exprSymbolsSet
+  , extendSubst
   , meetReft
   , pprReft
   ) where
 
+import           Data.List                 as List
 import           Data.Maybe
 import qualified Data.HashMap.Strict       as M
 import qualified Data.HashSet              as S
@@ -61,7 +66,8 @@
 targetSubstSyms :: Subst -> [Symbol]
 targetSubstSyms (Su ms) = syms $ M.elems ms
 
-
+substSymbolsSet :: Subst -> S.HashSet Symbol
+substSymbolsSet (Su m) = S.unions $ map exprSymbolsSet (M.elems m)
 
 instance Subable () where
   syms _      = []
@@ -120,17 +126,19 @@
 subSymbol Nothing         x = x
 subSymbol a               b = errorstar (printf "Cannot substitute symbol %s with expression %s" (showFix b) (showFix a))
 
-substfLam :: (Symbol -> Expr) -> (Symbol, Sort) -> Expr -> Expr
-substfLam f s@(x, _) e =  ELam s (substf (\y -> if y == x then EVar x else f y) e)
+captureAvoiding :: Symbol -> (Symbol -> Expr) -> Symbol -> Expr
+captureAvoiding x f y = if y == x then EVar x else f y
 
 instance Subable Expr where
   syms                     = exprSymbols
   substa f                 = substf (EVar . f)
+  substf :: (Symbol -> Expr) -> Expr -> Expr
   substf f (EApp s e)      = EApp (substf f s) (substf f e)
-  substf f (ELam x e)      = substfLam f x e
+  substf f (ELam (x,t) e)  = ELam (x, t) (substf (captureAvoiding x f) e)
   substf f (ECoerc a t e)  = ECoerc a t (substf f e)
   substf f (ENeg e)        = ENeg (substf f e)
   substf f (EBin op e1 e2) = EBin op (substf f e1) (substf f e2)
+  substf f (ELet x e1 e2)  = ELet x (substf f e1) (substf (captureAvoiding x f) e2)
   substf f (EIte p e1 e2)  = EIte (substf f p) (substf f e1) (substf f e2)
   substf f (ECst e so)     = ECst (substf f e) so
   substf f (EVar x)        = f x
@@ -141,43 +149,154 @@
   substf f (PIff p1 p2)    = PIff (substf f p1) (substf f p2)
   substf f (PAtom r e1 e2) = PAtom r (substf f e1) (substf f e2)
   substf f (PKVar k (Su su)) = PKVar k (Su $ M.map (substf f) su)
-  substf _  (PAll _ _)     = errorstar "substf: FORALL"
-  substf f (PGrad k su i e)= PGrad k su i (substf f e)
+  substf _ (PAll _ _)      = errorstar "substf: FORALL"
+  substf f (PExist xts e)  = PExist xts (substf f e)
   substf _  p              = p
 
 
-  subst su (EApp f e)      = EApp (subst su f) (subst su e)
-  subst su (ELam x e)      = ELam x (subst (removeSubst su (fst x)) e)
-  subst su (ECoerc a t e)  = ECoerc a t (subst su e)
-  subst su (ENeg e)        = ENeg (subst su e)
-  subst su (EBin op e1 e2) = EBin op (subst su e1) (subst su e2)
-  subst su (EIte p e1 e2)  = EIte (subst su p) (subst su e1) (subst su e2)
-  subst su (ECst e so)     = ECst (subst su e) so
-  subst su (EVar x)        = appSubst su x
-  subst su (PAnd ps)       = PAnd $ map (subst su) ps
-  subst su (POr  ps)       = POr  $ map (subst su) ps
-  subst su (PNot p)        = PNot $ subst su p
-  subst su (PImp p1 p2)    = PImp (subst su p1) (subst su p2)
-  subst su (PIff p1 p2)    = PIff (subst su p1) (subst su p2)
-  subst su (PAtom r e1 e2) = PAtom r (subst su e1) (subst su e2)
-  subst su (PKVar k su')   = PKVar k $ su' `catSubst` su
-  subst su (PGrad k su' i e) = PGrad k (su' `catSubst` su) i (subst su e)
-  subst su (PAll bs p)
-          | disjoint su bs = PAll bs $ subst su p --(substExcept su (fst <$> bs)) p
-          | otherwise      = errorstar "subst: PAll (without disjoint binds)"
-  subst su (PExist bs p)
-          | disjoint su bs = PExist bs $ subst su p --(substExcept su (fst <$> bs)) p
-          | otherwise      = errorstar ("subst: EXISTS (without disjoint binds)" ++ show (bs, su, p))
-  subst _  p               = p
+  subst = go
+    where
+      -- The auxiliary go function skips the HasCallStack constraint on every
+      -- recursive call. In case of error, the call stack only contains the
+      -- point at which subst was first called.
+      go su e0 = case e0 of
+        EApp f e ->
+          EApp (go su f) (go su e)
+        ELam x e ->
+          let su' = removeSubst su (fst x)
+           in ELam x (go su' e)
+        ELet x e1 e2 ->
+          let su' = removeSubst su x
+           in ELet x (go su e1) (go su' e2)
+        ECoerc a t e ->
+          ECoerc a t (go su e)
+        ENeg e ->
+          ENeg (go su e)
+        EBin op e1 e2 ->
+          EBin op (go su e1) (go su e2)
+        EIte p e1 e2 ->
+          EIte (go su p) (go su e1) (go su e2)
+        ECst e so ->
+          ECst (go su e) so
+        EVar x ->
+          appSubst su x
+        PAnd ps ->
+          PAnd $ map (go su) ps
+        POr  ps ->
+          POr  $ map (go su) ps
+        PNot p ->
+          PNot $ go su p
+        PImp p1 p2 ->
+          PImp (go su p1) (go su p2)
+        PIff p1 p2 ->
+          PIff (go su p1) (go su p2)
+        PAtom r e1 e2 ->
+          PAtom r (go su e1) (go su e2)
+        PKVar k su' ->
+          PKVar k $ su' `catSubst` su
+        PAll bs p
+          | disjoint su bs ->
+            PAll bs $ go su p --(substExcept su (fst <$> bs)) p
+          | otherwise ->
+            errorstar $ unlines
+              [ "subst: FORALL without disjoint binds"
+              , "su: " ++ showpp su
+              , "expr: " ++ showpp e0
+              ]
+        PExist bs p
+          | disjoint su bs ->
+            PExist bs $ go su p --(substExcept su (fst <$> bs)) p
+          | otherwise ->
+            errorstar $ unlines
+              [ "subst: EXISTS without disjoint binds"
+              , "su: " ++ showpp su
+              , "expr: " ++ showpp e0
+              ]
+        p ->
+          p
 
 removeSubst :: Subst -> Symbol -> Subst
 removeSubst (Su su) x = Su $ M.delete x su
 
+-- | Rapier style capture-avoiding substitution
+--
+-- The scope set parameter must contain any symbols that are expected
+-- to appear free in the result expression. Typically, this is the set of
+-- symbols that are free in the range of the substitution, plus any symbols
+-- that are already free in the input expression.
+rapierSubstExpr :: S.HashSet Symbol -> Subst -> Expr -> Expr
+rapierSubstExpr s su e0 =
+  let go = rapierSubstExpr
+   in case e0 of
+    EApp f e -> EApp (go s su f) (go s su e)
+    ELam (x, t) e ->
+      if x `S.member` s then
+        let x' = fresh x
+            su' = extendSubst su x (EVar x')
+         in ELam (x', t) (go (S.insert x' s) su' e)
+      else
+        ELam (x, t) (go (S.insert x s) (removeSubst su x) e)
+    ELet x e1 e2 ->
+      if x `S.member` s then
+        let x' = fresh x
+            su' = extendSubst su x (EVar x')
+         in ELet x' (go s su e1) (go (S.insert x' s) su' e2)
+      else
+        let su' = removeSubst su x
+         in ELet x (go s su e1) (go (S.insert x s) su' e2)
+
+    ECoerc a t e -> ECoerc a t (go s su e)
+    ENeg e -> ENeg (go s su e)
+    EBin op e1 e2 -> EBin op (go s su e1) (go s su e2)
+    EIte p e1 e2 -> EIte (go s su p) (go s su e1) (go s su e2)
+    ECst e so -> ECst (go s su e) so
+    EVar x -> appSubst su x
+    PAnd ps -> PAnd $ map (go s su) ps
+    POr ps -> POr $ map (go s su) ps
+    PNot p -> PNot $ go s su p
+    PImp p1 p2 -> PImp (go s su p1) (go s su p2)
+    PIff p1 p2 -> PIff (go s su p1) (go s su p2)
+    PAtom r e1 e2 -> PAtom r (go s su e1) (go s su e2)
+    PKVar k su' -> PKVar k $ catSubstGo su' su
+    PAll bs p ->
+      let mfs = map (maybeFresh . fst) bs
+          fs = map (either (\x -> (x, x)) id) mfs
+          su' = List.foldl' (\su1 (x, x') -> extendSubst su1 x (EVar x')) su fs
+          bs' = zip (map (either id snd) mfs) (map snd bs)
+          s' = foldr (S.insert . fst) s bs'
+       in
+          PAll bs' $ go s' su' p
+    PExist bs p ->
+      let mfs = map (maybeFresh . fst) bs
+          fs = map (either (\x -> (x, x)) id) mfs
+          su' = List.foldl' (\su1 (x, x') -> extendSubst su1 x (EVar x')) su fs
+          bs' = zip (map (either id snd) mfs) (map snd bs)
+          s' = foldr (S.insert . fst) s bs'
+       in
+          PExist bs' $ go s' su' p
+    p -> p
+  where
+    fresh :: Symbol -> Symbol
+    fresh x = head $ dropWhile (`S.member` s) candidates
+      where
+        candidates = [ renameSubstSymbol x i | i <- [0..] ]
+
+    maybeFresh x =
+      if x `S.member` s then Right (x, fresh x) else Left x
+
+    catSubstGo :: Subst -> Subst -> Subst
+    catSubstGo (Su s1) su2@(Su s2) = Su $ M.union s1' s2
+      where
+        s1' = rapierSubstExpr s su2 <$> s1
+
+extendSubst :: Subst -> Symbol -> Expr -> Subst
+extendSubst (Su m) x e = Su $ M.insert x e m
+
 disjoint :: Subst -> [(Symbol, Sort)] -> Bool
 disjoint (Su su) bs = S.null $ suSyms `S.intersection` bsSyms
   where
-    suSyms = S.fromList $ syms (M.elems su) ++ syms (M.keys su)
-    bsSyms = S.fromList $ syms $ fst <$> bs
+    suSyms = S.fromList $ syms (M.elems su) ++ M.keys su
+    bsSyms = S.fromList $ fst <$> bs
 
 meetReft :: Reft -> Reft -> Reft
 meetReft (Reft (v, ra)) (Reft (v', ra'))
diff --git a/src/Language/Fixpoint/Types/Templates.hs b/src/Language/Fixpoint/Types/Templates.hs
--- a/src/Language/Fixpoint/Types/Templates.hs
+++ b/src/Language/Fixpoint/Types/Templates.hs
@@ -46,6 +46,8 @@
   = matchesTemplate (xs, t) e
 matchesTemplate (xs, EBin b t1 t2) (EBin b' e1 e2)
   = b == b' && matchesTemplate (xs, t1) e1 && matchesTemplate (xs, t2) e2
+matchesTemplate (xs, ELet x t1 t2) (ELet x' e1 e2)
+  = x == x' && matchesTemplate (xs, t1) e1 && matchesTemplate (xs, t2) e2
 matchesTemplate (xs, EIte t1 t2 t3) (EIte e1 e2 e3)
   = matchesTemplate (xs, t1) e1 && matchesTemplate (xs, t2) e2 && matchesTemplate (xs, t3) e3
 matchesTemplate (xs, ECst t s) (ECst e s')
@@ -72,8 +74,6 @@
   = s == s' && matchesTemplate (xs, t) e
 matchesTemplate (xs, PExist s t) (PExist s' e)
   = s == s' && matchesTemplate (xs, t) e
-matchesTemplate (xs, PGrad s1 s2 s3 t) (PGrad s1' s2' s3' e)
-  = s1 == s1' && s2 == s2' && s3 == s3' && matchesTemplate (xs, t) e
 matchesTemplate (xs, ECoerc s1 s2 t) (ECoerc s1' s2' e)
   = s1 == s1' && s2 == s2' && matchesTemplate (xs, t) e
 matchesTemplate (_, t) e
diff --git a/src/Language/Fixpoint/Types/Theories.hs b/src/Language/Fixpoint/Types/Theories.hs
--- a/src/Language/Fixpoint/Types/Theories.hs
+++ b/src/Language/Fixpoint/Types/Theories.hs
@@ -20,11 +20,18 @@
 
     -- * Theory Sorts
     , SmtSort (..)
+    , FuncSort
     , sortSmtSort
     , isIntSmtSort
 
+    , mergeTopAppls
+    , pushAppls
+    , popAppls
+    , peekAppls
+
     -- * Symbol Environments
     , SymEnv (..)
+    , SymM
     , symEnv
     , symEnvSort
     , symEnvTheory
@@ -32,9 +39,10 @@
     , deleteSymEnv
     , insertsSymEnv
     , symbolAtName
-    , symbolAtSmtName
+    , symbolAtSortIndex
 
     -- * Coercing sorts in environments
+    , coerceSort
     , coerceEnv
     , coerceSortEnv
     , TheorySymbols(..)
@@ -45,12 +53,13 @@
 import           Data.Typeable             (Typeable)
 import           Data.Hashable
 import           GHC.Generics              (Generic)
+import           Control.Applicative
+import           Control.Monad.State
 import           Control.DeepSeq
 import           Language.Fixpoint.Types.Config
 import           Language.Fixpoint.Types.PrettyPrint
 import           Language.Fixpoint.Types.Names
 import           Language.Fixpoint.Types.Sorts
-import           Language.Fixpoint.Types.Errors
 import           Language.Fixpoint.Types.Environments
 
 import           Text.PrettyPrint.HughesPJ.Compat
@@ -69,41 +78,96 @@
 --------------------------------------------------------------------------------
 -- | 'SymEnv' is used to resolve the 'Sort' and 'Sem' of each 'Symbol'
 --------------------------------------------------------------------------------
+
+-- | Apply tags already used to declare @apply@ symbols in the SMT solver.
+--
+-- The tags are organized in a stack because every time we pop the SMT solver
+-- state, it forgets the tags declared since the last push.
+--
+-- Each entry in the stack describes the integer tag corresponding to a
+-- particular function sort. Every time we issue a `push` a new level
+-- is added to the stack, and correspondingly, a `pop` removes a level.
+--
+-- See 'seApplsCur' in 'SymEnv' for details about actually declaring the
+-- tags to the SMT solver.
+type Appls = [M.HashMap FuncSort Int]
+
+lookupAppls :: FuncSort -> Appls -> Maybe Int
+lookupAppls fs = foldr (\hm acc -> acc <|> M.lookup fs hm) Nothing
+
+mergeTopAppls :: M.HashMap FuncSort Int -> Appls -> Appls
+mergeTopAppls m (top : rest) = (top <> m) : rest
+mergeTopAppls m [] = [m]
+
+pushAppls :: Appls -> Appls
+pushAppls aps = M.empty : aps
+
+popAppls :: Appls -> Appls
+popAppls [] = []
+popAppls (_:xs) = xs
+
+peekAppls :: Appls -> Maybe (M.HashMap FuncSort Int)
+peekAppls [] = Nothing
+peekAppls (x:_) = Just x
+
 data SymEnv = SymEnv
-  { seSort   :: !(SEnv Sort)              -- ^ Sorts of *all* defined symbols
-  , seTheory :: !(SEnv TheorySymbol)      -- ^ Information about theory-specific Symbols
-  , seData   :: !(SEnv DataDecl)          -- ^ User-defined data-declarations
-  , seLits   :: !(SEnv Sort)              -- ^ Distinct Constant symbols
-  , seAppls  :: !(M.HashMap FuncSort Int) -- ^ Types at which `apply` was used;
-                                           --   see [NOTE:apply-monomorphization]
+  { seSort     :: !(SEnv Sort)              -- ^ Sorts of *all* defined symbols
+  , seTheory   :: !(SEnv TheorySymbol)      -- ^ Information about theory-specific Symbols
+  , seData     :: !(SEnv DataDecl)          -- ^ User-defined data-declarations
+  , seLits     :: !(SEnv Sort)              -- ^ Distinct Constant symbols
+
+    -- | Apply tags already declared in the SMT solver.
+    --
+    -- This is inspected when serializing applications of functions to determine
+    -- if a new tag needs to be created for a given function sort
+    -- (@funcSortIndex@).
+  , seAppls    :: !Appls
+
+    -- | Apply tags that have been created while serializing expressions for the
+    -- SMT solver, but which have not been used to declare apply symbols yet in
+    -- the SMT solver.
+    --
+    -- The apply symbols using the tags are declared whenever we need to send
+    -- the serialized expressions to the SMT solver (using @funcSortVars@). At
+    -- this point, the contents of this map are merged into the top of the
+    -- 'seAppls' stack, and @seApplsCur@ is cleared.
+  , seApplsCur :: !(M.HashMap FuncSort Int)
+  , seIx       :: !Int                      -- ^ Largest unused index for sorts
   }
   deriving (Eq, Show, Data, Typeable, Generic)
 
 {- type FuncSort = {v:Sort | isFFunc v} @-}
 type FuncSort = (SmtSort, SmtSort)
 
+-- | Generating SMT expressions is a stateful process because new symbols ('apply', 'coerce',
+--   'smt_lambda' and 'lam_arg') need to be emitted with unique ids for each newly encountered
+--   function sort. The 'SymM' monad carries the 'SymEnv' state required to track the ids.
+--   The state updates are performed in `L.F.Smt.Serialize` (functions `smt2App`, `smt2Coerc`,
+--   `smt2Lam` and `smtLamArg`, correspondingly).
+type SymM a = State SymEnv a
+
 instance NFData   SymEnv
 instance S.Store SymEnv
 
 instance Semigroup SymEnv where
-  e1 <> e2 = SymEnv { seSort   = seSort   e1 <> seSort   e2
-                    , seTheory = seTheory e1 <> seTheory e2
-                    , seData   = seData   e1 <> seData   e2
-                    , seLits   = seLits   e1 <> seLits   e2
-                    , seAppls  = seAppls  e1 <> seAppls  e2
+  e1 <> e2 = SymEnv { seSort     = seSort     e1 <> seSort     e2
+                    , seTheory   = seTheory   e1 <> seTheory   e2
+                    , seData     = seData     e1 <> seData     e2
+                    , seLits     = seLits     e1 <> seLits     e2
+                    , seAppls    = zipWith (<>) (seAppls e1) (seAppls e2)
+                    , seApplsCur = seApplsCur e1 <> seApplsCur e2
+                    , seIx       = seIx       e1 `max` seIx    e2
                     }
 
 instance Monoid SymEnv where
-  mempty        = SymEnv emptySEnv emptySEnv emptySEnv emptySEnv mempty
+  mempty        = SymEnv emptySEnv emptySEnv emptySEnv emptySEnv [] mempty 0
   mappend       = (<>)
 
 symEnv :: SEnv Sort -> SEnv TheorySymbol -> [DataDecl] -> SEnv Sort -> [Sort] -> SymEnv
-symEnv xEnv fEnv ds ls ts = SymEnv xEnv' fEnv dEnv ls sortMap
+symEnv xEnv fEnv ds ls _ = SymEnv xEnv' fEnv dEnv ls [] mempty 0
   where
     xEnv'   = unionSEnv xEnv wiredInEnv
     dEnv    = fromListSEnv [(symbol d, d) | d <- ds]
-    sortMap = M.fromList (zip smts [0..])
-    smts    = funcSorts dEnv ts
 
 -- | These are "BUILT-in" polymorphic functions which are
 --   UNINTERPRETED but POLYMORPHIC, hence need to go through
@@ -114,88 +178,6 @@
   , (tyCastName, FAbs 0 $ FAbs 1 $ FFunc (FVar 0) (FVar 1))
   ]
 
-
--- | 'funcSorts' attempts to compute a list of all the input-output sorts
---   at which applications occur. This is a gross hack; as during unfolding
---   we may create _new_ terms with weird new sorts. Ideally, we MUST allow
---   for EXTENDING the apply-sorts with those newly created terms.
---   the solution is perhaps to *preface* each VC query of the form
---
---      push
---      assert p
---      check-sat
---      pop
---
---   with the declarations needed to make 'p' well-sorted under SMT, i.e.
---   change the above to
---
---      declare apply-sorts
---      push
---      assert p
---      check-sat
---      pop
---
---   such a strategy would NUKE the entire apply-sort machinery from the CODE base.
---   [TODO]: dynamic-apply-declaration
-
-funcSorts :: SEnv DataDecl -> [Sort] -> [FuncSort]
-funcSorts dEnv ts = [ (t1, t2) | t1 <- smts, t2 <- smts]
-  where
-    smts = Misc.sortNub $ concat $ [ tx t1 ++ tx t2 | FFunc t1 t2 <- ts ]
-    tx   = inlineArrSetBag False dEnv
-
--- Related to the above, after merging #688, we now allow types other than
--- Int to which Arrays/Sets/Bags can be applied.
--- However, the `sortSmtSort` function below, previously used in `funcSorts`,
--- only instantiates type variables at Ints. This causes the solver to crash
--- when PLE generates apply queries for polymorphic sets (see
--- https://github.com/ucsd-progsys/liquidhaskell/issues/2438). The following
--- pair of functions is a temporary fix for this - it generates additional
--- array/set/bag sorts instantiated at all user types for a "polymorphic depth 1"
--- (i.e., `Array (Foo Int) Int` but not `Array (Foo (Foo Int)) Int`, to keep
--- the applys table from blowing up exponentially). Ultimately, a general
--- solution should be implemented for generating ad-hoc sets of applys on the
--- fly, as described above.
-
-inlineArrSetBag :: Bool -> SEnv DataDecl -> Sort -> [SmtSort]
-inlineArrSetBag isASB env t = go . unAbs $ t
-  where
-    m = sortAbs t
-    go (FFunc _ _)    = [SInt]
-    go FInt           = [SInt]
-    go FReal          = [SReal]
-    go t
-      | t == boolSort = [SBool]
-      | isString t    = [SString]
-    go (FVar _)
-      | isASB     = SInt : map (\q -> let dd = snd q in
-                                      SData (ddTyCon dd) (replicate (ddVars dd) SInt))
-                               (M.toList $ seBinds env)
-      | otherwise = [SInt]
-    go t
-      | (ct:ts) <- unFApp t = inlineArrSetBagFApp m env ct ts
-      | otherwise = error "Unexpected empty 'unFApp t'"
-
-inlineArrSetBagFApp :: Int -> SEnv DataDecl -> Sort -> [Sort] -> [SmtSort]
-inlineArrSetBagFApp m env = go
-  where
-    go (FTC c) [a]
-      | setConName == symbol c   = SSet <$> inlineArrSetBag True env a
-    go (FTC c) [a]
-      | bagConName == symbol c   = SBag <$> inlineArrSetBag True env a
-    go (FTC c) [a, b]
-      | arrayConName == symbol c = SArray <$> inlineArrSetBag True env a <*> inlineArrSetBag True env b
-    go (FTC bv) [FTC s]
-      | bitVecName == symbol bv
-      , Just n <- sizeBv s      = [SBitVec n]
-    go s []
-      | isString s              = [SString]
-    go (FTC c) ts
-      | Just n <- tyArgs c env
-      , let i = n - length ts   = [SData c ((inlineArrSetBag False env . FAbs m =<< ts) ++ replicate i SInt)]
-    go _ _                      = [SInt]
-
-
 symEnvTheory :: Symbol -> SymEnv -> Maybe TheorySymbol
 symEnvTheory x env = lookupSEnv x (seTheory env)
 
@@ -211,20 +193,31 @@
 insertsSymEnv :: SymEnv -> [(Symbol, Sort)] -> SymEnv
 insertsSymEnv = L.foldl' (\env (x, s) -> insertSymEnv x s env)
 
-symbolAtName :: (PPrint a) => Symbol -> SymEnv -> a -> Sort -> Text
-symbolAtName mkSym env e = symbolAtSmtName mkSym env e . ffuncSort env
-{-# SCC symbolAtName #-}
+symbolAtSortIndex :: Symbol -> Int -> Text
+symbolAtSortIndex mkSym si = appendSymbolText mkSym . Text.pack . show $ si
 
-symbolAtSmtName :: (PPrint a) => Symbol -> SymEnv -> a -> FuncSort -> Text
-symbolAtSmtName mkSym env e =
-  -- formerly: intSymbol mkSym . funcSortIndex env e
-  appendSymbolText mkSym . Text.pack . show . funcSortIndex env e
-{-# SCC symbolAtSmtName #-}
+symbolAtName :: Symbol -> Sort -> SymM Text
+symbolAtName mkSym s =
+  do env <- get
+     fsi <- funcSortIndex (ffuncSort env s)
+     pure $ symbolAtSortIndex mkSym fsi
+{-# SCC symbolAtName #-}
 
-funcSortIndex :: (PPrint a) => SymEnv -> a -> FuncSort -> Int
-funcSortIndex env e fs = M.lookupDefault err fs (seAppls env)
-  where
-    err = panic ("Unknown func-sort: " ++ show fs ++ " for " ++ showpp e)
+-- See 'seAppls' and 'seApplsCur' in 'SymEnv' for explanation.
+funcSortIndex :: FuncSort -> SymM Int
+funcSortIndex fs =
+  do env <- get
+     let aps = seAppls env
+     let apsc = seApplsCur env
+     case lookupAppls fs aps of
+      Just i  -> pure i
+      Nothing ->
+        case M.lookup fs apsc of
+          Just i  -> pure i
+          Nothing ->
+           do let i = seIx env
+              modify (\env -> env { seApplsCur = M.insert fs i apsc , seIx = 1 + i })
+              pure i
 
 ffuncSort :: SymEnv -> Sort -> FuncSort
 ffuncSort env t      = {- tracepp ("ffuncSort " ++ showpp (t1,t2)) -} (tx t1, tx t2)
@@ -292,8 +285,11 @@
   | SBool
   | SReal
   | SString
+  --- CVC(5) only
   | SSet !SmtSort
   | SBag !SmtSort
+  | SFFld !Integer
+  ---
   | SArray !SmtSort !SmtSort
   | SBitVec !Int
   | SVar    !Int
@@ -334,9 +330,11 @@
 -- HKT    go t@(FVar _) ts            = SApp (sortSmtSort poly env <$> (t:ts))
 
     go (FTC c) [a]
-      | setConName == symbol c  = SSet (sortSmtSort poly env a)
+      | setConName == symbol c   = SSet (sortSmtSort poly env a)
     go (FTC c) [a]
-      | bagConName == symbol c  = SBag (sortSmtSort poly env a)
+      | bagConName == symbol c   = SBag (sortSmtSort poly env a)
+    go (FTC c) [FNatNum n]
+      | ffldConName == symbol c  = SFFld n
     go (FTC c) [a, b]
       | arrayConName == symbol c = SArray (sortSmtSort poly env a) (sortSmtSort poly env b)
     go (FTC bv) [FTC s]
@@ -362,6 +360,7 @@
   pprintTidy _ SString      = text "Str"
   pprintTidy k (SSet a)     = ppParens k (text "Set") [a]
   pprintTidy k (SBag a)     = ppParens k (text "Bag") [a]
+  pprintTidy _ (SFFld n)    = text "FiniteField" <+> integer n
   pprintTidy k (SArray a b) = ppParens k (text "Array") [a, b]
   pprintTidy _ (SBitVec n)  = text "BitVec" <+> int n
   pprintTidy _ (SVar i)     = text "@" <-> int i
@@ -376,13 +375,18 @@
 --------------------------------------------------------------------------------
 
 coerceSortEnv :: ElabFlags -> SEnv Sort -> SEnv Sort
-coerceSortEnv ef ss = (if elabSetBag ef then coerceSetBagToArray else id) . coerceMapToArray <$> ss
+coerceSortEnv ef ss = coerceSort ef <$> ss
 
+coerceSort :: ElabFlags -> Sort -> Sort
+coerceSort ef = (if elabSetBag ef then coerceSetBagToArray else id) . coerceMapToArray
+
 coerceEnv :: ElabFlags -> SymEnv -> SymEnv
 coerceEnv slv env =
-  SymEnv { seSort   = coerceSortEnv slv (seSort env)
-         , seTheory = seTheory env
-         , seData   = seData   env
-         , seLits   = seLits   env
-         , seAppls  = seAppls  env
+  SymEnv { seSort     = coerceSortEnv slv (seSort env)
+         , seTheory   = seTheory env
+         , seData     = seData   env
+         , seLits     = seLits   env
+         , seAppls    = seAppls  env
+         , seApplsCur = seApplsCur env
+         , seIx       = seIx     env
          }
diff --git a/src/Language/Fixpoint/Types/Utils.hs b/src/Language/Fixpoint/Types/Utils.hs
--- a/src/Language/Fixpoint/Types/Utils.hs
+++ b/src/Language/Fixpoint/Types/Utils.hs
@@ -31,14 +31,14 @@
 --------------------------------------------------------------------------------
 -- | Compute the domain of a kvar
 --------------------------------------------------------------------------------
-kvarDomain :: SInfo a -> KVar -> [Symbol]
+kvarDomain :: GInfo c a -> KVar -> [Symbol]
 --------------------------------------------------------------------------------
 kvarDomain si k = domain (bs si) (getWfC si k)
 
 domain :: BindEnv a -> WfC a -> [Symbol]
 domain be wfc = fst3 (wrft wfc) : map fst (envCs be $ wenv wfc)
 
-getWfC :: SInfo a -> KVar -> WfC a
+getWfC :: GInfo c a -> KVar -> WfC a
 getWfC si k = ws si M.! k
 
 --------------------------------------------------------------------------------
@@ -49,19 +49,20 @@
 reftFreeVars r@(Reft (v, _)) = S.delete v $ S.fromList $ syms r
 
 --------------------------------------------------------------------------------
--- | Split a SortedReft into its concrete and KVar components
+-- | Split a SortedReft into its concrete and KVar conjuncts
+--
+-- Produces @(concrete conjunts, normal kvars)@
 --------------------------------------------------------------------------------
-sortedReftConcKVars :: Symbol -> SortedReft -> ([Pred], [KVSub], [KVSub])
-sortedReftConcKVars x sr = go [] [] [] ves
+sortedReftConcKVars :: Symbol -> SortedReft -> ([Pred], [KVSub])
+sortedReftConcKVars x sr = go [] [] ves
   where
     ves                  = [(v, p `subst1` (v, eVar x)) | Reft (v, p) <- rs ]
     rs                   = reftConjuncts (sr_reft sr)
     t                    = sr_sort sr
 
-    go ps ks gs ((v, PKVar k su    ):xs) = go ps (KVS v t k su:ks) gs xs
-    go ps ks gs ((v, PGrad k su _ _):xs) = go ps ks (KVS v t k su:gs) xs
-    go ps ks gs ((_, p):xs)              = go (p:ps) ks gs xs
-    go ps ks gs []                       = (ps, ks, gs)
+    go ps ks ((v, PKVar k su    ):xs) = go ps (KVS v t k su:ks) xs
+    go ps ks ((_, p):xs)              = go (p:ps) ks xs
+    go ps ks []                       = (ps, ks)
 
 
 -------------------------------------------------------------------------------
diff --git a/src/Language/Fixpoint/Types/Visitor.hs b/src/Language/Fixpoint/Types/Visitor.hs
--- a/src/Language/Fixpoint/Types/Visitor.hs
+++ b/src/Language/Fixpoint/Types/Visitor.hs
@@ -33,7 +33,7 @@
   , envKVars
   , envKVarsN
   , rhsKVars
-  , mapKVars, mapKVars', mapGVars', mapKVarSubsts
+  , mapKVars, mapKVars', mapKVarSubsts
   , mapExpr, mapExprOnExpr, mapMExpr
 
   -- * Coercion Substitutions
@@ -112,6 +112,7 @@
       step (EApp e1 e2)       = EApp (vE e1) (vE e2)
       step (ENeg e)         = ENeg (vE e)
       step (EBin o e1 e2)   = EBin o (vE e1) (vE e2)
+      step (ELet x e1 e2)   = ELet x (vE e1) (vE e2)
       step (EIte p e1 e2)   = EIte (vE p) (vE e1) (vE e2)
       step (ECst e t)       = ECst (vE e) t
       step (PAnd ps)        = PAnd (map vE ps)
@@ -127,7 +128,6 @@
       step (ETApp e s)      = ETApp (vE e) s
       step (ETAbs e s)      = ETAbs (vE e) s
       step p@(PKVar _ _)    = p
-      step (PGrad k su i e) = PGrad k su i (vE e)
 
 instance Visitable Reft where
   transE v (Reft (x, ra)) = Reft (x, transE v ra)
@@ -164,7 +164,7 @@
     aenvEqs = transE v <$> aenvEqs x,
     aenvSimpl = transE v <$> aenvSimpl x
   }
-    
+
 instance Visitable Equation where
   transE v eq = eq {
     eqBody = transE v (eqBody eq)
@@ -180,12 +180,12 @@
   rn <- newIORef a
   result <- runReaderT (f v c x) rn
   finalAcc <- readIORef rn
-  return (result, finalAcc) 
+  return (result, finalAcc)
 
 type FoldM acc = ReaderT (IORef acc) IO
 
 accum :: (Monoid a) => a -> FoldM a ()
-accum !z = do 
+accum !z = do
   ref <- ask
   liftIO $ modifyIORef' ref (mappend z)
 
@@ -259,6 +259,7 @@
     step !c (EApp f e)      = EApp        <$> vE c f  <*> vE c e
     step !c (ENeg e)        = ENeg        <$> vE c e
     step !c (EBin o e1 e2)  = EBin o      <$> vE c e1 <*> vE c e2
+    step !c (ELet x e1 e2)  = ELet x      <$> vE c e1 <*> vE c e2
     step !c (EIte p e1 e2)  = EIte        <$> vE c p  <*> vE c e1 <*> vE c e2
     step !c (ECst e t)      = (`ECst` t)  <$> vE c e
     step !c (PAnd  ps)      = PAnd        <$> (vE c `traverse` ps)
@@ -274,7 +275,6 @@
     step !c (ETApp e s)     = (`ETApp` s) <$> vE c e
     step !c (ETAbs e s)     = (`ETAbs` s) <$> vE c e
     step _  p@(PKVar _ _)   = return p
-    step !c (PGrad k su i e) = PGrad k su i <$> vE c e
 
 mapKVars :: Visitable t => (KVar -> Maybe Expr) -> t -> t
 mapKVars f = mapKVars' f'
@@ -286,19 +286,9 @@
   where
     txK (PKVar k su)
       | Just p' <- f (k, su) = subst su p'
-    txK (PGrad k su _ _)
-      | Just p' <- f (k, su) = subst su p'
     txK p = p
 
 
-
-mapGVars' :: Visitable t => ((KVar, Subst) -> Maybe Expr) -> t -> t
-mapGVars' f            = trans txK
-  where
-    txK (PGrad k su _ _)
-      | Just p' <- f (k, su) = subst su p'
-    txK p            = p
-
 mapExpr :: Visitable t => (Expr -> Expr) -> t -> t
 mapExpr f = trans f
 
@@ -318,6 +308,10 @@
         let !e1' = go e1
             !e2' = go e2
         in EBin o e1' e2'
+      ELet x e1 e2 ->
+        let !e1' = go e1
+            !e2' = go e2
+        in ELet x e1' e2'
       EIte p e1 e2 ->
         let !p' = go p
             !e1' = go e1
@@ -365,10 +359,7 @@
       ETAbs e s ->
         let !e' = go e
         in ETAbs e' s
-      PGrad k su i e ->
-        let !e' = go e
-        in PGrad k su i e'
-      e@PKVar{} -> e
+      PKVar k (Su m) -> PKVar k (Su (go <$>m))
       e@EVar{} -> e
       e@ESym{} -> e
       e@ECon{} -> e
@@ -408,7 +399,6 @@
     go e@(ECon _)      = f e
     go e@(EVar _)      = f e
     go e@(PKVar _ _)   = f e
-    go (PGrad k s i e) = f . PGrad k s i =<< go e
     go (ENeg e)        = f . ENeg =<< go e
     go (PNot p)        = f . PNot =<< go p
     go (ECst e t)      = f . (`ECst` t) =<< go e
@@ -423,6 +413,7 @@
     go (PImp p1 p2)    = f =<< (PImp        <$>  go p1 <*> go p2          )
     go (PIff p1 p2)    = f =<< (PIff        <$>  go p1 <*> go p2          )
     go (PAtom r e1 e2) = f =<< (PAtom r     <$>  go e1 <*> go e2          )
+    go (ELet x e1 e2)  = f =<< (ELet x      <$>  go e1 <*> go e2          )
     go (EIte p e1 e2)  = f =<< (EIte        <$>  go p  <*> go e1 <*> go e2)
     go (PAnd ps)       = f . PAnd =<< (go `traverse` ps)
     go (POr ps)        = f . POr =<< (go `traverse` ps)
@@ -431,7 +422,6 @@
 mapKVarSubsts f          = trans txK
   where
     txK (PKVar k su)   = PKVar k (f k su)
-    txK (PGrad k su i e) = PGrad k (f k su) i e
     txK p              = p
 
 newtype MInt = MInt Integer -- deriving (Eq, NFData)
@@ -475,7 +465,6 @@
       ECon _ -> acc
       EVar _ -> acc
       PKVar k _ -> k : acc
-      PGrad k _ _ _ -> k : acc
       ENeg e -> go acc e
       PNot p -> go acc p
       ECst e _t -> go acc e
@@ -490,6 +479,7 @@
       PImp p1 p2 -> go (go acc p2) p1
       PIff p1 p2 -> go (go acc p2) p1
       PAtom _r e1 e2 -> go (go acc e2) e1
+      ELet _ e1 e2 -> go (go acc e2) e1
       EIte p e1 e2 -> go (go (go acc e2) e1) p
       PAnd ps -> foldr (flip go) acc ps
       POr ps -> foldr (flip go) acc ps
@@ -517,7 +507,6 @@
 
 isKvar :: Expr -> Bool
 isKvar PKVar{} = True
-isKvar PGrad{} = True
 isKvar _       = False
 
 isConc :: Expr -> Bool
diff --git a/src/Language/Fixpoint/Utils/Builder.hs b/src/Language/Fixpoint/Utils/Builder.hs
--- a/src/Language/Fixpoint/Utils/Builder.hs
+++ b/src/Language/Fixpoint/Utils/Builder.hs
@@ -6,6 +6,7 @@
   ( fromText
   , fromString
   , parens
+  , quotes
   , (<+>)
   , parenSeqs
   , seqs
@@ -31,6 +32,9 @@
 
 parens :: Builder -> Builder
 parens b = "(" <>  b <> ")"
+
+quotes :: Builder -> Builder
+quotes b = "\"" <> b <> "\""
 
 infixl 9 <+>
 (<+>) :: Builder -> Builder -> Builder
diff --git a/src/Language/Fixpoint/Utils/Files.hs b/src/Language/Fixpoint/Utils/Files.hs
--- a/src/Language/Fixpoint/Utils/Files.hs
+++ b/src/Language/Fixpoint/Utils/Files.hs
@@ -14,6 +14,7 @@
   , extFileName
   , extFileNameR
   , tempDirectory
+  , tempFileName
   , extModuleName
   , withExt
   , isExtFile
@@ -139,11 +140,10 @@
 withExt f ext   =  replaceExtension f (extMap ext)
 
 extFileName     :: Ext -> FilePath -> FilePath
-extFileName e f = path </> addExtension file ext
-  where
-    path        = tempDirectory f
-    file        = takeFileName  f
-    ext         = extMap e
+extFileName e f = tempFileName (addExtension f (extMap e))
+
+tempFileName     :: FilePath -> FilePath
+tempFileName f = tempDirectory f </> takeFileName f
 
 tempDirectory   :: FilePath -> FilePath
 tempDirectory f
diff --git a/tests/neg/ebind-00.fq b/tests/neg/ebind-00.fq
deleted file mode 100644
--- a/tests/neg/ebind-00.fq
+++ /dev/null
@@ -1,16 +0,0 @@
-fixpoint "--eliminate=all"
-
-// bind  0 x1 : {v: int | v = 10 }
-ebind 0 x1 : { int }
-
-constraint:
-  env [0]
-  lhs {v1 : int | v1 = 10}
-  rhs {v1 : int | v1 = x1}
-  id 1 tag []
-
-constraint:
-  env [0]
-  lhs {v2 : int | v2 = x1 + 1 }
-  rhs {v2 : int | v2 = 110    }
-  id 2 tag []
diff --git a/tests/neg/ebind-01.fq b/tests/neg/ebind-01.fq
deleted file mode 100644
--- a/tests/neg/ebind-01.fq
+++ /dev/null
@@ -1,17 +0,0 @@
-fixpoint "--eliminate=all"
-
-bind  1 m  : {v: int | true }
-ebind 2 x1 : { int }
-// bind  2 x1 : {v: int | v = m + 1 }
-
-constraint:
-  env [1; 2]
-  lhs {v : int | v = m + 1 }
-  rhs {v : int | v = x1    }
-  id 1 tag []
-
-constraint:
-  env [1; 2]
-  lhs {v : int | v = x1 + 1}
-  rhs {v : int | v = 20 + m }
-  id 2 tag []
diff --git a/tests/neg/ebind-02.fq b/tests/neg/ebind-02.fq
deleted file mode 100644
--- a/tests/neg/ebind-02.fq
+++ /dev/null
@@ -1,28 +0,0 @@
-fixpoint "--eliminate=all"
-
-bind  0 m  : {v: int | true }
-bind  1 z  : {v: int | v = m - 1 }
-ebind 2 x1 : { int }
-// bind  2 x1 : {v: int | v = m + 1 }
-
-constraint:
-  env [0; 1]
-  lhs {v : int | v = z + 2 }
-  rhs {v : int | $k         }
-  id 1 tag []
-
-constraint:
-  env [0; 2]
-  lhs {v : int | $k      }
-  rhs {v : int | v = x1 }
-  id 2 tag []
-
-constraint:
-  env [0; 2]
-  lhs {v : int | v = x1 + 1 }
-  rhs {v : int | v = m  + 20 }
-  id 3 tag []
-
-wf: 
-  env [0]
-  reft {v:int | [$k] }
diff --git a/tests/neg/ebind-03.fq b/tests/neg/ebind-03.fq
deleted file mode 100644
--- a/tests/neg/ebind-03.fq
+++ /dev/null
@@ -1,45 +0,0 @@
-fixpoint "--eliminate=all"
-
-ebind 1 x1 : { int }
-ebind 2 x2 : { int }
-
-
-constraint:
-  env [1]
-  lhs {v : int | v = 1  }
-  rhs {v : int | v = x1 }
-  id 1 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | v = x1 + 1 }
-  rhs {v : int | $ka        }
-  id 2 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | $ka    }
-  rhs {v : int | v = x2 }
-  id 3 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | v = x2 + 1}
-  rhs {v : int | $kb       }
-  id 4 tag []
-
-constraint:
-  env []
-  lhs {v : int | $kb   }
-  rhs {v : int | v = 30 } 
-  id 5 tag []
-
-
-wf: 
-  env []
-  reft {v:int | [$ka] }
-
-wf: 
-  env []
-  reft {v:int | [$kb] }
-
diff --git a/tests/neg/ebind-04.fq b/tests/neg/ebind-04.fq
deleted file mode 100644
--- a/tests/neg/ebind-04.fq
+++ /dev/null
@@ -1,35 +0,0 @@
-fixpoint "--eliminate=all"
-
-ebind 1 x : { int }
-
-constraint:
-  env [1]
-  lhs {v : int | $k1   }
-  rhs {v : int | v = x }
-  id 1 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | v = x + 1 }
-  rhs {v : int | $k2       }
-  id 2 tag []
-
-constraint:
-  env []
-  lhs {v : int | v = 3 }
-  rhs {v : int | $k1   }
-  id 3 tag []
-
-constraint:
-  env []
-  lhs {v : int | $k2   }
-  rhs {v : int | v = 40 }
-  id 4 tag []
-
-wf: 
-  env []
-  reft {v:int | [$k1] }
-
-wf: 
-  env []
-  reft {v:int | [$k2] }
diff --git a/tests/neg/ebind-elim2.fq b/tests/neg/ebind-elim2.fq
deleted file mode 100644
--- a/tests/neg/ebind-elim2.fq
+++ /dev/null
@@ -1,26 +0,0 @@
-bind  1 a    : { a : int | true }
-ebind 2 c    : { int }
-
-wf:
-  env [1]
-  reft {VV##1 : Tuple | [$k_##1]}
-
-constraint:
-  env [1;2]
-  lhs {VV##F##4 : int | VV##F##4 = c }
-  rhs {VV##F##4 : int | $k_##1[VV##1 := VV##F##4] }
-  id 1 tag []
-
-constraint:
-  env [1;2]
-  lhs {VV##F##5 : int | $k_##1[VV##1:=VV##F##5] }
-  rhs {VV##F##5 : int | VV##F##5 = c }
-  id 2 tag []
-
-// The following constraint is needed only to prevent eliminate's **sharing** optimization.
-
-constraint:
-  env []
-  lhs {VV##F##6 : int | $k_##1[VV##1:=VV##F##6] }
-  rhs {VV##F##6 : int | VV##F##6 = 0 }
-  id 3 tag []
diff --git a/tests/neg/elim-dep-00.fq b/tests/neg/elim-dep-00.fq
--- a/tests/neg/elim-dep-00.fq
+++ b/tests/neg/elim-dep-00.fq
@@ -1,7 +1,7 @@
 
-qualif False(v:int) : (0 = 1)
-qualif Zero(v:int) : (0 = v)
-qualif One(v:int) : (1 = v)
+qualif False(v:int)  { 0 = 1 }
+qualif Zero(v:int)  { 0 = v }
+qualif One(v:int)  { 1 = v }
 
 bind 1 x : {v:int | $k2 }
 
diff --git a/tests/neg/qualif-template-00.fq b/tests/neg/qualif-template-00.fq
--- a/tests/neg/qualif-template-00.fq
+++ b/tests/neg/qualif-template-00.fq
@@ -1,4 +1,4 @@
-qualif Prefix(v:a, z as (moon . $1) : b) : (v = z)
+qualif Prefix(v:a, z as (moon . $1) : b)  { v = z }
 
 bind 0  monday  : {v : int | true}
 bind 10 tuesday : {v : int | true}
diff --git a/tests/neg/qualif-template-01.fq b/tests/neg/qualif-template-01.fq
--- a/tests/neg/qualif-template-01.fq
+++ b/tests/neg/qualif-template-01.fq
@@ -1,6 +1,6 @@
 // qualif Goob(v:a, z: b) : (v = z)
 // qualif Prefix2(v:a, x as (mon . $1) : b, y as (sun . $1)) : (v = x + y)
-qualif Prefix(v:a, z as ($1 . sday) : b) : (v = z)
+qualif Prefix(v:a, z as ($1 . sday) : b)  { v = z }
 
 bind 0  monday  : {v : int | true}
 bind 10 tuesday : {v : int | true}
diff --git a/tests/neg/qualif-template-02.fq b/tests/neg/qualif-template-02.fq
--- a/tests/neg/qualif-template-02.fq
+++ b/tests/neg/qualif-template-02.fq
@@ -1,6 +1,6 @@
 fixpoint "--eliminate=none"
 
-qualif Prefix2(v:a, x as (sun . $1)  : b, y as (tues . $1) : b) : (v = x + y)
+qualif Prefix2(v:a, x as (sun . $1)  : b, y as (tues . $1) : b)  { v = x + y }
 
 bind 0  sunday  : {v : int | v = 6  }
 bind 1  monday  : {v : int | v = 4  }
diff --git a/tests/neg/test00.fq b/tests/neg/test00.fq
--- a/tests/neg/test00.fq
+++ b/tests/neg/test00.fq
@@ -1,6 +1,6 @@
 
-qualif Zog(v:a) : (10 <= v)
-qualif Bog(v:a, x:a) : (x <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Bog(v:a, x:a)  { x <= v }
 
 bind 0 a : {v: int | $k0}
 
diff --git a/tests/neg/test00.hs.fq b/tests/neg/test00.hs.fq
--- a/tests/neg/test00.hs.fq
+++ b/tests/neg/test00.hs.fq
@@ -1,51 +1,51 @@
-qualif Fst(v : @(1), y : @(0)): (v = fst([y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 29, column 8)
-qualif Snd(v : @(1), y : @(0)): (v = snd([y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 30, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) > 0)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 13, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) = 0)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 14, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) = 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 16, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) >= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 17, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) > 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 18, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) = len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 20, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) >= len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 21, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) > len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 22, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) <= len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 23, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) < len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 24, column 8)
-qualif EqLen(v : int, xs : [@(0)]): (v = len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 26, column 8)
-qualif LenEq(v : [@(0)], x : int): (x = len([v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 27, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x + 1)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 28, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x - 1)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 29, column 8)
-qualif LenAcc(v : int, xs : [@(0)], n : int): (v = (len([xs]) + n)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 30, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 3, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 4, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 5, column 8)
-qualif Bot(v : bool): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 6, column 8)
-qualif Bot(v : int): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 7, column 8)
-qualif CmpZ(v : @(0)): (v < 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 9, column 8)
-qualif CmpZ(v : @(0)): (v <= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 10, column 8)
-qualif CmpZ(v : @(0)): (v > 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 11, column 8)
-qualif CmpZ(v : @(0)): (v >= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 12, column 8)
-qualif CmpZ(v : @(0)): (v = 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 13, column 8)
-qualif CmpZ(v : @(0)): (v != 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 14, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v < x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 16, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v <= x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 17, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v > x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 18, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v >= x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 19, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v = x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 20, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v != x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 21, column 8)
-qualif One(v : int): (v = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 28, column 8)
-qualif True(v : bool): (? v) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 29, column 8)
-qualif False(v : bool): (~ ((? v))) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 30, column 8)
-qualif True1(v : GHC.Types.Bool): (? Prop([v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 31, column 8)
-qualif False1(v : GHC.Types.Bool): (~ ((? Prop([v])))) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 32, column 8)
-qualif Papp(v : @(0), p : (Pred  @(0))): (? papp1([p;
-                                                   v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 35, column 8)
-qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))): (? papp2([p;
+qualif Fst(v : @(1), y : @(0)) { v = fst([y])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 29, column 8)
+qualif Snd(v : @(1), y : @(0)) { v = snd([y])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 30, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (Prop([v])) <=> (len([xs]) > 0)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 13, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (Prop([v])) <=> (len([xs]) = 0)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 14, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) = 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 16, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) >= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 17, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) > 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 18, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) = len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 20, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) >= len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 21, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) > len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 22, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) <= len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 23, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) < len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 24, column 8)
+qualif EqLen(v : int, xs : [@(0)]) { v = len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 26, column 8)
+qualif LenEq(v : [@(0)], x : int) { x = len([v])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 27, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x + 1)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 28, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x - 1)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 29, column 8)
+qualif LenAcc(v : int, xs : [@(0)], n : int) { v = (len([xs]) + n)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 30, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 3, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 4, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 5, column 8)
+qualif Bot(v : bool) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 6, column 8)
+qualif Bot(v : int) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 7, column 8)
+qualif CmpZ(v : @(0)) { v < 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 9, column 8)
+qualif CmpZ(v : @(0)) { v <= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 10, column 8)
+qualif CmpZ(v : @(0)) { v > 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 11, column 8)
+qualif CmpZ(v : @(0)) { v >= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 12, column 8)
+qualif CmpZ(v : @(0)) { v = 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 13, column 8)
+qualif CmpZ(v : @(0)) { v != 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 14, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v < x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 16, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v <= x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 17, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v > x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 18, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v >= x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 19, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v = x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 20, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v != x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 21, column 8)
+qualif One(v : int) { v = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 28, column 8)
+qualif True(v : bool) { v  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 29, column 8)
+qualif False(v : bool) { ~ ((v))  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 30, column 8)
+qualif True1(v : GHC.Types.Bool) { Prop([v])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 31, column 8)
+qualif False1(v : GHC.Types.Bool) { ~ ((Prop([v])))  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 32, column 8)
+qualif Papp(v : @(0), p : (Pred  @(0))) { (papp1([p;
+                                                   v])) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 35, column 8)
+qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))) { (papp2([p;
                                                                     v;
-                                                                    x])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 38, column 8)
-qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))): (? papp3([p;
+                                                                    x])) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 38, column 8)
+qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))) { (papp3([p;
                                                                                     v;
                                                                                     x;
-                                                                                    y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 41, column 8)
+                                                                                    y])) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 41, column 8)
 
 
 
@@ -137,33 +137,33 @@
 bind 3 GHC.Types.EQ$35$6U : {VV$35$167 : GHC.Types.Ordering | [(VV$35$167 = GHC.Types.EQ$35$6U)]}
 bind 4 GHC.Types.LT$35$6S : {VV$35$168 : GHC.Types.Ordering | [(VV$35$168 = GHC.Types.LT$35$6S)]}
 bind 5 GHC.Types.GT$35$6W : {VV$35$169 : GHC.Types.Ordering | [(VV$35$169 = GHC.Types.GT$35$6W)]}
-bind 6 GHC.Types.True$35$6u : {v$35$4 : GHC.Types.Bool | [(? Prop([v$35$4]))]}
-bind 7 GHC.Types.False$35$68 : {v$35$5 : GHC.Types.Bool | [(~ ((? Prop([v$35$5]))))]}
-bind 8 GHC.Types.False$35$68 : {v$35$5 : GHC.Types.Bool | [(~ ((? Prop([v$35$5]))))]}
+bind 6 GHC.Types.True$35$6u : {v$35$4 : GHC.Types.Bool | [(Prop([v$35$4]))]}
+bind 7 GHC.Types.False$35$68 : {v$35$5 : GHC.Types.Bool | [(~ ((Prop([v$35$5]))))]}
+bind 8 GHC.Types.False$35$68 : {v$35$5 : GHC.Types.Bool | [(~ ((Prop([v$35$5]))))]}
 bind 9 GHC.Types.$91$$93$$35$6m : {VV : func(1, [[@(0)]]) | []}
-bind 10 GHC.Types.True$35$6u : {v$35$4 : GHC.Types.Bool | [(? Prop([v$35$4]))]}
+bind 10 GHC.Types.True$35$6u : {v$35$4 : GHC.Types.Bool | [(Prop([v$35$4]))]}
 bind 11 GHC.Types.GT$35$6W : {VV$35$214 : GHC.Types.Ordering | [(cmp([VV$35$214]) = GHC.Types.GT$35$6W)]}
 bind 12 GHC.Types.LT$35$6S : {VV$35$215 : GHC.Types.Ordering | [(cmp([VV$35$215]) = GHC.Types.LT$35$6S)]}
 bind 13 GHC.Types.EQ$35$6U : {VV$35$216 : GHC.Types.Ordering | [(cmp([VV$35$216]) = GHC.Types.EQ$35$6U)]}
 bind 14 GHC.Base.Nothing$35$r1d : {VV : func(1, [(GHC.Base.Maybe  @(0))]) | []}
 bind 15 z$35$a10N : {VV$35$221 : int | [$k_$35$222]}
 bind 16 lq_anf$36$_d116 : {lq_tmp$36$x$35$229 : int | [(lq_tmp$36$x$35$229 = (100  :  int))]}
-bind 17 lq_anf$36$_d117 : {lq_tmp$36$x$35$236 : GHC.Types.Bool | [((? Prop([lq_tmp$36$x$35$236])) <=> (z$35$a10N >= lq_anf$36$_d116))]}
+bind 17 lq_anf$36$_d117 : {lq_tmp$36$x$35$236 : GHC.Types.Bool | [((Prop([lq_tmp$36$x$35$236])) <=> (z$35$a10N >= lq_anf$36$_d116))]}
 bind 18 lq_anf$36$_d118 : {lq_tmp$36$x$35$254 : int | [(lq_tmp$36$x$35$254 = (0  :  int))]}
 bind 19 Test0.x$35$rYP : {VV$35$250 : int | [$k_$35$251]}
 bind 20 lq_anf$36$_d119 : {lq_tmp$36$x$35$269 : int | [(lq_tmp$36$x$35$269 = (0  :  int))]}
-bind 21 lq_anf$36$_d11a : {lq_tmp$36$x$35$275 : GHC.Types.Bool | [((? Prop([lq_tmp$36$x$35$275])) <=> (Test0.x$35$rYP > lq_anf$36$_d119))]}
+bind 21 lq_anf$36$_d11a : {lq_tmp$36$x$35$275 : GHC.Types.Bool | [((Prop([lq_tmp$36$x$35$275])) <=> (Test0.x$35$rYP > lq_anf$36$_d119))]}
 bind 22 lq_anf$36$_d11b : {lq_tmp$36$x$35$291 : GHC.Types.Bool | [(lq_tmp$36$x$35$291 = lq_anf$36$_d11a)]}
 bind 23 lq_anf$36$_d11b : {lq_tmp$36$x$35$293 : GHC.Types.Bool | [(lq_tmp$36$x$35$293 = lq_anf$36$_d11a)]}
 bind 24 lq_anf$36$_d11b : {lq_tmp$36$x$35$293 : GHC.Types.Bool | [(lq_tmp$36$x$35$293 = lq_anf$36$_d11a);
-                                                                  (~ ((? Prop([lq_tmp$36$x$35$293]))));
-                                                                  (~ ((? Prop([lq_tmp$36$x$35$293]))));
-                                                                  (~ ((? Prop([lq_tmp$36$x$35$293]))))]}
+                                                                  (~ ((Prop([lq_tmp$36$x$35$293]))));
+                                                                  (~ ((Prop([lq_tmp$36$x$35$293]))));
+                                                                  (~ ((Prop([lq_tmp$36$x$35$293]))))]}
 bind 25 lq_anf$36$_d11b : {lq_tmp$36$x$35$299 : GHC.Types.Bool | [(lq_tmp$36$x$35$299 = lq_anf$36$_d11a)]}
 bind 26 lq_anf$36$_d11b : {lq_tmp$36$x$35$299 : GHC.Types.Bool | [(lq_tmp$36$x$35$299 = lq_anf$36$_d11a);
-                                                                  (? Prop([lq_tmp$36$x$35$299]));
-                                                                  (? Prop([lq_tmp$36$x$35$299]));
-                                                                  (? Prop([lq_tmp$36$x$35$299]))]}
+                                                                  (Prop([lq_tmp$36$x$35$299]));
+                                                                  (Prop([lq_tmp$36$x$35$299]));
+                                                                  (Prop([lq_tmp$36$x$35$299]))]}
 bind 27 Test0.prop_abs$35$r10h : {VV$35$265 : GHC.Types.Bool | [$k_$35$266]}
 bind 28 VV$35$310 : {VV$35$310 : GHC.Types.Bool | [$k_$35$226[lq_tmp$36$x$35$307:=Test0.x$35$rYP][lq_tmp$36$x$35$305:=VV$35$310][VV$35$225:=VV$35$310][z$35$a10N:=Test0.x$35$rYP]]}
 bind 29 VV$35$310 : {VV$35$310 : GHC.Types.Bool | [$k_$35$226[lq_tmp$36$x$35$307:=Test0.x$35$rYP][lq_tmp$36$x$35$305:=VV$35$310][VV$35$225:=VV$35$310][z$35$a10N:=Test0.x$35$rYP]]}
@@ -183,8 +183,8 @@
 bind 43 VV$35$331 : {VV$35$331 : int | [(VV$35$331 = lq_anf$36$_d118)]}
 bind 44 VV$35$334 : {VV$35$334 : int | [(VV$35$334 = 0)]}
 bind 45 VV$35$334 : {VV$35$334 : int | [(VV$35$334 = 0)]}
-bind 46 VV$35$337 : {VV$35$337 : GHC.Types.Bool | [(? Prop([VV$35$337]))]}
-bind 47 VV$35$337 : {VV$35$337 : GHC.Types.Bool | [(? Prop([VV$35$337]))]}
+bind 46 VV$35$337 : {VV$35$337 : GHC.Types.Bool | [(Prop([VV$35$337]))]}
+bind 47 VV$35$337 : {VV$35$337 : GHC.Types.Bool | [(Prop([VV$35$337]))]}
 bind 48 VV$35$340 : {VV$35$340 : GHC.Types.Bool | [(VV$35$340 = lq_anf$36$_d117)]}
 bind 49 VV$35$340 : {VV$35$340 : GHC.Types.Bool | [(VV$35$340 = lq_anf$36$_d117)]}
 bind 50 VV$35$343 : {VV$35$343 : int | [(VV$35$343 = lq_anf$36$_d116)]}
@@ -260,7 +260,7 @@
        14;
        15]
   lhs {VV$35$F8 : GHC.Types.Bool | [(VV$35$F8 = lq_anf$36$_d117)]}
-  rhs {VV$35$F8 : GHC.Types.Bool | [(? Prop([VV$35$F8]))]}
+  rhs {VV$35$F8 : GHC.Types.Bool | [(Prop([VV$35$F8]))]}
   id 8 tag [1]
   // META constraint id 8 : tests/neg/test00.hs:11:23-35
 
diff --git a/tests/neg/test00a.fq b/tests/neg/test00a.fq
--- a/tests/neg/test00a.fq
+++ b/tests/neg/test00a.fq
@@ -1,6 +1,6 @@
 // This qualifier saves the day; solve constraints WITHOUT IT
 
-qualif Zog(v:a) : (10 <= v)
+qualif Zog(v:a)  { 10 <= v }
 
 bind 0 x : {v : int | true}
 bind 1 y : {v : int | true}
diff --git a/tests/neg/test1.fq b/tests/neg/test1.fq
--- a/tests/neg/test1.fq
+++ b/tests/neg/test1.fq
@@ -1,6 +1,6 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
-qualif Zog(v:a) : (10 <= v)
+qualif Zog(v:a)  { 10 <= v }
 
 bind 0 x : {v : int | v = 9}
 bind 1 y : {v : int | v = 20}
diff --git a/tests/neg/test2.fq b/tests/neg/test2.fq
--- a/tests/neg/test2.fq
+++ b/tests/neg/test2.fq
@@ -1,9 +1,9 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
-qualif Zog(v:a): (10 <= v)
+qualif Zog(v:a) { 10 <= v }
 
 // But you may use this one
-qualif Pog(v:a): (0 <= v)
+qualif Pog(v:a) { 0 <= v }
 
 bind 0 x: {v: int | v = 9 }
 bind 1 a: {v: int | $k1    }
diff --git a/tests/neg/test3.fq b/tests/neg/test3.fq
--- a/tests/neg/test3.fq
+++ b/tests/neg/test3.fq
@@ -1,5 +1,5 @@
 
-qualif Zog(v:a, z:b) : (v = z)
+qualif Zog(v:a, z:b)  { v = z }
 
 bind 0 x : {v : int | true}
 bind 1 q : {v : int | true}
diff --git a/tests/pos/LogicCurry1.hs.fq b/tests/pos/LogicCurry1.hs.fq
--- a/tests/pos/LogicCurry1.hs.fq
+++ b/tests/pos/LogicCurry1.hs.fq
@@ -1,46 +1,46 @@
 fixpoint "--allowho"
 
-qualif Fst(v : @(1), y : @(0)): ((v = (fst y))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.spec" (line 28, column 8)
-qualif Snd(v : @(1), y : @(0)): ((v = (snd y))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.spec" (line 29, column 8)
-qualif Auto(v##1 : int, n : int, x : int): ((v##1 = (ack n x))) // "/Users/rjhala/research/stack/liquidhaskell/tests/pos/LogicCurry1.hs" (line 10, column 1)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((v <=> ((len xs) > 0))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 13, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((v <=> ((len xs) = 0))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 14, column 8)
-qualif ListZ(v : [@(0)]): (((len v) = 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 16, column 8)
-qualif ListZ(v : [@(0)]): (((len v) >= 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 17, column 8)
-qualif ListZ(v : [@(0)]): (((len v) > 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 18, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) = (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 20, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) >= (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 21, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) > (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 22, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) <= (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 23, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) < (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 24, column 8)
-qualif EqLen(v : int, xs : [@(0)]): ((v = (len xs))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 26, column 8)
-qualif LenEq(v : [@(0)], x : int): ((x = (len v))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 27, column 8)
-qualif LenDiff(v : [@(0)], x : int): (((len v) = (x + 1))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 28, column 8)
-qualif LenDiff(v : [@(0)], x : int): (((len v) = (x - 1))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 29, column 8)
-qualif LenAcc(v : int, xs : [@(0)], n : int): ((v = ((len xs) + n))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 30, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 3, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 4, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 5, column 8)
-qualif Bot(v : bool): ((0 = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 6, column 8)
-qualif Bot(v : int): ((0 = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 7, column 8)
-qualif CmpZ(v : @(0)): ((v < 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 9, column 8)
-qualif CmpZ(v : @(0)): ((v <= 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 10, column 8)
-qualif CmpZ(v : @(0)): ((v > 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 11, column 8)
-qualif CmpZ(v : @(0)): ((v >= 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 12, column 8)
-qualif CmpZ(v : @(0)): ((v = 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 13, column 8)
-qualif CmpZ(v : @(0)): ((v != 0)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 14, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v < x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 16, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v <= x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 17, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v > x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 18, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v >= x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 19, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v = x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 20, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v != x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 21, column 8)
-qualif One(v : int): ((v = 1)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 28, column 8)
-qualif True1(v : GHC.Types.Bool): (v) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 29, column 8)
-qualif False1(v : GHC.Types.Bool): ((~ (v))) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 30, column 8)
-qualif Papp(v : @(0), p : (Pred  @(0))): ((papp1 p v)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 34, column 8)
-qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))): ((papp2 p v x)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 37, column 8)
-qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))): ((papp3 p v x y)) // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 39, column 8)
+qualif Fst(v : @(1), y : @(0)) { (v = (fst y))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.spec" (line 28, column 8)
+qualif Snd(v : @(1), y : @(0)) { (v = (snd y))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.spec" (line 29, column 8)
+qualif Auto(v##1 : int, n : int, x : int) { (v##1 = (ack n x))  } // "/Users/rjhala/research/stack/liquidhaskell/tests/pos/LogicCurry1.hs" (line 10, column 1)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (v <=> ((len xs) > 0))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 13, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (v <=> ((len xs) = 0))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 14, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) = 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 16, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) >= 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 17, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) > 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 18, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) = (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 20, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) >= (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 21, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) > (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 22, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) <= (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 23, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) < (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 24, column 8)
+qualif EqLen(v : int, xs : [@(0)]) { (v = (len xs))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 26, column 8)
+qualif LenEq(v : [@(0)], x : int) { (x = (len v))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 27, column 8)
+qualif LenDiff(v : [@(0)], x : int) { ((len v) = (x + 1))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 28, column 8)
+qualif LenDiff(v : [@(0)], x : int) { ((len v) = (x - 1))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 29, column 8)
+qualif LenAcc(v : int, xs : [@(0)], n : int) { (v = ((len xs) + n))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/GHC/Base.hquals" (line 30, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 3, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 4, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 5, column 8)
+qualif Bot(v : bool) { (0 = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 6, column 8)
+qualif Bot(v : int) { (0 = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 7, column 8)
+qualif CmpZ(v : @(0)) { (v < 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 9, column 8)
+qualif CmpZ(v : @(0)) { (v <= 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 10, column 8)
+qualif CmpZ(v : @(0)) { (v > 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 11, column 8)
+qualif CmpZ(v : @(0)) { (v >= 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 12, column 8)
+qualif CmpZ(v : @(0)) { (v = 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 13, column 8)
+qualif CmpZ(v : @(0)) { (v != 0)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 14, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v < x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 16, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v <= x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 17, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v > x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 18, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v >= x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 19, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v = x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 20, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v != x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 21, column 8)
+qualif One(v : int) { (v = 1)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 28, column 8)
+qualif True1(v : GHC.Types.Bool) { v  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 29, column 8)
+qualif False1(v : GHC.Types.Bool) { (~ (v))  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 30, column 8)
+qualif Papp(v : @(0), p : (Pred  @(0))) { (papp1 p v)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 34, column 8)
+qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))) { (papp2 p v x)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 37, column 8)
+qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))) { (papp3 p v x y)  } // "/Users/rjhala/research/stack/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2016-05-21/7.10.3/share/x86_64-osx-ghc-7.10.3/liquidhaskell-0.6.0.0/include/Prelude.hquals" (line 39, column 8)
 
 
 
diff --git a/tests/pos/MergeSort.fq b/tests/pos/MergeSort.fq
--- a/tests/pos/MergeSort.fq
+++ b/tests/pos/MergeSort.fq
@@ -1,52 +1,52 @@
-qualif Fst(v : @(1), y : @(0)): ((v = (fst y))) // "tests/todo/MergeSort.new.min.fq" (line 1, column 8)
-qualif Snd(v : @(1), y : @(0)): ((v = (snd y))) // "tests/todo/MergeSort.new.min.fq" (line 2, column 8)
-qualif Auto(VV : @(0), fld##0 : @(0)): ((VV >= fld##0)) // "tests/todo/MergeSort.new.min.fq" (line 3, column 8)
-qualif Auto(VV : @(0), fld##0 : @(0)): ((VV >= fld##0)) // "tests/todo/MergeSort.new.min.fq" (line 4, column 8)
-qualif Auto(VV : [@(0)], xs : [@(0)], ys : [@(0)]): (((len VV) = ((len xs) + (len ys)))) // "tests/todo/MergeSort.new.min.fq" (line 5, column 8)
-qualif Auto(VV : @(0), fld##0 : @(0)): ((VV >= fld##0)) // "tests/todo/MergeSort.new.min.fq" (line 6, column 8)
-qualif Auto(VV : [@(0)], xs : [@(0)]): (((len VV) = (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 7, column 8)
-qualif Auto(VV : @(0), fld##0 : @(0)): ((VV >= fld##0)) // "tests/todo/MergeSort.new.min.fq" (line 8, column 8)
-qualif Auto(v##0 : (Tuple  [@(0)]  [@(0)]), xs : [@(0)]): ((((len (fst v##0)) + (len (snd v##0))) = (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 9, column 8)
-qualif Auto(v##0 : [@(0)], xs : [@(0)]): ((((len v##0) > 1) => ((len v##0) < (len xs)))) // "tests/todo/MergeSort.new.min.fq" (line 10, column 8)
-qualif Auto(v##0 : [@(0)], xs : [@(0)]): ((((len v##0) > 1) => ((len v##0) < (len xs)))) // "tests/todo/MergeSort.new.min.fq" (line 11, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): (((Prop v) <=> ((len xs) > 0))) // "tests/todo/MergeSort.new.min.fq" (line 12, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): (((Prop v) <=> ((len xs) = 0))) // "tests/todo/MergeSort.new.min.fq" (line 13, column 8)
-qualif ListZ(v : [@(0)]): (((len v) = 0)) // "tests/todo/MergeSort.new.min.fq" (line 14, column 8)
-qualif ListZ(v : [@(0)]): (((len v) >= 0)) // "tests/todo/MergeSort.new.min.fq" (line 15, column 8)
-qualif ListZ(v : [@(0)]): (((len v) > 0)) // "tests/todo/MergeSort.new.min.fq" (line 16, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) = (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 17, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) >= (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 18, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) > (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 19, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) <= (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 20, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (((len v) < (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 21, column 8)
-qualif EqLen(v : int, xs : [@(0)]): ((v = (len xs))) // "tests/todo/MergeSort.new.min.fq" (line 22, column 8)
-qualif LenEq(v : [@(0)], x : int): ((x = (len v))) // "tests/todo/MergeSort.new.min.fq" (line 23, column 8)
-qualif LenDiff(v : [@(0)], x : int): (((len v) = (x + 1))) // "tests/todo/MergeSort.new.min.fq" (line 24, column 8)
-qualif LenDiff(v : [@(0)], x : int): (((len v) = (x - 1))) // "tests/todo/MergeSort.new.min.fq" (line 25, column 8)
-qualif LenAcc(v : int, xs : [@(0)], n : int): ((v = ((len xs) + n))) // "tests/todo/MergeSort.new.min.fq" (line 26, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "tests/todo/MergeSort.new.min.fq" (line 27, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "tests/todo/MergeSort.new.min.fq" (line 28, column 8)
-qualif Bot(v : @(0)): ((0 = 1)) // "tests/todo/MergeSort.new.min.fq" (line 29, column 8)
-qualif Bot(v : bool): ((0 = 1)) // "tests/todo/MergeSort.new.min.fq" (line 30, column 8)
-qualif Bot(v : int): ((0 = 1)) // "tests/todo/MergeSort.new.min.fq" (line 31, column 8)
-qualif CmpZ(v : @(0)): ((v < 0)) // "tests/todo/MergeSort.new.min.fq" (line 32, column 8)
-qualif CmpZ(v : @(0)): ((v <= 0)) // "tests/todo/MergeSort.new.min.fq" (line 33, column 8)
-qualif CmpZ(v : @(0)): ((v > 0)) // "tests/todo/MergeSort.new.min.fq" (line 34, column 8)
-qualif CmpZ(v : @(0)): ((v >= 0)) // "tests/todo/MergeSort.new.min.fq" (line 35, column 8)
-qualif CmpZ(v : @(0)): ((v = 0)) // "tests/todo/MergeSort.new.min.fq" (line 36, column 8)
-qualif CmpZ(v : @(0)): ((v != 0)) // "tests/todo/MergeSort.new.min.fq" (line 37, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v < x)) // "tests/todo/MergeSort.new.min.fq" (line 38, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v <= x)) // "tests/todo/MergeSort.new.min.fq" (line 39, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v > x)) // "tests/todo/MergeSort.new.min.fq" (line 40, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v >= x)) // "tests/todo/MergeSort.new.min.fq" (line 41, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v = x)) // "tests/todo/MergeSort.new.min.fq" (line 42, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v != x)) // "tests/todo/MergeSort.new.min.fq" (line 43, column 8)
-qualif One(v : int): ((v = 1)) // "tests/todo/MergeSort.new.min.fq" (line 44, column 8)
-qualif True1(v : GHC.Types.Bool): ((Prop v)) // "tests/todo/MergeSort.new.min.fq" (line 45, column 8)
-qualif False1(v : GHC.Types.Bool): ((~ ((Prop v)))) // "tests/todo/MergeSort.new.min.fq" (line 46, column 8)
-qualif Papp(v : @(0), p : (Pred  @(0))): ((papp1 p v)) // "tests/todo/MergeSort.new.min.fq" (line 47, column 8)
-qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))): ((papp2 p v x)) // "tests/todo/MergeSort.new.min.fq" (line 48, column 8)
-qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))): ((papp3 p v x y)) // "tests/todo/MergeSort.new.min.fq" (line 49, column 8)
+qualif Fst(v : @(1), y : @(0)) { (v = (fst y))  } // "tests/todo/MergeSort.new.min.fq" (line 1, column 8)
+qualif Snd(v : @(1), y : @(0)) { (v = (snd y))  } // "tests/todo/MergeSort.new.min.fq" (line 2, column 8)
+qualif Auto(VV : @(0), fld##0 : @(0)) { (VV >= fld##0)  } // "tests/todo/MergeSort.new.min.fq" (line 3, column 8)
+qualif Auto(VV : @(0), fld##0 : @(0)) { (VV >= fld##0)  } // "tests/todo/MergeSort.new.min.fq" (line 4, column 8)
+qualif Auto(VV : [@(0)], xs : [@(0)], ys : [@(0)]) { ((len VV) = ((len xs) + (len ys)))  } // "tests/todo/MergeSort.new.min.fq" (line 5, column 8)
+qualif Auto(VV : @(0), fld##0 : @(0)) { (VV >= fld##0)  } // "tests/todo/MergeSort.new.min.fq" (line 6, column 8)
+qualif Auto(VV : [@(0)], xs : [@(0)]) { ((len VV) = (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 7, column 8)
+qualif Auto(VV : @(0), fld##0 : @(0)) { (VV >= fld##0)  } // "tests/todo/MergeSort.new.min.fq" (line 8, column 8)
+qualif Auto(v##0 : (Tuple  [@(0)]  [@(0)]), xs : [@(0)]) { (((len (fst v##0)) + (len (snd v##0))) = (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 9, column 8)
+qualif Auto(v##0 : [@(0)], xs : [@(0)]) { (((len v##0) > 1) => ((len v##0) < (len xs)))  } // "tests/todo/MergeSort.new.min.fq" (line 10, column 8)
+qualif Auto(v##0 : [@(0)], xs : [@(0)]) { (((len v##0) > 1) => ((len v##0) < (len xs)))  } // "tests/todo/MergeSort.new.min.fq" (line 11, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { ((Prop v) <=> ((len xs) > 0))  } // "tests/todo/MergeSort.new.min.fq" (line 12, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { ((Prop v) <=> ((len xs) = 0))  } // "tests/todo/MergeSort.new.min.fq" (line 13, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) = 0)  } // "tests/todo/MergeSort.new.min.fq" (line 14, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) >= 0)  } // "tests/todo/MergeSort.new.min.fq" (line 15, column 8)
+qualif ListZ(v : [@(0)]) { ((len v) > 0)  } // "tests/todo/MergeSort.new.min.fq" (line 16, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) = (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 17, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) >= (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 18, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) > (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 19, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) <= (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 20, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { ((len v) < (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 21, column 8)
+qualif EqLen(v : int, xs : [@(0)]) { (v = (len xs))  } // "tests/todo/MergeSort.new.min.fq" (line 22, column 8)
+qualif LenEq(v : [@(0)], x : int) { (x = (len v))  } // "tests/todo/MergeSort.new.min.fq" (line 23, column 8)
+qualif LenDiff(v : [@(0)], x : int) { ((len v) = (x + 1))  } // "tests/todo/MergeSort.new.min.fq" (line 24, column 8)
+qualif LenDiff(v : [@(0)], x : int) { ((len v) = (x - 1))  } // "tests/todo/MergeSort.new.min.fq" (line 25, column 8)
+qualif LenAcc(v : int, xs : [@(0)], n : int) { (v = ((len xs) + n))  } // "tests/todo/MergeSort.new.min.fq" (line 26, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 27, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 28, column 8)
+qualif Bot(v : @(0)) { (0 = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 29, column 8)
+qualif Bot(v : bool) { (0 = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 30, column 8)
+qualif Bot(v : int) { (0 = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 31, column 8)
+qualif CmpZ(v : @(0)) { (v < 0)  } // "tests/todo/MergeSort.new.min.fq" (line 32, column 8)
+qualif CmpZ(v : @(0)) { (v <= 0)  } // "tests/todo/MergeSort.new.min.fq" (line 33, column 8)
+qualif CmpZ(v : @(0)) { (v > 0)  } // "tests/todo/MergeSort.new.min.fq" (line 34, column 8)
+qualif CmpZ(v : @(0)) { (v >= 0)  } // "tests/todo/MergeSort.new.min.fq" (line 35, column 8)
+qualif CmpZ(v : @(0)) { (v = 0)  } // "tests/todo/MergeSort.new.min.fq" (line 36, column 8)
+qualif CmpZ(v : @(0)) { (v != 0)  } // "tests/todo/MergeSort.new.min.fq" (line 37, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v < x)  } // "tests/todo/MergeSort.new.min.fq" (line 38, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v <= x)  } // "tests/todo/MergeSort.new.min.fq" (line 39, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v > x)  } // "tests/todo/MergeSort.new.min.fq" (line 40, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v >= x)  } // "tests/todo/MergeSort.new.min.fq" (line 41, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v = x)  } // "tests/todo/MergeSort.new.min.fq" (line 42, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v != x)  } // "tests/todo/MergeSort.new.min.fq" (line 43, column 8)
+qualif One(v : int) { (v = 1)  } // "tests/todo/MergeSort.new.min.fq" (line 44, column 8)
+qualif True1(v : GHC.Types.Bool) { (Prop v)  } // "tests/todo/MergeSort.new.min.fq" (line 45, column 8)
+qualif False1(v : GHC.Types.Bool) { (~ ((Prop v)))  } // "tests/todo/MergeSort.new.min.fq" (line 46, column 8)
+qualif Papp(v : @(0), p : (Pred  @(0))) { (papp1 p v)  } // "tests/todo/MergeSort.new.min.fq" (line 47, column 8)
+qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))) { (papp2 p v x)  } // "tests/todo/MergeSort.new.min.fq" (line 48, column 8)
+qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))) { (papp3 p v x y)  } // "tests/todo/MergeSort.new.min.fq" (line 49, column 8)
 
 
 cut $k_##795
diff --git a/tests/pos/T416.fq b/tests/pos/T416.fq
--- a/tests/pos/T416.fq
+++ b/tests/pos/T416.fq
@@ -7,7 +7,7 @@
 ]
 
 define compose (lq1:func(0,[b;c]), lq2:func(0,[a;b]), lq3:a) : c = { lq1 (lq2 lq3) }
-define first (lq1:func(0,[a;c]),  lq2:(Pair a b)) : Pair a c = {
+define first (lq1:func(0,[a;c]),  lq2:(Pair a b)) : Pair c b = {
   Pair (lq1 (pfst lq2)) (psnd lq2)
 }
 define plus1 (x:int) : int = { x + 1 }
@@ -22,7 +22,7 @@
                                  @(1)]))
 constant first : (func(3 , [func(0 , [@(1); @(2)]);
                                            (Pair @(1) @(0));
-                                           (Pair @(2) @(0))]))                                 
+                                           (Pair @(2) @(0))]))
 
 bind 0 g : {VV : func(0 , [b; c]) | []}
 bind 1 f : {VV : func(0 , [a; b]) | []}
diff --git a/tests/pos/T753A.fq b/tests/pos/T753A.fq
new file mode 100644
--- /dev/null
+++ b/tests/pos/T753A.fq
@@ -0,0 +1,45 @@
+// Config {srcFile = "tests/pos/T2535.hs", cores = Nothing, minPartSize = 500, maxPartSize = 700, solver = z3, linear = False, stringTheory = False, defunction = False, allowHO = True, allowHOqs = False, eliminate = some, scrape = no, elimBound = Nothing, smtTimeout = Nothing, elimStats = False, solverStats = False, metadata = False, stats = False, parts = False, save = True, minimize = False, minimizeQs = False, minimizeKs = False, minimalSol = False, etaElim = False, gradual = False, ginteractive = False, autoKuts = False, nonLinCuts = False, noslice = False, rewriteAxioms = False, pleWithUndecidedGuards = False, etabeta = False, localRewrites = False, interpreter = False, oldPLE = False, noIncrPle = False, noEnvironmentReduction = True, inlineANFBindings = False, checkCstr = [], extensionality = False, rwTerminationCheck = False, stdin = False, json = False, noLazyPLE = False, fuel = Nothing, restOrdering = "rpo", noSmtHorn = False}
+
+data AB 2 = [
+       | B {selectB : int}
+       | A {selectA : int}
+     ]
+data T 1 = [
+       | T {selectT1 : @(0)}
+     ]
+
+match selectA A lq_tmp$x##423   { lq_tmp$x##423 }
+match selectB B lq_tmp$x##502   { lq_tmp$x##502 }
+match check A lq_tmp$x##423   { true }
+match check B lq_tmp$x##502   { false }
+match isA A lq_tmp$x##423   { true }
+match isA B lq_tmp$x##502   { false }
+
+constant A : (func(2 , [int; (AB @(0) @(1))]))
+constant selectA : (func(2 , [(AB @(0) @(1)); int]))
+constant B : (func(2 , [int; (AB @(0) @(1))]))
+constant selectB : (func(2 , [(AB @(0) @(1)); int]))
+constant T : (func(1 , [@(0); (T @(0))]))
+distinct A : (func(2 , [int; (AB @(0) @(1))]))
+distinct B : (func(2 , [int; (AB @(0) @(1))]))
+
+
+bind 1 A : {VV : func(2 , [int; (AB @(0) @(1))]) | []}
+bind 2 B : {VV : func(2 , [int; (AB @(0) @(1))]) | []}
+bind 3 T : {VV : func(1 , [@(0); (T @(0))]) | []}
+bind 4 check : {VV : func(2 , [(AB @(0) @(1)); bool]) | []}
+bind 5 x : {v : (AB int (T aFD)) | [(check v)]}
+
+constraint:
+  env [1; 2; 3; 4; 5]
+  lhs {VV : (AB int (T aFD)) | [(VV = (if (is$A VV) then (A (selectA VV)) else (B (selectB VV))))]}
+  rhs {VV : (AB int (T aFD)) | [( 3 = (1 + 2) )]}
+  id 24 tag [4]
+  // META constraint id 24 : tests/pos/T2535.hs:11:1-9
+
+
+// unless the sort of application is propagated in both the function and the argument 
+// as fixed in PR: https://github.com/ucsd-progsys/liquid-fixpoint/pull/753
+// the above query crashes with 
+// Crash!: :1:1-1:1: Error
+//   crash: SMTLIB2 respSat = Error "line 3 column 13267: Sorts (AB Int (T Int)) and (AB Int Int) are incompatible"
diff --git a/tests/pos/adt_qual.fq b/tests/pos/adt_qual.fq
--- a/tests/pos/adt_qual.fq
+++ b/tests/pos/adt_qual.fq
@@ -1,6 +1,6 @@
 
-qualif Eq(v:@(0), x:@(0)): (v = x)
-qualif Leq(v:@(0), x:@(0)): (v <= x)
+qualif Eq(v:@(0), x:@(0)) { v = x }
+qualif Leq(v:@(0), x:@(0)) { v <= x }
 
 data Zob 0 = [
   | boo { choo : int } 
diff --git a/tests/pos/bad-subst00.fq b/tests/pos/bad-subst00.fq
--- a/tests/pos/bad-subst00.fq
+++ b/tests/pos/bad-subst00.fq
@@ -1,5 +1,5 @@
-qualif Zog(v:a) : (10 <= v)
-qualif Bog(v:a, x:a) : (x <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Bog(v:a, x:a)  { x <= v }
 
 bind 0 a : {v: int | $k0[zogbert := pikachu] }
 
diff --git a/tests/pos/bad-subst01.fq b/tests/pos/bad-subst01.fq
--- a/tests/pos/bad-subst01.fq
+++ b/tests/pos/bad-subst01.fq
@@ -1,8 +1,8 @@
 // fixpoint "--eliminate=some"
 
 
-qualif Zog(v:a) : (10 <= v)
-qualif Bog(v:a, x:a) : (x <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Bog(v:a, x:a)  { x <= v }
 
 bind 0 a : {v: int | $k0 }
 
diff --git a/tests/pos/bad-subst02.fq b/tests/pos/bad-subst02.fq
--- a/tests/pos/bad-subst02.fq
+++ b/tests/pos/bad-subst02.fq
@@ -1,6 +1,6 @@
 fixpoint "--eliminate=none"
 
-qualif Eq(v:a, x:a): (v = x)
+qualif Eq(v:a, x:a) { v = x }
 
 bind 0 x0 : {v: a0 | true }
 bind 1 x1 : {v: a1 | true }
diff --git a/tests/pos/bool00.fq b/tests/pos/bool00.fq
--- a/tests/pos/bool00.fq
+++ b/tests/pos/bool00.fq
@@ -1,5 +1,5 @@
-qualif Zog(v:a) : (10 <= v)
-qualif Bog(v:a, x:a) : (x <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Bog(v:a, x:a)  { x <= v }
 
 bind 0 a  : {v: int  | $k0}
 bind 1 tt : {v: bool | v}
diff --git a/tests/pos/bool03.fq b/tests/pos/bool03.fq
--- a/tests/pos/bool03.fq
+++ b/tests/pos/bool03.fq
@@ -1,7 +1,7 @@
 
 // qualif LE(v:a, x:a): (bool_to_int x <= bool_to_int v)
 
-qualif LE(v:a, x:a): (x <= v)
+qualif LE(v:a, x:a) { x <= v }
 
 constant lit$36$not$45$the$45$hippopotamus : (Str)
 constant lit#cat : (Str)
diff --git a/tests/pos/bool04.fq b/tests/pos/bool04.fq
deleted file mode 100644
--- a/tests/pos/bool04.fq
+++ /dev/null
@@ -1,20 +0,0 @@
-fixpoint "--eliminate=some"
-
-bind 1 bx : {v: int  | true }
-bind 2 by : {v: bool | true }
-
-constraint:
-  env [ 2 ]
-  lhs {v : int | true }
-  rhs {v : int | $k1[bx := by] }
-  id 1 tag []
-
-constraint:
-  env [ 1 ]
-  lhs {v : int | $k1    }
-  rhs {v : int | v <= v + 1 }
-  id 2 tag []
-
-wf:
-  env [1]
-  reft {v : int | $k1 }
diff --git a/tests/pos/ebind-00.fq b/tests/pos/ebind-00.fq
deleted file mode 100644
--- a/tests/pos/ebind-00.fq
+++ /dev/null
@@ -1,16 +0,0 @@
-fixpoint "--eliminate=all"
-
-// bind  0 x1 : {v: int | v = 10 }
-ebind 0 x1 : { int }
-
-constraint:
-  env [0]
-  lhs {v1 : int | v1 = 10}
-  rhs {v1 : int | v1 = x1}
-  id 1 tag []
-
-constraint:
-  env [0]
-  lhs {v2 : int | v2 = x1 + 1 }
-  rhs {v2 : int | v2 = 11     }
-  id 2 tag []
diff --git a/tests/pos/ebind-01.fq b/tests/pos/ebind-01.fq
deleted file mode 100644
--- a/tests/pos/ebind-01.fq
+++ /dev/null
@@ -1,17 +0,0 @@
-fixpoint "--eliminate=all"
-
-bind  1 m  : {v: int | true }
-ebind 2 x1 : { int }
-// bind  2 x1 : {v: int | v = m + 1 }
-
-constraint:
-  env [1; 2]
-  lhs {v : int | v = m + 1 }
-  rhs {v : int | v = x1    }
-  id 1 tag []
-
-constraint:
-  env [1; 2]
-  lhs {v : int | v = x1 + 1}
-  rhs {v : int | v = 2 + m }
-  id 2 tag []
diff --git a/tests/pos/ebind-02.fq b/tests/pos/ebind-02.fq
deleted file mode 100644
--- a/tests/pos/ebind-02.fq
+++ /dev/null
@@ -1,28 +0,0 @@
-fixpoint "--eliminate=all"
-
-bind  0 m  : {v: int | true }
-bind  1 z  : {v: int | v = m - 1 }
-ebind 2 x1 : { int }
-// bind  2 x1 : {v: int | v = m + 1 }
-
-constraint:
-  env [0; 1]
-  lhs {v : int | v = z + 2 }
-  rhs {v : int | $k         }
-  id 1 tag []
-
-constraint:
-  env [0; 2]
-  lhs {v : int | $k      }
-  rhs {v : int | v = x1 }
-  id 2 tag []
-
-constraint:
-  env [0; 2]
-  lhs {v : int | v = x1 + 1 }
-  rhs {v : int | v = m  + 2 }
-  id 3 tag []
-
-wf: 
-  env [0]
-  reft {v:int | [$k] }
diff --git a/tests/pos/ebind-03.fq b/tests/pos/ebind-03.fq
deleted file mode 100644
--- a/tests/pos/ebind-03.fq
+++ /dev/null
@@ -1,45 +0,0 @@
-fixpoint "--eliminate=all"
-
-ebind 1 x1 : { int }
-ebind 2 x2 : { int }
-
-
-constraint:
-  env [1]
-  lhs {v : int | v = 1  }
-  rhs {v : int | v = x1 }
-  id 1 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | v = x1 + 1 }
-  rhs {v : int | $ka        }
-  id 2 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | $ka    }
-  rhs {v : int | v = x2 }
-  id 3 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | v = x2 + 1}
-  rhs {v : int | $kb       }
-  id 4 tag []
-
-constraint:
-  env []
-  lhs {v : int | $kb   }
-  rhs {v : int | v = 3 } 
-  id 5 tag []
-
-
-wf: 
-  env []
-  reft {v:int | [$ka] }
-
-wf: 
-  env []
-  reft {v:int | [$kb] }
-
diff --git a/tests/pos/ebind-03a.fq b/tests/pos/ebind-03a.fq
deleted file mode 100644
--- a/tests/pos/ebind-03a.fq
+++ /dev/null
@@ -1,55 +0,0 @@
-fixpoint "--eliminate=all"
-
-ebind 1 x1 : { int }
-ebind 2 x2 : { int }
-
-constraint:
-  env []
-  lhs {v : int | v = 1 }
-  rhs {v : int | $kone }
-  id 10 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | $kone  }
-  rhs {v : int | v = x1 }
-  id 1 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | v = x1 + 1 }
-  rhs {v : int | $ka        }
-  id 2 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | $ka    }
-  rhs {v : int | v = x2 }
-  id 3 tag []
-
-constraint:
-  env [2]
-  lhs {v : int | v = x2 + 1}
-  rhs {v : int | $kb       }
-  id 4 tag []
-
-constraint:
-  env []
-  lhs {v : int | $kb   }
-  rhs {v : int | v = 3 } 
-  id 5 tag []
-
-
-wf: 
-  env []
-  reft {v:int | [$ka] }
-
-wf: 
-  env []
-  reft {v:int | [$kb] }
-
-wf: 
-  env []
-  reft {v:int | [$kone] }
-
-
diff --git a/tests/pos/ebind-04.fq b/tests/pos/ebind-04.fq
deleted file mode 100644
--- a/tests/pos/ebind-04.fq
+++ /dev/null
@@ -1,35 +0,0 @@
-fixpoint "--eliminate=all"
-
-ebind 1 x : { int }
-
-constraint:
-  env [1]
-  lhs {v : int | $k1   }
-  rhs {v : int | v = x }
-  id 1 tag []
-
-constraint:
-  env [1]
-  lhs {v : int | v = x + 1 }
-  rhs {v : int | $k2       }
-  id 2 tag []
-
-constraint:
-  env []
-  lhs {v : int | v = 3 }
-  rhs {v : int | $k1   }
-  id 3 tag []
-
-constraint:
-  env []
-  lhs {v : int | $k2   }
-  rhs {v : int | v = 4 }
-  id 4 tag []
-
-wf: 
-  env []
-  reft {v:int | [$k1] }
-
-wf: 
-  env []
-  reft {v:int | [$k2] }
diff --git a/tests/pos/ebind-05.fq b/tests/pos/ebind-05.fq
deleted file mode 100644
--- a/tests/pos/ebind-05.fq
+++ /dev/null
@@ -1,31 +0,0 @@
-fixpoint "--eliminate=some"
-
-// This file is SAFE with --eliminate=none
-// but both qualifiers are needed
-
-bind  16 m    : {VV##131 : int | true }
-ebind 19 n    : { int }
-
-constraint:
-  env [16; 19]
-  lhs {VV##F##4 : int | $k_##137[VV##136:=VV##F##4] }
-  rhs {VV##F##4 : int | VV##F##4 = n                }
-  id 4 tag []
-
-constraint:
-  env [16]
-  lhs {VV##F##5 : int | VV##F##5 = m + 1          }
-  rhs {VV##F##5 : int | $k_##137[VV##136:=VV##F##5] }
-  id 5 tag []
-
-// Constraint 3 is only needed to prevent the *sharing* optimization
-
-constraint:
-  env [16; 19]
-  lhs {VV##F##3 : int | false     }
-  rhs {VV##F##3 : int | $k_##137[VV##136:=VV##F##3] }
-  id 3 tag []
-
-wf:
-  env [16]
-  reft {VV##136 : int   | [$k_##137]}
diff --git a/tests/pos/ebind-06.fq b/tests/pos/ebind-06.fq
deleted file mode 100644
--- a/tests/pos/ebind-06.fq
+++ /dev/null
@@ -1,27 +0,0 @@
-fixpoint "--eliminate=some"
-
-bind  16 m    : {VV##131 : int | true }
-ebind 19 n    : { int }
-
-constraint:
-  env [16; 19]
-  lhs {VV##F##3 : int | VV##F##3 = n                     }
-  rhs {VV##F##3 : int | VV##F##3 = m + 1 && VV##F##3 = 3 }
-  id 3 tag []
-
-constraint:
-  env [16; 19]
-  lhs {VV##F##4 : int | VV##F##4 = 3 }
-  rhs {VV##F##4 : int | VV##F##4 = n }
-  id 4 tag []
-
-constraint:
-  env [16; 19]
-  lhs {VV##F##5 : int | VV##F##5 = m + 1 }
-  rhs {VV##F##5 : int | VV##F##5 = n     }
-  id 5 tag []
-
-
-wf:
-  env [16]
-  reft {VV##136 : int   | [$k_##137]}
diff --git a/tests/pos/elim00.fq b/tests/pos/elim00.fq
--- a/tests/pos/elim00.fq
+++ b/tests/pos/elim00.fq
@@ -1,8 +1,8 @@
 fixpoint "--defunct"
 
 // trick is to do it without these
-qualif Cmp(v : @(0), x : @(0)): ((v > x)) // "tests/todo/elim00.hs.fq" (line 1, column 8)
-qualif Cmp(v : @(0), x : @(0)): ((v = x)) // "tests/todo/elim00.hs.fq" (line 2, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v > x)  } // "tests/todo/elim00.hs.fq" (line 1, column 8)
+qualif Cmp(v : @(0), x : @(0)) { (v = x)  } // "tests/todo/elim00.hs.fq" (line 2, column 8)
 
 
 constant Control.Exception.Base.irrefutPatError##09 : (func(1, [int;
diff --git a/tests/pos/func00.fq b/tests/pos/func00.fq
--- a/tests/pos/func00.fq
+++ b/tests/pos/func00.fq
@@ -2,7 +2,7 @@
 bind 0 f : {v: func(0, [int; int]) | []}
 
 constraint:
-  env [ ]
+  env [ 0 ]
   lhs {v : int | [f = f]}
   rhs {v : int | [0 < 7]}
   id 1 tag []
diff --git a/tests/pos/gfp00.fq b/tests/pos/gfp00.fq
--- a/tests/pos/gfp00.fq
+++ b/tests/pos/gfp00.fq
@@ -1,7 +1,7 @@
-qualif Pos(v:int)  : (0 <= v)
-qualif Neg(v:int)  : (v <= 0)
-qualif NeqZ(v:int) : (0 != v)
-qualif False(v:int) : (66 = 77)
+qualif Pos(v:int)   { 0 <= v }
+qualif Neg(v:int)   { v <= 0 }
+qualif NeqZ(v:int)  { 0 != v }
+qualif False(v:int)  { 66 = 77 }
 
 constraint:
   env []
diff --git a/tests/pos/hex.ts.fq b/tests/pos/hex.ts.fq
--- a/tests/pos/hex.ts.fq
+++ b/tests/pos/hex.ts.fq
@@ -1,23 +1,23 @@
-qualif Bot(v : a): (0 = 1) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Bot(v : obj): (0 = 1) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Bot(v : Boolean): (0 = 1) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Bot(v : int): (0 = 1) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v < 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v <= 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v > 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v >= 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v = 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif CmpZ(v : int): (v != 0) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : int, x : int): (v < x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : int, x : int): (v <= x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : int, x : int): (v > x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : int, x : int): (v >= x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : a, x : a): (v ~~ x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Cmp(v : a, x : a): (v != x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif True1(v : Boolean): (? Prop([v])) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif False1(v : Boolean): (~ ((? Prop([v])))) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Tag(v : a, x : Str): (ttag([v]) = x) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
-qualif Len(v : b, w : a): (v < len([w])) // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Bot(v : a) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Bot(v : obj) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Bot(v : Boolean) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Bot(v : int) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v < 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v <= 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v > 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v >= 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v = 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif CmpZ(v : int) { v != 0  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : int, x : int) { v < x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : int, x : int) { v <= x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : int, x : int) { v > x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : int, x : int) { v >= x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : a, x : a) { v ~~ x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Cmp(v : a, x : a) { v != x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif True1(v : Boolean) { Prop([v])  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif False1(v : Boolean) { ~ ((Prop([v])))  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Tag(v : a, x : Str) { ttag([v]) = x  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
+qualif Len(v : b, w : a) { v < len([w])  } // "/Users/rjhala/research/stack/liquid/refscript/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/refscript-0.1.0.0/include/prelude.ts" (line 1, column 1)
 
 
 
@@ -74,139 +74,139 @@
 
 
 bind 0 undefined : {v : Undefined | [(ttag([v]) = lit$36$undefined);
-                                     (~ ((? Prop([v]))))]}
-bind 1 Object : {VV$35$285 : Object | [(? Prop([VV$35$285]));
+                                     (~ ((Prop([v]))))]}
+bind 1 Object : {VV$35$285 : Object | [(Prop([VV$35$285]));
                                        (ttag([VV$35$285]) = lit$36$object)]}
-bind 2 Object.prototype : {VV : (Object  Immutable) | [(? extends_interface([VV;
+bind 2 Object.prototype : {VV : (Object  Immutable) | [(extends_interface([VV;
                                                                              lit$36$Object]));
-                                                       (? Prop([VV]));
+                                                       (Prop([VV]));
                                                        (ttag([VV]) = lit$36$object);
                                                        (VV ~~ offset([Object; lit$36$prototype]))]}
 bind 3 NaN : {v : int | [(ttag([v]) = lit$36$number);
-                         ((? Prop([v])) <=> (v != 0));
+                         ((Prop([v])) <=> (v != 0));
                          (v = numeric_nan)]}
-bind 4 Number : {VV$35$325 : Object | [(? Prop([VV$35$325]));
+bind 4 Number : {VV$35$325 : Object | [(Prop([VV$35$325]));
                                        (ttag([VV$35$325]) = lit$36$object)]}
 bind 5 Number.POSITIVE_INFINITY : {v : int | [(ttag([v]) = lit$36$number);
-                                              ((? Prop([v])) <=> (v != 0));
+                                              ((Prop([v])) <=> (v != 0));
                                               (v ~~ offset([Number; lit$36$POSITIVE_INFINITY]))]}
 bind 6 Number.MIN_VALUE : {v : int | [(ttag([v]) = lit$36$number);
-                                      ((? Prop([v])) <=> (v != 0));
+                                      ((Prop([v])) <=> (v != 0));
                                       (v ~~ offset([Number; lit$36$MIN_VALUE]))]}
-bind 7 Number.prototype : {VV : (Number  Immutable) | [(? extends_interface([VV;
+bind 7 Number.prototype : {VV : (Number  Immutable) | [(extends_interface([VV;
                                                                              lit$36$Number]));
-                                                       (? Prop([VV]));
+                                                       (Prop([VV]));
                                                        (ttag([VV]) = lit$36$object);
                                                        (VV ~~ offset([Number; lit$36$prototype]))]}
 bind 8 Number.NaN : {v : int | [(ttag([v]) = lit$36$number);
-                                ((? Prop([v])) <=> (v != 0));
+                                ((Prop([v])) <=> (v != 0));
                                 (v ~~ offset([Number; lit$36$NaN]))]}
 bind 9 Number.NEGATIVE_INFINITY : {v : int | [(ttag([v]) = lit$36$number);
-                                              ((? Prop([v])) <=> (v != 0));
+                                              ((Prop([v])) <=> (v != 0));
                                               (v ~~ offset([Number; lit$36$NEGATIVE_INFINITY]))]}
 bind 10 Number.MAX_VALUE : {v : int | [(ttag([v]) = lit$36$number);
-                                       ((? Prop([v])) <=> (v != 0));
+                                       ((Prop([v])) <=> (v != 0));
                                        (v ~~ offset([Number; lit$36$MAX_VALUE]))]}
-bind 11 Math : {VV$35$387 : (Math  Immutable) | [(? extends_interface([VV$35$387;
+bind 11 Math : {VV$35$387 : (Math  Immutable) | [(extends_interface([VV$35$387;
                                                                        lit$36$Math]));
-                                                 (? Prop([VV$35$387]));
+                                                 (Prop([VV$35$387]));
                                                  (ttag([VV$35$387]) = lit$36$object)]}
 bind 12 Math.SQRT2 : {v : int | [(ttag([v]) = lit$36$number);
-                                 ((? Prop([v])) <=> (v != 0));
+                                 ((Prop([v])) <=> (v != 0));
                                  (v ~~ offset([Math; lit$36$SQRT2]))]}
 bind 13 Math.LN2 : {v : int | [(ttag([v]) = lit$36$number);
-                               ((? Prop([v])) <=> (v != 0));
+                               ((Prop([v])) <=> (v != 0));
                                (v ~~ offset([Math; lit$36$LN2]))]}
 bind 14 Math.PI : {v : int | [(ttag([v]) = lit$36$number);
-                              ((? Prop([v])) <=> (v != 0));
+                              ((Prop([v])) <=> (v != 0));
                               (v ~~ offset([Math; lit$36$PI]))]}
 bind 15 Math.LOG10E : {v : int | [(ttag([v]) = lit$36$number);
-                                  ((? Prop([v])) <=> (v != 0));
+                                  ((Prop([v])) <=> (v != 0));
                                   (v ~~ offset([Math; lit$36$LOG10E]))]}
 bind 16 Math.LOG2E : {v : int | [(ttag([v]) = lit$36$number);
-                                 ((? Prop([v])) <=> (v != 0));
+                                 ((Prop([v])) <=> (v != 0));
                                  (v ~~ offset([Math; lit$36$LOG2E]))]}
 bind 17 Math.E : {v : int | [(ttag([v]) = lit$36$number);
-                             ((? Prop([v])) <=> (v != 0));
+                             ((Prop([v])) <=> (v != 0));
                              (v ~~ offset([Math; lit$36$E]))]}
 bind 18 Math.SQRT1_2 : {v : int | [(ttag([v]) = lit$36$number);
-                                   ((? Prop([v])) <=> (v != 0));
+                                   ((Prop([v])) <=> (v != 0));
                                    (v ~~ offset([Math; lit$36$SQRT1_2]))]}
 bind 19 Math.LN10 : {v : int | [(ttag([v]) = lit$36$number);
-                                ((? Prop([v])) <=> (v != 0));
+                                ((Prop([v])) <=> (v != 0));
                                 (v ~~ offset([Math; lit$36$LN10]))]}
-bind 20 String : {VV$35$469 : (StringConstructor  Immutable) | [(? extends_interface([VV$35$469;
+bind 20 String : {VV$35$469 : (StringConstructor  Immutable) | [(extends_interface([VV$35$469;
                                                                                       lit$36$StringConstructor]));
-                                                                (? Prop([VV$35$469]));
+                                                                (Prop([VV$35$469]));
                                                                 (ttag([VV$35$469]) = lit$36$object)]}
-bind 21 String.prototype : {VV : (String  Immutable) | [(? extends_interface([VV;
+bind 21 String.prototype : {VV : (String  Immutable) | [(extends_interface([VV;
                                                                               lit$36$String]));
-                                                        (? Prop([VV]));
+                                                        (Prop([VV]));
                                                         (ttag([VV]) = lit$36$object);
                                                         (VV ~~ offset([String; lit$36$prototype]))]}
-bind 22 Array : {VV$35$727 : Object | [(? Prop([VV$35$727]));
+bind 22 Array : {VV$35$727 : Object | [(Prop([VV$35$727]));
                                        (ttag([VV$35$727]) = lit$36$object)]}
-bind 23 Array.prototype : {VV : (Array  Mutable  Top) | [(? extends_interface([VV;
+bind 23 Array.prototype : {VV : (Array  Mutable  Top) | [(extends_interface([VV;
                                                                                lit$36$Array]));
-                                                         (? Prop([VV]));
+                                                         (Prop([VV]));
                                                          (ttag([VV]) = lit$36$object);
                                                          (VV ~~ offset([Array; lit$36$prototype]))]}
-bind 24 Function : {VV$35$762 : Object | [(? Prop([VV$35$762]));
+bind 24 Function : {VV$35$762 : Object | [(Prop([VV$35$762]));
                                           (ttag([VV$35$762]) = lit$36$object)]}
-bind 25 Function.prototype : {VV : (Function  Immutable) | [(? extends_interface([VV;
+bind 25 Function.prototype : {VV : (Function  Immutable) | [(extends_interface([VV;
                                                                                   lit$36$Function]));
-                                                            (? Prop([VV]));
+                                                            (Prop([VV]));
                                                             (ttag([VV]) = lit$36$object);
                                                             (VV ~~ offset([Function;
                                                                            lit$36$prototype]))]}
-bind 26 Console : {VV$35$891 : Object | [(? Prop([VV$35$891]));
+bind 26 Console : {VV$35$891 : Object | [(Prop([VV$35$891]));
                                          (ttag([VV$35$891]) = lit$36$object)]}
-bind 27 Console.prototype : {VV : (Console  Immutable) | [(? extends_interface([VV;
+bind 27 Console.prototype : {VV : (Console  Immutable) | [(extends_interface([VV;
                                                                                 lit$36$Console]));
-                                                          (? Prop([VV]));
+                                                          (Prop([VV]));
                                                           (ttag([VV]) = lit$36$object);
                                                           (VV ~~ offset([Console;
                                                                          lit$36$prototype]))]}
-bind 28 console : {VV$35$893 : (Console  Immutable) | [(? extends_interface([VV$35$893;
+bind 28 console : {VV$35$893 : (Console  Immutable) | [(extends_interface([VV$35$893;
                                                                              lit$36$Console]));
-                                                       (? Prop([VV$35$893]));
+                                                       (Prop([VV$35$893]));
                                                        (ttag([VV$35$893]) = lit$36$object)]}
-bind 29 Error : {VV$35$983 : Object | [(? Prop([VV$35$983]));
+bind 29 Error : {VV$35$983 : Object | [(Prop([VV$35$983]));
                                        (ttag([VV$35$983]) = lit$36$object)]}
-bind 30 Error.prototype : {VV : (Error  Immutable) | [(? extends_interface([VV;
+bind 30 Error.prototype : {VV : (Error  Immutable) | [(extends_interface([VV;
                                                                             lit$36$Error]));
-                                                      (? Prop([VV]));
+                                                      (Prop([VV]));
                                                       (ttag([VV]) = lit$36$object);
                                                       (VV ~~ offset([Error; lit$36$prototype]))]}
-bind 31 Event : {VV$35$1025 : Object | [(? Prop([VV$35$1025]));
+bind 31 Event : {VV$35$1025 : Object | [(Prop([VV$35$1025]));
                                         (ttag([VV$35$1025]) = lit$36$object)]}
 bind 32 Event.CAPTURING_PHASE : {v : int | [(ttag([v]) = lit$36$number);
-                                            ((? Prop([v])) <=> (v != 0));
+                                            ((Prop([v])) <=> (v != 0));
                                             (v ~~ offset([Event; lit$36$CAPTURING_PHASE]))]}
 bind 33 Event.AT_TARGET : {v : int | [(ttag([v]) = lit$36$number);
-                                      ((? Prop([v])) <=> (v != 0));
+                                      ((Prop([v])) <=> (v != 0));
                                       (v ~~ offset([Event; lit$36$AT_TARGET]))]}
-bind 34 Event.prototype : {VV : (Event  Immutable) | [(? extends_interface([VV;
+bind 34 Event.prototype : {VV : (Event  Immutable) | [(extends_interface([VV;
                                                                             lit$36$Event]));
-                                                      (? Prop([VV]));
+                                                      (Prop([VV]));
                                                       (ttag([VV]) = lit$36$object);
                                                       (VV ~~ offset([Event; lit$36$prototype]))]}
 bind 35 Event.BUBBLING_PHASE : {v : int | [(ttag([v]) = lit$36$number);
-                                           ((? Prop([v])) <=> (v != 0));
+                                           ((Prop([v])) <=> (v != 0));
                                            (v ~~ offset([Event; lit$36$BUBBLING_PHASE]))]}
-bind 36 document : {VV$35$1027 : (Document  Immutable) | [(? extends_interface([VV$35$1027;
+bind 36 document : {VV$35$1027 : (Document  Immutable) | [(extends_interface([VV$35$1027;
                                                                                 lit$36$Document]));
-                                                          (? Prop([VV$35$1027]));
+                                                          (Prop([VV$35$1027]));
                                                           (ttag([VV$35$1027]) = lit$36$object)]}
-bind 37 document.documentElement : {VV : (HTMLElement  Immutable) | [(? extends_interface([VV;
+bind 37 document.documentElement : {VV : (HTMLElement  Immutable) | [(extends_interface([VV;
                                                                                            lit$36$HTMLElement]));
-                                                                     (? Prop([VV]));
+                                                                     (Prop([VV]));
                                                                      (ttag([VV]) = lit$36$object);
                                                                      (VV ~~ offset([document;
                                                                                     lit$36$documentElement]))]}
-bind 38 window : {VV$35$1031 : (Window  Immutable) | [(? extends_interface([VV$35$1031;
+bind 38 window : {VV$35$1031 : (Window  Immutable) | [(extends_interface([VV$35$1031;
                                                                             lit$36$Window]));
-                                                      (? Prop([VV$35$1031]));
+                                                      (Prop([VV$35$1031]));
                                                       (ttag([VV$35$1031]) = lit$36$object)]}
 bind 39 lq_tmp_nano_1 : {VV : (BitVec  Size32) | [(VV = (lit "#x00000008" (BitVec  Size32)))]}
 bind 40 a_SSA_0 : {VV : (BitVec  Size32) | [(VV ~~ lq_tmp_nano_1);
@@ -217,7 +217,7 @@
 bind 43 lq_tmp_nano_3 : {v : (BitVec  Size32) | [(v = bvor([a_SSA_0;
                                                             a_SSA_0]))]}
 bind 44 lq_tmp_nano_6 : {v : Boolean | [(ttag([v]) = lit$36$boolean);
-                                        ((? Prop([v])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
+                                        ((Prop([v])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
 bind 45 lq_tmp_nano_9 : {VV$35$4 : Void | []}
 
 
@@ -272,8 +272,8 @@
   lhs {VV$35$F1 : Boolean | [(ttag([VV$35$F1]) = lit$36$boolean);
                              (VV$35$F1 ~~ lq_tmp_nano_6);
                              (ttag([VV$35$F1]) = lit$36$boolean);
-                             ((? Prop([VV$35$F1])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
-  rhs {VV$35$F1 : Boolean | [(? Prop([VV$35$F1]))]}
+                             ((Prop([VV$35$F1])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
+  rhs {VV$35$F1 : Boolean | [(Prop([VV$35$F1]))]}
   id 1 tag [1]
   // META constraint id 1 : /Users/rjhala/research/stack/liquid/refscript/tests/pos/simple/hex.ts:7:1-7:22
 
@@ -327,8 +327,8 @@
   lhs {VV$35$F2 : Boolean | [(ttag([VV$35$F2]) = lit$36$boolean);
                              (VV$35$F2 ~~ lq_tmp_nano_6);
                              (ttag([VV$35$F2]) = lit$36$boolean);
-                             ((? Prop([VV$35$F2])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
-  rhs {VV$35$F2 : Boolean | [(? Prop([VV$35$F2]))]}
+                             ((Prop([VV$35$F2])) <=> (b_SSA_1 ~~ lq_tmp_nano_3))]}
+  rhs {VV$35$F2 : Boolean | [(Prop([VV$35$F2]))]}
   id 2 tag [1]
   // META constraint id 2 : /Users/rjhala/research/stack/liquid/refscript/tests/pos/simple/hex.ts:7:1-7:22
 
diff --git a/tests/pos/kvar-param-poly-00.fq b/tests/pos/kvar-param-poly-00.fq
--- a/tests/pos/kvar-param-poly-00.fq
+++ b/tests/pos/kvar-param-poly-00.fq
@@ -2,7 +2,7 @@
 // a kvar's params can be instantiated / substituted with values of a different
 // type. Here, K0(v:alpha, x:alpha) but is instantiated with int.
 
-qualif Bog(v:a, x:a) : (x = v)
+qualif Bog(v:a, x:a)  { x = v }
 
 bind 1 x : {v: alpha | true}
 
diff --git a/tests/pos/len00-rename.fq b/tests/pos/len00-rename.fq
--- a/tests/pos/len00-rename.fq
+++ b/tests/pos/len00-rename.fq
@@ -1,7 +1,7 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
 
-qualif ListZ(v : [@(0)]): (len v >= 0)
+qualif ListZ(v : [@(0)]) { len v >= 0 }
 
 constant len : (func(2, [(@(0)  @(1)); int]))
 
diff --git a/tests/pos/len00.fq b/tests/pos/len00.fq
--- a/tests/pos/len00.fq
+++ b/tests/pos/len00.fq
@@ -1,7 +1,7 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
 
-qualif ListZ(v : [@(0)]): (len v >= 0)
+qualif ListZ(v : [@(0)]) { len v >= 0 }
 
 constant len : (func(2, [(@(0)  @(1)); int]))
 
diff --git a/tests/pos/listqual.hs.fq b/tests/pos/listqual.hs.fq
--- a/tests/pos/listqual.hs.fq
+++ b/tests/pos/listqual.hs.fq
@@ -1,53 +1,53 @@
-qualif Append(v : [@(0)], xs : [@(0)], ys : [@(0)]): (len([v]) = (len([xs]) + len([ys]))) // "tests/pos/listqual.hs" (line 3, column 12)
-qualif Fst(v : @(1), y : @(0)): (v = fst([y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 29, column 8)
-qualif Snd(v : @(1), y : @(0)): (v = snd([y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 30, column 8)
-qualif Auto(v : [int]): (len([v]) = 2) // "tests/pos/listqual.hs" (line 10, column 1)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) > 0)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 13, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) = 0)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 14, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) = 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 16, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) >= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 17, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) > 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 18, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) = len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 20, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) >= len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 21, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) > len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 22, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) <= len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 23, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) < len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 24, column 8)
-qualif EqLen(v : int, xs : [@(0)]): (v = len([xs])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 26, column 8)
-qualif LenEq(v : [@(0)], x : int): (x = len([v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 27, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x + 1)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 28, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x - 1)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 29, column 8)
-qualif LenAcc(v : int, xs : [@(0)], n : int): (v = (len([xs]) + n)) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 30, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 3, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 4, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 5, column 8)
-qualif Bot(v : bool): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 6, column 8)
-qualif Bot(v : int): (0 = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 7, column 8)
-qualif CmpZ(v : @(0)): (v < 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 9, column 8)
-qualif CmpZ(v : @(0)): (v <= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 10, column 8)
-qualif CmpZ(v : @(0)): (v > 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 11, column 8)
-qualif CmpZ(v : @(0)): (v >= 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 12, column 8)
-qualif CmpZ(v : @(0)): (v = 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 13, column 8)
-qualif CmpZ(v : @(0)): (v != 0) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 14, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v < x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 16, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v <= x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 17, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v > x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 18, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v >= x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 19, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v = x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 20, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v != x) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 21, column 8)
-qualif One(v : int): (v = 1) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 28, column 8)
-qualif True(v : bool): (? v) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 29, column 8)
-qualif False(v : bool): (~ ((? v))) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 30, column 8)
-qualif True1(v : GHC.Types.Bool): (? Prop([v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 31, column 8)
-qualif False1(v : GHC.Types.Bool): (~ ((? Prop([v])))) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 32, column 8)
-qualif Papp(v : @(0), p : (Pred  @(0))): (? papp1([p;
-                                                   v])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 35, column 8)
-qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))): (? papp2([p;
+qualif Append(v : [@(0)], xs : [@(0)], ys : [@(0)]) { len([v]) = (len([xs]) + len([ys]))  } // "tests/pos/listqual.hs" (line 3, column 12)
+qualif Fst(v : @(1), y : @(0)) { v = fst([y])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 29, column 8)
+qualif Snd(v : @(1), y : @(0)) { v = snd([y])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.spec" (line 30, column 8)
+qualif Auto(v : [int]) { len([v]) = 2  } // "tests/pos/listqual.hs" (line 10, column 1)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { Prop([v]) <=> len([xs]) > 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 13, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { Prop([v]) <=> len([xs]) = 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 14, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) = 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 16, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) >= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 17, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) > 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 18, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) = len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 20, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) >= len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 21, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) > len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 22, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) <= len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 23, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) < len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 24, column 8)
+qualif EqLen(v : int, xs : [@(0)]) { v = len([xs])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 26, column 8)
+qualif LenEq(v : [@(0)], x : int) { x = len([v])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 27, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x + 1)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 28, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x - 1)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 29, column 8)
+qualif LenAcc(v : int, xs : [@(0)], n : int) { v = (len([xs]) + n)  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/GHC/Base.hquals" (line 30, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 3, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 4, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 5, column 8)
+qualif Bot(v : bool) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 6, column 8)
+qualif Bot(v : int) { 0 = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 7, column 8)
+qualif CmpZ(v : @(0)) { v < 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 9, column 8)
+qualif CmpZ(v : @(0)) { v <= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 10, column 8)
+qualif CmpZ(v : @(0)) { v > 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 11, column 8)
+qualif CmpZ(v : @(0)) { v >= 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 12, column 8)
+qualif CmpZ(v : @(0)) { v = 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 13, column 8)
+qualif CmpZ(v : @(0)) { v != 0  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 14, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v < x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 16, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v <= x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 17, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v > x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 18, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v >= x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 19, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v = x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 20, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v != x  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 21, column 8)
+qualif One(v : int) { v = 1  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 28, column 8)
+qualif True(v : bool) {  v  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 29, column 8)
+qualif False(v : bool) { ~ v  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 30, column 8)
+qualif True1(v : GHC.Types.Bool) {  Prop([v])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 31, column 8)
+qualif False1(v : GHC.Types.Bool) { ~ Prop([v])  } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 32, column 8)
+qualif Papp(v : @(0), p : (Pred  @(0))) { papp1([p;
+                                                   v]) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 35, column 8)
+qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))) { papp2([p;
                                                                     v;
-                                                                    x])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 38, column 8)
-qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))): (? papp3([p;
+                                                                    x]) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 38, column 8)
+qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))) { papp3([p;
                                                                                     v;
                                                                                     x;
-                                                                                    y])) // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 41, column 8)
+                                                                                    y]) } // "/Users/rjhala/research/stack/liquid/liquidhaskell/.stack-work/install/x86_64-osx/nightly-2015-09-24/7.10.2/share/x86_64-osx-ghc-7.10.2/liquidhaskell-0.5.0.2/include/Prelude.hquals" (line 41, column 8)
 
 
 cut $k__185
@@ -163,10 +163,10 @@
 bind 13 lq_anf__dwU : {lq_tmp_x_198 : [a_awA] | [(lq_tmp_x_198 = ds_dwT);
                                                  (len([lq_tmp_x_198]) >= 0);
                                                  (len([lq_tmp_x_198]) = 0);
-                                                 ((? null([lq_tmp_x_198])) <=> true);
+                                                 (null([lq_tmp_x_198]) <=> true);
                                                  (lq_tmp_x_198 = GHC.Types.$91$$93$$35$6m([]));
                                                  (len([lq_tmp_x_198]) = 0);
-                                                 ((? null([lq_tmp_x_198])) <=> true);
+                                                 (null([lq_tmp_x_198]) <=> true);
                                                  (len([lq_tmp_x_198]) >= 0)]}
 bind 14 lq_anf__dwU : {lq_tmp_x_208 : [a_awA] | [(lq_tmp_x_208 = ds_dwT);
                                                  (len([lq_tmp_x_208]) >= 0);
@@ -176,13 +176,13 @@
 bind 17 lq_anf__dwU : {lq_tmp_x_208 : [a_awA] | [(lq_tmp_x_208 = ds_dwT);
                                                  (len([lq_tmp_x_208]) >= 0);
                                                  (len([lq_tmp_x_208]) = (1 + len([xs$35$awp])));
-                                                 ((? null([lq_tmp_x_208])) <=> false);
+                                                 (null([lq_tmp_x_208]) <=> false);
                                                  (xsListSelector([lq_tmp_x_208]) = xs$35$awp);
                                                  (xListSelector([lq_tmp_x_208]) = x$35$awo);
                                                  (lq_tmp_x_208 = GHC.Types.$58$$35$64([x$35$awo;
                                                                                        xs$35$awp]));
                                                  (len([lq_tmp_x_208]) = (1 + len([xs$35$awp])));
-                                                 ((? null([lq_tmp_x_208])) <=> false);
+                                                 (null([lq_tmp_x_208]) <=> false);
                                                  (xsListSelector([lq_tmp_x_208]) = xs$35$awp);
                                                  (xListSelector([lq_tmp_x_208]) = x$35$awo);
                                                  (len([lq_tmp_x_208]) >= 0)]}
@@ -192,22 +192,22 @@
 bind 20 lq_anf__dwW : {lq_tmp_x_264 : int | [(lq_tmp_x_264 = (1  :  int))]}
 bind 21 lq_tmp_x_278 : {VV$35$279 : int | []}
 bind 22 lq_anf__dwX : {lq_tmp_x_270 : [int] | [(len([lq_tmp_x_270]) = 0);
-                                               ((? null([lq_tmp_x_270])) <=> true);
+                                               (null([lq_tmp_x_270]) <=> true);
                                                (len([lq_tmp_x_270]) >= 0)]}
 bind 23 lq_tmp_x_296 : {VV$35$297 : int | []}
 bind 24 lq_anf__dwY : {lq_tmp_x_284 : [int] | [(len([lq_tmp_x_284]) = (1 + len([lq_anf__dwX])));
-                                               ((? null([lq_tmp_x_284])) <=> false);
+                                               (null([lq_tmp_x_284]) <=> false);
                                                (xsListSelector([lq_tmp_x_284]) = lq_anf__dwX);
                                                (xListSelector([lq_tmp_x_284]) = lq_anf__dwW);
                                                (len([lq_tmp_x_284]) >= 0)]}
 bind 25 lq_anf__dwZ : {lq_tmp_x_305 : int | [(lq_tmp_x_305 = (2  :  int))]}
 bind 26 lq_tmp_x_319 : {VV$35$320 : int | []}
 bind 27 lq_anf__dx0 : {lq_tmp_x_311 : [int] | [(len([lq_tmp_x_311]) = 0);
-                                               ((? null([lq_tmp_x_311])) <=> true);
+                                               (null([lq_tmp_x_311]) <=> true);
                                                (len([lq_tmp_x_311]) >= 0)]}
 bind 28 lq_tmp_x_337 : {VV$35$338 : int | []}
 bind 29 lq_anf__dx1 : {lq_tmp_x_325 : [int] | [(len([lq_tmp_x_325]) = (1 + len([lq_anf__dx0])));
-                                               ((? null([lq_tmp_x_325])) <=> false);
+                                               (null([lq_tmp_x_325]) <=> false);
                                                (xsListSelector([lq_tmp_x_325]) = lq_anf__dx0);
                                                (xListSelector([lq_tmp_x_325]) = lq_anf__dwZ);
                                                (len([lq_tmp_x_325]) >= 0)]}
@@ -269,12 +269,12 @@
 bind 72 VV$35$427 : {VV$35$427 : int | [(VV$35$427 = 1)]}
 bind 73 VV$35$427 : {VV$35$427 : int | [(VV$35$427 = 1)]}
 bind 74 VV$35$430 : {VV$35$430 : [a_awA] | [(len([VV$35$430]) = (1 + len([lq_anf__dwV])));
-                                            ((? null([VV$35$430])) <=> false);
+                                            (null([VV$35$430]) <=> false);
                                             (xsListSelector([VV$35$430]) = lq_anf__dwV);
                                             (xListSelector([VV$35$430]) = x$35$awo);
                                             (len([VV$35$430]) >= 0)]}
 bind 75 VV$35$430 : {VV$35$430 : [a_awA] | [(len([VV$35$430]) = (1 + len([lq_anf__dwV])));
-                                            ((? null([VV$35$430])) <=> false);
+                                            (null([VV$35$430]) <=> false);
                                             (xsListSelector([VV$35$430]) = lq_anf__dwV);
                                             (xListSelector([VV$35$430]) = x$35$awo);
                                             (len([VV$35$430]) >= 0)]}
@@ -384,7 +384,7 @@
 constraint:
   env [0; 16; 1; 17; 2; 18; 3; 4; 5; 6; 7; 8; 9; 10; 74; 11; 14; 15]
   lhs {VV$35$F18 : [a_awA] | [(len([VV$35$F18]) = (1 + len([lq_anf__dwV])));
-                              ((? null([VV$35$F18])) <=> false);
+                              (null([VV$35$F18]) <=> false);
                               (xsListSelector([VV$35$F18]) = lq_anf__dwV);
                               (xListSelector([VV$35$F18]) = x$35$awo);
                               (len([VV$35$F18]) >= 0)]}
diff --git a/tests/pos/meas02.fq b/tests/pos/meas02.fq
--- a/tests/pos/meas02.fq
+++ b/tests/pos/meas02.fq
@@ -1,7 +1,7 @@
 
-qualif SumZ(v:[real]): (sumD v = 0.0)
+qualif SumZ(v:[real]) { sumD v = 0.0 }
 
-qualif SumZ(v:[real]): (((sumD v) / (sumD v)) = 1.0)
+qualif SumZ(v:[real]) { ((sumD v) / (sumD v)) = 1.0 }
 
 constant sumD : (func(0, [[real]; real]))
 
diff --git a/tests/pos/min00.fq b/tests/pos/min00.fq
--- a/tests/pos/min00.fq
+++ b/tests/pos/min00.fq
@@ -1,8 +1,8 @@
 
-qualif Zog(v:a) : (10 <= v)
-qualif Zog(v:a) : (9 <= v)
-qualif Zog(v:a) : (8 <= v)
-qualif Zog(v:a) : (99 <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Zog(v:a)  { 9 <= v }
+qualif Zog(v:a)  { 8 <= v }
+qualif Zog(v:a)  { 99 <= v }
 
 constraint:
   env []
diff --git a/tests/pos/num00.fq b/tests/pos/num00.fq
--- a/tests/pos/num00.fq
+++ b/tests/pos/num00.fq
@@ -1,6 +1,6 @@
 // This qualifier saves the day; solve constraints WITHOUT IT
 
-qualif Zog(v:a) : (0 <= v)
+qualif Zog(v:a)  { 0 <= v }
 
 bind 0 alpha : {v : num | true}
 
diff --git a/tests/pos/numoverload00.fq b/tests/pos/numoverload00.fq
--- a/tests/pos/numoverload00.fq
+++ b/tests/pos/numoverload00.fq
@@ -1,6 +1,6 @@
 
-qualif Foo(v:real, xiggety:real): (v = xiggety * xiggety)
-qualif Bar(v:real): (v = 0.0)
+qualif Foo(v:real, xiggety:real) { v = xiggety * xiggety }
+qualif Bar(v:real) { v = 0.0 }
 
 bind 0 zero  : {VV : real | VV = 0.0 }
 bind 1 one   : {VV : real | VV = (1.0 / 1.0) }
diff --git a/tests/pos/qualif-inst.fq b/tests/pos/qualif-inst.fq
--- a/tests/pos/qualif-inst.fq
+++ b/tests/pos/qualif-inst.fq
@@ -1,6 +1,6 @@
 // adapted from LH test eqelems.hs
 
-qualif Cmp(v : @(0), fix##126#X : @(0)): (v >= fix##126#X)
+qualif Cmp(v : @(0), fix##126#X : @(0)) { v >= fix##126#X }
 
 constant elems : (func(1, [(Goo.T  @(0)); (Set_Set  @(0))]))
 
diff --git a/tests/pos/qualif-template-00.fq b/tests/pos/qualif-template-00.fq
--- a/tests/pos/qualif-template-00.fq
+++ b/tests/pos/qualif-template-00.fq
@@ -1,6 +1,6 @@
 // qualif Goob(v:a, z: b) : (v = z)
 // qualif Prefix2(v:a, x as (mon . $1) : b, y as (sun . $1)) : (v = x + y)
-qualif Prefix(v:a, z as (mon . $1) : b) : (v = z)
+qualif Prefix(v:a, z as (mon . $1) : b)  { v = z }
 
 bind 0  monday  : {v : int | true}
 bind 10 tuesday : {v : int | true}
diff --git a/tests/pos/qualif-template-01.fq b/tests/pos/qualif-template-01.fq
--- a/tests/pos/qualif-template-01.fq
+++ b/tests/pos/qualif-template-01.fq
@@ -1,6 +1,6 @@
 // qualif Goob(v:a, z: b) : (v = z)
 // qualif Prefix2(v:a, x as (mon . $1) : b, y as (sun . $1)) : (v = x + y)
-qualif Prefix(v:a, z as ($1 . nday) : b) : (v = z)
+qualif Prefix(v:a, z as ($1 . nday) : b)  { v = z }
 
 bind 0  monday  : {v : int | true}
 bind 10 tuesday : {v : int | true}
diff --git a/tests/pos/qualif-template-02.fq b/tests/pos/qualif-template-02.fq
--- a/tests/pos/qualif-template-02.fq
+++ b/tests/pos/qualif-template-02.fq
@@ -1,4 +1,4 @@
-qualif Prefix2(v:a, x as (sun . $1)  : b, y as (mon . $1) : b) : (v = x + y)
+qualif Prefix2(v:a, x as (sun . $1)  : b, y as (mon . $1) : b)  { v = x + y }
 
 bind 0  sunday  : {v : int | v = 6  }
 bind 1  monday  : {v : int | v = 4  }
diff --git a/tests/pos/sets.fq b/tests/pos/sets.fq
--- a/tests/pos/sets.fq
+++ b/tests/pos/sets.fq
@@ -2,6 +2,6 @@
 
 constraint:
   env []
-  lhs {v : Set_Set a_aTp | [(? Set_emp([v]))]}
+  lhs {v : Set_Set a_aTp | [(Set_emp([v]))]}
   rhs {v : Set_Set a_aTp | [(v = Set_empty([0]))]}
   id 3 tag [2]
diff --git a/tests/pos/test00.fq b/tests/pos/test00.fq
--- a/tests/pos/test00.fq
+++ b/tests/pos/test00.fq
@@ -1,5 +1,5 @@
-qualif Zog(v:a) : (10 <= v)
-qualif Bog(v:a, x:a) : (x <= v)
+qualif Zog(v:a)  { 10 <= v }
+qualif Bog(v:a, x:a)  { x <= v }
 
 bind 0 a : {va: int | $k0[v := va][thing := thang] }
 bind 1 thing : {v: int | true }
diff --git a/tests/pos/test00.hs.fq b/tests/pos/test00.hs.fq
--- a/tests/pos/test00.hs.fq
+++ b/tests/pos/test00.hs.fq
@@ -1,61 +1,61 @@
 
-qualif IsEmp(v:GHC.Types.Bool, xs: [a]) : (Prop(v) <=> len([xs]) > 0)
-qualif IsEmp(v:GHC.Types.Bool, xs: [a]) : (Prop(v) <=> len([xs]) = 0)
-qualif ListZ(v: [a])          : len([v]) =  0 
-qualif ListZ(v: [a])          : len([v]) >= 0 
-qualif ListZ(v: [a])          : len([v]) >  0 
-qualif CmpLen(v:[a], xs:[b])  : len([v]) =  len([xs]) 
-qualif CmpLen(v:[a], xs:[b])  : len([v]) >= len([xs]) 
-qualif CmpLen(v:[a], xs:[b])  : len([v]) >  len([xs]) 
-qualif CmpLen(v:[a], xs:[b])  : len([v]) <= len([xs]) 
-qualif CmpLen(v:[a], xs:[b])  : len([v]) <  len([xs]) 
-qualif EqLen(v:int, xs: [a]): v = len([xs]) 
-qualif LenEq(v:[a], x: int) :  x = len([v]) 
-qualif LenDiff(v:[a], x:int): len([v]) = x + 1
-qualif LenDiff(v:[a], x:int): len([v]) = x - 1
-qualif LenAcc(v:int, xs:[a], n: int): (v = len([xs]) + n)
+qualif IsEmp(v:GHC.Types.Bool, xs: [a])  { Prop(v) <=> len([xs]) > 0 }
+qualif IsEmp(v:GHC.Types.Bool, xs: [a])  { Prop(v) <=> len([xs]) = 0 }
+qualif ListZ(v: [a])           { len([v]) =  0  }
+qualif ListZ(v: [a])           { len([v]) >= 0  }
+qualif ListZ(v: [a])           { len([v]) >  0  }
+qualif CmpLen(v:[a], xs:[b])   { len([v]) =  len([xs])  }
+qualif CmpLen(v:[a], xs:[b])   { len([v]) >= len([xs])  }
+qualif CmpLen(v:[a], xs:[b])   { len([v]) >  len([xs])  }
+qualif CmpLen(v:[a], xs:[b])   { len([v]) <= len([xs])  }
+qualif CmpLen(v:[a], xs:[b])   { len([v]) <  len([xs])  }
+qualif EqLen(v:int, xs: [a]) { v = len([xs])  }
+qualif LenEq(v:[a], x: int)  { x = len([v])  }
+qualif LenDiff(v:[a], x:int) { len([v]) = x + 1 }
+qualif LenDiff(v:[a], x:int) { len([v]) = x - 1 }
+qualif LenAcc(v:int, xs:[a], n: int) { v = len([xs]) + n }
 
-qualif Bot(v:obj): 0 = 1 
-qualif Bot(v:a): 0 = 1 
-qualif Bot(v:bool): 0 = 1 
-qualif Bot(v:int): 0 = 1 
-qualif CmpZ(v:a): (v <  0)
-qualif CmpZ(v:a): (v <= 0)
-qualif CmpZ(v:a): (v >  0)
-qualif CmpZ(v:a): (v >= 0)
-qualif CmpZ(v:a): (v  = 0)
-qualif CmpZ(v:a): (v != 0)
+qualif Bot(v:obj) { 0 = 1  }
+qualif Bot(v:a) { 0 = 1  }
+qualif Bot(v:bool) { 0 = 1  }
+qualif Bot(v:int) { 0 = 1  }
+qualif CmpZ(v:a) { v <  0 }
+qualif CmpZ(v:a) { v <= 0 }
+qualif CmpZ(v:a) { v >  0 }
+qualif CmpZ(v:a) { v >= 0 }
+qualif CmpZ(v:a) { v  = 0 }
+qualif CmpZ(v:a) { v != 0 }
 
-qualif Cmp(v:a, x:a): (v <  x)
-qualif Cmp(v:a, x:a): (v <= x)
-qualif Cmp(v:a, x:a): (v >  x)
-qualif Cmp(v:a, x:a): (v >= x)
-qualif Cmp(v:a, x:a): (v  = x)
-qualif Cmp(v:a, x:a): (v != x)
+qualif Cmp(v:a, x:a) { v <  x }
+qualif Cmp(v:a, x:a) { v <= x }
+qualif Cmp(v:a, x:a) { v >  x }
+qualif Cmp(v:a, x:a) { v >= x }
+qualif Cmp(v:a, x:a) { v  = x }
+qualif Cmp(v:a, x:a) { v != x }
 
-qualif One(v:int)     : v = 1
-qualif True(v:bool)   : (? v) 
-qualif False(v:bool)  : ~ (? v) 
-qualif True1(v:GHC.Types.Bool): (Prop(v))
-qualif False1(v:GHC.Types.Bool): (~ Prop(v))
-qualif Papp(v:a, p:Pred a) : (papp1 p v)
+qualif One(v:int)      { v = 1 }
+qualif True(v:bool)    { v  }
+qualif False(v:bool)   { ~ (v)  }
+qualif True1(v:GHC.Types.Bool) { Prop(v) }
+qualif False1(v:GHC.Types.Bool) { ~ Prop(v) }
+qualif Papp(v:a, p:Pred a)  { papp1 p v }
 
 constant papp1 : func(1, [Pred @(0); @(0); bool])
 
-qualif Papp2(v:a,x:b,p:Pred a b) : (papp2 p v x)
+qualif Papp2(v:a,x:b,p:Pred a b)  { papp2 p v x }
 constant papp2 : func(4, [Pred @(0) @(1); @(2); @(3); bool])
 
-qualif Papp3(v:a,x:b, y:c, p:Pred a b c) : (papp3 p v x y)
+qualif Papp3(v:a,x:b, y:c, p:Pred a b c)  { papp3 p v x y }
 constant papp3 : func(6, [Pred @(0) @(1) @(2); @(3); @(4); @(5); bool])
 
-qualif Papp4(v:a,x:b, y:c, z:d, p:Pred a b c d) : (papp4 p v x y z)
+qualif Papp4(v:a,x:b, y:c, z:d, p:Pred a b c d)  { papp4 p v x y z }
 constant papp4 : func(8, [Pred @(0) @(1) @(2) @(6); @(3); @(4); @(5); @(7); bool])
 
 
 
 constant Prop : func(0, [GHC.Types.Bool; bool])
-qualif Fst(v : @(1), fix##126#Y : @(0)): (v = fst([fix##126#Y])) // "/Users/rjhala/research/liquid/liquidhaskell/.cabal-sandbox/share/x86_64-osx-ghc-7.8.3/liquidhaskell-0.3.1.0/include/GHC/Base.spec" (line 26, column 8)
-qualif Snd(v : @(1), fix##126#Y : @(0)): (v = snd([fix##126#Y])) // "/Users/rjhala/research/liquid/liquidhaskell/.cabal-sandbox/share/x86_64-osx-ghc-7.8.3/liquidhaskell-0.3.1.0/include/GHC/Base.spec" (line 27, column 8)
+qualif Fst(v : @(1), fix##126#Y : @(0)) { v = fst([fix##126#Y])  } // "/Users/rjhala/research/liquid/liquidhaskell/.cabal-sandbox/share/x86_64-osx-ghc-7.8.3/liquidhaskell-0.3.1.0/include/GHC/Base.spec" (line 26, column 8)
+qualif Snd(v : @(1), fix##126#Y : @(0)) { v = snd([fix##126#Y])  } // "/Users/rjhala/research/liquid/liquidhaskell/.cabal-sandbox/share/x86_64-osx-ghc-7.8.3/liquidhaskell-0.3.1.0/include/GHC/Base.spec" (line 27, column 8)
 
 constant Prop : func(0, [GHC.Types.Bool; bool])
 constant x_Tuple54 : func(5, [FAppTy (FAppTy (FAppTy (FAppTy (FAppTy fix##40##41#  @(0))  @(1))  @(2))  @(3))  @(4);
@@ -138,8 +138,8 @@
 bind 7 GHC.Types.EQ#6U : {VV#179 : GHC.Types.Ordering | []}
 bind 8 GHC.Types.LT#6S : {VV#180 : GHC.Types.Ordering | []}
 bind 9 GHC.Types.GT#6W : {VV#181 : GHC.Types.Ordering | []}
-bind 10 GHC.Types.True#6u : {v : GHC.Types.Bool | [(? Prop([v]))]}
-bind 11 GHC.Types.False#68 : {v : GHC.Types.Bool | [(~ ((? Prop([v]))))]}
+bind 10 GHC.Types.True#6u : {v : GHC.Types.Bool | [(Prop([v]))]}
+bind 11 GHC.Types.False#68 : {v : GHC.Types.Bool | [(~ ((Prop([v]))))]}
 bind 12 Language.Haskell.Liquid.Prelude.plus#rou : {VV : func(0, [int;
                                                                   int;
                                                                   int]) | []}
@@ -206,25 +206,25 @@
 bind 39 gooberding#a15N : {VV#234 : a_a164 | [$k_235]}
 bind 40 lq_anf__d16w : {lq_tmp_x241 : int | [(lq_tmp_x241 = 0)]}
 bind 41 lq_anf__d16x : {VV : a_a164 | [(VV = lq_anf__d16w)]}
-bind 42 lq_anf__d16y : {lq_tmp_x254 : GHC.Types.Bool | [((? Prop([lq_tmp_x254])) <=> (gooberding#a15N >= lq_anf__d16x))]}
+bind 42 lq_anf__d16y : {lq_tmp_x254 : GHC.Types.Bool | [((Prop([lq_tmp_x254])) <=> (gooberding#a15N >= lq_anf__d16x))]}
 bind 43 lq_anf__d16z : {lq_tmp_x276 : int | [(lq_tmp_x276 = (0  :  int))]}
 bind 44 Test0.x#r12i : {VV#272 : int | [$k_273]}
 bind 45 lq_anf__d16A : {lq_tmp_x291 : int | [(lq_tmp_x291 = (0  :  int))]}
-bind 46 lq_anf__d16B : {lq_tmp_x297 : GHC.Types.Bool | [((? Prop([lq_tmp_x297])) <=> (Test0.x#r12i > lq_anf__d16A))]}
-bind 47 lq_anf__d16C : {lq_tmp_x313 : GHC.Types.Bool | [((? Prop([lq_tmp_x313])) <=> (Test0.x#r12i > lq_anf__d16A));
+bind 46 lq_anf__d16B : {lq_tmp_x297 : GHC.Types.Bool | [((Prop([lq_tmp_x297])) <=> (Test0.x#r12i > lq_anf__d16A))]}
+bind 47 lq_anf__d16C : {lq_tmp_x313 : GHC.Types.Bool | [((Prop([lq_tmp_x313])) <=> (Test0.x#r12i > lq_anf__d16A));
                                                         (lq_tmp_x313 = lq_anf__d16B)]}
-bind 48 lq_anf__d16C : {lq_tmp_x315 : GHC.Types.Bool | [((? Prop([lq_tmp_x315])) <=> (Test0.x#r12i > lq_anf__d16A));
+bind 48 lq_anf__d16C : {lq_tmp_x315 : GHC.Types.Bool | [((Prop([lq_tmp_x315])) <=> (Test0.x#r12i > lq_anf__d16A));
                                                         (lq_tmp_x315 = lq_anf__d16B)]}
-bind 49 lq_anf__d16C : {lq_tmp_x315 : GHC.Types.Bool | [((? Prop([lq_tmp_x315])) <=> (Test0.x#r12i > lq_anf__d16A));
+bind 49 lq_anf__d16C : {lq_tmp_x315 : GHC.Types.Bool | [((Prop([lq_tmp_x315])) <=> (Test0.x#r12i > lq_anf__d16A));
                                                         (lq_tmp_x315 = lq_anf__d16B);
-                                                        (~ ((? Prop([lq_tmp_x315]))));
-                                                        (~ ((? Prop([lq_tmp_x315]))))]}
-bind 50 lq_anf__d16C : {lq_tmp_x321 : GHC.Types.Bool | [((? Prop([lq_tmp_x321])) <=> (Test0.x#r12i > lq_anf__d16A));
+                                                        (~ ((Prop([lq_tmp_x315]))));
+                                                        (~ ((Prop([lq_tmp_x315]))))]}
+bind 50 lq_anf__d16C : {lq_tmp_x321 : GHC.Types.Bool | [((Prop([lq_tmp_x321])) <=> (Test0.x#r12i > lq_anf__d16A));
                                                         (lq_tmp_x321 = lq_anf__d16B)]}
-bind 51 lq_anf__d16C : {lq_tmp_x321 : GHC.Types.Bool | [((? Prop([lq_tmp_x321])) <=> (Test0.x#r12i > lq_anf__d16A));
+bind 51 lq_anf__d16C : {lq_tmp_x321 : GHC.Types.Bool | [((Prop([lq_tmp_x321])) <=> (Test0.x#r12i > lq_anf__d16A));
                                                         (lq_tmp_x321 = lq_anf__d16B);
-                                                        (? Prop([lq_tmp_x321]));
-                                                        (? Prop([lq_tmp_x321]))]}
+                                                        (Prop([lq_tmp_x321]));
+                                                        (Prop([lq_tmp_x321]))]}
 bind 52 Test0.prop_abs#r12j : {VV#287 : GHC.Types.Bool | [$k_288]}
 bind 53 VV#343 : {VV#343 : GHC.Types.Bool | [$k_239[VV#238:=VV#343][fix##36#dOrd_a165:=fix#GHC.Classes.#36#fOrdInt#35#rhx][fix##36#dNum_a166:=fix#GHC.Num.#36#fNumInt#35#rhy][gooberding#a15N:=Test0.x#r12i][lq_tmp_x332:=fix#GHC.Classes.#36#fOrdInt#35#rhx][lq_tmp_x333:=fix#GHC.Num.#36#fNumInt#35#rhy][lq_tmp_x334:=Test0.x#r12i][lq_tmp_x328:=VV#343]]}
 bind 54 VV#343 : {VV#343 : GHC.Types.Bool | [$k_239[VV#238:=VV#343][fix##36#dOrd_a165:=fix#GHC.Classes.#36#fOrdInt#35#rhx][fix##36#dNum_a166:=fix#GHC.Num.#36#fNumInt#35#rhy][gooberding#a15N:=Test0.x#r12i][lq_tmp_x332:=fix#GHC.Classes.#36#fOrdInt#35#rhx][lq_tmp_x333:=fix#GHC.Num.#36#fNumInt#35#rhy][lq_tmp_x334:=Test0.x#r12i][lq_tmp_x328:=VV#343]]}
@@ -232,9 +232,9 @@
                                   (VV#346 = Test0.x#r12i)]}
 bind 56 VV#346 : {VV#346 : int | [$k_273[VV#272:=VV#346][lq_tmp_x341:=VV#346];
                                   (VV#346 = Test0.x#r12i)]}
-bind 57 VV#349 : {VV#349 : GHC.Types.Bool | [(~ ((? Prop([VV#349]))));
+bind 57 VV#349 : {VV#349 : GHC.Types.Bool | [(~ ((Prop([VV#349]))));
                                              (VV#349 = GHC.Types.False#68)]}
-bind 58 VV#349 : {VV#349 : GHC.Types.Bool | [(~ ((? Prop([VV#349]))));
+bind 58 VV#349 : {VV#349 : GHC.Types.Bool | [(~ ((Prop([VV#349]))));
                                              (VV#349 = GHC.Types.False#68)]}
 bind 59 VV#352 : {VV#352 : int | [(VV#352 = (0  :  int));
                                   (VV#352 = lq_anf__d16A)]}
@@ -254,11 +254,11 @@
                                   (VV#364 = lq_anf__d16z)]}
 bind 69 VV#367 : {VV#367 : int | [(VV#367 = 0)]}
 bind 70 VV#367 : {VV#367 : int | [(VV#367 = 0)]}
-bind 71 VV#370 : {VV#370 : GHC.Types.Bool | [(? Prop([VV#370]))]}
-bind 72 VV#370 : {VV#370 : GHC.Types.Bool | [(? Prop([VV#370]))]}
-bind 73 VV#373 : {VV#373 : GHC.Types.Bool | [((? Prop([VV#373])) <=> (gooberding#a15N >= lq_anf__d16x));
+bind 71 VV#370 : {VV#370 : GHC.Types.Bool | [(Prop([VV#370]))]}
+bind 72 VV#370 : {VV#370 : GHC.Types.Bool | [(Prop([VV#370]))]}
+bind 73 VV#373 : {VV#373 : GHC.Types.Bool | [((Prop([VV#373])) <=> (gooberding#a15N >= lq_anf__d16x));
                                              (VV#373 = lq_anf__d16y)]}
-bind 74 VV#373 : {VV#373 : GHC.Types.Bool | [((? Prop([VV#373])) <=> (gooberding#a15N >= lq_anf__d16x));
+bind 74 VV#373 : {VV#373 : GHC.Types.Bool | [((Prop([VV#373])) <=> (gooberding#a15N >= lq_anf__d16x));
                                              (VV#373 = lq_anf__d16y)]}
 bind 75 VV : {VV : a_a164 | [(VV = lq_anf__d16w);
                              (VV = lq_anf__d16x)]}
@@ -417,7 +417,7 @@
        15;
        31;
        47]
-  lhs {VV#F3 : GHC.Types.Bool | [(~ ((? Prop([VV#F3]))));
+  lhs {VV#F3 : GHC.Types.Bool | [(~ ((Prop([VV#F3]))));
                                  (VV#F3 = GHC.Types.False#68)]}
   rhs {VV#F3 : GHC.Types.Bool | [$k_288[VV#287:=VV#F3][VV#349:=VV#F3][VV#F:=VV#F3]]}
   id 3 tag [3]
@@ -603,7 +603,7 @@
        30;
        15;
        31]
-  lhs {VV#F7 : GHC.Types.Bool | [(? Prop([VV#F7]))]}
+  lhs {VV#F7 : GHC.Types.Bool | [(Prop([VV#F7]))]}
   rhs {VV#F7 : GHC.Types.Bool | [$k_239[VV#238:=VV#F7][VV#370:=VV#F7][VV#F:=VV#F7]]}
   id 7 tag [1]
 
@@ -653,9 +653,9 @@
        30;
        15;
        31]
-  lhs {VV#F8 : GHC.Types.Bool | [((? Prop([VV#F8])) <=> (gooberding#a15N >= lq_anf__d16x));
+  lhs {VV#F8 : GHC.Types.Bool | [((Prop([VV#F8])) <=> (gooberding#a15N >= lq_anf__d16x));
                                  (VV#F8 = lq_anf__d16y)]}
-  rhs {VV#F8 : GHC.Types.Bool | [(? Prop([VV#F8]))]}
+  rhs {VV#F8 : GHC.Types.Bool | [(Prop([VV#F8]))]}
   id 8 tag [1]
 
 
diff --git a/tests/pos/test000.hs.fq b/tests/pos/test000.hs.fq
--- a/tests/pos/test000.hs.fq
+++ b/tests/pos/test000.hs.fq
@@ -1,51 +1,51 @@
-qualif Fst(v : @(1), y : @(0)): (v = fst([y])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.spec" (line 29, column 8)
-qualif Snd(v : @(1), y : @(0)): (v = snd([y])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.spec" (line 30, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) > 0)) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 13, column 8)
-qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]): ((? Prop([v])) <=> (len([xs]) = 0)) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 14, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) = 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 16, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) >= 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 17, column 8)
-qualif ListZ(v : [@(0)]): (len([v]) > 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 18, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) = len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 20, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) >= len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 21, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) > len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 22, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) <= len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 23, column 8)
-qualif CmpLen(v : [@(1)], xs : [@(0)]): (len([v]) < len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 24, column 8)
-qualif EqLen(v : int, xs : [@(0)]): (v = len([xs])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 26, column 8)
-qualif LenEq(v : [@(0)], x : int): (x = len([v])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 27, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x + 1)) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 28, column 8)
-qualif LenDiff(v : [@(0)], x : int): (len([v]) = (x - 1)) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 29, column 8)
-qualif LenAcc(v : int, xs : [@(0)], n : int): (v = (len([xs]) + n)) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 30, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 3, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 4, column 8)
-qualif Bot(v : @(0)): (0 = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 5, column 8)
-qualif Bot(v : bool): (0 = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 6, column 8)
-qualif Bot(v : int): (0 = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 7, column 8)
-qualif CmpZ(v : @(0)): (v < 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 9, column 8)
-qualif CmpZ(v : @(0)): (v <= 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 10, column 8)
-qualif CmpZ(v : @(0)): (v > 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 11, column 8)
-qualif CmpZ(v : @(0)): (v >= 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 12, column 8)
-qualif CmpZ(v : @(0)): (v = 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 13, column 8)
-qualif CmpZ(v : @(0)): (v != 0) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 14, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v < x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 16, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v <= x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 17, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v > x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 18, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v >= x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 19, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v = x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 20, column 8)
-qualif Cmp(v : @(0), x : @(0)): (v != x) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 21, column 8)
-qualif One(v : int): (v = 1) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 28, column 8)
-qualif True(v : bool): (? v) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 29, column 8)
-qualif False(v : bool): (~ ((? v))) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 30, column 8)
-qualif True1(v : GHC.Types.Bool): (? Prop([v])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 31, column 8)
-qualif False1(v : GHC.Types.Bool): (~ ((? Prop([v])))) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 32, column 8)
-qualif Papp(v : @(0), p : (Pred  @(0))): (? papp1([p;
-                                                   v])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 35, column 8)
-qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))): (? papp2([p;
+qualif Fst(v : @(1), y : @(0)) { v = fst([y])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.spec" (line 29, column 8)
+qualif Snd(v : @(1), y : @(0)) { v = snd([y])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.spec" (line 30, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (Prop([v])) <=> (len([xs]) > 0)  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 13, column 8)
+qualif IsEmp(v : GHC.Types.Bool, xs : [@(0)]) { (Prop([v])) <=> (len([xs]) = 0)  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 14, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) = 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 16, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) >= 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 17, column 8)
+qualif ListZ(v : [@(0)]) { len([v]) > 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 18, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) = len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 20, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) >= len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 21, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) > len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 22, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) <= len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 23, column 8)
+qualif CmpLen(v : [@(1)], xs : [@(0)]) { len([v]) < len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 24, column 8)
+qualif EqLen(v : int, xs : [@(0)]) { v = len([xs])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 26, column 8)
+qualif LenEq(v : [@(0)], x : int) { x = len([v])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 27, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x + 1)  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 28, column 8)
+qualif LenDiff(v : [@(0)], x : int) { len([v]) = (x - 1)  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 29, column 8)
+qualif LenAcc(v : int, xs : [@(0)], n : int) { v = (len([xs]) + n)  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/GHC/Base.hquals" (line 30, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 3, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 4, column 8)
+qualif Bot(v : @(0)) { 0 = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 5, column 8)
+qualif Bot(v : bool) { 0 = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 6, column 8)
+qualif Bot(v : int) { 0 = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 7, column 8)
+qualif CmpZ(v : @(0)) { v < 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 9, column 8)
+qualif CmpZ(v : @(0)) { v <= 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 10, column 8)
+qualif CmpZ(v : @(0)) { v > 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 11, column 8)
+qualif CmpZ(v : @(0)) { v >= 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 12, column 8)
+qualif CmpZ(v : @(0)) { v = 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 13, column 8)
+qualif CmpZ(v : @(0)) { v != 0  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 14, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v < x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 16, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v <= x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 17, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v > x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 18, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v >= x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 19, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v = x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 20, column 8)
+qualif Cmp(v : @(0), x : @(0)) { v != x  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 21, column 8)
+qualif One(v : int) { v = 1  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 28, column 8)
+qualif True(v : bool) { v  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 29, column 8)
+qualif False(v : bool) { ~ ((v))  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 30, column 8)
+qualif True1(v : GHC.Types.Bool) { Prop([v])  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 31, column 8)
+qualif False1(v : GHC.Types.Bool) { ~ ((Prop([v])))  } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 32, column 8)
+qualif Papp(v : @(0), p : (Pred  @(0))) { (papp1([p;
+                                                   v])) } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 35, column 8)
+qualif Papp2(v : @(1), x : @(0), p : (Pred  @(1)  @(0))) { (papp2([p;
                                                                     v;
-                                                                    x])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 38, column 8)
-qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))): (? papp3([p;
+                                                                    x])) } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 38, column 8)
+qualif Papp3(v : @(2), x : @(0), y : @(1), p : (Pred  @(2)  @(0)  @(1))) { (papp3([p;
                                                                                     v;
                                                                                     x;
-                                                                                    y])) // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 41, column 8)
+                                                                                    y])) } // "/Users/benjamin/UCSDrepos/liquidhaskell/include/Prelude.hquals" (line 41, column 8)
 
 
 
@@ -138,11 +138,11 @@
 bind 4 GHC.Types.EQ$35$6U : {VV$35$180 : GHC.Types.Ordering | [(VV$35$180 = GHC.Types.EQ$35$6U)]}
 bind 5 GHC.Types.LT$35$6S : {VV$35$181 : GHC.Types.Ordering | [(VV$35$181 = GHC.Types.LT$35$6S)]}
 bind 6 GHC.Types.GT$35$6W : {VV$35$182 : GHC.Types.Ordering | [(VV$35$182 = GHC.Types.GT$35$6W)]}
-bind 7 GHC.Types.True$35$6u : {v_4 : GHC.Types.Bool | [(? Prop([v_4]))]}
-bind 8 GHC.Types.False$35$68 : {v_5 : GHC.Types.Bool | [(~ ((? Prop([v_5]))))]}
-bind 9 GHC.Types.False$35$68 : {v_5 : GHC.Types.Bool | [(~ ((? Prop([v_5]))))]}
+bind 7 GHC.Types.True$35$6u : {v_4 : GHC.Types.Bool | [(Prop([v_4]))]}
+bind 8 GHC.Types.False$35$68 : {v_5 : GHC.Types.Bool | [(~ ((Prop([v_5]))))]}
+bind 9 GHC.Types.False$35$68 : {v_5 : GHC.Types.Bool | [(~ ((Prop([v_5]))))]}
 bind 10 GHC.Types.$91$$93$$35$6m : {VV : func(1, [[@(0)]]) | []}
-bind 11 GHC.Types.True$35$6u : {v_4 : GHC.Types.Bool | [(? Prop([v_4]))]}
+bind 11 GHC.Types.True$35$6u : {v_4 : GHC.Types.Bool | [(Prop([v_4]))]}
 bind 12 GHC.Types.GT$35$6W : {VV$35$227 : GHC.Types.Ordering | [(cmp([VV$35$227]) = GHC.Types.GT$35$6W)]}
 bind 13 GHC.Types.LT$35$6S : {VV$35$228 : GHC.Types.Ordering | [(cmp([VV$35$228]) = GHC.Types.LT$35$6S)]}
 bind 14 GHC.Types.EQ$35$6U : {VV$35$229 : GHC.Types.Ordering | [(cmp([VV$35$229]) = GHC.Types.EQ$35$6U)]}
@@ -154,29 +154,29 @@
 bind 20 lq_anf__d12s : {lq_tmp_x_275 : GHC.Types.Bool | [(lq_tmp_x_275 = Test0.toss$35$rYP)]}
 bind 21 lq_anf__d12s : {lq_tmp_x_277 : GHC.Types.Bool | [(lq_tmp_x_277 = Test0.toss$35$rYP)]}
 bind 22 lq_anf__d12s : {lq_tmp_x_277 : GHC.Types.Bool | [(lq_tmp_x_277 = Test0.toss$35$rYP);
-                                                         (~ ((? Prop([lq_tmp_x_277]))));
-                                                         (~ ((? Prop([lq_tmp_x_277]))));
-                                                         (~ ((? Prop([lq_tmp_x_277]))))]}
+                                                         (~ ((Prop([lq_tmp_x_277]))));
+                                                         (~ ((Prop([lq_tmp_x_277]))));
+                                                         (~ ((Prop([lq_tmp_x_277]))))]}
 bind 23 lq_anf__d12s : {lq_tmp_x_283 : GHC.Types.Bool | [(lq_tmp_x_283 = Test0.toss$35$rYP)]}
 bind 24 lq_anf__d12s : {lq_tmp_x_283 : GHC.Types.Bool | [(lq_tmp_x_283 = Test0.toss$35$rYP);
-                                                         (? Prop([lq_tmp_x_283]));
-                                                         (? Prop([lq_tmp_x_283]));
-                                                         (? Prop([lq_tmp_x_283]))]}
+                                                         (Prop([lq_tmp_x_283]));
+                                                         (Prop([lq_tmp_x_283]));
+                                                         (Prop([lq_tmp_x_283]))]}
 bind 25 lq_anf__d12t : {lq_tmp_x_288 : GHC.Types.Bool | [(lq_tmp_x_288 = lq_anf__d12s)]}
 bind 26 lq_anf__d12t : {lq_tmp_x_290 : GHC.Types.Bool | [(lq_tmp_x_290 = lq_anf__d12s)]}
 bind 27 lq_anf__d12t : {lq_tmp_x_290 : GHC.Types.Bool | [(lq_tmp_x_290 = lq_anf__d12s);
-                                                         (~ ((? Prop([lq_tmp_x_290]))));
-                                                         (~ ((? Prop([lq_tmp_x_290]))));
-                                                         (~ ((? Prop([lq_tmp_x_290]))))]}
+                                                         (~ ((Prop([lq_tmp_x_290]))));
+                                                         (~ ((Prop([lq_tmp_x_290]))));
+                                                         (~ ((Prop([lq_tmp_x_290]))))]}
 bind 28 lq_anf__d12t : {lq_tmp_x_296 : GHC.Types.Bool | [(lq_tmp_x_296 = lq_anf__d12s)]}
 bind 29 lq_anf__d12t : {lq_tmp_x_296 : GHC.Types.Bool | [(lq_tmp_x_296 = lq_anf__d12s);
-                                                         (? Prop([lq_tmp_x_296]));
-                                                         (? Prop([lq_tmp_x_296]));
-                                                         (? Prop([lq_tmp_x_296]))]}
+                                                         (Prop([lq_tmp_x_296]));
+                                                         (Prop([lq_tmp_x_296]));
+                                                         (Prop([lq_tmp_x_296]))]}
 bind 30 Test0.prop_abs$35$r10h : {VV$35$272 : GHC.Types.Bool | [$k__273]}
 bind 31 x$35$a11A : {VV$35$307 : int | [$k__308]}
 bind 32 lq_anf__d12u : {lq_tmp_x_315 : int | [(lq_tmp_x_315 = (0  :  int))]}
-bind 33 lq_anf__d12v : {lq_tmp_x_321 : GHC.Types.Bool | [((? Prop([lq_tmp_x_321])) <=> (x$35$a11A > lq_anf__d12u))]}
+bind 33 lq_anf__d12v : {lq_tmp_x_321 : GHC.Types.Bool | [((Prop([lq_tmp_x_321])) <=> (x$35$a11A > lq_anf__d12u))]}
 bind 34 lq_anf__d12w : {lq_tmp_x_345 : int | [$k__343[lq_tmp_x_340:=lq_anf__d12v][VV$35$342:=lq_tmp_x_345][lq_tmp_x_341:=x$35$a11A]]}
 bind 35 lq_anf__d12x : {lq_tmp_x_350 : int | [(lq_tmp_x_350 = (1  :  int))]}
 bind 36 lq_anf__d12y : {lq_tmp_x_373 : int | [(lq_tmp_x_373 = (12  :  int))]}
@@ -223,16 +223,16 @@
 bind 77 VV$35$477 : {VV$35$477 : int | [(VV$35$477 = x$35$a11A)]}
 bind 78 VV$35$480 : {VV$35$480 : int | [(VV$35$480 = 0)]}
 bind 79 VV$35$480 : {VV$35$480 : int | [(VV$35$480 = 0)]}
-bind 80 VV$35$483 : {VV$35$483 : GHC.Types.Bool | [(? Prop([VV$35$483]))]}
-bind 81 VV$35$483 : {VV$35$483 : GHC.Types.Bool | [(? Prop([VV$35$483]))]}
+bind 80 VV$35$483 : {VV$35$483 : GHC.Types.Bool | [(Prop([VV$35$483]))]}
+bind 81 VV$35$483 : {VV$35$483 : GHC.Types.Bool | [(Prop([VV$35$483]))]}
 bind 82 VV$35$486 : {VV$35$486 : GHC.Types.Bool | [(VV$35$486 = lq_anf__d12t)]}
 bind 83 VV$35$486 : {VV$35$486 : GHC.Types.Bool | [(VV$35$486 = lq_anf__d12t)]}
 bind 84 VV$35$489 : {VV$35$489 : GHC.Types.Bool | [(VV$35$489 = GHC.Types.False$35$68)]}
 bind 85 VV$35$489 : {VV$35$489 : GHC.Types.Bool | [(VV$35$489 = GHC.Types.False$35$68)]}
 bind 86 VV$35$492 : {VV$35$492 : GHC.Types.Bool | [(VV$35$492 = GHC.Types.False$35$68)]}
 bind 87 VV$35$492 : {VV$35$492 : GHC.Types.Bool | [(VV$35$492 = GHC.Types.False$35$68)]}
-bind 88 VV$35$495 : {VV$35$495 : GHC.Types.Bool | [((? Prop([VV$35$495])) <=> (lq_anf__d12q > lq_anf__d12r))]}
-bind 89 VV$35$495 : {VV$35$495 : GHC.Types.Bool | [((? Prop([VV$35$495])) <=> (lq_anf__d12q > lq_anf__d12r))]}
+bind 88 VV$35$495 : {VV$35$495 : GHC.Types.Bool | [((Prop([VV$35$495])) <=> (lq_anf__d12q > lq_anf__d12r))]}
+bind 89 VV$35$495 : {VV$35$495 : GHC.Types.Bool | [((Prop([VV$35$495])) <=> (lq_anf__d12q > lq_anf__d12r))]}
 bind 90 VV$35$498 : {VV$35$498 : int | [(VV$35$498 = lq_anf__d12r)]}
 bind 91 VV$35$498 : {VV$35$498 : int | [(VV$35$498 = lq_anf__d12r)]}
 bind 92 VV$35$501 : {VV$35$501 : int | [(VV$35$501 = lq_anf__d12q)]}
@@ -309,7 +309,7 @@
        15;
        31]
   lhs {VV$35$F8 : GHC.Types.Bool | [(VV$35$F8 = lq_anf__d12v)]}
-  rhs {VV$35$F8 : GHC.Types.Bool | [(? Prop([VV$35$F8]))]}
+  rhs {VV$35$F8 : GHC.Types.Bool | [(Prop([VV$35$F8]))]}
   id 8 tag [3]
   // META constraint id 8 : tests/pos/test000.hs:14:23-29
 
@@ -339,7 +339,7 @@
        29;
        14;
        15]
-  lhs {VV$35$F11 : GHC.Types.Bool | [(? Prop([VV$35$F11]))]}
+  lhs {VV$35$F11 : GHC.Types.Bool | [(Prop([VV$35$F11]))]}
   rhs {VV$35$F11 : GHC.Types.Bool | [$k__273[VV$35$272:=VV$35$F11][VV$35$F:=VV$35$F11][VV$35$483:=VV$35$F11]]}
   id 11 tag [6]
   // META constraint id 11 : tests/pos/test000.hs:10:33-50
@@ -371,7 +371,7 @@
        14;
        15]
   lhs {VV$35$F12 : GHC.Types.Bool | [(VV$35$F12 = lq_anf__d12t)]}
-  rhs {VV$35$F12 : GHC.Types.Bool | [(? Prop([VV$35$F12]))]}
+  rhs {VV$35$F12 : GHC.Types.Bool | [(Prop([VV$35$F12]))]}
   id 12 tag [6]
   // META constraint id 12 : tests/pos/test000.hs:10:47-50
 
@@ -456,7 +456,7 @@
        13;
        14;
        15]
-  lhs {VV$35$F15 : GHC.Types.Bool | [((? Prop([VV$35$F15])) <=> (lq_anf__d12q > lq_anf__d12r))]}
+  lhs {VV$35$F15 : GHC.Types.Bool | [((Prop([VV$35$F15])) <=> (lq_anf__d12q > lq_anf__d12r))]}
   rhs {VV$35$F15 : GHC.Types.Bool | [$k__235[VV$35$F:=VV$35$F15][VV$35$495:=VV$35$F15][VV$35$234:=VV$35$F15]]}
   id 15 tag [5]
   // META constraint id 15 : tests/pos/test000.hs:6:1-22
diff --git a/tests/pos/test00a.fq b/tests/pos/test00a.fq
--- a/tests/pos/test00a.fq
+++ b/tests/pos/test00a.fq
@@ -1,5 +1,5 @@
 // This qualifier saves the day; solve constraints WITHOUT IT
-qualif Zog(v:a) : (10 <= v)
+qualif Zog(v:a)  { 10 <= v }
 
 bind 0 x : {v : int | true}
 bind 1 y : {v : int | true}
diff --git a/tests/pos/test1.fq b/tests/pos/test1.fq
--- a/tests/pos/test1.fq
+++ b/tests/pos/test1.fq
@@ -1,6 +1,6 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
-qualif Zog(v:a) : (10 <= v)
+qualif Zog(v:a)  { 10 <= v }
 
 bind 0 x : {v : int | v = 10}
 bind 1 y : {v : int | v = 20}
diff --git a/tests/pos/test2.fq b/tests/pos/test2.fq
--- a/tests/pos/test2.fq
+++ b/tests/pos/test2.fq
@@ -1,9 +1,9 @@
 
 // This qualifier saves the day; solve constraints WITHOUT IT
-qualif Zog(v:a): (10 <= v)
+qualif Zog(v:a) { 10 <= v }
 
 // But you may use this one
-qualif Pog(v:a): (0 <= v)
+qualif Pog(v:a) { 0 <= v }
 
 bind 0 x: {v: int | v = 10}
 bind 1 a: {v: int | $k1    }
diff --git a/tests/pos/test3.fq b/tests/pos/test3.fq
--- a/tests/pos/test3.fq
+++ b/tests/pos/test3.fq
@@ -1,5 +1,5 @@
 
-qualif Zog(v:a, z:b) : (v = z)
+qualif Zog(v:a, z:b)  { v = z }
 
 bind 0 x : {v : int | true}
 bind 1 q : {v : int | true}
diff --git a/tests/pos/test4.fq b/tests/pos/test4.fq
--- a/tests/pos/test4.fq
+++ b/tests/pos/test4.fq
@@ -1,4 +1,4 @@
-qualif Auto(v_2 : a_ax6, A0 : a_ax6): (v_2 = A0)
+qualif Auto(v_2 : a_ax6, A0 : a_ax6) { v_2 = A0 }
 
 bind 20 ds_dxx : {VV263 : a_ax6 | []}
 
diff --git a/tests/pos/unexpected-ge.fq b/tests/pos/unexpected-ge.fq
--- a/tests/pos/unexpected-ge.fq
+++ b/tests/pos/unexpected-ge.fq
@@ -1,2 +1,2 @@
 
-qualif Auto(v : int, x : int): (v = (if (x > 0) then 0 else 0))
+qualif Auto(v : int, x : int) { v = (if (x > 0) then 0 else 0) }
diff --git a/tests/pos/wl00.fq b/tests/pos/wl00.fq
--- a/tests/pos/wl00.fq
+++ b/tests/pos/wl00.fq
@@ -1,6 +1,6 @@
-qualif Nat(v:int) : (0 <= v)
-qualif N10(v:int) : (10 = v)
-qualif N20(v:int) : (20 = v)
+qualif Nat(v:int)  { 0 <= v }
+qualif N10(v:int)  { 10 = v }
+qualif N20(v:int)  { 20 = v }
 
 bind 0 x0 : {v: int | [$k0]}
 
diff --git a/tests/pos/wl01.fq b/tests/pos/wl01.fq
--- a/tests/pos/wl01.fq
+++ b/tests/pos/wl01.fq
@@ -1,4 +1,4 @@
-qualif Nat(v:int) : (0 <= v)
+qualif Nat(v:int)  { 0 <= v }
 
 bind 0 x : {v: int | [$k0]}
 bind 1 y : {v: int | [$k0]}
diff --git a/tests/pos/wl02.fq b/tests/pos/wl02.fq
--- a/tests/pos/wl02.fq
+++ b/tests/pos/wl02.fq
@@ -1,4 +1,4 @@
-qualif Nat(v:int) : (0 <= v)
+qualif Nat(v:int)  { 0 <= v }
 
 bind 0  x0  : {v: int | [$k0]}
 bind 1  x1  : {v: int | [$k1]}
diff --git a/tests/tasty/Arbitrary.hs b/tests/tasty/Arbitrary.hs
--- a/tests/tasty/Arbitrary.hs
+++ b/tests/tasty/Arbitrary.hs
@@ -22,7 +22,6 @@
 import Language.Fixpoint.Types.Refinements as R
 import Language.Fixpoint.Parse             (isNotReserved)
 import Language.Fixpoint.Types             as T hiding (Result)
-import Language.Fixpoint.Types.Spans       as Spans
 import Data.Traversable                    (for)
 
 {-
@@ -76,8 +75,8 @@
 subexprs (PKVar _ _)     = []
 subexprs (PAll _ e)      = [e]
 subexprs (PExist _ e)    = [e]
-subexprs (PGrad _ _ _ e) = [e]
 subexprs (ECoerc _ _ e)  = [e]
+subexprs (ELet _ e1 e2)  = [e1, e2]
 
 -- TODO: Adjust frequencies
 -- | To ensure this reliably terminates we require that `zeroExprGen` generates
@@ -105,8 +104,8 @@
   , (1, PKVar <$> arbitrary <*> arbitrary)
   , (1, PAll <$> arbitraryList arbitrary <*> arbitraryExpr')
   , (1, PExist <$> arbitraryList arbitrary <*> arbitraryExpr')
-  , (1, PGrad <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitraryExpr')
   , (1, ECoerc <$> arbitrary <*> arbitrary <*> arbitraryExpr')
+  , (1, ELet <$> arbitrary <*> arbitraryExpr' <*> arbitraryExpr')
   ]
   where
     arbitraryExpr' = arbitraryFiniteExpr zeroExprGen (n `div` 2)
@@ -132,12 +131,6 @@
 
 instance Arbitrary KVar where
   arbitrary = KV <$> arbitrary
-
--- NOTE: This dummy Arbitrary instance returns a constant GradInfo.
-instance Arbitrary GradInfo where
-  arbitrary = pure $ GradInfo (SS pos pos) Nothing
-    where pos = Spans.dummyPos "<unknown>"
-  shrink _ = mempty
 
 instance Arbitrary Subst where
   arbitrary = do
diff --git a/tests/tasty/Main.hs b/tests/tasty/Main.hs
--- a/tests/tasty/Main.hs
+++ b/tests/tasty/Main.hs
@@ -5,6 +5,7 @@
 import qualified ParserTests
 import qualified ShareMapTests
 import qualified SimplifyTests
+import qualified SimplifyKVarTests
 import qualified InterpretTests
 import qualified UndoANFTests
 import Test.Tasty
@@ -14,6 +15,7 @@
   [ ParserTests.tests
   , ShareMapTests.tests
   , SimplifyTests.tests
+  , SimplifyKVarTests.tests
   , InterpretTests.tests
   , UndoANFTests.tests
   ]
diff --git a/tests/tasty/ParserTests.hs b/tests/tasty/ParserTests.hs
--- a/tests/tasty/ParserTests.hs
+++ b/tests/tasty/ParserTests.hs
@@ -131,11 +131,7 @@
 testFunAppP :: TestTree
 testFunAppP =
   testGroup "FunAppP"
-    [ testCase "ECon (litP)" $
-        show (doParse' funAppP "test" "lit \"#x00000008\" (BitVec  Size32)") @?=
-          "ECon (L \"#x00000008\" (FApp (FTC (TC \"BitVec\" defined at: test:1:19-1:25 (TCInfo {tc_isNum = False, tc_isReal = False, tc_isString = False}))) (FTC (TC \"Size32\" defined at: test:1:27-1:33 (TCInfo {tc_isNum = False, tc_isReal = False, tc_isString = False})))))"
-
-    , testCase "ECon (exprFunSpacesP)" $
+    [ testCase "ECon (exprFunSpacesP)" $
         show (doParse' funAppP "test" "fooBar baz qux") @?= "EApp (EApp (EVar \"fooBar\") (EVar \"baz\")) (EVar \"qux\")"
 
     , testCase "ECon (exprFunCommasP)" $
@@ -200,54 +196,14 @@
         show (doParse' expr0P "test" "1") @?= "ECon (I 1)"
     ]
 
--- ---------------------------------------------------------------------
-{-
-
-pred = expressionParse (prefixOp++infixOp) pred0
-
-prefixOp = '~' | 'not'
-
-infixOp  = '&&' | '||' | '=>' | '==>' | '<=>'
-
--- terms are pred0
-pred0 = 'true' | 'false'
-      | '??'
-      | kvarPred
-      | fastIfP
-      | predr
-      | '(' pred ')'
-      | '?' expr
-      | funApp
-      | symbol
-      | '&&' preds
-      | '||' preds
-
-kvarPred = kvar substs
-
-kvar = '$' symbol
-
-substs = {- empty -}
-       | subst substs
-
-subst = '[' symbol ':=' expr ']'
-
-preds = '[' predslist ']'
-
-predslist = pred
-          | pred `;` predslist
-
-fastIf = 'if' pred 'then' pred 'else' pred
-
-predr = expr brel expr
-
-brelP = '==' | '=' | '~~' | '!=' | '/=' | '!~' | '<' | '<=' | '>' | '>='
-
--}
-
 testPredP :: TestTree
 testPredP =
-  testGroup "predP"
-    [ testCase "PTrue" $
+  testGroup "exprP"
+    [ testCase "ECon (litP)" $
+        show (doParse' exprP "test" "lit \"#x00000008\" (BitVec  Size32)") @?=
+          "ECon (L \"#x00000008\" (FApp (FTC (TC \"BitVec\" defined at: test:1:19-1:25 (TCInfo {tc_isNum = False, tc_isReal = False, tc_isString = False}))) (FTC (TC \"Size32\" defined at: test:1:27-1:33 (TCInfo {tc_isNum = False, tc_isReal = False, tc_isString = False})))))"
+
+    , testCase "PTrue" $
         show (doParse' predP "test" "true") @?= "PAnd []" -- pattern for PTrue
 
     , testCase "PFalse" $
@@ -268,8 +224,7 @@
 
     , testCase "fastIf" $
         show (doParse' predP "test" "if true then true else false" ) @?=
-          -- note conversion
-          "PAnd [PImp (PAnd []) (PAnd []),PImp (PNot (PAnd [])) (POr [])]"
+          "EIte (PAnd []) (PAnd []) (POr [])"
 
     , testCase "brel" $
         show (doParse' predP "test" "1 == 2") @?= "PAtom Eq (ECon (I 1)) (ECon (I 2))"
@@ -277,9 +232,6 @@
     , testCase "parens pred" $
         show (doParse' predP "test" "((1 == 2))") @?= "PAtom Eq (ECon (I 1)) (ECon (I 2))"
 
-    , testCase "? expr" $
-        show (doParse' predP "test" "? (1+2)") @?= "EBin Plus (ECon (I 1)) (ECon (I 2))"
-
     , testCase "funApp 1" $
         show (doParse' predP "test" "f a b") @?= "EApp (EApp (EVar \"f\") (EVar \"a\")) (EVar \"b\")"
 
@@ -290,10 +242,10 @@
         show (doParse' predP "test" "f ([a; b])") @?= "EApp (EApp (EVar \"f\") (EVar \"a\")) (EVar \"b\")"
 
     , testCase "funApp 4" $
-        show (doParse' funAppP "" "f ?(x > 1)") @?= "EApp (EVar \"f\") (PAtom Gt (EVar \"x\") (ECon (I 1)))"
+        show (doParse' funAppP "" "f (x > 1)") @?= "EApp (EVar \"f\") (PAtom Gt (EVar \"x\") (ECon (I 1)))"
 
     , testCase "funApp 5" $
-        show (doParse' predP "" "f ?(x > 1)") @?= "EApp (EVar \"f\") (PAtom Gt (EVar \"x\") (ECon (I 1)))"
+        show (doParse' predP "" "f (x > 1)") @?= "EApp (EVar \"f\") (PAtom Gt (EVar \"x\") (ECon (I 1)))"
 
     , testCase "symbol" $
         show (doParse' predP "test" "f") @?= "EVar \"f\""
@@ -302,7 +254,7 @@
         show (doParse' predP "test" "&& []") @?= "PAnd []"
 
     , testCase "&& 1" $
-        show (doParse' predP "test" "&& [x]") @?= "EVar \"x\""
+        show (doParse' predP "test" "&& [x]") @?= "PAnd [EVar \"x\"]"
 
     , testCase "&& 2" $
         show (doParse' predP "test" "&& [x;y]") @?= "PAnd [EVar \"x\",EVar \"y\"]"
diff --git a/tests/tasty/SimplifyPLE.hs b/tests/tasty/SimplifyPLE.hs
--- a/tests/tasty/SimplifyPLE.hs
+++ b/tests/tasty/SimplifyPLE.hs
@@ -17,7 +17,6 @@
       KN
         { knSims = M.empty, -- :: Map Symbol [(Rewrite, IsUserDataSMeasure)]
           knAms = M.empty, -- :: Map Symbol Equation
-          knContext = undefined, -- :: SMT.Context
           knPreds = undefined, -- :: SMT.Context -> [(Symbol, Sort)] -> Expr -> IO Bool
           knLams = [], -- :: ![(Symbol, Sort)]
           knSummary = [], -- :: ![(Symbol, Int)]
@@ -33,13 +32,16 @@
     emptyICtx =
       ICtx
         { icAssms = S.empty,      -- S.HashSet Pred
-          icCands = S.empty,      -- :: S.HashSet Expr
-          icEquals = S.empty,     -- :: EvAccum
+          icCands = mempty,      -- :: S.HashSet Expr
+          icEquals = mempty,     -- :: EvAccum
           icSimpl = SM.empty,     -- :: !ConstMap
           icSubcId = Nothing,     -- :: Maybe SubcId
           icANFs = [],            -- :: [[(Symbol, SortedReft)]]
           icLRWs = mempty,
           icEtaBetaFlag        = False,
           icExtensionalityFlag = False,
-          icLocalRewritesFlag  = False
+          icLocalRewritesFlag  = False,
+          icBindIds = mempty,
+          icFreshExistentialCounter = 0,
+          icInitialLHSs = mempty
         }
diff --git a/tests/tasty/ghc-9.12.1/SimplifyKVarTests.hs b/tests/tasty/ghc-9.12.1/SimplifyKVarTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/tasty/ghc-9.12.1/SimplifyKVarTests.hs
@@ -0,0 +1,400 @@
+{-# LANGUAGE MultilineStrings #-}
+
+module SimplifyKVarTests (tests) where
+
+import Control.Monad (when)
+import qualified Data.HashSet as HashSet
+import Language.Fixpoint.Parse
+import qualified Language.Fixpoint.Types as F
+import qualified Language.Fixpoint.Solver.Solution as F
+import Test.Tasty
+import Test.Tasty.HUnit
+
+
+tests :: TestTree
+tests =
+  testGroup "simplifyKVar" $ map simplificationTest
+    [ SimplificationTest
+        { name = "single elimination"
+        , expected = """
+            exists [y : int] . P C y
+          """
+        , input = """
+            exists [x : int, y : int] . x == C && P x y
+          """
+        }
+
+    , SimplificationTest
+        { name = "full elimination"
+        , expected = """
+            P C D
+          """
+        , input = """
+            exists [x : int, y : int] . x == C && P x y && y == D
+          """
+        }
+
+    , SimplificationTest
+        { name = "alpha equivalence"
+        , expected = """
+            (exists [w : int, z : int] . Q w z) &&
+            (exists [w : int, z : int] . P w z)
+          """
+        , input = """
+            (exists [w : int, z : int] . Q w z) &&
+            (exists [w : int, z : int] . P w z) &&
+            (exists [x : int, y : int] . P x y)
+          """
+        }
+
+    , SimplificationTest
+        { name = "floating"
+        , expected = """
+            (exists [x : int, y : int] . P x y) && A == C
+          """
+        , input = """
+            exists [x : int, y : int] . A == C && P x y
+          """
+        }
+
+    , SimplificationTest
+        { name = "inner floating"
+        , expected = """
+            (exists [x : int] . P x && Q x) && (exists [y : int] . P y)
+          """
+        , input = """
+            exists [x : int] . P x && (exists [ y : int] . P y && Q x)
+          """
+        }
+
+    , largeSimplificationTest
+    ]
+
+data SimplificationTest = SimplificationTest
+  { input :: String
+  , expected :: String
+  , name :: String
+  }
+
+simplificationTest :: SimplificationTest -> TestTree
+simplificationTest test =
+  testCase (name test) $ do
+    let actual =
+          F.simplifyKVar
+            HashSet.empty
+            (doParse'' True predP (name test) (input test))
+        expectedE = doParse'' True predP (name test) (expected test)
+    when (not (F.alphaEq HashSet.empty actual expectedE)) $ do
+      assertFailure $ unlines
+        [ "output is not as expected"
+        , "Expected:"
+        , expected test
+        , ""
+        , "Actual:"
+        , F.showpp actual
+        ]
+
+largeSimplificationTest :: SimplificationTest
+largeSimplificationTest =
+  SimplificationTest
+    { name = "large simplification"
+    , expected = """
+        exists [w : int] . Test.gt0xy w i##aS7
+      """
+    , input = """
+        exists [VV##1821##k_ : int,
+                i##aS7##k_ : int,
+                lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                lq_tmpx##1823##k_ : int,
+                lq_tmpx##1824##k_ : int]
+              . (exists [w : int,
+                         w2 : int,
+                         x : int,
+                         y : Tuple0,
+                         VV##F##13 : int]
+                   . VV##1821##k_ == VV##F##13
+                     && i##aS7##k_ == i##aS7
+                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                     && lq_tmpx##1823##k_ == w
+                     && lq_tmpx##1824##k_ == x
+                     && (exists [VV##1829 : int,
+                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                 VV##1805##k_ : int,
+                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                 i##aS7##k_ : int]
+                           . VV##1829 == w
+                             && VV##1805##k_ == w
+                             && i##aS7##k_ == i##aS7
+                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                             && (exists [VV##F##3 : int]
+                                   . Test.gt0xy VV##F##3 i##aS7
+                                     && VV##1805##k_ == VV##F##3
+                                     && i##aS7##k_ == i##aS7
+                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                     && (exists [VV##1830 : int,
+                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                 i##aS7##k_ : int,
+                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                 VV##1807##k_ : int]
+                           . VV##1830 == w2
+                             && VV##1807##k_ == w2
+                             && i##aS7##k_ == i##aS7
+                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                             && (exists [w : int,
+                                         VV##F##14 : int,
+                                         lq_rnmx##255 : Tuple0]
+                                   . (exists [VV##1828 : int,
+                                              lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                              lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                              VV##1805##k_ : int,
+                                              lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                              i##aS7##k_ : int]
+                                        . VV##1828 == w
+                                          && VV##1805##k_ == w
+                                          && i##aS7##k_ == i##aS7
+                                          && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                          && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                          && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                          && (exists [VV##F##3 : int]
+                                                . Test.gt0xy VV##F##3 i##aS7
+                                                  && VV##1805##k_ == VV##F##3
+                                                  && i##aS7##k_ == i##aS7
+                                                  && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                  && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                  && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                                     && (exists [lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                 i##aS7##k_ : int,
+                                                 lq_tmpx##1812##k_ : Tuple0,
+                                                 VV##1809##k_ : int,
+                                                 lq_tmpx##1811##k_ : int,
+                                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int)]
+                                           . VV##1809##k_ == VV##F##14
+                                             && i##aS7##k_ == i##aS7
+                                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                             && lq_tmpx##1811##k_ == w
+                                             && lq_tmpx##1812##k_ == lq_rnmx##255
+                                             && (exists [VV##F##12 : int,
+                                                         lq_tmpx##1811 : int,
+                                                         lq_tmpx##1812 : Tuple0,
+                                                         lq_tmpdb##43 : int,
+                                                         lq_tmpdb##44 : Tuple0]
+                                                   . VV##F##12 == i##aS7
+                                                     && VV##1809##k_ == VV##F##12
+                                                     && i##aS7##k_ == i##aS7
+                                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                     && lq_tmpx##1811##k_ == lq_tmpx##1811
+                                                     && lq_tmpx##1812##k_ == lq_tmpx##1812))
+                                     && (exists [i##aS7##k_ : int,
+                                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                                 lq_tmpx##1801 : Tuple0,
+                                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                 VV##1799##k_ : Tuple0]
+                                           . VV##1799##k_ == lq_rnmx##255
+                                             && i##aS7##k_ == i##aS7
+                                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                             && lq_tmpx##1801 == lq_rnmx##255
+                                             && (exists [VV##F##10 : Tuple0]
+                                                   . VV##1799##k_ == VV##F##10
+                                                     && i##aS7##k_ == i##aS7
+                                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                                     && VV##1807##k_ == VV##F##14
+                                     && i##aS7##k_ == i##aS7
+                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                     && (exists [i##aS7##k_ : int,
+                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                 lq_tmpx##1801 : Tuple0,
+                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                 VV##1799##k_ : Tuple0]
+                           . VV##1799##k_ == y
+                             && i##aS7##k_ == i##aS7
+                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                             && lq_tmpx##1801 == y
+                             && (exists [VV##F##10 : Tuple0]
+                                   . VV##1799##k_ == VV##F##10
+                                     && i##aS7##k_ == i##aS7
+                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                     && (exists [i##aS7##k_ : int,
+                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                 lq_tmpx##1804 : int,
+                                 lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                 VV##1802##k_ : int]
+                           . VV##1802##k_ == x
+                             && i##aS7##k_ == i##aS7
+                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                             && lq_tmpx##1804 == x
+                             && (exists [VV##F##6 : int]
+                                   . (exists [i##aS7##k_ : int,
+                                              lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                              lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                              VV##1796##k_ : int]
+                                        . VV##1796##k_ == VV##F##6
+                                          && i##aS7##k_ == i##aS7
+                                          && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                          && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                          && (exists [VV##F##7 : int]
+                                                . (exists [lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                           lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                           i##aS7##k_ : int,
+                                                           lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                                           VV##1807##k_ : int]
+                                                     . VV##1807##k_ == VV##F##7
+                                                       && i##aS7##k_ == i##aS7
+                                                       && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                       && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                       && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                       && (exists [w : int,
+                                                                   VV##F##14 : int,
+                                                                   lq_rnmx##255 : Tuple0]
+                                                             . (exists [VV##1828 : int,
+                                                                        lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                                        lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                                                        VV##1805##k_ : int,
+                                                                        lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                                        i##aS7##k_ : int]
+                                                                  . VV##1828 == w
+                                                                    && VV##1805##k_ == w
+                                                                    && i##aS7##k_ == i##aS7
+                                                                    && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                    && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                    && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                                    && (exists [VV##F##3 : int]
+                                                                          . Test.gt0xy VV##F##3 i##aS7
+                                                                            && VV##1805##k_ == VV##F##3
+                                                                            && i##aS7##k_ == i##aS7
+                                                                            && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                            && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                            && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                                                               && (exists [lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                                           lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                                           i##aS7##k_ : int,
+                                                                           lq_tmpx##1812##k_ : Tuple0,
+                                                                           VV##1809##k_ : int,
+                                                                           lq_tmpx##1811##k_ : int,
+                                                                           lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int)]
+                                                                     . VV##1809##k_ == VV##F##14
+                                                                       && i##aS7##k_ == i##aS7
+                                                                       && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                       && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                       && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                                       && lq_tmpx##1811##k_ == w
+                                                                       && lq_tmpx##1812##k_ == lq_rnmx##255
+                                                                       && (exists [VV##F##12 : int,
+                                                                                   lq_tmpx##1811 : int,
+                                                                                   lq_tmpx##1812 : Tuple0,
+                                                                                   lq_tmpdb##43 : int,
+                                                                                   lq_tmpdb##44 : Tuple0]
+                                                                             . VV##F##12 == i##aS7
+                                                                               && VV##1809##k_ == VV##F##12
+                                                                               && i##aS7##k_ == i##aS7
+                                                                               && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                               && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                               && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                                               && lq_tmpx##1811##k_ == lq_tmpx##1811
+                                                                               && lq_tmpx##1812##k_ == lq_tmpx##1812))
+                                                               && (exists [i##aS7##k_ : int,
+                                                                           lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                                           lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                                                           lq_tmpx##1801 : Tuple0,
+                                                                           lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                                           VV##1799##k_ : Tuple0]
+                                                                     . VV##1799##k_ == lq_rnmx##255
+                                                                       && i##aS7##k_ == i##aS7
+                                                                       && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                       && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                       && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                                                       && lq_tmpx##1801 == lq_rnmx##255
+                                                                       && (exists [VV##F##10 : Tuple0]
+                                                                             . VV##1799##k_ == VV##F##10
+                                                                               && i##aS7##k_ == i##aS7
+                                                                               && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                                               && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                                               && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                                                               && VV##1807##k_ == VV##F##14
+                                                               && i##aS7##k_ == i##aS7
+                                                               && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                               && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                                               && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                                                  && VV##1796##k_ == VV##F##7
+                                                  && i##aS7##k_ == i##aS7
+                                                  && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                  && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb))
+                                     && (exists [i##aS7##k_ : int,
+                                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                                 VV##1793##k_ : int]
+                                           . VV##1793##k_ == VV##F##6
+                                             && i##aS7##k_ == i##aS7
+                                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                             && (exists [VV##F##5 : int]
+                                                   . VV##1793##k_ == VV##F##5
+                                                     && i##aS7##k_ == i##aS7
+                                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb))
+                                     && VV##1802##k_ == VV##F##6
+                                     && i##aS7##k_ == i##aS7
+                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc))
+                     && (exists [lq_anf##7205759403792798200##d1cc##k_ : (Test.State int int),
+                                 lq_tmpx##1818##k_ : int,
+                                 lq_anf##7205759403792798198##d1ca##k_ : (GHC.Internal.Base.Monad (Test.State int)),
+                                 lq_tmpx##1817##k_ : int,
+                                 VV##1815##k_ : int,
+                                 lq_anf##7205759403792798199##d1cb##k_ : (Test.State int Tuple0),
+                                 i##aS7##k_ : int]
+                           . VV##1815##k_ == VV##F##13
+                             && i##aS7##k_ == i##aS7
+                             && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                             && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                             && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                             && lq_tmpx##1817##k_ == w2
+                             && lq_tmpx##1818##k_ == x
+                             && (exists [VV##F##8 : int,
+                                         lq_tmpx##1817 : int,
+                                         lq_tmpx##1818 : int,
+                                         lq_tmpdb##47 : int,
+                                         lq_tmpdb##48 : int]
+                                   . lq_tmpx##1818 == VV##F##8
+                                     && VV##F##8 == lq_tmpx##1817
+                                     && VV##1815##k_ == VV##F##8
+                                     && i##aS7##k_ == i##aS7
+                                     && lq_anf##7205759403792798198##d1ca##k_ == lq_anf##7205759403792798198##d1ca
+                                     && lq_anf##7205759403792798199##d1cb##k_ == lq_anf##7205759403792798199##d1cb
+                                     && lq_anf##7205759403792798200##d1cc##k_ == lq_anf##7205759403792798200##d1cc
+                                     && lq_tmpx##1817##k_ == lq_tmpx##1817
+                                     && lq_tmpx##1818##k_ == lq_tmpx##1818)))
+        """
+    }
diff --git a/tests/tasty/ghc-before-9.12.1/SimplifyKVarTests.hs b/tests/tasty/ghc-before-9.12.1/SimplifyKVarTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/tasty/ghc-before-9.12.1/SimplifyKVarTests.hs
@@ -0,0 +1,12 @@
+
+module SimplifyKVarTests (tests) where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+tests :: TestTree
+tests =
+  testGroup "simplifyKVar"
+    [ testCase "Disabled because it needs MultilineStrings (ghc >= 9.12.1)" $
+        return ()
+    ]
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -87,26 +87,27 @@
 unitTests :: FilePath -> IO TestTree
 unitTests lfDir
   = group "Unit" [
-      testGroup "native-pos" <$> dirTests nativeCmd "tests/pos"    skipNativePos  ExitSuccess
-    , testGroup "native-neg" <$> dirTests nativeCmd "tests/neg"    ["float.fq"]   (ExitFailure 1)
-    , testGroup "elim-crash" <$> dirTests nativeCmd "tests/crash"  []             (ExitFailure 1)
-    , testGroup "elim-pos1"  <$> dirTests elimCmd   "tests/pos"    []             ExitSuccess
-    , testGroup "elim-pos2"  <$> dirTests elimCmd   "tests/elim"   []             ExitSuccess
-    , testGroup "elim-neg"   <$> dirTests elimCmd   "tests/neg"    ["float.fq"]   (ExitFailure 1)
-    , testGroup "elim-crash" <$> dirTests elimCmd   "tests/crash"  []             (ExitFailure 1)
-    , testGroup "cvc5-pos"   <$> dirTests cvc5Cmd   "tests/pos"    skipNativePos  ExitSuccess
-    , testGroup "proof"      <$> dirTests elimCmd   "tests/proof"     []          ExitSuccess
-    , testGroup "rankN"      <$> dirTests elimCmd   "tests/rankNTypes" []         ExitSuccess
-    , testGroup "horn-pos-el"      <$> dirTests elimSaveCmd   "tests/horn/pos"  []          ExitSuccess
-    , testGroup "horn-pos-cvc5"    <$> dirTests cvc5Cmd       "tests/horn/pos"  []          ExitSuccess
-    , testGroup "horn-neg-el"      <$> dirTests elimSaveCmd   "tests/horn/neg"  []          (ExitFailure 1)
-    , testGroup "horn-neg-cvc5"    <$> dirTests cvc5Cmd       "tests/horn/neg"  []          (ExitFailure 1)
-    , testGroup "horn-json-pos-el" <$> dirJsonTests elimCmd   "tests/horn/pos/.liquid"  []  ExitSuccess
-    , testGroup "horn-json-neg-el" <$> dirJsonTests elimCmd   "tests/horn/neg/.liquid"  []  (ExitFailure 1)
-    , testGroup "horn-smt2-pos-el" <$> dirHornTests elimCmd  "tests/horn/pos/.liquid"  []  ExitSuccess
-    , testGroup "horn-smt2-neg-el" <$> dirHornTests elimCmd  "tests/horn/neg/.liquid"  []  (ExitFailure 1)
-    , testGroup "horn-pos-na"      <$> dirTests nativeCmd     "tests/horn/pos"  []          ExitSuccess
-    , testGroup "horn-neg-na"      <$> dirTests nativeCmd     "tests/horn/neg"  []          (ExitFailure 1)
+      testGroup "native-pos"       <$> dirTests     nativeCmd   "tests/pos"              skipNativePos  ExitSuccess
+    , testGroup "native-neg"       <$> dirTests     nativeCmd   "tests/neg"              ["float.fq"]  (ExitFailure 1)
+    , testGroup "elim-crash"       <$> dirTests     nativeCmd   "tests/crash"            []            (ExitFailure 1)
+    , testGroup "elim-pos1"        <$> dirTests     elimCmd     "tests/pos"              []             ExitSuccess
+    , testGroup "elim-pos2"        <$> dirTests     elimCmd     "tests/elim"             []             ExitSuccess
+    , testGroup "elim-neg"         <$> dirTests     elimCmd     "tests/neg"              ["float.fq"]  (ExitFailure 1)
+    , testGroup "elim-crash"       <$> dirTests     elimCmd     "tests/crash"            []            (ExitFailure 1)
+    , testGroup "cvc5-pos"         <$> dirTests     cvc5Cmd     "tests/pos"              skipNativePos  ExitSuccess
+    , testGroup "cvc5-spec"        <$> dirTests     cvc5Cmd     "tests/cvc5"             skipNativePos  ExitSuccess
+    , testGroup "proof"            <$> dirTests     elimCmd     "tests/proof"            []             ExitSuccess
+    , testGroup "rankN"            <$> dirTests     elimCmd     "tests/rankNTypes"       []             ExitSuccess
+    , testGroup "horn-pos-el"      <$> dirTests     elimSaveCmd "tests/horn/pos"         []             ExitSuccess
+    , testGroup "horn-pos-cvc5"    <$> dirTests     cvc5Cmd     "tests/horn/pos"         []             ExitSuccess
+    , testGroup "horn-neg-el"      <$> dirTests     elimSaveCmd "tests/horn/neg"         []            (ExitFailure 1)
+    , testGroup "horn-neg-cvc5"    <$> dirTests     cvc5Cmd     "tests/horn/neg"         []            (ExitFailure 1)
+    , testGroup "horn-json-pos-el" <$> dirJsonTests elimCmd     "tests/horn/pos/.liquid" []             ExitSuccess
+    , testGroup "horn-json-neg-el" <$> dirJsonTests elimCmd     "tests/horn/neg/.liquid" []            (ExitFailure 1)
+    , testGroup "horn-smt2-pos-el" <$> dirHornTests elimCmd     "tests/horn/pos/.liquid" []             ExitSuccess
+    , testGroup "horn-smt2-neg-el" <$> dirHornTests elimCmd     "tests/horn/neg/.liquid" []            (ExitFailure 1)
+    , testGroup "horn-pos-na"      <$> dirTests     nativeCmd   "tests/horn/pos"         []             ExitSuccess
+    , testGroup "horn-neg-na"      <$> dirTests     nativeCmd   "tests/horn/neg"         []            (ExitFailure 1)
    ]
    where
     dirTests     = dirTests' isTest
diff --git a/unix/Language/Fixpoint/Utils/Progress.hs b/unix/Language/Fixpoint/Utils/Progress.hs
--- a/unix/Language/Fixpoint/Utils/Progress.hs
+++ b/unix/Language/Fixpoint/Utils/Progress.hs
@@ -1,6 +1,8 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
 -- | Progress Bar API
 module Language.Fixpoint.Utils.Progress (
       withProgress
+    , withProgressM
     , progressInit
     , progressTick
     , progressClose
@@ -13,21 +15,32 @@
 import           System.Console.AsciiProgress
 -- import           Language.Fixpoint.Misc (traceShow)
 
+foreign import ccall unsafe "unistd.h isatty"
+  c_isatty :: Int -> IO Int
+
 {-# NOINLINE pbRef #-}
 pbRef :: IORef (Maybe ProgressBar)
 pbRef = unsafePerformIO (newIORef Nothing)
 
 withProgress :: Int -> IO a -> IO a
-withProgress n act = do
+withProgress = withProgressM id
+
+withProgressM :: (m a -> IO b) -> Int -> m a -> IO b
+withProgressM mToIO n act = do
   showBar <- (Quiet /=) <$> getVerbosity
-  if showBar
+  -- We don't show the progress bar if the output is not a terminal.
+  -- Besides improving the output, this also avoids a concurrency
+  -- issue:
+  -- https://github.com/ucsd-progsys/liquid-fixpoint/issues/782
+  isTTY <- (== 1) <$> c_isatty 1
+  if showBar && isTTY
     then displayConsoleRegions $ do
       -- putStrLn $ "withProgress: " ++ show n
       progressInit n
-      r <- act
+      r <- mToIO act
       progressClose
       return r
-    else act
+    else mToIO act
 
 progressInit :: Int -> IO ()
 progressInit n = do
diff --git a/win/Language/Fixpoint/Utils/Progress.hs b/win/Language/Fixpoint/Utils/Progress.hs
--- a/win/Language/Fixpoint/Utils/Progress.hs
+++ b/win/Language/Fixpoint/Utils/Progress.hs
@@ -1,6 +1,7 @@
 -- | Progress Bar API
 module Language.Fixpoint.Utils.Progress (
       withProgress
+    , withProgressM
     , progressInit
     , progressTick
     , progressClose
@@ -8,6 +9,9 @@
 
 withProgress :: Int -> IO a -> IO a
 withProgress _ x = x
+
+withProgressM :: (m a -> IO b) -> Int -> m a -> IO b
+withProgressM f _ = f
 
 progressInit :: Int -> IO ()
 progressInit _ = return ()
