shithub: MicroHs

ref: d870a2fe07edafdc829a76af55792551a8fae394
dir: /src/MicroHs/TypeCheck.hs/

View raw version
-- Copyright 2023 Lennart Augustsson
-- See LICENSE file for full license.
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns -Wno-unused-imports -Wno-unused-do-bind #-}
{-# LANGUAGE FlexibleContexts #-}
module MicroHs.TypeCheck(
  typeCheck,
  TModule(..), showTModule,
  GlobTables, emptyGlobTables, mergeGlobTables,
  impossible, impossibleShow,
  mkClassConstructor,
  mkSuperSel,
  setBindings,
  boolPrefix,
  listPrefix,
  ValueExport(..), TypeExport(..),
  Symbols,
  ) where
import Prelude(); import MHSPrelude
import Control.Applicative
import Control.Arrow(first)
import Control.Monad
import Data.Char
import Data.Function
import Data.List
import Data.Maybe
import MicroHs.Builtin
import MicroHs.Deriving
import MicroHs.Expr
import MicroHs.Fixity
import MicroHs.Graph
import MicroHs.Ident
import qualified MicroHs.IdentMap as M
import qualified MicroHs.IntMap as IM
import MicroHs.List
import MicroHs.MRnf
import MicroHs.Parse(dotDotIdent)
import MicroHs.SymTab
import MicroHs.TCMonad
import GHC.Stack
import Debug.Trace

boolPrefix :: String
boolPrefix = "Data.Bool_Type."

listPrefix :: String
listPrefix = "Data.List_Type."

nameList :: String
nameList = listPrefix ++ "[]"
identList :: Ident
identList = mkIdentB nameList

nameInt :: String
nameInt = "Primitives.Int"
identInt :: Ident
identInt = mkIdentB nameInt

nameWord :: String
nameWord = "Primitives.Word"
identWord :: Ident
identWord = mkIdentB nameWord

nameFloatW :: String
nameFloatW = "Primitives.FloatW"
identFloatW :: Ident
identFloatW = mkIdentB nameFloatW

nameChar :: String
nameChar = "Primitives.Char"
identChar :: Ident
identChar = mkIdentB nameChar

nameInteger :: String
nameInteger = "Data.Integer_Type.Integer"
identInteger :: Ident
identInteger = mkIdentB nameInteger

nameTypeEq :: String
nameTypeEq = "Primitives.~"
identTypeEq :: Ident
identTypeEq = mkIdentB nameTypeEq

nameImplies :: String
nameImplies = "Primitives.=>"
identImplies :: Ident
identImplies = mkIdentB nameImplies

nameArrow :: String
nameArrow = "Primitives.->"
identArrow :: Ident
identArrow = mkIdentB nameArrow

nameSymbol :: String
nameSymbol = "Primitives.Symbol"

nameNat :: String
nameNat = "Primitives.Nat"

nameType :: String
nameType = "Primitives.Type"

nameKind :: String
nameKind = "Primitives.Kind"

nameConstraint :: String
nameConstraint = "Primitives.Constraint"

nameKnownNat :: String
nameKnownNat = "Data.TypeLits.KnownNat"

nameKnownSymbol :: String
nameKnownSymbol = "Data.TypeLits.KnownSymbol"

nameCoercible :: String
nameCoercible = "Data.Coerce.Coercible"

--primitiveKinds :: [String]
--primitiveKinds = [nameType, nameConstraint, nameSymbol, nameNat]

----------------------

-- Certain data structures persist during the entire compilation
-- session.  The information is needed beyond the scope where it was defined.
data GlobTables = GlobTables {
  gSynTable   :: SynTable,        -- type synonyms are needed for expansion
  gDataTable  :: DataTable,       -- data/newtype definitions
  gClassTable :: ClassTable,      -- classes are neede for superclass expansion
  gInstInfo   :: InstTable        -- instances are implicitely global
  }

instance MRnf GlobTables where
  mrnf (GlobTables a b c d) = mrnf a `seq` mrnf b `seq` mrnf c `seq` mrnf d

emptyGlobTables :: GlobTables
emptyGlobTables = GlobTables { gSynTable = M.empty, gDataTable = M.empty, gClassTable = M.empty, gInstInfo = M.empty }

mergeGlobTables :: GlobTables -> GlobTables -> GlobTables
mergeGlobTables g1 g2 =
  GlobTables { gSynTable = M.merge (gSynTable g1) (gSynTable g2),
               gDataTable = M.merge (gDataTable g1) (gDataTable g2),
               gClassTable = M.merge (gClassTable g1) (gClassTable g2),
               gInstInfo = M.mergeWith mergeInstInfo (gInstInfo g1) (gInstInfo g2) }

type Symbols = (SymTab, SymTab)

data TModule a = TModule {
  tModuleName :: IdentModule,     -- module names
  tFixDefs    :: [FixDef],        -- all fixities, exported or not
  tTypeExps   :: [TypeExport],    -- exported types
  tValueExps  :: [ValueExport],   -- exported values (including from T(..))
  tDefaults   :: Defaults,        -- exported defaults
  tBindingsOf :: a                -- bindings
  }
--  deriving (Show)

instance MRnf a => MRnf (TModule a) where
  mrnf (TModule a b c d e f) = mrnf a `seq` mrnf b `seq` mrnf c `seq` mrnf d `seq` mrnf e `seq` mrnf f

setBindings :: TModule b -> a -> TModule a
setBindings (TModule x y z w v _) a = TModule x y z w v a

type FixDef = (Ident, Fixity)

type Sigma = EType
type Rho   = EType

typeCheck :: forall a . GlobTables -> ImpType -> [(ImportSpec, TModule a)] -> EModule -> (TModule [EDef], GlobTables, Symbols)
typeCheck globs impt aimps (EModule mn exps defs) =
--  trace (unlines $ map (showTModuleExps . snd) aimps) $
  let
    imps = map filterImports aimps
    tc = mkTCState mn globs imps
  in case tcRun (tcDefs impt defs) tc of
       (tds, tcs) ->
         let
           thisMdl = (mn, mkTModule impt tds tcs)
           impMdls = [(fromMaybe m mm, tm) | (ImportSpec _ _ m mm _, tm) <- imps]
           impMap = M.fromList [(i, m) | (i, m) <- thisMdl : impMdls]
           (texps, vexps) =
             unzip $ map (getTVExps impMap (typeTable tcs) (valueTable tcs) (assocTable tcs)) exps
           fexps = map tFixDefs (M.elems impMap)
           sexps = synTable tcs
           dexps = dataTable tcs
           iexps = instTable tcs
           ctbl  = classTable tcs
           dflts = M.fromList $ filter ((`elem` ds) . fst) $ M.toList $ defaults tcs
                 where ds = [ tyQIdent $ expLookup ti (typeTable tcs) | ExpDefault ti <- exps ]
         in  ( tModule mn (nubBy ((==) `on` fst) (concat fexps)) (concat texps) (concat vexps) dflts tds
             , GlobTables { gSynTable = sexps, gDataTable = dexps, gClassTable = ctbl, gInstInfo = iexps }
             , (typeTable tcs, valueTable tcs)
             )

-- A hack to force evaluation of errors.
-- This should be redone to all happen in the T monad.
tModule :: IdentModule -> [FixDef] -> [TypeExport] -> [ValueExport] -> Defaults -> [EDef] ->
           TModule [EDef]
tModule mn fs ts vs ds bs =
--  trace ("tmodule " ++ showIdent mn ++ ":\n" ++ show vs) $
  tseq ts `seq` vseq vs `seq` ds `seq` TModule mn fs ts vs ds bs
  where
    tseq [] = ()
    tseq (TypeExport _ e _:xs) = e `seq` tseq xs
    vseq [] = ()
    vseq (ValueExport _ e:xs) = e `seq` vseq xs

filterImports :: forall a . (ImportSpec, TModule a) -> (ImportSpec, TModule a)
filterImports it@(ImportSpec _ _ _ _ Nothing, _) = it
filterImports (imp@(ImportSpec _ _ _ _ (Just (hide, is))), TModule mn fx ts vs ds a) =
  let
    keep x xs = elem x xs /= hide
    ivs  = [ i | ImpValue i <- is ]
    vs'  = filter (\ (ValueExport i _) -> keep i ivs) vs ++
           if hide then []
           else [ ve | TypeExport _ _ ves <- ts, ve@(ValueExport i _) <- ves, i `elem` ivs ]
    aits = [ i | ImpTypeAll i <- is ]         -- all T(..) imports
    its  = [ i | ImpTypeSome i _ <- is ] ++ aits
    -- XXX This isn't quite right, hiding T() should hide T, but not the constructors
    ts' =
      if hide then
        let ok xs (ValueExport i _) = i `notElem` ivs && i `notElem` xs in
        [ TypeExport i e (filter (ok []) ves) | TypeExport i e ves <- ts, i `notElem` its ] ++
        [ TypeExport i e (filter (ok xs) ves) | TypeExport i e ves <- ts, ImpTypeSome i' xs <- is, i == i' ]
      else
        let ok xs (ValueExport i _) = i `elem` ivs || i `elem` xs || isDefaultMethodId i in
        [ TypeExport i e                 ves  | TypeExport i e ves <- ts, i `elem` aits ] ++
        [ TypeExport i e (filter (ok xs) ves) | TypeExport i e ves <- ts, ImpTypeSome i' xs <- is, i == i' ]
    msg = "not exported"
    allVs = map (\ (ValueExport i _) -> i) vs ++
            concatMap (\ (TypeExport _ _ xvs) -> map (\ (ValueExport i _) -> i) xvs) ts
    allTs = map (\ (TypeExport i _ _) -> i) ts
  in
    (if hide then
       id -- don't complain about missing hidden identifiers; we use it for compatibility
     else
       checkBad msg (ivs \\ allVs) .
       checkBad msg (its \\ allTs))
    --trace (show (ts, vs)) $
    (imp, TModule mn fx ts' vs' ds a)

checkBad :: forall a . String -> [Ident] -> a -> a
checkBad _ [] a = a
checkBad msg (i:_) _ =
  errorMessage (getSLoc i) $ msg ++ ": " ++ showIdent i

-- Type and value exports
getTVExps :: forall a . M.Map (TModule a) -> TypeTable -> ValueTable -> AssocTable -> ExportItem ->
             ([TypeExport], [ValueExport])
getTVExps impMap _ _ _ (ExpModule m) =
  case M.lookup m impMap of
    Just (TModule _ _ te ve _ _) -> (te, ve)
    _ -> errorMessage (getSLoc m) $ "undefined module: " ++ showIdent m
getTVExps _ tys vals ast (ExpTypeSome ti is) =
  let e = expLookup ti tys
      assc = getAssocs vals ast $ tyQIdent e   -- all associated values
      ves = concatMap one is
      one i | i == dotDotIdent = assc          -- '..' means all assocaited values
            | otherwise =
              case filter (\ (ValueExport i' _) -> i == i') assc of
                ee : _ -> [ee]                 -- Pick the assocaited value if it exists
                [] -> [ValueExport (unQualIdent i) $ expLookup i vals]
                                               -- otherwise, just look up a pattern synonym.
                                               -- This might accidentally pick up a constructor from
                                               -- another type, but it doesn't really matter.
  in ([TypeExport (unQualIdent ti) e ves], [])
  
getTVExps _ _ vals _ (ExpValue i) = ([], [ValueExport (unQualIdent i) (expLookup i vals)])
getTVExps _ _ _ _ (ExpDefault _) = ([], [])

expLookup :: Ident -> SymTab -> Entry
expLookup i m = either (errorMessage (getSLoc i)) id $ stLookup "export" i m

tyQIdent :: Entry -> Ident
tyQIdent (Entry (EVar qi) _) = qi
tyQIdent _ = error "tyQIdent"

eVarI :: SLoc -> String -> Expr
eVarI loc = EVar . mkIdentSLoc loc

getApp :: HasCallStack => EType -> (Ident, [EType])
getApp t = fromMaybe (impossibleShow t) $ getAppM t

-- Construct a dummy TModule for the currently compiled module.
-- It has all the relevant export tables.
-- The value&type export tables will later be filtered through the export list.
mkTModule :: forall a . HasCallStack => ImpType -> [EDef] -> TCState -> TModule a
mkTModule impt tds tcs =
  let
    mn = moduleName tcs
    tt = typeTable  tcs
    at = assocTable tcs
    vt = valueTable tcs

    -- Find the Entry for a type.
    tentry i =
      case stLookup "" (qualIdent mn i) tt of
        Right e -> e
        _       -> impossible
    ventry i t =
      let qi = qualIdent mn i in
      case stLookup "" qi vt of
        Right e -> e
        _       -> Entry (EVar qi) t  -- XXX A hack for boot modules
          
    -- Find all value Entry for names associated with a type.
    assoc i = case impt of
                ImpBoot -> []  -- XXX For boot files the tables are not set up correctly.
                _ -> getAssocs vt at (qualIdent mn i)

    -- All top level values possible to export.
    ves = [ ValueExport i (ventry i t) | Sign is t <- tds, i <- is ]

    -- All top level types possible to export.
    tes =
      [ TypeExport i (tentry i) (assoc i) | Data    (i, _) _ _ <- tds ] ++
      [ TypeExport i (tentry i) (assoc i) | Newtype (i, _) _ _ <- tds ] ++
      [ TypeExport i (tentry i) (assoc i) | Class _ (i, _) _ _ <- tds ] ++
      [ TypeExport i (tentry i) []        | Type    (i, _) _   <- tds ]

    -- All fixity declaration.
    fes = [ (qualIdent mn i, fx) | Infix fx is <- tds, i <- is ]

    -- All defaults
    des = defaults tcs

  in  TModule mn fes tes ves des impossible

-- Find all value Entry for names associated with a type.
-- XXX join stLookup code with tentry
getAssocs :: (HasCallStack) => ValueTable -> AssocTable -> Ident -> [ValueExport]
getAssocs _vt at ai = fromMaybe [] $ M.lookup ai at

mkTCState :: IdentModule -> GlobTables -> [(ImportSpec, TModule a)] -> TCState
mkTCState mdlName globs mdls =
  let
    allValues :: ValueTable
    allValues =
      let
        usyms (ImportSpec _ qual _ _ _, TModule _ _ tes ves _ _) =
          if qual then [] else
          [ (i, [e]) | ValueExport i e    <- ves, not (isInstId i)  ] ++
          [ (i, [e]) | TypeExport  _ _ cs <- tes, ValueExport i e <- cs, not (isDefaultMethodId i) ]
        qsyms (ImportSpec _ _ _ mas _, TModule mn _ tes ves _ _) =
          let m = fromMaybe mn mas in
          [ (v, [e]) | ValueExport i e    <- ves,                        let { v = qualIdent    m i } ] ++
          [ (v, [e]) | TypeExport  _ _ cs <- tes, ValueExport i e <- cs, let { v = qualIdentD e m i } ] ++
          [ (v, [Entry (EVar v) t]) | (i, ClassInfo _ _ t _ _) <- M.toList (gClassTable globs), let { v = mkClassConstructor i } ]
        -- Default methods are always entered with their qualified original name.
        qualIdentD (Entry e _) m i | not (isDefaultMethodId i) = qualIdent m i
                                   | otherwise = 
                                     case e of
                                       EVar qi -> qi
                                       _ -> undefined
      in  stFromList (concatMap usyms mdls) (concatMap qsyms mdls)
    allTypes :: TypeTable
    allTypes =
      let
        usyms (ImportSpec _ qual _ _ _, TModule _ _ tes _ _ _) =
          if qual then [] else [ (i, [e]) | TypeExport i e _ <- tes ]
        qsyms (ImportSpec _ _ _ mas _, TModule mn _ tes _ _ _) =
          let m = fromMaybe mn mas in
          [ (qualIdent m i, [e]) | TypeExport i e _ <- tes ]
      in stFromList (concatMap usyms mdls) (concatMap qsyms mdls)

    allFixes :: FixTable
    allFixes = M.fromList (concatMap (tFixDefs . snd) mdls)
    allAssocs :: AssocTable
    allAssocs =
      let assocs (_, TModule _ _ tes _ _ _) = [ (tyQIdent e, cs) | TypeExport _ e cs <- tes ]
      in  M.fromList $ concatMap assocs mdls

    dflts = foldr mergeDefaults M.empty (map (tDefaults . snd) mdls)

  in TC { moduleName = mdlName,
          unique = 1,
          fixTable = addPrimFixs allFixes,
          typeTable = foldr (uncurry stInsertGlbA) allTypes primTypes,
          synTable = gSynTable globs,
          dataTable = gDataTable globs,
          valueTable = foldr (uncurry stInsertGlbA) allValues primValues,
          assocTable = allAssocs,
          uvarSubst = IM.empty,
          tcMode = TCExpr,
          classTable = gClassTable globs,
          ctxTables = (gInstInfo globs, [], [], []),
          constraints = [],
          defaults = dflts
        }

mergeDefaults :: Defaults -> Defaults -> Defaults
mergeDefaults ds = foldr (uncurry $ M.insertWith mrg) ds . M.toList
  where mrg :: [EType] -> [EType] -> [EType]
        mrg ts ts' | null (filter (\ t -> not (elemBy eqEType t ts)) ts') = ts
                   | null (filter (\ t -> not (elemBy eqEType t ts')) ts) = ts'
                   | otherwise = []

mergeInstInfo :: InstInfo -> InstInfo -> InstInfo
mergeInstInfo (InstInfo m1 l1 fds) (InstInfo m2 l2 _) =
  let
    m = foldr (uncurry $ M.insertWith mrg) m2 (M.toList m1)
    mrg e1@(EVar i1) (EVar i2)
      | i1 == i2  = e1
      | otherwise = errorMessage (getSLoc i1) $ "Multiple instances: " ++ showSLoc (getSLoc i2)
    mrg e1 _e2 = e1 -- XXX improve this
    l = unionBy eqInstDict l1 l2
  in  InstInfo m l fds

-- Approximate equality for dictionaries.
-- The important thing is to avoid exact duplicates in the instance table.
eqInstDict :: InstDict -> InstDict -> Bool
eqInstDict (e, _) (e', _) = eqExpr e e'

-- Identifier should only be seen with it's qualified name.
isInstId :: Ident -> Bool
isInstId i = (instPrefix ++ uniqIdentSep) `isPrefixOf` unIdent i

mkInstId :: SLoc -> EType -> Ident
mkInstId loc ct = mkIdentSLoc loc $ instPrefix ++ uniqIdentSep ++ clsTy
  where clsTy = map (\ c -> if isSpace c then '@' else c) $ showExprRaw ct

instPrefix :: String
instPrefix = "inst"

--------------------------

-- Use the type table as the value table, and the primKind table as the type table.
withTypeTable :: forall a . T a -> T a
withTypeTable ta = do
  otcm <- gets tcMode
  vt <- gets valueTable
  tt <- gets typeTable
  putValueTable tt            -- use type table as value table
  let
    tcm = succ otcm
    next = case tcm of { TCType -> primKindTable; TCKind -> primSortTable; _ -> undefined }
  putTypeTable next           -- use kind/sort table as type table
  putTCMode tcm
  a <- ta
  tt' <- gets valueTable
  putValueTable vt
  putTypeTable tt'
  putTCMode otcm
  return a
  
addAssocTable :: Ident -> [ValueExport] -> T ()
addAssocTable i ids = modify $ \ ts -> ts { assocTable = M.insert i ids (assocTable ts) }

addClassTable :: Ident -> ClassInfo -> T ()
addClassTable i x = modify $ \ ts -> ts { classTable = M.insert i x (classTable ts) }

addInstTable :: [InstDictC] -> T ()
addInstTable ics = do
--  tcTrace $ "addInstTable: " ++ show ics
  let
    -- Change type variable to unique unification variables.
    -- These unification variables will never leak, but as an extra caution
    -- we use negative numbers..
    freshSubst u iks =
      zipWith (\ ik j -> (idKindIdent ik, EUVar j)) iks [u ..]

    mkInstInfo :: InstDictC -> T (Ident, InstInfo)
    mkInstInfo (e, iks, ctx, ct, fds) = do
      case (iks, ctx, getApp ct) of
        ([], [], (c, [EVar i])) -> return $ (c, InstInfo (M.singleton i e) [] fds)
        (_,  _,  (c, ts      )) -> return $ (c, InstInfo M.empty [(e, ii)] fds)
          where ii u =
                  let ctx' = map (subst s) ctx
                      ts'  = map (subst s) ts
                      s    = freshSubst u iks
                  in  (ctx', ts')
  iis <- mapM mkInstInfo ics
  it <- gets instTable
  putInstTable $ foldr (uncurry $ M.insertWith mergeInstInfo) it iis

addConstraint :: Ident -> EConstraint -> T ()
addConstraint d ctx = do
--  tcTrace $ "addConstraint: " ++ showIdent d ++ " :: " ++ showEType ctx
  ctx' <- expandSyn ctx
  modify $ \ ts -> ts{ constraints = (d, ctx') : constraints ts }

withDicts :: forall a . HasCallStack => [(Ident, EConstraint)] -> T a -> T a
withDicts ds ta = do
  ct <- gets ctxTables
  mapM_ addDict ds
  a <- ta
  putCtxTables ct
  return a

withDict :: forall a . HasCallStack => Ident -> EConstraint -> T a -> T a
withDict i c ta = do
--  tcTrace $ "+++ withDict enter " ++ show (i, c)
  ct <- gets ctxTables
  addDict (i, c)
  a <- ta
  putCtxTables ct
--  tcTrace $ "--- withDict leave " ++ show (i, c)
  return a

addDict :: HasCallStack => (Ident, EConstraint) -> T ()
addDict (i, c) = do
  c' <- expandSyn c >>= derefUVar
  if null (metaTvs [c']) then
    case c' of
      (EApp (EApp (EVar eq) t1) t2) | eq == mkIdent nameTypeEq -> addEqDict i t1 t2
      _ -> addInstDict i c'
   else
    -- With constraint variables we might get unification variables.
    -- We stash them away in hope that we will learn more later.
    addMetaDict i c'

addInstDict :: HasCallStack => Ident -> EConstraint -> T ()
addInstDict i c = do
  c' <- expandSyn c
  ics <- expandDict (EVar i) c'
  addInstTable ics
  addArgDict i c

addEqDict :: Ident -> EType -> EType -> T ()
addEqDict _i t1 t2 = do
  is <- gets typeEqTable
--  tcTrace ("withEqDict: " ++ show (is, (t1,t2), (addTypeEq t1 t2 is)))
  putTypeEqTable (addTypeEq t1 t2 is)

addMetaDict :: HasCallStack => Ident -> EConstraint -> T ()
addMetaDict i c = do
  ms <- gets metaTable
  putMetaTable ((i,c) : ms)

addArgDict :: HasCallStack => Ident -> EConstraint -> T ()
addArgDict i c = do
  ads <- gets argDicts
  putArgDicts ((i,c) : ads)

--stdDefaults :: [EType]
--stdDefaults = [EVar identInteger, EVar identFloatW, EApp (EVar identList) (EVar identChar)]

addPrimFixs :: FixTable -> FixTable
addPrimFixs =
  M.insert (mkIdent "Primitives.->") (AssocRight, -1) .
  M.insert (mkIdent "Primitives.=>") (AssocRight, -2)

-- r for 'realm', suggested by ChatGPT
rSort :: ESort
rSort = EVar (mkIdent "Primitives.Sort")

sKindKindKind :: EKind
sKindKindKind = sArrow sKind (sArrow sKind sKind)

kTypeTypeS :: EType
kTypeTypeS = kArrow kType kType

kTypeTypeTypeS :: EType
kTypeTypeTypeS = kArrow kType $ kArrow kType kType

mkIdentB :: String -> Ident
mkIdentB = mkIdentSLoc builtinLoc

-- E.g.
--  Kind :: Sort
primSortTable :: KindTable
primSortTable =
  let
    entry i s = Entry (EVar (mkIdentB i)) s
    qsorts = [
       -- The kinds are wired in (for now)
       (mkIdentB nameKind,       [entry nameKind rSort])
       ]
  in stFromList (map (first unQualIdent) qsorts) qsorts

-- E.g.
--  Type       :: Kind
--  Constraint :: Kind
--  (->)       :: Kind -> Kind -> Kind
primKindTable :: KindTable
primKindTable =
  let
    entry i k = Entry (EVar (mkIdentB i)) k
    qkinds = [
       -- The kinds are wired in (for now)
       (mkIdentB nameType,       [entry nameType sKind]),
       (mkIdentB nameConstraint, [entry nameConstraint sKind]),
       (mkIdentB nameSymbol,     [entry nameSymbol sKind]),
       (mkIdentB nameNat,        [entry nameNat sKind]),
       (identArrow,              [entry nameArrow sKindKindKind])
       ]
  in stFromList (map (first unQualIdent) qkinds) qkinds

-- E.g.
--  Bool  :: Type
--  Int   :: Type
--  (->)  :: Type -> Type -> Type
--  (=>)  :: forall k . Constraint -> k -> k
--  Maybe :: Type -> Type
primTypes :: [(Ident, [Entry])]
primTypes =
  let
    entry i = Entry (EVar i)
    k = mkIdent "k"
    kv = EVar k
    kk = IdKind k sKind
    -- Tuples are polykinded since they need to handle both Type and Constraint
    -- (,) :: forall k . k -> k -> k
    -- etc.
    tuple n =
      let
        i = tupleConstr builtinLoc n
      in  (i, [entry i $ EForall True [kk] $ foldr kArrow kv (replicate n kv)])
    -- (=>) :: forall k . Constraint -> k -> k
    kImplies = EForall True [kk] $ kConstraint `kArrow` (kv `kArrow` kv)
    -- (~) :: forall k . k -> k -> Constraint
    kTypeEqual = EForall True [kk] $ kv `kArrow` (kv `kArrow` kConstraint)
  in
      [
       -- The function arrow et al are bothersome to define in Primitives, so keep them here.
       -- But the fixity is defined in Primitives.
       (mkIdentB "->",           [entry identArrow    kTypeTypeTypeS]),
       (mkIdentB "=>",           [entry identImplies  kImplies]),
       (mkIdentB "~",            [entry identTypeEq   kTypeEqual]),
       -- Primitives.hs uses the type [], and it's annoying to fix that.
       -- XXX should not be needed
       (identList,               [entry identList     kTypeTypeS]),
       (mkIdentB "\x2192",       [entry identArrow    kTypeTypeTypeS]),  -- ->
       (mkIdentB "\x21d2",       [entry identImplies  kImplies])         -- =>
      ] ++
      map tuple (0 : enumFromTo 2 10)

-- E.g.
--  True :: Bool
--  (&&) :: Bool -> Bool
--  Just :: forall a . a -> Maybe a
--  ,    :: forall a b . a -> b -> (a,b)
primValues :: [(Ident, [Entry])]
primValues =
  let
    tuple n =
      let
        c = tupleConstr builtinLoc n
        vks = [IdKind (mkIdent ("a" ++ show i)) kType | i <- enumFromTo 1 n]
        ts = map tVarK vks
        r = tApps c ts
      in  (c, [Entry (ECon $ ConData [(c, n)] c []) $ EForall True vks $ EForall True [] $ foldr tArrow r ts ])
  in  map tuple (0 : enumFromTo 2 10)

kArrow :: EKind -> EKind -> EKind
kArrow = tArrow

sArrow :: ESort -> ESort -> ESort
sArrow = tArrow

setUVar :: TRef -> EType -> T ()
setUVar i t = modify $ \ ts -> ts{ uvarSubst = IM.insert i t (uvarSubst ts) }

getUVar :: Int -> T (Maybe EType)
getUVar i = gets (IM.lookup i . uvarSubst)

munify :: HasCallStack =>
          SLoc -> Expected -> EType -> T ()
munify loc (Infer r) b = tSetRefType loc r b
munify loc (Check a) b = unify loc a b

expandSyn :: HasCallStack =>
             EType -> T EType
expandSyn at = do
  let
    syn ts t =
      case t of
        EApp f a -> do
          aa <- expandSyn a
          syn (aa:ts) f
        EVar i -> do
          syns <- gets synTable
          case M.lookup i syns of
            Nothing -> return $ eApps t ts
            Just (EForall _ vks tt) ->
              let s = zip (map idKindIdent vks) ts
                  lvks = length vks
                  lts = length ts
              in  case compare lvks lts of
                    LT -> expandSyn $ eApps (subst s tt) (drop lvks ts)
                    EQ -> expandSyn $ subst s tt
                    GT -> tcError (getSLoc i) $ "bad synonym use"
            Just _ -> impossible
        EUVar _ -> return $ eApps t ts
        ESign a _ -> expandSyn a   -- Throw away signatures, they don't affect unification
        EForall b iks tt | null ts -> EForall b iks <$> expandSyn tt
        ELit _ (LStr _) -> return t
        ELit _ (LInteger _) -> return t
        _ -> impossible
  syn [] at

mapEType :: (EType -> EType) -> EType -> EType
mapEType fn = rec
  where
    rec (EApp f a) = EApp (rec f) (rec a)
    rec (ESign t k) = ESign (rec t) k
    rec (EForall b iks t) = EForall b iks (rec t)
    rec t = fn t

derefUVar :: EType -> T EType
derefUVar at =
  case at of
    EApp f a -> do
      fx <- derefUVar f
      ax <- derefUVar a
      return $ EApp fx ax
    EUVar i -> do
      mt <- getUVar i
      case mt of
        Nothing -> return at
        Just t -> do
          t' <- derefUVar t
          setUVar i t'
          return t'
    EVar _ -> return at
    ESign t k -> flip ESign k <$> derefUVar t
    EForall b iks t -> EForall b iks <$> derefUVar t
    ELit _ (LStr _) -> return at
    ELit _ (LInteger _) -> return at
    _ -> impossible

tcErrorTK :: HasCallStack =>
             SLoc -> String -> T ()
tcErrorTK loc msg = do
  tcm <- gets tcMode
  tcError loc $ msgTCMode' tcm ++ " error: " ++ msg

-- For error messages
msgTCMode :: TCMode -> String
msgTCMode TCExpr = "value"
msgTCMode TCType = "type"
msgTCMode TCKind = "kind"
msgTCMode TCSort = "sort"

msgTCMode' :: TCMode -> String
msgTCMode' TCExpr = "type"
msgTCMode' TCType = "kind"
msgTCMode' TCKind = "sort"
msgTCMode' TCSort = "realm"

unify :: HasCallStack =>
         SLoc -> EType -> EType -> T ()
unify loc a b = do
  aa <- expandSyn a
  bb <- expandSyn b
  unifyR loc aa bb

unifyR :: HasCallStack =>
          SLoc -> EType -> EType -> T ()
unifyR _   (EVar x1)    (EVar x2)      | x1 == x2  = return ()
unifyR loc (EApp f1 a1) (EApp f2 a2)               = do { unifyR loc f1 f2; unifyR loc a1 a2 }
unifyR loc t1@(EUVar r1) t2@(EUVar r2) | r1 < r2   = unifyVar loc r2 t1   -- always make higher
                                       | r1 > r2   = unifyVar loc r1 t2   --   TRefs point to lower
                                       | otherwise = return ()
unifyR loc (EUVar r1)   t2                         = unifyVar loc r1 t2
unifyR loc t1           (EUVar r2)                 = unifyVar loc r2 t1
unifyR loc t1           t2                         = do
  tcm <- gets tcMode
  case tcm of
    -- Defer to constraint solver.
    -- XXX needs changing if we have kind equalities.
    TCExpr -> addEqConstraint loc t1 t2
    _      -> tcErrorTK loc $ "cannot unify " ++ showExpr t1 ++ " and " ++ showExpr t2

unifyVar :: HasCallStack =>
            SLoc -> TRef -> EType -> T ()
unifyVar loc r t = do
  mt <- getUVar r
--  tcTrace $ "unifyVar: " ++ show (r,t)
  case mt of
    Nothing -> unifyUnboundVar loc r t
    Just t' -> unify loc t' t

unifyUnboundVar :: HasCallStack =>
                   SLoc -> TRef -> EType -> T ()
unifyUnboundVar loc r1 at2@(EUVar r2) = do
  -- We know r1 /= r2
  mt2 <- getUVar r2
  case mt2 of
    Nothing -> setUVar r1 at2
    Just t2 -> unify loc (EUVar r1) t2
unifyUnboundVar loc r1 t2 = do
  vs <- getMetaTyVars [t2]
  if elem r1 vs then
    tcErrorTK loc $ "cyclic " ++ showExpr (EUVar r1) ++ " = " ++ showExpr t2
   else
    setUVar r1 t2

-- Reset unification map
tcReset :: T ()
tcReset = modify $ \ ts -> ts{ uvarSubst = IM.empty }

newUVar :: T EType
newUVar = EUVar <$> newUniq

uniqIdentSep :: String
uniqIdentSep = "$"

newIdent :: SLoc -> String -> T Ident
newIdent loc s = do
  u <- newUniq
  return $ mkIdentSLoc loc $ s ++ uniqIdentSep ++ show u

tLookup :: HasCallStack =>
           String -> Ident -> T (Expr, EType)
tLookup msg i = do
  env <- gets valueTable
  case stLookup msg i env of
    Right (Entry e s) -> return (setSLocExpr (getSLoc i) e, s)
    Left            e -> do
{-
      tcm <- gets tcMode
      tcTrace ("TCMode=" ++ show tcm)
      tcTrace ("Value table:\n" ++ show env)
      tenv <- gets typeTable
      tcTrace ("Type table:\n" ++ show tenv)
-}
      tcError (getSLoc i) e

tLookupV :: HasCallStack =>
           Ident -> T (Expr, EType)
tLookupV i = do
  tcm <- gets tcMode
  tLookup (msgTCMode tcm) i

tInst :: HasCallStack => Expr -> EType -> T (Expr, EType)
tInst ae (EForall _ vks t) = do
  t' <- tInstForall vks t
  tInst ae t'
tInst ae at | Just (ctx, t) <- getImplies at = do
  --tcTrace $ "tInst: addConstraint: " ++ show ae ++ ", " ++ show d ++ " :: " ++ show ctx
  if eqExpr ae eCannotHappen then
    -- XXX Gruesome hack.  This avoid adding constraints in cases like
    --  (C a => a) -> T `subsCheck` b
    tInst ae t
   else do
    d <- newDictIdent (getSLoc ae)
    addConstraint d ctx
    tInst (EApp ae (EVar d)) t
tInst ae at = return (ae, at)

tInstForall :: [IdKind] -> EType -> T EType
tInstForall vks t =
  if null vks then
    return t
  else do
    let vs = map idKindIdent vks
    us <- mapM (const newUVar) vks
    return (subst (zip vs us) t)

tInst' :: EType -> T EType
tInst' (EForall _ vks t) = tInstForall vks t
tInst' t = return t

extValE :: HasCallStack =>
           Ident -> EType -> Expr -> T ()
extValE i t e = do
  venv <- gets valueTable
  putValueTable (stInsertLcl i (Entry e t) venv)

-- Extend the global symbol table with i = e :: t
-- Add both qualified and unqualified versions of i.
extValETop :: HasCallStack =>
              Ident -> EType -> Expr -> T ()
extValETop i t e = do
  mn <- gets moduleName
  venv <- gets valueTable
  let qi = qualIdent mn i
      venv'  = stInsertGlbQ qi [Entry e t] venv
      venv'' = stInsertGlbU  i [Entry e t] venv'
  putValueTable venv''

-- Extend symbol table with i::t.
-- The translation for i will be the qualified name.
-- Add both qualified and unqualified versions of i.
extValQTop :: HasCallStack =>
              Ident -> EType -> T ()
extValQTop i t = do
  mn <- gets moduleName
  extValETop i t (EVar (qualIdent mn i))

extVal :: HasCallStack =>
          Ident -> EType -> T ()
extVal i t = extValE i t $ EVar i

extVals :: HasCallStack =>
           [(Ident, EType)] -> T ()
extVals = mapM_ (uncurry extVal)

extTyp :: Ident -> EType -> T ()
extTyp i t = do
  tenv <- gets typeTable
  putTypeTable (stInsertLcl i (Entry (EVar i) t) tenv)

extTyps :: [(Ident, EType)] -> T ()
extTyps = mapM_ (uncurry extTyp)

extSyn :: Ident -> EType -> T ()
extSyn i t = do
  senv <- gets synTable
  putSynTable (M.insert i t senv)

extData :: Ident -> EDef -> T ()
extData i d = do
  denv <- gets dataTable
  putDataTable (M.insert i d denv)

extFix :: Ident -> Fixity -> T ()
extFix i fx = modify $ \ ts -> ts{ fixTable = M.insert i fx (fixTable ts) }

withExtVal :: forall a . HasCallStack =>
              Ident -> EType -> T a -> T a
withExtVal i t ta = do
  venv <- gets valueTable
  extVal i t
  a <- ta
  putValueTable venv
  return a

withExtVals :: forall a . HasCallStack =>
               [(Ident, EType)] -> T a -> T a
withExtVals env ta = do
  venv <- gets valueTable
  extVals env
  a <- ta
  putValueTable venv
  return a

withExtTyps :: forall a . [IdKind] -> T a -> T a
withExtTyps iks ta = do
  let env = map (\ (IdKind v k) -> (v, k)) iks
  venv <- gets typeTable
  extTyps env
  a <- ta
  putTypeTable venv
  return a

tcDefs :: ImpType -> [EDef] -> T [EDef]
tcDefs impt ds = do
--  tcTrace ("tcDefs 1:\n" ++ showEDefs ds)
  mapM_ tcAddInfix ds
  dst <- tcDefsType ds
--  tcTrace ("tcDefs 2:\n" ++ showEDefs dst)
  mapM_ addTypeAndData dst
  dste <- tcExpandClassInst impt dst
--  tcTrace ("tcDefs 3:\n" ++ showEDefs dste)
  case impt of
    ImpNormal -> do
      setDefault dste
      dste' <- tcDefsValue dste
      mapM_ addAssocs dste'
      return dste'
    ImpBoot ->
      return dste

setDefault :: [EDef] -> T ()
setDefault defs = do
  tys <- gets typeTable
  ds <- sequence [ do { ts' <- mapM expandSyn ts; return (tyQIdent $ expLookup c tys, ts') }
                 | Default (Just c) ts <- defs ]
  dflts <- gets defaults
  let dflts' = foldr (uncurry M.insert) dflts ds
--  traceM $ "Active defaults " ++ show (M.toList dflts')
  putDefaults dflts'

tcAddInfix :: EDef -> T ()
tcAddInfix (Infix fx is) = do
  mn <- gets moduleName
  mapM_ (\ i -> extFix (qualIdent mn i) fx) is
tcAddInfix _ = return ()

-- Check type definitions
tcDefsType :: HasCallStack => [EDef] -> T [EDef]
tcDefsType ds = withTypeTable $ do
  kindSigs <- getKindSigns ds
  mapM_ (addTypeKind kindSigs) ds              -- Add the kind of each type to the environment
  dst <- mapM tcDefType ds                     -- Kind check all top level type expressions
--  vars <- gets uvarSubst
--  tcTrace $ show vars
  vt <- gets valueTable
  let ent (Entry i t) = Entry i . mapEType def <$> derefUVar t
      def (EUVar _) = kType    -- default kind variables to Type
      def t = t
  vt' <- mapMSymTab ent vt
  putValueTable vt'
--  tcTrace $ "tcDefType value table:\n" ++ show vt'
  return dst

-- Get all kind signatures, and do sort checking of them.
getKindSigns :: HasCallStack => [EDef] -> T (M.Map EKind)
getKindSigns ds = do
  let iks = [ (i, k) | KindSign i k <- ds ]
      kind (i, k) = (,) i <$> tcKind (Check sKind) k
  multCheck (map fst iks)
  iks' <- mapM kind iks
  return $ M.fromList iks'

-- Expand class and instance definitions (must be done after type synonym processing)
tcExpandClassInst :: ImpType -> [EDef] -> T [EDef]
tcExpandClassInst impt dst = withTypeTable $ do
  dsc <- concat <$> mapM (expandClass impt) dst       -- Expand all class definitions
  dsf <- concat <$> mapM expandField dsc              -- Add HasField instances
--  tcTrace $ showEDefs dsf
  dsd <- concat <$> mapM doDeriving  dsf              -- Add derived instances
--  tcTrace $ showEDefs dsd
  dsi <- concat <$> mapM expandInst  dsd              -- Expand all instance definitions
  return dsi

-- Check&rename the given kinds, also insert the type variables in the symbol table.
withVks :: forall a . HasCallStack => [IdKind] -> ([IdKind] -> T a) -> T a
withVks vks fun = assertTCMode (>=TCType) $ do
  tcm <- gets tcMode
  let
    expect = case tcm of { TCType -> sKind; TCKind -> rSort; _ -> undefined }
    loop r [] = fun (reverse r)
    loop r (IdKind i mk : iks) = do
      -- When we have 'forall k (a :: k) . t' the k is a kind.
      -- Instead we have have to write 'forall (k :: Kind) (a :: k) . t' and then
      -- we have to guess that k needs sort checking.
      -- This doesn't work if there is not exactly 'Kind' (e.g., 'Primitives.Kind')
      -- This sucks.
      if tcm == TCType && guessIsKind mk then do
        s <- withTypeTable $ withTypeTable $ tcExpr (Check rSort) mk
        let ik = IdKind i s
        withExtTyps [ik] $ loop (ik : r) iks
      else do
        k' <- case mk of
                EVar d | d == dummyIdent -> newUVar
                _                        -> withTypeTable $ tcExpr (Check expect) mk   -- bump to next level
        withExtVal i k' $ loop (IdKind i k' : r) iks
  loop [] vks

guessIsKind :: EType -> Bool
guessIsKind (EVar i)                      = i == mkIdent "Kind"
guessIsKind t | Just (f, a) <- getArrow t = guessIsKind f || guessIsKind a
guessIsKind _                             = False

-- Add symbol a table entry (with kind) for each top level typeish definition.
-- If there is a kind signature, use it.  If not, use a kind variable.
addTypeKind :: M.Map EKind -> EDef -> T ()
addTypeKind kdefs adef = do
  let
    addDef (i, _) = do
      k <-
        case M.lookup i kdefs of
           Nothing -> newUVar
           Just k' -> return k'
      extValQTop i k
      
  case adef of
    Data    lhs _ _ -> addDef lhs
    Newtype lhs _ _ -> addDef lhs
    Type    lhs _   -> addDef lhs
    Class _ lhs _ _ -> addDef lhs
    _               -> return ()

-- Add symbols associated with a type.
addAssocs :: EDef -> T ()
addAssocs adef = do
  mn <- gets moduleName
  let
    addAssoc ti is = do
      vt <- gets valueTable
      let val i =
            case stLookup "" i vt of
              Right e -> ValueExport i e
              _       -> impossibleShow i
      addAssocTable (qualIdent mn ti) (map val is)

    assocData (Constr _ _ c (Left _)) = [c]
    assocData (Constr _ _ c (Right its)) = c : map fst its

  case adef of
    Data    (i, _) cs _ | not (isMatchDataTypeName i)
                        -> addAssoc i (nub $ concatMap assocData cs)
    Newtype (i, _) c  _ -> addAssoc i (assocData c)
    Class _ (i, _) _ ms -> addAssoc i [ x | Sign ns _ <- ms, m <- ns, x <- [m, mkDefaultMethodId m] ]
    _                   -> return ()

-- Add type synonyms to the synonym table, and data/newtype to the data table
addTypeAndData :: EDef -> T ()
addTypeAndData adef = do
  mn <- gets moduleName
  case adef of
    Type    (i, vs) t  -> extSyn  (qualIdent mn i) (EForall True vs t)
    Data    (i, _) _ _ -> extData (qualIdent mn i) adef
    Newtype (i, _) _ _ -> extData (qualIdent mn i) adef
    _                  -> return ()

-- Do kind checking of all typeish definitions.
tcDefType :: HasCallStack => EDef -> T EDef
tcDefType def = do
  --tcReset
  case def of
    Data    lhs cs ds      -> withLHS lhs $ \ lhs' -> flip (,) kType       <$> (Data    lhs'  <$> mapM tcConstr cs <*> mapM tcDerive ds)
    Newtype lhs c  ds      -> withLHS lhs $ \ lhs' -> flip (,) kType       <$> (Newtype lhs'  <$> tcConstr c       <*> mapM tcDerive ds)
    Type    lhs t          -> withLHS lhs $ \ lhs' -> first                    (Type    lhs') <$> tInferTypeT t
    Class   ctx lhs fds ms -> withLHS lhs $ \ lhs' -> flip (,) kConstraint <$> (Class         <$> tcCtx ctx <*> return lhs' <*> mapM tcFD fds <*> mapM tcMethod ms)
    Sign      is t         ->                                                  Sign      is   <$> tCheckTypeTImpl kType t
    ForImp ie i t          ->                                                  ForImp ie i    <$> tCheckTypeTImpl kType t
    Instance ct m          ->                                                  Instance       <$> tCheckTypeTImpl kConstraint ct <*> return m
    Default mc ts          ->                                                  Default (Just c) <$> mapM (tcDefault c) ts
                                                                                 where c = fromMaybe num mc
    Deriving ct            ->                                                  Deriving       <$> tCheckTypeTImpl kConstraint ct
    _                      -> return def
 where
   tcMethod (Sign is t) = Sign is <$> tCheckTypeTImpl kType t
   tcMethod (DfltSign i t) = DfltSign i <$> tCheckTypeTImpl kType t
   tcMethod m = return m
   tcFD (is, os) = (,) <$> mapM tcV is <*> mapM tcV os
     where tcV i = do { _ <- tLookup "fundep" i; return i }
   tcDerive = tCheckTypeT (kType `kArrow` kConstraint)
   num = mkBuiltin noSLoc "Num"
   tcDefault c t = do
     EApp _ t' <- tCheckTypeT kConstraint (EApp (EVar c) t)
     return t'

withLHS :: forall a . HasCallStack => LHS -> (LHS -> T (a, EKind)) -> T a
withLHS (i, vks) ta = do
  (_, ki) <- tLookupV i
  withVks vks $ \ vks' -> do
    (a, kr) <- ta (i, vks')
    let kapp = foldr kArrow kr (map (\ (IdKind _ k) -> k) vks')
    --unify (getSLoc i) ki kapp
    _ <- subsCheckRho (getSLoc i) eCannotHappen ki kapp
    return a

tcCtx :: HasCallStack => [EConstraint] -> T [EConstraint]
tcCtx = mapM (tCheckTypeT kConstraint)

tcConstr :: HasCallStack => Constr -> T Constr
tcConstr (Constr iks ct c ets) =
  assertTCMode (==TCType) $
  withVks iks $ \ iks' ->
    Constr iks' <$> tcCtx ct <*> pure c <*>
      either (\ x -> Left  <$> mapM (\ (s,t)     ->         (,)s  <$> tcTypeT (Check kType) t) x)
             (\ x -> Right <$> mapM (\ (i,(s,t)) -> ((,)i . (,)s) <$> tcTypeT (Check kType) t) x) ets

-- Expand a class defintion to
--  * a "data" type for the dictionary, with kind Constraint
--  * superclass selectors
--  * method selectors
--  * default methods
-- E.g.
--   class Eq a where
--     (==) :: a -> a -> Bool
--     (/=) :: a -> a -> a
--     x /= y = not (x == y)
-- expands to
--   data Eq a = Eq$ (a -> a -> Bool) (a -> a -> Bool)
--               :: Constraint
--   == :: forall a . Eq a -> (a -> a -> Bool)
--   == (Eq x _) = x
--   /= :: forall a . Eq a -> (a -> a -> Bool)
--   /= (Eq _ x) = x
--   ==$dflt :: forall a . (Eq a) => (a -> a -> Bool)
--   ==$dflt = _noDefault "Eq.=="
--   /=$dflt :: forall a . (Eq a) => (a -> a -> Bool)
--   /=$dflt x y = not (x == y)
--
--   class (Eq a) => Ord a where
--     (<=) :: a -> a -> Bool
-- expands to
--   data Ord a = Ord$ (Eq a) (a -> a -> Bool)
--   Ord$super1 :: forall a . Ord a -> Eq a
--   <= :: forall a . Ord a -> (a -> a -> Bool)
--   <=$dflt = _noDefault "Ord.<="
--
--   instance Eq Int where (==) = primEqInt
-- expands to
--   inst$999 = Eq$ meth$1 meth$2
--     where meth$1 = primEqInt
--           meth$2 = /=$dflt dict$999
--
--   instance Ord Int where (<=) = primLEInt
-- expands to
--   inst$888 = Ord$ dict$ meth$1
--     where meth$1 = primLEInt
-- where dict$ is a special magic identifier that the type checker expands
-- to whatever dictionary is forced by the type.
-- In this case (dict$ :: Eq Int), so it with be inst$999
--
-- The actual definitions for the constructor and methods are added
-- in the desugaring pass.
-- Default methods are added as actual definitions.
-- The constructor and methods are added to the symbol table in addValueType.
expandClass :: ImpType -> EDef -> T [EDef]
expandClass impt dcls@(Class ctx (iCls, vks) fds ms) = do
  mn <- gets moduleName
  let
      meths = [ b | b@(Sign _ _) <- ms ]
      methIds = concatMap (\ (Sign is _) -> is) meths
      mdflts = [ (i, eqns) | Fcn i eqns <- ms ]
      dflttys = [ (i, t) | DfltSign i t <- ms ]
      tCtx = tApps (qualIdent mn iCls) (map (EVar . idKindIdent) vks)
      mkDflt (Sign is t) = concatMap method is
        where method methId = [ Sign [iDflt] $ EForall True vks $ tCtx `tImplies` ty, def $ lookup methId mdflts ]
                where ty = fromMaybe t $ lookup methId dflttys
                      def Nothing = Fcn iDflt $ simpleEqn noDflt
                      def (Just eqns) = Fcn iDflt eqns
                      iDflt = mkDefaultMethodId methId
                      noDflt = mkExn (getSLoc methId) (unIdent methId) "noMethodError"
              
      mkDflt _ = impossible
      dDflts = case impt of
                 ImpNormal -> concatMap mkDflt meths
                 ImpBoot   -> []
  -- Add to the class table.  XXX also in addValueClass???
  addClassTable (qualIdent mn iCls)
                (ClassInfo vks ctx (EUVar 0) methIds (mkIFunDeps (map idKindIdent vks) fds))   -- Initial entry, no type needed.
  return $ dcls : dDflts
expandClass _ d = return [d]

simpleEqn :: Expr -> [Eqn]
simpleEqn e = [Eqn [] $ simpleAlts e]

simpleAlts :: Expr -> EAlts
simpleAlts e = EAlts [([], e)] []

-- Keep the list empty if there are no fundeps
mkIFunDeps :: [Ident] -> [FunDep] -> [IFunDep]
--mkIFunDeps vs [] = [(map (const True) vs, map (const False) vs)]
mkIFunDeps vs fds = map (\ (is, os) -> (map (`elem` is) vs, map (`elem` os) vs)) fds

-- Turn (unqualified) class and method names into a default method name
mkDefaultMethodId :: Ident -> Ident
mkDefaultMethodId meth = addIdentSuffix meth defaultSuffix

isDefaultMethodId :: Ident -> Bool
isDefaultMethodId i = defaultSuffix `isSuffixOf` unIdent i

defaultSuffix :: String
defaultSuffix = uniqIdentSep ++ "dflt"

splitInst :: EConstraint -> ([IdKind], [EConstraint], EConstraint)
splitInst (EForall _ iks t) =
  case splitInst t of
    (iks', ctx, ct) -> (iks ++ iks', ctx, ct)
splitInst act | Just (ctx, ct) <- getImplies act =
  case splitInst ct of
    (iks, ctxs, ct') -> (iks, ctx : ctxs, ct')
splitInst ct = ([], [], ct)

expandInst :: EDef -> T [EDef]
expandInst dinst@(Instance act bs) = do
  (vks, ctx, cc) <- splitInst <$> expandSyn act
  let loc = getSLoc act
      qiCls = getAppCon cc
  let iInst = mkInstId loc cc
      sign = Sign [iInst] act
--  tcTrace ("expandInst " ++ show iInst)
--  (e, _) <- tLookupV iCls
  ct <- gets classTable
--  let qiCls = getAppCon e
  (ClassInfo _ supers _ mis fds) <-
    case M.lookup qiCls ct of
      Nothing -> tcError loc $ "not a class " ++ showIdent qiCls
      Just x -> return x
  -- XXX this ignores type signatures and other bindings
  -- XXX should tack on signatures with ESign
  let clsMdl = qualOf qiCls                   -- get class's module name
      ies = [(i, ELam noSLoc qs) | Fcn i qs <- bs]
      meth i = fromMaybe (ELam noSLoc $ simpleEqn $ EVar $ setSLocIdent loc $ mkDefaultMethodId $ qualIdent clsMdl i) $ lookup i ies
      meths = map meth mis
      sups = map (const (EVar $ mkIdentSLoc loc dictPrefixDollar)) supers
      args = sups ++ meths
      instBind (Fcn i _) = i `elem` mis
      instBind _ = False
  case filter (not . instBind) bs of
    [] -> return ()
    b:_ -> tcError (getSLoc b) $ "superflous instance binding"
  let bind = Fcn iInst $ eEqns [] $ eApps (EVar $ mkClassConstructor qiCls) args
  mn <- gets moduleName
  addInstTable [(EVar $ qualIdent mn iInst, vks, ctx, cc, fds)]
  return [dinst, sign, bind]
expandInst d = return [d]

---------------------

tcDefsValue :: [EDef] -> T [EDef]
tcDefsValue defs = do
--  tcTrace $ "tcDefsValue: ------------ start"
  -- Gather up all type signatures, and put them in the environment.
  mapM_ addValueType defs
  let smap = M.fromList $ [ (i, ()) | Sign is _ <- defs, i <- is ]
      -- Split Fcn into those without and with type signatures
      unsigned = filter noSign defs
        where noSign (Fcn i _) = hasNoSign i
              noSign (Pattern (i, _) _ _) = hasNoSign i
              noSign (PatBind p _) = any hasNoSign (patVars p)
              noSign _ = False
              hasNoSign i = isNothing $ M.lookup i smap
      -- split the unsigned defs into strongly connected components
      sccs = stronglyConnComp $ map node unsigned
        where node d@(Fcn i e)             = (d, i,                tr $ allVarsEqns e)
              node d@(Pattern (i, _) p me) = (d, i,                tr $ allVarsPat p $ maybe [] allVarsEqns me)
              node d@(PatBind p e)         = (d, head $ patVars p, tr $ allVarsExpr e)  -- use the first bound var as the key
              node _ = undefined
              tr x | null sub = x  -- do nothing when there are no PatBinds
                   | otherwise = map (\ i -> fromMaybe i $ lookup i sub) x
              -- Map all (bound) identifiers in a PatBind into the first (bound) identifier
              sub = [ (d, i) | PatBind p _ <- unsigned, i:ds <- [patVars p], d <- ds ]
      tcSCC (AcyclicSCC d@(Pattern _ _ _)) = tcPatSyn d
      tcSCC (AcyclicSCC d) = tInferDefs smap [d]
      tcSCC (CyclicSCC ds) = tInferDefs smap ds
  --traceM $ "tcDefsValue: unsigned=" ++ show unsigned
  -- type infer and enter each SCC in the symbol table
  -- return inferred Sign
  signDefs <- mapM tcSCC sccs
  defs' <- concat <$> mapM expandPatSyn defs
--  traceM $ "tcDefsValue: ------------ expandPatSyn"
--  traceM $ showEDefs defs'
--  tcTrace $ "tcDefsValue: ------------ check"
  --  type check all definitions (the inferred ones will be rechecked)
  defs'' <- mapM (\ d -> do { tcReset; tcDefValue d}) defs'
  let defs''' = concat signDefs ++ defs''
--  traceM $ "tcDefsValue: ------------ done"
--  traceM $ showEDefs defs'''
  pure defs'''

-- Infer a type for a definition
tInferDefs :: M.Map () -> [EDef] -> T [EDef]
tInferDefs smap fcns = do
--  traceM "tInferDefs"
  tcReset
  -- Invent type variables for the definitions
  xts <- 
           let f (Fcn i _)            = do t <- newUVar; pure [(i, t)]
               f (Pattern (i, _) _ _) = do t <- newUVar; pure [(i, t)]
               f (PatBind p _)        = concat <$> mapM g (patVars p)
               f _                    = impossible
               -- Only add type variables for those variables that don't have a signature
               g i = case M.lookup i smap of
                       Nothing -> do t <- newUVar; pure [(i, t)]
                       _       -> pure []
           in  concat <$> mapM f fcns
  --tcTrace $ "tInferDefs: " ++ show (map fst xts)
  -- Temporarily extend the local environment with the type variables
  withExtVals xts $ do
    -- Infer types for all the Fcns, ignore the new bodies.
    -- The bodies will be re-typecked in tcDefsValues.
    let tc (Fcn _ eqns) (_, t)   = do tcEqns False t eqns; return ()
        tc (Pattern (i,_) _ _) _ = tcError (getSLoc i) "Cannot infer recursive pattern synonym types"
        tc (PatBind p e)       _ = do tcPatBind PatBind p e; return ()
        tc _ _ = impossible
    zipWithM_ tc fcns xts
  -- Get the unsolved constraints
  ctx <- getUnsolved
  -- For each definition, quantify over the free meta variables, and include
  -- context mentioning them.
  let genTop :: (Ident, EType) -> T EDef
      genTop (i, t) = do
        t' <- derefUVar t
        let vs = metaTvs [t']
            ctx' = filter (\ c -> not (null (intersect vs (metaTvs [c])))) ctx
            t'' = addConstraints ctx' t'
            vs' = metaTvs [t'']
        t''' <- quantify vs' t''
        --tcTrace $ "tInferDefs: " ++ showIdent i ++ " :: " ++ showEType t'''
        extValQTop i t'''
        return $ Sign [i] t'''
  mapM genTop xts

getUnsolved :: T [EConstraint]
getUnsolved = do
  _ <- solveConstraints
  ctx <- gets (map snd . constraints)
  ctx' <- mapM derefUVar ctx
  putConstraints []
  return $ nubBy eqEType ctx'

addValueType :: EDef -> T ()
addValueType adef = do
  mn <- gets moduleName
  -- tcTrace ("addValueType: " ++ showEDefs [adef])
  let addConFields _     (Constr _ _ _ (Left _)) = return ()
      addConFields tycon (Constr _ _ _ (Right fs)) = mapM_ addField fs
        where addField (fld, _) = do
                (fe, fty) <- tLookup "???" $ mkGetName tycon fld
                extValETop fld fty fe
  case adef of
    Sign is@(i:_) t | isConIdent i -> do
      -- pattern synonym
      t' <- canonPatSynType t
      mapM_ (addPatSyn t') is
    Sign is t ->
      -- regular synonym
      mapM_ (\ i -> extValQTop i t) is
    Data (tycon, vks) cs _ -> do
      let
        cti = [ (qualIdent mn c, either length length ets + if null ctx then 0 else 1) | Constr _ ctx c ets <- cs ]
        tret = tApps (qualIdent mn tycon) (map tVarK vks)
        addCon (Constr evks ectx c ets) = do
          let ts = either id (map snd) ets
              cty = EForall True vks $ EForall True evks $ addConstraints ectx $ foldr (tArrow . snd) tret ts
              fs = either (const []) (map fst) ets
          extValETop c cty (ECon $ ConData cti (qualIdent mn c) fs)
      mapM_ addCon cs
      mapM_ (addConFields tycon) cs
    Newtype (tycon, vks) con@(Constr _ _ c ets) _ -> do
      let
        t = snd $ head $ either id (map snd) ets
        tret = tApps (qualIdent mn tycon) (map tVarK vks)
        fs = either (const []) (map fst) ets
      extValETop c (EForall True vks $ EForall True [] $ tArrow t tret) (ECon $ ConNew (qualIdent mn c) fs)
      addConFields tycon con
    ForImp _ i t -> extValQTop i t
    Class ctx (i, vks) fds ms -> addValueClass ctx i vks fds ms
    _ -> return ()

-- Add a pattern synonym to the symbol table.
addPatSyn :: EType -> Ident -> T ()
addPatSyn at i = do
  mn <- gets moduleName
  let (_, _, _, _, t) = splitPatSynType at
      n = length $ fst $ getArrows t
      qi = qualIdent mn i
      qip = mkPatSynMatch qi
      mtch = (EVar qip, mkPatSynMatchType qip at)
  extValETop i at $ ECon $ ConSyn qi n mtch

-- XXX FunDep
addValueClass :: [EConstraint] -> Ident -> [IdKind] -> [FunDep] -> [EBind] -> T ()
addValueClass ctx iCls vks fds ms = do
  mn <- gets moduleName
  let
      meths = [ b | b@(Sign _ _) <- ms ]
      methTys = map (\ (Sign _ t) -> t) meths
      methIds = concatMap (\ (Sign is _) -> is) meths
      supTys = ctx  -- XXX should do some checking
      targs = supTys ++ methTys
      qiCls = qualIdent mn iCls
      tret = tApps qiCls (map tVarK vks)
      cti = [ (qualIdent mn iCon, length targs) ]
      iCon = mkClassConstructor iCls
      iConTy = EForall True vks $ foldr tArrow tret targs
  extValETop iCon iConTy (ECon $ ConData cti (qualIdent mn iCon) [])
  let addMethod (Sign is t) = mapM_ method is
        where method i = extValETop i (EForall True vks $ tApps qiCls (map (EVar . idKindIdent) vks) `tImplies` t) (EVar $ qualIdent mn i)
      addMethod _ = impossible
--  tcTrace ("addValueClass " ++ showEType (ETuple ctx))
  mapM_ addMethod meths
  -- Update class table, now with actual constructor type.
  addClassTable qiCls (ClassInfo vks ctx iConTy methIds (mkIFunDeps (map idKindIdent vks) fds))

mkClassConstructor :: Ident -> Ident
mkClassConstructor i = addIdentSuffix i "$C"

tcDefValue :: HasCallStack =>
              EDef -> T EDef
tcDefValue adef =
  assertTCMode (==TCExpr) $
  case adef of
    Fcn i eqns -> do
      (_, t) <- tLookup "type signature" i
      t' <- expandSyn t
--      when (isConIdent i) $ do
--        tcTrace $ "tcDefValue: patsyn\n" ++ show i ++ " :: " ++ show t'
--        tcTrace $ "tcDefValue:\n" ++ showEDefs [adef]
--      tcTrace $ "tcDefValue: ------- start " ++ showIdent i
--      tcTrace $ "tcDefValue: " ++ showIdent i ++ " :: " ++ showExpr t'
--      tcTrace $ "tcDefValue: " ++ showEDefs [adef]
      teqns <- tcEqns True t' eqns
--      tcTrace ("tcDefValue: after\n" ++ showEDefs [adef, Fcn i teqns])
--      cs <- gets constraints
--      tcTrace $ "tcDefValue: constraints: " ++ show cs
      checkConstraints
      mn <- gets moduleName
--      tcTrace $ "tcDefValue: " ++ showIdent i ++ " done"
      return $ Fcn (qualIdent mn i) teqns
    PatBind p e -> tcPatBind PatBind p e
    ForImp ie i t -> do
      mn <- gets moduleName
      t' <- expandSyn t
      return (ForImp ie (qualIdent mn i) t')
    Pattern _ _ _ -> impossible
    _ -> return adef

-- This is only used during inference.
-- When doing type checking the actual Pattern definition will have been
-- removed by expandPatSyn.
-- The important thing here is the call to addPatSyn
tcPatSyn :: EDef -> T [EDef]
tcPatSyn (Pattern (ip, vks) p me) = do
--  traceM $ "tcPatSyn: enter " ++ show (ip, vks, p, me)
  let step [] t = tcPat (Check t) p
      step (ik:iks) t = do
        (ti, tr) <- unArrow (getSLoc ik) t
        withExtVal (idKindIdent ik) ti $ step iks tr
  pty <- newUVar   -- invent a type
  (sks, dicts, _p) <- step vks pty
  let ctx2 = map snd dicts
--  traceM $ "tcPatSyn: pat " ++ show (sks, ctx2)
  case me of Nothing -> pure (); Just e -> void $ tcEqns False pty e
  ctx1 <- getUnsolved
--  traceM $ "tcPatSyn: ctx " ++ show ctx1
  ty0 <- addConstraints ctx2 <$> derefUVar pty
  let ctx1' = deleteFirstsBy eqEType ctx1 ctx2   -- remove provided from required
      (sks', sub) = tyVarSubst sks ty0           -- turn skolems
      ty1 = subst sub ty0                        --    into rigid tyvars
  ty2 <- quantify (metaTvs [ty1]) (addConstraints ctx1' ty1)
  let (vs, ty3) = unForall ty2
      ty4 = eForall' False (sks' ++ vs) ty3      -- add the skolems tyvars
  ty5 <- canonPatSynType ty4
--  traceM $ "tcPatSyn: tys " ++ show (ty0, ty1, ty2, ty3, ty4, ty5)
  addPatSyn ty5 ip
--  traceM ("tcPatSyn: after " ++ show (ip, ty5))
  return [ Sign [ip] ty3 ]
tcPatSyn _ = impossible

-- Add implicit forall and type check.
tCheckTypeTImpl :: HasCallStack => EType -> EType -> T EType
tCheckTypeTImpl tchk t@(EForall _ _ _) = tCheckTypeT tchk t
tCheckTypeTImpl tchk t = do
  bvs <- stKeysLcl <$> gets valueTable         -- bound outside
  let fvs = freeTyVars [t]                     -- free variables in t
      -- these are free, and need quantification.  eDummy indicates missing kind
      iks = map (\ i -> IdKind i eDummy) (fvs \\ bvs)
  --when (not (null iks)) $ tcTrace ("tCheckTypeTImpl: " ++ show (t, eForall iks t))
  tCheckTypeT tchk (eForall' False iks t)

tCheckTypeT :: HasCallStack => EType -> EType -> T EType
tCheckTypeT = tCheck tcTypeT

tInferTypeT :: HasCallStack => EType -> T (EType, EKind)
tInferTypeT t = tInfer tcTypeT t

-- Kind check a type while already in type checking mode
tcTypeT :: HasCallStack =>
           Expected -> EType -> T EType
tcTypeT mk t = assertTCMode (==TCType) $ tcExpr mk (dsType t)

-- Kind check a type while in value checking mode
tcType :: HasCallStack =>
          Expected -> EType -> T EType
tcType mk = assertTCMode (==TCExpr) . withTypeTable . tcTypeT mk

-- Sort check a kind while already in sort checking mode
tcKindT :: HasCallStack =>
           Expected -> EKind -> T EKind
tcKindT mk t =
--  trace ("tcKindT: " ++ show (mk, t)) $
  assertTCMode (==TCKind) $ tcExpr mk t

-- Sort check a kind while in type checking mode
tcKind :: HasCallStack =>
          Expected -> EKind -> T EKind
tcKind mk = assertTCMode (==TCType) . withTypeTable . tcKindT mk

-- When inferring the type, the resulting type will
-- be assigned to the TRef (using tSetRefType),
-- and can then be read of (using tGetRefType).
-- When checking, the expected type is simply given.
data Expected = Infer TRef | Check EType
--  deriving(Show)

instance Show Expected where
  show (Infer r) = "(Infer " ++ show r ++ ")"
  show (Check t) = "(Check " ++ show t ++ ")"

tInfer :: forall a b . HasCallStack =>
          (Expected -> a -> T b) -> a -> T (Typed b)
tInfer tc a = do
  ref <- newUniq
  a' <- tc (Infer ref) a
  t <- tGetRefType ref
  return (a', t)

tCheck :: forall a b . (Expected -> a -> T b) -> EType -> a -> T b
tCheck tc t = tc (Check t)

tInferExpr :: HasCallStack =>
              Expr -> T (Typed Expr)
tInferExpr = tInfer tcExpr

tCheckExpr :: HasCallStack =>
              EType -> Expr -> T Expr
tCheckExpr t e | Just (ctx, t') <- getImplies t = do
--  tcTrace $ "tCheckExpr: " ++ show (e, ctx, t')
  d <- newADictIdent (getSLoc e)
  e' <- withDict d ctx $ tCheckExprAndSolve t' e
  return $ eLam [EVar d] e'

tCheckExpr t e = tCheck tcExpr t e

tGetRefType :: HasCallStack =>
               TRef -> T EType
tGetRefType ref = do
  m <- gets uvarSubst
  case IM.lookup ref m of
    Nothing -> return (EUVar ref)
    Just t  -> return t

-- Set the type for an Infer
tSetRefType :: HasCallStack =>
               SLoc -> TRef -> EType -> T ()
tSetRefType loc ref t = do
  m <- gets uvarSubst
  case IM.lookup ref m of
    Nothing -> putUvarSubst (IM.insert ref t m)
    Just tt -> unify loc tt t

-- Get the type of an already set Expected
tGetExpType :: Expected -> T EType
tGetExpType (Check t) = return t
tGetExpType (Infer r) = tGetRefType r

tcExpr :: HasCallStack =>
          Expected -> Expr -> T Expr
tcExpr mt ae = do
--  tcTrace ("tcExpr enter: mt=" ++ show mt ++ " ae=" ++ showExpr ae)
  r <- tcExprR mt ae
--  tcTrace ("tcExpr exit: " ++ showExpr r)
  return r
tcExprR :: HasCallStack =>
           Expected -> Expr -> T Expr
tcExprR mt ae =
  let { loc = getSLoc ae } in
--  trace ("tcExprR " ++ show ae) $
  case ae of
    EVar i | isIdent dictPrefixDollar i -> do
             -- Magic variable that just becomes the dictionary
             d <- newIdent (getSLoc i) dictPrefixDollar
             case mt of
               Infer _ -> impossible
               Check t -> addConstraint d t
             return (EVar d)

           | isDummyIdent i -> tcError loc "_ cannot be used as a variable"
           | otherwise -> do
             -- Type checking an expression (or type)
             (e, t) <- tLookupV i
             -- Variables bound in patterns start out with an (EUVar ref) type,
             -- which can be instantiated to a polytype.
             -- Dereference such a ref.
             t' <-
               case t of
                 EUVar r -> fmap (fromMaybe t) (getUVar r)
                 _ -> return t
--             tcTrace $ "EVar: " ++ showIdent i ++ " :: " ++ showExpr t ++ " = " ++ showExpr t' ++ " mt=" ++ show mt
             instSigma loc e t' mt
    EQVar e t ->  -- already resolved, just instantiate
             instSigma loc e t mt

    EApp _ _ -> tcExprAp mt ae []

    EOper e ies -> tcOper e ies >>= tcExpr mt
    ELam _ qs -> tcExprLam mt loc qs
    ELit _ lit -> do
      tcm <- gets tcMode
      case tcm of
        TCType ->
          case lit of
            LStr _ -> instSigma loc (ELit loc lit) (tConI loc nameSymbol) mt
            LInteger _ -> instSigma loc (ELit loc lit) (tConI loc nameNat) mt
            _      -> impossible
        TCExpr -> do
          let getExpected (Infer _) = pure Nothing
              getExpected (Check t) = Just <$> (derefUVar t >>= expandSyn)
          case lit of
            LInteger i -> do
              mex <- getExpected mt
              case mex of
                -- Convert to Int in the compiler, that way (99::Int) will never involve fromInteger
                -- (which is not always in scope).
                Just (EVar v) | v == identInt     -> tcLit  mt loc (LInt (fromInteger i))
                              | v == identWord    -> tcLit' mt loc (LInt (fromInteger i)) (tConI loc nameWord)
                              | v == identFloatW  -> tcLit  mt loc (LDouble (fromInteger i))
                              | v == identInteger -> tcLit  mt loc lit
                _ -> do
                  (f, ft) <- tInferExpr (EVar (mkBuiltin loc "fromInteger"))
                  (_at, rt) <- unArrow loc ft
                  -- We don't need to check that _at is Integer, it's part of the fromInteger type.
                  instSigma loc (EApp f ae) rt mt
            LRat r -> do
              mex <- getExpected mt
              case mex of
                Just (EVar v) | v == mkIdent nameFloatW -> tcLit mt loc (LDouble (fromRational r))
                _ -> do
                  (f, ft) <- tInferExpr (EVar (mkBuiltin loc "fromRational"))
                  (_at, rt) <- unArrow loc ft
                  -- We don't need to check that _at is Rational, it's part of the fromRational type.
                  instSigma loc (EApp f ae) rt mt
            -- This implements OverloadedStrings.  It causes a small slowdown (2%)
            LStr _ -> do
              mex <- getExpected mt
              case mex of
                Just (EApp (EVar lst) (EVar c))
                 | lst == identList, c == identChar -> tcLit mt loc lit
                _ -> do
                  (f, ft) <- tInferExpr (EVar (mkBuiltin loc "fromString"))
                  (_at, rt) <- unArrow loc ft
                  -- We don't need to check that _at is String, it's part of the fromString type.
                  --tcTrace ("LStr " ++ show (loc, r))
                  instSigma loc (EApp f ae) rt mt
            -- Not LInteger, LRat, LStr
            _ -> tcLit mt loc lit
        _ -> impossible
    ECase a arms -> do
      -- XXX should look more like EIf
      (ea, ta) <- tInferExpr a
      tt <- tGetExpType mt
      earms <- mapM (tcArm tt ta) arms
      return (ECase ea earms)
    ELet bs a -> tcBinds bs $ \ ebs -> do { ea <- tcExpr mt a; return (ELet ebs ea) }
    ETuple es -> do
      -- XXX checking if mt is a tuple would give better inference
      let
        n = length es
      (ees, tes) <- fmap unzip (mapM tInferExpr es)
      let
        ttup = tApps (tupleConstr loc n) tes
      munify loc mt ttup
      return (ETuple ees)
    EParen e -> tcExpr mt e
    EDo mmn ass -> do
      case ass of
        [] -> impossible
        [as] ->
          case as of
            SThen a -> tcExpr mt a
            _ -> tcError loc $ "bad final do statement"
        as : ss -> do
          case as of
            SBind p a -> do
              nofail <- failureFree p
              let
                ibind = mkBuiltin loc ">>="
                sbind = maybe ibind (\ mn -> qualIdent mn ibind) mmn
                x = eVarI loc "$b"
                patAlt = [(p, simpleAlts $ EDo mmn ss)]
                failMsg s = EApp (EVar (mkBuiltin loc "fail")) (ELit loc (LStr s))
                failAlt =
                  if nofail then []
                  else [(eDummy, simpleAlts $ failMsg "bind")]
              tcExpr mt (EApp (EApp (EVar sbind) a)
                              (eLam [x] (ECase x (patAlt ++ failAlt))))
            SThen a -> do
              let
                ithen = mkBuiltin loc ">>"
                sthen = maybe ithen (\ mn -> qualIdent mn ithen) mmn
              tcExpr mt (EApp (EApp (EVar sthen) a) (EDo mmn ss))
                
            SLet bs ->
              tcExpr mt (ELet bs (EDo mmn ss))

    ESectL e i -> tcLSect e i >>= tcExpr mt
    ESectR i e -> tcRSect i e >>= tcExpr mt
    EIf e1 e2 e3 -> do
      e1' <- tCheckExpr (tBool (getSLoc e1)) e1
      case mt of
        Check t -> do
          e2' <- checkSigma e2 t
          e3' <- checkSigma e3 t
          return (EIf e1' e2' e3')
        Infer ref -> do
          (e2', t2) <- tInferExpr e2
          (e3', t3) <- tInferExpr e3
          e2'' <- subsCheck loc e2' t2 t3
          e3'' <- subsCheck loc e3' t3 t2
          tSetRefType loc ref t2
          return (EIf e1' e2'' e3'')

    -- Translate (if | a1; | a2 ...) into
    --           (case [] of _ | a1; | a2 ...)
    EMultiIf a ->
      tcExpr mt $ ECase (EListish (LList [])) [(EVar (mkIdent "_"), a)]

    EListish (LList es) -> do
      te <- newUVar
      munify loc mt (tApp (tList loc) te)
      es' <- mapM (tCheckExpr te) es
      return (EListish (LList es'))
    EListish (LCompr eret ass) -> do
      let
        doStmts :: [EStmt] -> [EStmt] -> T ([EStmt], Typed Expr)
        doStmts rss xs =
          case xs of
            [] -> do
              r <- tInferExpr eret
              return (reverse rss, r)
            as : ss ->
              case as of
                SBind p a -> do
                  v <- newUVar
                  ea <- tCheckExprAndSolve (tApp (tList loc) v) a
                  tCheckPatC v p $ \ ep -> doStmts (SBind ep ea : rss) ss
                SThen a -> do
                  ea <- tCheckExprAndSolve (tBool (getSLoc a)) a
                  doStmts (SThen ea : rss) ss
                SLet bs ->
                  tcBinds bs $ \ ebs ->
                    doStmts (SLet ebs : rss) ss
      (rss, (ea, ta)) <- doStmts [] ass
      let
        tr = tApp (tList loc) ta
      munify loc mt tr
      return (EListish (LCompr ea rss))
    EListish (LFrom       e)        -> tcExpr mt (enum loc "From" [e])
    EListish (LFromTo     e1 e2)    -> tcExpr mt (enum loc "FromTo" [e1, e2])
    EListish (LFromThen   e1 e2)    -> tcExpr mt (enum loc "FromThen" [e1,e2])
    EListish (LFromThenTo e1 e2 e3) -> tcExpr mt (enum loc "FromThenTo" [e1,e2,e3])
    ESign e t -> do
      t' <- tcType (Check kType) t
      e' <- instSigma loc e t' mt
      checkSigma e' t'
    -- Only happens in type&kind checking mode.
    EForall b vks t ->
--      assertTCMode (==TCType) $
      withVks vks $ \ vks' -> do
        tt <- tcExpr mt t
        return (EForall b vks' tt)
    EUpdate e flds -> do
      ises <- concat <$> mapM (dsEField e) flds
      me <- dsUpdate unsetField e ises
      case me of
        Just e' -> tcExpr mt e'
        Nothing -> tcExpr mt $ foldr eSetFields e ises
    ESelect is -> do
        let x = eVarI loc "$x"
        tcExpr mt $ eLam [x] $ foldl (\ e i -> EApp (eGetField i) e) x is
    ETypeArg _ ->
        tcError loc $ "Bad type application"
    _ -> error $ "tcExpr: cannot handle: " ++ show (getSLoc ae) ++ " " ++ show ae
      -- impossible

tcExprAp :: HasCallStack =>
            Expected -> Expr -> [Expr] -> T Expr
tcExprAp mt ae args =
  case ae of
    EApp f a -> tcExprAp mt f (a : args)
    EParen f -> tcExprAp mt f args
    EOper e ies -> tcOper e ies >>= \ eop -> tcExprAp mt eop args
    EVar i | isIdent dictPrefixDollar i -> impossibleShow ae
           | isDummyIdent i -> impossibleShow ae
           | otherwise -> do
             -- Type checking an expression (or type)
             (fn, t) <- tLookupV i
             -- Variables bound in patterns start out with an (EUVar ref) type,
             -- which can be instantiated to a polytype.
             -- Dereference such a ref.
             t' <-
               case t of
                 EUVar r -> fmap (fromMaybe t) (getUVar r)
                 _ -> return t
--             tcTrace $ "exExprAp: EVar " ++ showIdent i ++ " :: " ++ showExpr t ++ " = " ++ showExpr t' ++ " mt=" ++ show mt
             case fn of
               EVar ii | ii == mkIdent "Data.Function.$", f:as <- args -> tcExprAp mt f as
               _ -> tcExprApFn mt fn t' args
    EQVar f t ->  -- already resolved
      tcExprApFn mt f t args
    _ -> do
      (f, t) <- tInferExpr ae
      tcExprApFn mt f t args
    
tcExprApFn :: Expected -> Expr -> EType -> [Expr] -> T Expr
--tcExprApFn _ fn fnt args | trace (show (fn, fnt, args)) False = undefined
tcExprApFn mt fn (EForall {-True-}_ (IdKind i _:iks) ft) (ETypeArg t : args) = do
  t' <- if t `eqEType` EVar dummyIdent then newUVar else tcType (Check kType) t
  tcExprApFn mt fn (eForall iks (subst [(i, t')] ft)) args
tcExprApFn mt fn tfn args = do
--  traceM $ "tcExprApFn: " ++ show (mt, fn, tfn, args)
--  xx <- gets ctxTables
--  traceM $ "tcExprApFn: ctxTables=" ++ show xx
  let loc = getSLoc fn
  (fn', tfn') <- tInst fn tfn
  let loop ats     [] ft = final ats ft
      loop ats as@(_:_) (EForall _ vks ft) = do
        ft' <- tInstForall vks ft
        loop ats as ft'
      loop ats (a:as) ft = do
        (at, rt) <- unArrow loc ft
        loop ((a, at):ats) as rt
      final aats rt = do

        -- We want to do the unification of rt ant mt before checking the argument to
        -- have more type information.  See tests/Eq1.hs.
        -- But instSigma may transform the input expression, so we have to be careful.
        let etmp = EUVar ugly
            ugly = -1::Int
        etmp' <- instSigma loc etmp rt mt

        let apply f [] = return f
            apply f ((a,at):ats) = do
              a' <- checkSigma a at
              apply (EApp f a') ats
        res <- apply fn' (reverse aats)
--        cc <- gets constraints
--        traceM $ "tcExprApFn: constraints=" ++ show cc

        case etmp' of
          EUVar _ -> return res   -- instSigma did nothing, this is the common case
          _ -> return $ substEUVar [(ugly, res)] etmp'

        instSigma loc res rt mt
      
  loop [] args tfn'

-- Is a pattern failure free?
failureFree :: EPat -> T Bool
failureFree p@(EVar _) = failureFreeAp [] p
failureFree p@(EApp _ _) = failureFreeAp [] p
failureFree (ETuple ps) = and <$> mapM failureFree ps
failureFree (ESign p _) = failureFree p
failureFree (EAt _ p) = failureFree p
failureFree (ELazy True _) = return True
failureFree (ELazy False p) = failureFree p
failureFree (EViewPat _ p) = failureFree p
failureFree (EParen p) = failureFree p
failureFree _ = return False

failureFreeAp :: [Bool] -> EPat -> T Bool
failureFreeAp bs (EApp f a) = do
  b <- failureFree a
  failureFreeAp (b:bs) f
failureFreeAp bs (EVar v) | not (isConIdent v) = return True
                          | otherwise = do
                              (con, _) <- tLookupV v
                              return $ case con of
                                ECon (ConNew _ _) -> and bs
                                ECon (ConData [_] _ _) -> and bs  -- single constructor
                                _ -> False
failureFreeAp bs (ESign p _) = failureFreeAp bs p
failureFreeAp _ _ = return False  -- bad pattern, just ignore
                           
eSetFields :: EField -> Expr -> Expr
eSetFields (EField is e) r =
  let loc = getSLoc is
      eCompose = EVar $ mkBuiltin loc "composeSet"
      has = map eHasField $ init is
      set1 = eSetField (last is)
      set = foldr (EApp . EApp eCompose) set1 has
  in  EApp (EApp set r) e
eSetFields _ _ = impossible

eHasField :: Ident -> Expr
eHasField i = EApp (EVar ihas) (eProxy i)
  where ihas = mkBuiltin (getSLoc i) "hasField"

eSetField :: Ident -> Expr
eSetField i = EApp (EVar iset) (eProxy i)
  where iset = mkBuiltin (getSLoc i) "setField"

eGetField :: Ident -> Expr
eGetField i = EApp (EVar iget) (eProxy i)
  where iget = mkBuiltin (getSLoc i) "getField"

eProxy :: Ident -> Expr
eProxy i = ESign proxy (EApp proxy (ELit loc (LStr (unIdent i))))
  where proxy = EVar $ mkBuiltin loc "Proxy"
        loc = getSLoc i

dsEField :: Expr -> EField -> T [EField]
dsEField _ e@(EField _ _) = return [e]
dsEField _ (EFieldPun is) = return [EField is $ EVar (last is)]
dsEField e EFieldWild = do
  (e', _) <- tInferExpr e
  case e' of
    ECon c -> return [ EField [f] (EVar f) | f <- conFields c ]
    _ -> tcError (getSLoc e) "record wildcard not allowed"

-- Patterns need to expand EFieldWild before type checking
dsEFields :: EPat -> T EPat
dsEFields apat =
  case apat of
    EVar _ -> return apat
    EApp p1 p2 -> EApp <$> dsEFields p1 <*> dsEFields p2
    EOper p1 ips -> EOper <$> dsEFields p1 <*> mapM (\ (i, p2) -> (,) i <$> dsEFields p2) ips
    ELit _ _ -> return apat
    ETuple ps -> ETuple <$> mapM dsEFields ps
    EListish (LList ps) -> EListish . LList <$> mapM dsEFields ps
    ESign p t -> ESign <$> dsEFields p <*> pure t
    EAt i p -> EAt i <$> dsEFields p
    EViewPat e p -> EViewPat e <$> dsEFields p
    ELazy z p -> ELazy z <$> dsEFields p
    ECon _ -> return apat
    EUpdate c fs -> EUpdate c . concat <$> mapM (dsEField c) fs
    EParen p -> dsEFields p
    ENegApp _ -> return apat
    EOr ps -> EOr <$> mapM dsEFields ps
    _ -> error $ "dsEFields " ++ show apat

unsetField :: Ident -> Expr
unsetField i = mkExn (getSLoc i) (unIdent i) "recConError"

dsUpdate :: (Ident -> Expr) -> Expr -> [EField] -> T (Maybe Expr)
dsUpdate unset e flds = do
  (e', _) <- tInferExpr e
  case e' of
    ECon c -> do
      let ises = map unEField flds
          fs = conFields c
          ies = map (first head) ises
          is = map fst ies
          as = map field fs
          field i = fromMaybe (unset i) $ lookup i ies
      case filter ((> 1) . length . fst) ises of
        (i:_, _):_ -> tcError (getSLoc i) "Nested fields not allowed"
        _ -> return ()
      case is \\ fs of
        vs@(v:_) -> tcError (getSLoc v) $ "extra field(s) " ++ unwords (map unIdent vs)
        _ -> return ()
      return $ Just $ eApps e as
    _ -> return Nothing

enum :: SLoc -> String -> [Expr] -> Expr
enum loc f = eApps (EVar (mkBuiltin loc ("enum" ++ f)))

tcLit :: HasCallStack => Expected -> SLoc -> Lit -> T Expr
tcLit mt loc l@(LPrim _) = newUVar >>= tcLit' mt loc l
tcLit mt loc l@(LExn  _) = newUVar >>= tcLit' mt loc l
tcLit mt loc l = do
  let t =
        case l of
          LInt _     -> tConI loc nameInt
          LInteger _ -> tConI loc nameInteger
          LDouble _  -> tConI loc nameFloatW
          LChar _    -> tConI loc nameChar
          LStr _     -> tApp (tList loc) (tConI loc nameChar)
          _          -> impossible
  tcLit' mt loc l t

tcLit' :: Expected -> SLoc -> Lit -> EType -> T Expr
tcLit' mt loc l t = instSigma loc (ELit loc l) t mt

-- tcOper is in T because it has to look up identifiers, and get the fixity table.
-- But there is no type checking happening here.
tcOper :: HasCallStack =>
          Expr -> [(Ident, Expr)] -> T Expr
tcOper ae aies = do
  fixs <- gets fixTable
  let
    opfix :: (Ident, Expr) -> T ((Expr, Fixity), Expr)
    opfix (i, e) = do
      (ei, _) <- tLookupV i
      let fx = getFixity fixs (getAppCon ei)
      return ((EVar i, fx), e)

  ites <- mapM opfix aies
  case resolveFixity ae ites of
    Left (loc, err) -> tcError loc err
    Right e -> return e

tcLSect :: Expr -> Ident -> T Expr
tcLSect (EOper e ies) op = do
  let x = eVarI loc "$x"
      loc = getSLoc op
  e' <- tcOper e (ies ++ [(op, x)])
  case e' of
    EApp f x' | x' `eqExpr` x -> return f
    _                   -> tcError loc "Bad section fixity"
tcLSect e op =
  return (EApp (EVar op) e)

tcRSect :: Ident -> Expr -> T Expr
tcRSect op (EOper e ies) = do
  let x = eVarI loc "$x"
      loc = getSLoc op
  e' <- tcOper x ((op, e):ies)
  case e' of
    EApp (EApp _ x') _ | x `eqExpr` x' -> return (eLam [x] e')
    _                            -> tcError loc "Bad section fixity"
tcRSect op e = do
  let x = eVarI (getSLoc op) "$x"
  return (eLam [x] (EApp (EApp (EVar op) x) e))

unArrow :: HasCallStack =>
           SLoc -> EType -> T (EType, EType)
--unArrow loc t@(EForall _ _ _) | trace ("unArrow: " ++ show t) False = undefined
unArrow loc t = do
  case getArrow t of
    Just ar -> return ar
    Nothing -> do
      a <- newUVar
      r <- newUVar
      unify loc t (tArrow a r)
      return (a, r)

getFixity :: FixTable -> Ident -> Fixity
getFixity fixs i = fromMaybe (AssocLeft, 9) $ M.lookup i fixs

-- Dictionary argument names
adictPrefix :: String
adictPrefix = "adict"

newADictIdent :: SLoc -> T Ident
newADictIdent loc = newIdent loc adictPrefix

-- Needed dictionaries
dictPrefix :: String
dictPrefix = "dict"

dictPrefixDollar :: String
dictPrefixDollar = dictPrefix ++ uniqIdentSep

newDictIdent :: SLoc -> T Ident
newDictIdent loc = newIdent loc dictPrefix

tcExprLam :: Expected -> SLoc -> [Eqn] -> T Expr
tcExprLam mt loc qs = do
  t <- tGetExpType mt
  ELam loc <$> tcEqns False t qs

tcEqns :: Bool -> EType -> [Eqn] -> T [Eqn]
--tcEqns _ t eqns | trace ("tcEqns: " ++ showEBind (Fcn dummyIdent eqns) ++ " :: " ++ show t) False = undefined
tcEqns top (EForall expl iks t) eqns | expl      = withExtTyps iks $ tcEqns top t eqns
                                     | otherwise =                   tcEqns top t eqns
tcEqns top t eqns | Just (ctx, t') <- getImplies t = do
  let loc = getSLoc eqns
  d <- newADictIdent loc
  f <- newIdent loc "fcnD"
  withDict d ctx $ do
    eqns' <- tcEqns top t' eqns
    let eqn =
          case eqns' of
            [Eqn [] alts] -> Eqn [EVar d] alts
            _             -> Eqn [EVar d] $ EAlts [([], EVar f)] [Fcn f eqns']
    return [eqn]
tcEqns top t eqns = do
  let loc = getSLoc eqns
  f <- newIdent loc "fcnS"
  (eqns', ds) <- solveAndDefault top $ mapM (tcEqn t) eqns
--  tcTrace $ "tcEqns done: " ++ showEBind (Fcn dummyIdent eqns')
  case ds of
    [] -> return eqns'
    _  -> do
      let
        bs = eBinds ds
        eqn = Eqn [] $ EAlts [([], EVar f)] (bs ++ [Fcn f eqns'])
      return [eqn]

tcEqn :: EType -> Eqn -> T Eqn
--tcEqn t eqn | trace ("tcEqn: " ++ show eqn ++ " :: " ++ show t) False = undefined
tcEqn t eqn =
  case eqn of
    Eqn ps alts -> tcPats t ps $ \ t' ps' -> do
--      tcTrace $ "tcEqn " ++ show ps ++ " ---> " ++ show ps'
      alts' <- tcAlts t' alts
      return (Eqn ps' alts')

-- Only used above
tcPats :: EType -> [EPat] -> (EType -> [EPat] -> T Eqn) -> T Eqn
tcPats t [] ta = ta t []
tcPats t (p:ps) ta = do
  (tp, tr) <- unArrow (getSLoc p) t
  -- tCheckPatC dicts used in tcAlt solve
  tCheckPatC tp p $ \ p' -> tcPats tr ps $ \ t' ps' -> ta t' (p' : ps')

tcAlts :: EType -> EAlts -> T EAlts
tcAlts t (EAlts alts bs) =
--  trace ("tcAlts: bs in " ++ showEBinds bs) $
  tcBinds bs $ \ bs' -> do
--    tcTrace ("tcAlts: bs out " ++ showEBinds bbs)
    alts' <- mapM (tcAlt t) alts
    return (EAlts alts' bs')

tcAlt :: EType -> EAlt -> T EAlt
--tcAlt t (_, rhs) | trace ("tcAlt: " ++ showExpr rhs ++ " :: " ++ showEType t) False = undefined
tcAlt t (ss, rhs) = tcGuards ss $ \ ss' -> do
  rhs' <- tCheckExprAndSolve t rhs
  return (ss', rhs')

tcGuards :: [EStmt] -> ([EStmt] -> T EAlt) -> T EAlt
tcGuards [] ta = ta []
tcGuards (s:ss) ta = tcGuard s $ \ rs -> tcGuards ss $ \ rss -> ta (rs:rss)

tcGuard :: EStmt -> (EStmt -> T EAlt) -> T EAlt
tcGuard (SBind p e) ta = do
  (e', tt) <- tInferExpr e
  -- tCheckPatC dicts used in solving in tcAlt
  tCheckPatC tt p $ \ p' -> ta (SBind p' e')
tcGuard (SThen e) ta = do
  e' <- tCheckExprAndSolve (tBool (getSLoc e)) e
  ta (SThen e')
-- XXX do we have solves
tcGuard (SLet bs) ta = tcBinds bs $ \ bs' -> ta (SLet bs')

tcArm :: EType -> EType -> ECaseArm -> T ECaseArm
tcArm t tpat arm =
  case arm of
    -- The dicts introduced by tCheckPatC are
    -- used in the tCheckExprAndSolve in tcAlt.
    (p, alts) -> tCheckPatC tpat p $ \ pp -> do
      alts' <- tcAlts t alts
      return (pp, alts')

tCheckExprAndSolve :: EType -> Expr -> T Expr
tCheckExprAndSolve t e = do
  (e', bs) <- solveLocalConstraints $ tCheckExpr t e
  if null bs then
    return e'
   else
    return $ ELet (eBinds bs) e'

eBinds :: [(Ident, Expr)] -> [EBind]
eBinds ds = [Fcn i $ simpleEqn e | (i, e) <- ds]

instPatSigma :: HasCallStack =>
                 SLoc -> Sigma -> Expected -> T ()
instPatSigma loc pt (Infer r) = tSetRefType loc r pt
instPatSigma loc pt (Check t) = do { _ <- subsCheck loc undefined t pt; return () } -- XXX really?

subsCheck :: HasCallStack =>
              SLoc -> Expr -> Sigma -> Sigma -> T Expr
-- (subsCheck args off exp) checks that
-- 'off' is at least as polymorphic as 'args -> exp'
subsCheck loc exp1 sigma1 sigma2 = do -- Rule DEEP-SKOL
  (skol_tvs, rho2) <- skolemise sigma2
  exp1' <- subsCheckRho loc exp1 sigma1 rho2
  esc_tvs <- getFreeTyVars [sigma1,sigma2]
  let bad_tvs = filter (\ i -> elem i esc_tvs) skol_tvs
  when (not (null bad_tvs)) $
    tcErrorTK loc "Subsumption check failed"
  return exp1'

tCheckPatC :: forall a . EType -> EPat -> (EPat -> T a) -> T a
tCheckPatC t p@(EVar v) ta | not (isConIdent v) = do  -- simple special case
  withExtVals [(v, t)] $ ta p
tCheckPatC t app ta = do
--  tcTrace $ "tCheckPatC: " ++ show app ++ " :: " ++ show t
  app' <- dsEFields app
  let vs = patVars app'
  multCheck vs
  env <- mapM (\ v -> (,) v <$> newUVar) vs
  withExtVals env $ do
    (_sks, ds, pp) <- tCheckPat t app'
--    tcTrace ("tCheckPatC: " ++ show pp)
--    xt <- derefUVar t
--    tcTrace ("tCheckPatC ds=" ++ show ds ++ "t=" ++ show xt)
    -- XXX must check for leaking skolems
    withDicts ds $
      ta pp

type EPatRet = ([TyVar], [(Ident, EConstraint)], EPat)  -- skolems, dictionaries, pattern

tCheckPat :: EType -> EPat -> T EPatRet
tCheckPat = tCheck tcPat
tInferPat :: EPat -> T (Typed EPatRet)
tInferPat = tInfer tcPat

-- XXX Has some duplication with tcExpr
tcPat :: Expected -> EPat -> T EPatRet
tcPat mt ae =
  let loc = getSLoc ae
      lit = tcPat mt (EViewPat (EApp (EVar (mkBuiltin loc "==")) ae) (EVar (mkBuiltin loc "True")))
      isNeg (EVar i) = i == mkIdent "negate"
      isNeg _ = False
  in
  case ae of
    EVar i | isDummyIdent i -> do
               -- _ can be anything, so just ignore it
               _ <- tGetExpType mt
               return ([], [], ae)
           | not (isConIdent i) -> do
               -- All pattern variables are in the environment as
               -- type references.  Assign the reference the given type.
               ext <- tGetExpType mt
               (p, t) <- tLookupV i
               unify loc t ext
               return ([], [], p)
           | otherwise -> tcPatAp mt [] ae
    EQVar _ _ -> tcPatAp mt [] ae
    EApp f _
           | isNeg f   -> lit            -- if it's (negate e) it must have been a negative literal
           | otherwise -> tcPatAp mt [] ae

    EOper e ies -> do e' <- tcOper e ies; tcPat mt e'

    ETuple es -> do
      let
        n = length es
      (xs, tes) <- fmap unzip (mapM tInferPat es)
      let
        (sks, ds, ees) = unzip3 xs
        ttup = tApps (tupleConstr loc n) tes
      munify loc mt ttup
      return (concat sks, concat ds, ETuple ees)

    EParen e -> tcPat mt e

    EListish (LList es) -> do
      te <- newUVar
      munify loc mt (tApp (tList loc) te)
      xs <- mapM (tCheckPat te) es
      let !(sks, ds, es') = unzip3 xs
      return (concat sks, concat ds, EListish (LList es'))

    ELit _ _ -> lit

    ESign e t -> do
      t' <- tcType (Check kType) t
      instPatSigma loc t' mt
      tCheckPat t' e

    EAt i p -> do
      (_, ti) <- tLookupV i
      (sk, d, p') <- tcPat mt p
      tt <- tGetExpType mt
      case ti of
        EUVar r -> tSetRefType loc r tt
        _ -> impossible
      return (sk, d, EAt i p')

    EViewPat e p -> do
      (e', te) <- tInferExpr e
      (tea, ter) <- unArrow loc te
      munify loc mt tea
      (sk, d, p') <- tcPat (Check ter) p
      return (sk, d, EViewPat e' p')

    ELazy z p -> do
      (sk, d, p') <- tcPat mt p
      return (sk, d, ELazy z p')

    -- Allow C{} syntax even for non-records
    EUpdate p [] -> do
      (p', _) <- tInferExpr p
      case p' of
        ECon c -> tcPat mt $ eApps p (replicate (conArity c) eDummy)          
        _      -> impossible
    EUpdate p isps -> do
      me <- dsUpdate (const eDummy) p isps
      case me of
        Just p' -> tcPat mt p'
        Nothing -> impossible

    EOr ps -> do
      let orFun = ELam noSLoc $ [ eEqn [p] true | p <- ps] ++ [ eEqn [eDummy] (eFalse loc) ]
          true = eTrue loc
      tcPat mt $ EViewPat orFun true

    _ -> error $ "tcPat: not handled " ++ show (getSLoc ae) ++ " " ++ show ae

-- The expected type is for (eApps afn (reverse args))
tcPatAp :: HasCallStack =>
           Expected -> [EPat] -> EPat -> T EPatRet
--tcPatAp mt args afn | trace ("tcPatAp: " ++ show (mt, args, afn)) False = undefined
tcPatAp mt args afn =
  case afn of
    EVar i | isConIdent i -> do
      (con, xpt) <- tLookupV i
      tcPatApCon mt args con xpt

    EQVar con xpt -> tcPatApCon mt args con xpt

    EApp f a -> tcPatAp mt (a:args) f

    EParen e -> tcPatAp mt args e

    _ -> tcError (getSLoc afn) ("Bad pattern " ++ show afn)

tcPatApCon :: Expected -> [EPat] -> EPat -> EType -> T EPatRet
tcPatApCon mt args con xpt = do
  let loc = getSLoc con
      nargs = length args
      checkArity ary =
        if nargs < ary then
          tcError loc "too few arguments"
        else if nargs > ary then
          tcError loc "too many arguments"
        else
          return ()
  case con of
    -- Pattern synonym
    ECon (ConSyn qi n (e, t)) -> do
      checkArity n
      let (_, yes, _) = mkMatchDataTypeConstr (mkPatSynMatch qi) xpt
          vp = EViewPat (EQVar e t) (eApps yes args)
      --traceM ("patsyn " ++ show vp)
      tcPat mt vp

    -- Regular constructor
    _ -> do 
      case xpt of
         -- Sanity check
         EForall _ _ (EForall _ _ _) -> return ()
         _ -> impossibleShow con
      EForall _ avs apt <- tInst' xpt

      (sks, spt) <- shallowSkolemise avs apt
      (df, pf, pt) <-
        case getImplies spt of
          Nothing -> return ([], con, apt)
          Just (ctx, pt') -> do
            di <- newADictIdent loc
            return ([(di, ctx)], EApp con (EVar di), pt')
          
      let ary = arity pf
            where arity (ECon c) = conArity c
                  arity (EApp f _) = arity f - 1  -- deal with dictionary added above
                  arity e = impossibleShow e
      checkArity ary

      let step [] t r = return (t, r)
          step (a:as) t (sk, d, f) = do
            (at, rt) <- unArrow loc t
            (ska, da, a') <- tCheckPat at a
            step as rt (ska ++ sk, da ++ d, EApp f a')
      (tt, (skr, dr, pr)) <- step args pt (sks, df, pf)

      pp <- case mt of
              Check ext -> subsCheck loc pr ext tt
              Infer r   -> do { tSetRefType loc r tt; return pr }
      return (skr, dr, pp)

eTrue :: SLoc -> Expr
eTrue l = EVar $ mkBuiltin l "True"

eFalse :: SLoc -> Expr
eFalse l = EVar $ mkBuiltin l "False"

multCheck :: [Ident] -> T ()
multCheck vs =
  when (anySame vs) $ do
    let v = head vs
    tcError (getSLoc v) $ "Multiply defined: " ++ showIdent v

tcBinds :: forall a . [EBind] -> ([EBind] -> T a) -> T a
tcBinds xbs ta = withFixes [ (i, fx) | Infix fx is <- xbs, i <- is ] $ do
  let
    tmap = M.fromList [ (i, t) | Sign is t <- xbs, i <- is ]
    xs = getBindsVars xbs
  multCheck xs
  xts <- mapM (tcBindVarT tmap) xs
  withExtVals xts $ do
    nbs <- mapM tcBind xbs
    ta nbs

-- Temporarily exten the fixity table
withFixes :: [FixDef] -> T a -> T a
withFixes [] ta = ta
withFixes fixs ta = do
  ft <- gets fixTable
  modify $ \ st -> st{ fixTable = foldr (uncurry M.insert) ft fixs }
  a <- ta
  modify $ \ st -> st{ fixTable = ft }
  return a

tcBindVarT :: HasCallStack => M.Map EType -> Ident -> T (Ident, EType)
tcBindVarT tmap x = do
  case M.lookup x tmap of
    Nothing -> do
      t <- newUVar
      return (x, t)
    Just t -> do
      tt <- withTypeTable $ tCheckTypeTImpl kType t
      return (x, tt)

tcBind :: EBind -> T EBind
tcBind abind =
  case abind of
    Fcn i eqns -> do
      (_, tt) <- tLookupV i
      teqns <- tcEqns False tt eqns
      return $ Fcn i teqns
    PatBind p a -> tcPatBind PatBind p a
    _ -> return abind

tcPatBind :: (EPat -> Expr -> a) -> EPat -> Expr -> T a
tcPatBind con p a = do
  ((sk, ds, ep), tp) <- tInferPat p  -- pattern variables already bound
  -- This is just to complicated.
  when (not (null sk) || not (null ds)) $
    tcError (getSLoc p) "existentials not allowed in pattern binding"
  ea <- tCheckExprAndSolve tp a
  return $ con ep ea


-- Desugar [T] and (T,T,...)
dsType :: EType -> EType
dsType at =
  case at of
    EVar _ -> at
    EApp f a -> EApp (dsType f) (dsType a)
    EOper t ies -> EOper (dsType t) [(i, dsType e) | (i, e) <- ies]
    EListish (LList [t]) -> tApp (tList (getSLoc at)) (dsType t)
    ETuple ts -> tApps (tupleConstr (getSLoc at) (length ts)) (map dsType ts)
    EParen t -> dsType t
    ESign t k -> ESign (dsType t) k
    EForall b iks t -> EForall b iks (dsType t)
    ELit _ (LStr _) -> at
    ELit _ (LInteger _) -> at
    _ -> impossible

tListI :: SLoc -> Ident
tListI loc = mkIdentSLoc loc nameList

tList :: SLoc -> EType
tList = tCon . tListI

tBool :: SLoc -> EType
tBool loc = tConI loc $ boolPrefix ++ "Bool"

showTModule :: forall a . (a -> String) -> TModule a -> String
showTModule sh amdl = "Tmodule " ++ showIdent (tModuleName amdl) ++ "\n" ++ sh (tBindingsOf amdl) ++ "\n"

-----------------------------------------------------

getFreeTyVars :: [EType] -> T [TyVar]
getFreeTyVars tys = do
  tys' <- mapM derefUVar tys
  return (freeTyVars tys')

getMetaTyVars :: [EType] -> T [TRef]
getMetaTyVars tys = do
  tys' <- mapM derefUVar tys
  return (metaTvs tys')

getEnvTypes :: T [EType]
getEnvTypes = gets (map entryType . stElemsLcl . valueTable)

tyVarSubst :: [a] -> EType -> ([IdKind], [(a, EType)])
tyVarSubst tvs ty =
  let usedVars = allVarsExpr ty -- Avoid used type variables
      newVars = take (length tvs) (allBinders \\ usedVars)
      newVarsK = map (\ i -> IdKind i noKind) newVars
      noKind = eDummy
  in  (newVarsK, zipWith (\ tv n -> (tv, EVar n)) tvs newVars)

-- Quantify over the specified type variables.
-- The type should be zonked.
quantify :: [TRef] -> Rho -> T Sigma
quantify [] ty = return ty
quantify tvs ty = do
  let (newVarsK, sub) = tyVarSubst tvs ty
  osubst <- gets uvarSubst
  mapM_ (uncurry setUVar) sub
  ty' <- derefUVar ty
  putUvarSubst osubst  -- reset the setUVar we did above
  return (EForall False newVarsK ty')

allBinders :: [Ident] -- a,b,...,z,a1,a2,...
allBinders = [ mkIdent [x] | x <- ['a' .. 'z'] ] ++
             [ mkIdent ('a' : show i) | i <- [1::Int ..]]

-- Skolemize the given variables
shallowSkolemise :: [IdKind] -> EType -> T ([TyVar], EType)
shallowSkolemise tvs ty = do
  sks <- mapM (newSkolemTyVar . idKindIdent) tvs
  return (sks, subst (zip (map idKindIdent tvs) (map EVar sks)) ty)

skolemise :: HasCallStack =>
             Sigma -> T ([TyVar], Rho)
-- Performs deep skolemisation, returning the
-- skolem constants and the skolemised type.
skolemise (EForall _ tvs ty) = do -- Rule PRPOLY
  (sks1, ty') <- shallowSkolemise tvs ty
  (sks2, ty'') <- skolemise ty'
  return (sks1 ++ sks2, ty'')
skolemise t@(EApp _ _) | Just (arg_ty, res_ty) <- getArrow t = do
  (sks, res_ty') <- skolemise res_ty
  return (sks, arg_ty `tArrow` res_ty')
skolemise (EApp f a) = do
  (sks1, f') <- skolemise f
  (sks2, a') <- skolemise a
  return (sks1 ++ sks2, EApp f' a')
skolemise ty =
  return ([], ty)

-- Skolem tyvars are just identifiers that start with a uniq
newSkolemTyVar :: Ident -> T Ident
newSkolemTyVar tv = do
  uniq <- newUniq
  return (mkIdentSLoc (getSLoc tv) (unIdent tv ++ "#" ++ show uniq))

metaTvs :: [EType] -> [TRef]
-- Get the MetaTvs from a type; no duplicates in result
metaTvs tys = foldr go [] tys
  where
    go (EUVar tv) acc
      | elem tv acc = acc
      | otherwise = tv : acc
    go (EVar _) acc = acc
    go (EForall _ _ ty) acc = go ty acc
    go (EApp fun arg) acc = go fun (go arg acc)
    go (ELit _ _) acc = acc
    go _ _ = impossible

{-
inferSigma :: Expr -> T (Expr, Sigma)
inferSigma e = do
  (e', exp_ty) <- inferRho e
  env_tys      <- getEnvTypes
  env_tvs      <- getMetaTyVars env_tys
  res_tvs      <- getMetaTyVars [exp_ty]
  let forall_tvs = res_tvs \\ env_tvs
  (e',) <$> quantify forall_tvs exp_ty
-}

checkSigma :: HasCallStack =>
              Expr -> Sigma -> T Expr
checkSigma expr sigma = do
  (skol_tvs, rho) <- skolemise sigma
--  sigma' <- derefUVar sigma
--  tcTrace $ "**** checkSigma: " ++ show expr ++ " :: " ++ show sigma ++ " = " ++ show sigma' ++ " " ++ show skol_tvs
  expr' <- tCheckExpr rho expr
  if null skol_tvs then
    -- Fast special case
    return expr'
   else do
    env_tys <- getEnvTypes
    esc_tvs <- getFreeTyVars (sigma : env_tys)
    let bad_tvs = filter (\ i -> elem i esc_tvs) skol_tvs
    when (not (null bad_tvs)) $
      tcErrorTK (getSLoc expr) $ "not polymorphic enough: " ++ unwords (map showIdent bad_tvs)
    return expr'

subsCheckRho :: HasCallStack =>
                SLoc -> Expr -> Sigma -> Rho -> T Expr
--subsCheckRho _ e1 t1 t2 | trace ("subsCheckRho: " ++ show e1 ++ " :: " ++ show t1 ++ " = " ++ show t2) False = undefined
-- XXX Is this even right?  It's not part of the paper.
subsCheckRho loc exp1 (EForall _ vs1 t1) (EForall _ vs2 t2) | length vs1 == length vs2 = do
  let sub = [(v1, EVar v2) | (IdKind v1 _, IdKind v2 _) <- zip vs1 vs2]
  unify loc (subst sub t1) t2
  return exp1
subsCheckRho loc exp1 sigma1@(EForall _ _ _) rho2 = do -- Rule SPEC
  (exp1', rho1) <- tInst exp1 sigma1
  subsCheckRho loc exp1' rho1 rho2
subsCheckRho loc exp1 arho1 rho2 | Just _ <- getImplies arho1 = do
  (exp1', rho1) <- tInst exp1 arho1
  subsCheckRho loc exp1' rho1 rho2
subsCheckRho loc exp1 rho1 rho2 | Just (a2, r2) <- getArrow rho2 = do -- Rule FUN
  (a1, r1) <- unArrow loc rho1
  subsCheckFun loc exp1 a1 r1 a2 r2
subsCheckRho loc exp1 rho1 rho2 | Just (a1, r1) <- getArrow rho1 = do -- Rule FUN
  (a2,r2) <- unArrow loc rho2
  subsCheckFun loc exp1 a1 r1 a2 r2
subsCheckRho loc exp1 tau1 tau2 = do  -- Rule MONO
--  tcTrace $ "subsCheckRho: MONO " ++ show (tau1, tau2)
  unify loc tau1 tau2 -- Revert to ordinary unification
  return exp1

subsCheckFun :: HasCallStack =>
                SLoc -> Expr -> Sigma -> Rho -> Sigma -> Rho -> T Expr
subsCheckFun loc e1 a1 r1 a2 r2 = do
  _ <- subsCheck loc eCannotHappen a2 a1   -- XXX
  subsCheckRho loc e1 r1 r2

instSigma :: HasCallStack =>
             SLoc -> Expr -> Sigma -> Expected -> T Expr
instSigma loc e1 t1 (Check t2) = do
--  tcTrace ("instSigma: Check " ++ showEType t1 ++ " = " ++ showEType t2)
  subsCheckRho loc e1 t1 t2
instSigma loc e1 t1 (Infer r) = do
  (e1', t1') <- tInst e1 t1
  --tcTrace ("instSigma: Infer " ++ showEType t1 ++ " ==> " ++ showEType t1')
  tSetRefType loc r t1'
  return e1'

eCannotHappen :: Expr
eCannotHappen = --undefined
                EVar $ mkIdent "cannot-happen"

-----

--
-- Pattern synonyms look like
--   pattern P :: forall a1...an . ctxr => forall b1...bm . ctxp => t1 -> ... -> ti -> t
--   pattern P x1...xi <- p where P = e
-- (this type is the canonicalized type, generated by canonPatSynType).
-- The synonym is translated into a builder, a matcher and a type.
-- Each synonym use is replaced by a simple view pattern.
--
-- The builder is simple.  It gets the same name and type as the pattern synonym,
-- and the definition is the one provided in the definition.
--   P :: forall a1...an . ctxr => forall b1...bm . ctxp => t1 -> ... ti -> t
--   P = e
--
-- The matcher needs to account for possible existentials so we get a data type
-- for the match result that can have existentials.
--   data P%T a1...an = forall b1...bm . ctxp => M t1 ... ti
--                    | N
--
-- The matcher itself has the required part of the synonym type, whereas
-- the provided part is in the data type.  The matcher simply matches on the given pattern.
--   P% :: forall a1...an . ctxr => t -> P%T a1...an
--   P% p = M x1...xi
--   P% _ = N
-- So when the synonym P matches the matcher P% will return the M constructor
-- of the P%T type, and then N constructor when there is no match.
--
-- Each use of the pattern synonym
--   P p1...pi
-- is replaced by
--   (P% -> M p1...pi)
--
-- The data type, P%T, is not entered into any symbol tables.
-- The matcher, P%, is in the symbol table, but is not part of the exported symbols.
-- The transformed expression simply carries enough information about the types
-- (using EQVar).  The exported ECon for P has this information.
--

emptyCtx :: EConstraint
emptyCtx = EVar $ tupleConstr noSLoc 0

isEmptyCtx :: EConstraint -> Bool
isEmptyCtx (EVar i) = i == tupleConstr noSLoc 0
isEmptyCtx _ = False

-- Expand a pattern synonym into the builder and matcher definitions.
-- Removes that actual pattern definition
expandPatSyn :: EDef -> T [EDef]
expandPatSyn (Pattern (i, vks) p me) = do
  (_, t) <- tLookup "type signature" i
  (im, qim) <- addPatSynMatch i t
  let (ddata, yes, no) = mkMatchDataTypeConstr qim t
      mexp = fmap (Fcn i) me
      pat = Fcn im [ eEqn [p] match
                   , eEqn [eDummy] no]
      match = eApps yes (map (EVar . idKindIdent) vks)
      dname = case ddata of Data (n, _) _ _ -> n; _ -> impossible
  kvar <- newUVar    -- We don't care about the kind
  withTypeTable $ extValQTop dname kvar
  pure $ maybeToList mexp ++ [pat, ddata]
expandPatSyn d = pure [d]

-- Add the matcher for a pattern synonym to the symbol table.
-- Return the added identifier.
addPatSynMatch :: Ident -> EType -> T (Ident, Ident)
addPatSynMatch i at = do
  mn <- gets moduleName
  let ip = mkPatSynMatch i
      qip = qualIdent mn ip
  extValETop ip (mkPatSynMatchType qip at) (EVar qip)
  return (ip, qip)

mkPatSynMatchType :: Ident -> EType -> EType
mkPatSynMatchType qip at =
  let (vks1, ctx1, _vks2, _ctx2, ty) = splitPatSynType at
      (_ats, rt) = getArrows ty
      pstycon = mkMatchDataTypeName qip
  in  eForall vks1 $ etImplies ctx1 $ rt `tArrow` tApps pstycon (map (EVar . idKindIdent) vks1)

-- Given the (qualified) name of a synonym and its type generate:
-- match-constructor, nomatch-constructor
mkMatchDataTypeConstr :: HasCallStack => Ident -> EType -> (EDef, Expr, Expr)
mkMatchDataTypeConstr qi at =
  let (vks1, _ctx1, vks2, ctx2, ty) = splitPatSynType at
      (ats, _rt) = getArrows ty
      n = length ats
      mi = addIdentSuffix qi "M"
      ni = addIdentSuffix qi "N"
      cti = [ (mi, n + if isEmptyCtx ctx2 then 0 else 1), (ni, 0) ]
      conm = ConData cti mi []
      conn = ConData cti ni []
      tycon = mkMatchDataTypeName qi
      tr = tApps tycon $ map (EVar . idKindIdent) vks1
      tn = EForall True vks1 $ EForall True [] tr
      tm = EForall True vks1 $ EForall True vks2 $ etImplies ctx2 $ foldr tArrow tr ats

      ddata = Data lhs [cm, cn] []
            where lhs = (unQualIdent tycon, vks1)
                  cm = Constr vks2 (if isEmptyCtx ctx2 then [] else [ctx2]) (unQualIdent mi) (Left $ zip (repeat False) ats)
                  cn = Constr []   []                                       (unQualIdent ni) (Left [])

  in  -- trace ("M :: " ++ show tm ++ ",  N :: " ++ show tn) $
      -- trace (showEDefs [ddata]) $
      (ddata, EQVar (ECon conm) tm, EQVar (ECon conn) tn)

mkPatSynMatch :: Ident -> Ident
mkPatSynMatch i = addIdentSuffix i "%"

mkMatchDataTypeName :: Ident -> Ident
mkMatchDataTypeName i = addIdentSuffix i "T"

isMatchDataTypeName :: Ident -> Bool
isMatchDataTypeName = isSuffixOf "%T" . unIdent

-- A pattern synonym always has a type of the form
--  forall vs1 . ctx1 => forall vs2 . ctx2 => ty
--         required             provided
-- The input has forall inserted, but the implicit forall
-- may be in the wrong place.
canonPatSynType :: EType -> T EType
canonPatSynType at = do
  let mkTyp rVks rCtx pVks pCtx ty =
        EForall True rVks $ tImplies rCtx $
        EForall True pVks $ tImplies pCtx ty
      getImplies' :: EType -> (EConstraint, EType)
      getImplies' ty = fromMaybe (emptyCtx, ty) $ getImplies ty

  at' <- expandSyn at
  case at' of
    EForall False vks t0 -> do
      -- Implicit forall, the xs need to be split between required and provided.
      let (reqCtx, t1) = getImplies' t0
          (proCtx, t2) = getImplies' t1
          proVs = freeTyVars [proCtx]
          (proVks, reqVks) = partition ((`elem` proVs) . idKindIdent) vks
--      traceM "%%% implicit"
      pure $ mkTyp reqVks reqCtx proVks proCtx t2

    EForall True reqVks t0 -> do
      -- Explicit forall
      let (reqCtx, t1) = getImplies' t0
          (proVks, t2) = unForall t1
          (proCtx, t3) = getImplies' t2
--      traceM "%%% explicit"
      pure $ mkTyp reqVks reqCtx proVks proCtx t3

    ty -> do
      -- No forall at all.  XXX doesn't work with nullary classes
--      traceM "%%% none"
      pure $ mkTyp [] emptyCtx [] emptyCtx ty

splitPatSynType :: EType -> ([IdKind], EConstraint, [IdKind], EConstraint, EType)
splitPatSynType (EForall _ vks1 t0)
  | Just  (ctx1, EForall _ vks2 t1) <- getImplies t0
  , Just  (ctx2, ty) <- getImplies t1
  = (vks1, ctx1, vks2, ctx2, ty)
splitPatSynType t = impossibleShow t

-----

-- Given a dictionary of a (constraint type), split it up
--  * components of a tupled constraint
--  * superclasses of a constraint
expandDict :: HasCallStack => Expr -> EConstraint -> T [InstDictC]
expandDict edict ct = expandDict' [] [] edict =<< expandSyn ct

expandDict' :: HasCallStack => [IdKind] -> [EConstraint] -> Expr -> EConstraint -> T [InstDictC]
expandDict' avks actx edict acc = do
  let
    (bvks, bctx, cc) = splitInst acc
    (iCls, args) = getApp cc
    vks = avks ++ bvks
    ctx = actx ++ bctx
  case getTupleConstr iCls of
    Just _ -> do
      concat <$> mapM (\ (i, a) -> expandDict' vks ctx (mkTupleSel i (length args) `EApp` edict) a) (zip [0..] args)
    Nothing -> do
      ct <- gets classTable
      case M.lookup iCls ct of
        Nothing ->
         -- XXX ~ could be in the symbol table
         if iCls == mkIdent "Primitives.~" then
          return []
         else do
          -- if iCls is a variable it's not in the class table, otherwise it's an error
          when (isConIdent iCls) $
            --impossible
            -- XXX it seems we can get here, e.g., Control.Monad.Fail without Applicative import
            error ("expandDict: " ++ showExprRaw acc)
          return [(edict, vks, ctx, cc, [])]
        Just (ClassInfo iks sups _ _ fds) -> do
          let 
            vs = map idKindIdent iks
            sub = zip vs args
            sups' = map (subst sub) sups
          insts <- concat <$> mapM (\ (i, sup) -> expandDict' vks ctx (EVar (mkSuperSel iCls i) `EApp` edict) sup) (zip [1 ..] sups')
          return $ (edict, vks, ctx, cc, fds) : insts

mkSuperSel :: HasCallStack =>
              Ident -> Int -> Ident
mkSuperSel c i = addIdentSuffix c ("$super" ++ show i)

---------------------------------

type Solved = (Ident, Expr)

-- Solve constraints generated locally in 'ta'.
-- Keep any unsolved ones for later.
solveLocalConstraints :: forall a . T a -> T (a, [Solved])
solveLocalConstraints ta = do
  cs <- gets constraints           -- old constraints
  putConstraints []                -- start empty
  a <- ta                          -- compute, generating constraints
  ds <- solveConstraints           -- solve those
  un <- gets constraints           -- get remaining unsolved
--  traceM $ "solveLocalConstraints: " ++ show (cs, ds, un)
  putConstraints (un ++ cs)        -- put back unsolved and old constraints
  return (a, ds)

solveAndDefault :: forall a . Bool -> T a -> T (a, [Solved])
solveAndDefault False ta = solveLocalConstraints ta
solveAndDefault True  ta = do
  a <- ta
  ds <- solveConstraints
  cs <- gets constraints
  vs <- getMetaTyVars (map snd cs)    -- These are the type variables that need defaulting
--  tcTrace $ "solveAndDefault: meta=" ++ show vs
  -- XXX may have to iterate this with fundeps
  ds' <- concat <$> mapM defaultOneTyVar vs
  return (a, ds ++ ds')

defaultOneTyVar :: TRef -> T [Solved]
defaultOneTyVar tv = do
--  traceM $ "defaultOneTyVar: " ++ show tv
  old <- get             -- get entire old state
  let cvs = nub [ c | (_, EApp (EVar c) (EUVar tv')) <- constraints old, tv == tv' ]  -- all C v constraints
--  traceM $ "defaultOneTyVar: cvs = " ++ show cvs
  dvs <- getSuperClasses cvs                            -- add superclasses
--  traceM $ "defaultOneTyVar: dvs = " ++ show dvs
  let oneCls c | Just ts <- M.lookup c (defaults old) =
        take 1 $ filter (\ t -> all (\ cc -> soluble cc t) cvs) ts
               | otherwise = []
      soluble c t = fst $ flip tcRun old $ do
        putConstraints [(dummyIdent, EApp (EVar c) t)]  -- Use current (C T) constraint
        _ <- solveConstraints                           -- and solve.
        cs <- gets constraints
        return $ null cs                                -- No constraints left?
      tys = nubBy eqEType $ concatMap oneCls dvs
--  traceM $ "defaultOneTyVar: " ++ show (tv, tys)
  case tys of
    [ty] -> do            -- There is a single type solving everything
      setUVar tv ty
      solveConstraints
    _ -> return []        -- Nothing solved

-- The transitive closure of super-classes.
-- XXX Somewhat duplicated with expandDict
getSuperClasses :: [Ident] -> T [Ident]
getSuperClasses ais = do
  ct <- gets classTable
  let loop done [] = done
      loop done (i:is) | i `elem` done = loop done is
                       | otherwise = i :
        case M.lookup i ct of
          Nothing -> error $ "getSuperClasses: " ++ show i
          Just (ClassInfo _ supers _ _ _) ->
            loop done (concatMap flatten supers ++ is)
      flatten a | (c, ts) <- getApp a =
        case getTupleConstr c of
          Nothing -> [c]
          Just _ -> concatMap flatten ts
  return $ loop [] ais



{-
showInstInfo :: InstInfo -> String
showInstInfo (InstInfo m ds fds) = "InstInfo " ++ show (M.toList m) ++ " " ++ showListS showInstDict ds ++ show fds

showInstDict :: InstDict -> String
showInstDict (e, ctx, ts) = showExpr e ++ " :: " ++ show (addConstraints (ctx 10000) (tApps (mkIdent "_") ts))

showInstDef :: InstDef -> String
showInstDef (cls, InstInfo m ds _) = "instDef " ++ show cls ++ ": "
            ++ show (M.toList m) ++ ", " ++ showListS showInstDict ds

showConstraint :: (Ident, EConstraint) -> String
showConstraint (i, t) = show i ++ " :: " ++ show t

showMatch :: (Expr, [EConstraint]) -> String
showMatch (e, ts) = show e ++ " " ++ show ts
-}

type Goal = (Ident, EType)     -- What we want to solve
type UGoal = Goal              -- Unsolved goal
type Soln = (Ident, Expr)      -- Solution, i.e., binding of a dictionary
type Improve = (SLoc, EType, EType)  -- Unify to get an improvement substitution

-- Solve as many constraints as possible.
-- Return bindings for the dictionary witnesses.
solveConstraints :: T [Soln]
solveConstraints = do
  cs <- gets constraints
  if null cs then
    return []
   else do
    addMetaDicts
--    tcTrace "------------------------------------------\nsolveConstraints"
    eqs <- gets typeEqTable
    cs' <- mapM (\ (i,t) -> do { t' <- derefUVar t; return (i, normTypeEq eqs t') }) cs
--    tcTrace ("constraints:\n" ++ unlines (map showConstraint cs'))
    (unsolved, solved, improves) <- solveMany cs' [] [] []
    putConstraints unsolved
--    tcTrace ("solved:\n"   ++ unlines [ showIdent i ++ " = "  ++ showExpr  e | (i, e) <- solved ])
--    tcTrace ("unsolved:\n" ++ unlines [ showIdent i ++ " :: " ++ showEType t | (i, t) <- unsolved ])
    if null improves then
      return solved
     else do
      -- We have improving substitutions.
      -- Do the unifications, and try to solve more.
      mapM_ (\ (l, a, b) -> unify l a b) improves
      (++ solved) <$> solveConstraints

-- A solver get a location, class&types (i.e. (C t1 ... tn)),
-- and, if successful, returns a dictionary expression and new goals.
type SolveOne = SLoc -> Ident -> [EType] -> T (Maybe (Expr, [Goal], [Improve]))

-- Table of constraint solvers.
-- The predicate gets the class name and picks a solver.
-- There must always by at least one solver that matches
solvers :: [(Ident -> Bool, SolveOne)]
solvers =
  [ (isJust . getTupleConstr,      solveTuple)        -- handle tuple constraints, i.e. (C1 t1, C2 t2, ...)
  , ((== mkIdent nameTypeEq),      solveTypeEq)       -- handle equality constraints, i.e. (t1 ~ t2)
  , ((== mkIdent nameKnownNat),    solveKnownNat)     -- KnownNat 123 constraints
  , ((== mkIdent nameKnownSymbol), solveKnownSymbol)  -- KnownSymbol "abc" constraints
  , ((== mkIdent nameCoercible),   solveCoercible)    -- Coercible a b constraints
  , (const True,                   solveInst)         -- handle constraints with instances
  ]

-- Examine each goal, either solve it (possibly producing new goals) or let it remain unsolved.
solveMany :: [Goal] -> [UGoal] -> [Soln] -> [Improve] -> T ([UGoal], [Soln], [Improve])
solveMany [] uns sol imp = return (uns, sol, imp)
-- Need to handle ct of the form C => T, and forall a . T
solveMany (cns@(di, ct) : cnss) uns sol imp = do
--  tcTrace ("trying " ++ showEType ct)
  let loc = getSLoc di
      !(iCls, cts) = getApp ct
      solver = head [ s | (p, s) <- solvers, p iCls ]
  ads <- gets argDicts
  -- Check if we have an exact match among the arguments dictionaries.
  -- This is important to find tupled dictionaries in recursive calls.
  case [ ai | (ai, act) <- ads, ct `eqEType` act ] of
    ai : _ -> do
      --tcTrace $ "solve with arg " ++ show ct
      solveMany cnss uns ((di, EVar ai) : sol) imp
    [] -> do
      msol <- solver loc iCls cts
      case msol of
        Nothing           -> solveMany        cnss  (cns : uns)            sol         imp
        Just (de, gs, is) -> solveMany (gs ++ cnss)        uns ((di, de) : sol) (is ++ imp)

solveInst :: SolveOne
solveInst loc iCls cts = do
  it <- gets instTable
--  tcTrace ("instances:\n" ++ unlines (map showInstDef (M.toList it)))
  -- XXX The solveGen&co functions are not in the T monad.
  -- But we sometimes need to instantiate type variable, so we use the
  -- hack to pass dowwn a starting uniq.
  -- This ought to be fixed, but is will be less efficient.
  uniq <- do ts <- get; let { u = unique ts }; put ts{ unique = u+100 }; return u   -- make room for many UVars
  case M.lookup iCls it of
    Nothing -> return Nothing   -- no instances, so no chance
    Just (InstInfo atomMap insts fds) -> do
      -- tcTrace $ "solveInst: " ++ showIdent iCls ++ " atomMap size=" ++ show (M.size atomMap)
      case cts of
        [EVar i] -> do
          case M.lookup i atomMap of
            -- If the goal is just (C T) and there is an instance, the solution is simple
            Just e  -> return $ Just (e, [], [])
            -- Not found, but there might be a generic instance
            Nothing -> solveGen uniq (M.null atomMap) fds insts loc iCls cts
        _           -> solveGen uniq (M.null atomMap) fds insts loc iCls cts

-- When matching constraint (C _a) against an instance of the form
-- instance (C b) then we don't want to match if the
-- _a is later instantiated and it turns out we should
-- have matched a (C T) instead.
solveGen :: TRef -> Bool -> [IFunDep] -> [InstDict] -> SolveOne
solveGen uniq noAtoms fds insts loc iCls cts = do
--  tcTrace ("solveGen: " ++ show (iCls, cts))
--  tcTrace ("solveGen: insts=" ++ show insts)
  let matches = getBestMatches $ findMatches uniq noAtoms loc fds insts cts
--  tcTrace ("solveGen: matches allMatches =" ++ showListS show (findMatches uniq noAtoms loc fds insts cts))
--  tcTrace ("solveGen: matches bestMatches=" ++ showListS showMatch matches)
  case matches of
    []              -> return Nothing
    [(de, ctx, is)] ->
      if null ctx then
        return $ Just (de, [], is)
      else do
        d <- newDictIdent loc
--        tcTrace ("constraint " ++ showIdent di ++ " :: " ++ showEType ct ++ "\n" ++
--                "   turns into " ++ showIdent d ++ " :: " ++ showEType (tupleConstraints ctx) ++ ", " ++
--                showIdent di ++ " = " ++ showExpr (EApp de (EVar d)))
        return $ Just (EApp de (EVar d), [(d, tupleConstraints ctx)], is)
    _ -> tcError loc $ "Multiple constraint solutions for: " ++ showEType (tApps iCls cts)
--                     ++ show (map fst matches)

-- Split a tupled contraint into its parts.
-- XXX should look for a direct (tupled) dictionary
solveTuple :: SolveOne
solveTuple loc _iCls cts = do
  goals <- mapM (\ c -> do { d <- newDictIdent loc; return (d, c) }) cts
  return $ Just (ETuple (map (EVar . fst) goals), goals, [])

solveTypeEq :: SolveOne
-- If either type is a unification variable, just do the unification.
solveTypeEq loc _iCls [t1, t2] | isEUVar t1 || isEUVar t2 = return $ Just (ETuple [], [], [(loc, t1, t2)])
                               | otherwise = do
  eqs <- gets typeEqTable
  --tcTrace ("solveTypeEq eqs=" ++ show eqs)
  case solveEq eqs t1 t2 of
    Nothing -> return Nothing
    Just tts -> do
      let mkEq (u1, u2) = do
            i <- newDictIdent loc
            return (i, mkEqType loc u1 u2)
      ncs <- mapM mkEq tts
      return $ Just (ETuple [], ncs, [])
solveTypeEq _ _ _ = impossible

solveCoercible :: SolveOne
solveCoercible loc _iCls [t1, t2] = do
  st <- gets synTable
  extNewtypeSyns        -- pretend newtypes are type synonyms
  t1' <- expandSyn t1
  t2' <- expandSyn t2
  putSynTable st
  -- walk over the types in parallel,
  -- and generate new Coercible constraints when not equal.
--  traceM $ "solveCoercible: " ++ showExprRaw t1' ++ " and " ++ showExprRaw t2'
  case genCoerce t1' t2' of
    Nothing -> return Nothing
    Just [(u1, u2)] | u1 `eqEType` t1 && u2 `eqEType` t2 -> return Nothing  -- Nothing has improved
    Just tts -> do
      let mkCo (u1, u2) = do
            i <- newDictIdent loc
            return (i, mkCoercible loc u1 u2)
      ncs <- mapM mkCo tts
      return $ Just (ETuple [], ncs, [])
solveCoercible _ _ _ = impossible

genCoerce :: EType -> EType -> Maybe [(EType, EType)]
genCoerce t1 t2 | eqEType t1 t2 = Just []
genCoerce t1@(EUVar _) t2 = Just [(t1, t2)]
genCoerce t1@(EVar _)  t2 = Just [(t1, t2)]
genCoerce t1 t2@(EUVar _) = Just [(t1, t2)]
genCoerce t1 t2@(EVar _)  = Just [(t1, t2)]
genCoerce (EApp f1 a1) (EApp f2 a2) = (++) <$> genCoerce f1 f2 <*> genCoerce a1 a2
genCoerce _ _ = Nothing

-- Pretend newtypes are type synonyms.
-- XXX It's rather inefficient to do this over and over.
extNewtypeSyns :: T ()
extNewtypeSyns = do
  dt <- gets dataTable
  let ext (qi, Newtype (_, vs) (Constr _ _ _c et) _) = do
          -- XXX We should check that the constructor name (_c) is visible.
          -- But this is tricky since we don't know under what qualified name it
          -- it should be visible.
          let t = either (snd . head) (snd . snd . head) et
--          traceM $ "extNewtypeSyns: " ++ showIdent qi ++ show vs ++ " = " ++ showExprRaw t
          extSyn qi (EForall True vs t)  -- extend synonym table
      ext _ = return ()
  mapM_ ext $ M.toList dt
  

isEUVar :: EType -> Bool
isEUVar (EUVar _) = True
isEUVar _ = False

solveKnownNat :: SolveOne
solveKnownNat loc iCls [e@(ELit _ (LInteger _))] = mkConstDict loc iCls e
solveKnownNat loc iCls ts = solveInst loc iCls ts  -- look for a dict argument

solveKnownSymbol :: SolveOne
solveKnownSymbol loc iCls [e@(ELit _ (LStr _))] = mkConstDict loc iCls e
solveKnownSymbol loc iCls ts = solveInst loc iCls ts  -- look for a dict argument

mkConstDict :: SLoc -> Ident -> Expr -> T (Maybe (Expr, [Goal], [Improve]))
mkConstDict loc iCls e = do
  let res = EApp (EVar $ mkClassConstructor iCls) fcn
      fcn = EApp (ELit loc (LPrim "K")) e                -- constant function
  return $ Just (res, [], [])

type TySubst = [(TRef, EType)]

-- Given some instances and a constraint, find the matching instances.
-- For each matching instance return: (subst-size, (dict-expression, new-constraints))
-- The subst-size is the size of the substitution that made the input instance match.
-- It is a measure of how exact the match is.
findMatches :: TRef -> Bool -> SLoc -> [IFunDep] -> [InstDict] -> [EType] -> [(Int, (Expr, [EConstraint], [Improve]))]
findMatches _ False _ _ _ [EUVar _] = []
findMatches uniq _ loc fds ds its =
 let rrr =
       [ (length s, (de, map (substEUVar s) ctx, imp))
       | (de, ctxts) <- ds
       , let (ctx, ts) = ctxts uniq
       , Just (s, imp) <- [matchTypes loc ts its fds]
       ]
 in --trace ("findMatches: " ++ showListS showInstDict ds ++ "; " ++ show its ++ "; " ++ show fds ++ "; " ++ show rrr)
    rrr

-- Do substitution for EUVar.
-- XXX similar to derefUVar
substEUVar :: TySubst -> EType -> EType
substEUVar [] t = t
substEUVar _ t@(EVar _) = t
substEUVar _ t@(ELit _ _) = t
substEUVar s (EApp f a) = EApp (substEUVar s f) (substEUVar s a)
substEUVar s t@(EUVar i) = fromMaybe t $ lookup i s
substEUVar s (EForall b iks t) = EForall b iks (substEUVar s t)
substEUVar _ _ = impossible

-- Length of lists match, because of kind correctness.
-- fds is a non-empty list.
matchTypes :: SLoc -> [EType] -> [EType] -> [IFunDep] -> Maybe (TySubst, [Improve])
matchTypes _ ats ats' [] = do
  -- Simple special case when there are no fundeps.
  let loop r (t:ts) (t':ts') = matchType r t t' >>= \ r' -> loop r' ts ts'
      loop r _ _ = pure r
  s <- loop [] ats ats'
  pure (s, [])
matchTypes loc ts ts' fds = asum $ map (matchTypesFD loc ts ts') fds

matchTypesFD :: SLoc -> [EType] -> [EType] -> IFunDep -> Maybe (TySubst, [Improve])
--matchTypesFD _ ts ts' io | trace ("matchTypesFD: " ++ show (ts, ts', io)) False = undefined
matchTypesFD loc ts ts' (ins, outs) = do
  let matchFD :: Bool -> EType -> EType -> Maybe TySubst
      matchFD True  = \ _ _ -> Just []     -- if it's an output, don't match
      matchFD False = matchType []          -- match types for non-outputs
  tms <- sequence $ zipWith3 matchFD outs ts ts'
  tm  <- combineTySubsts tms               -- combine all substitutions
  is  <- combineTySubsts [ s | (True, s) <- zip ins tms]  -- subst from input FDs
  let imp = [ (loc, substEUVar is t, t') | (True, t, t') <- zip3 outs ts ts' ]  -- improvements
  pure (tm, imp)

-- Match two types, instantiate variables in the first type.
matchType :: TySubst -> EType -> EType -> Maybe TySubst
matchType = match
  where
    match r (EVar i)   (EVar i')   | i == i' = pure r
    match r (ELit _ l) (ELit _ l') | l == l' = pure r
    match r (EApp f a) (EApp f' a') = do
      r' <- match r f f'
      match r' a a'
--    match _ _ (EUVar _) = Nothing
    match r (EUVar i) t' =
      -- For a variable, check that any previous match is the same.
      case lookup i r of
        Just t  -> match r t t'
        Nothing -> pure ((i, t') : r)
    match _ _ _ = Nothing

-- XXX This shouldn't be this complicated.
combineTySubsts :: [TySubst] -> Maybe TySubst
combineTySubsts = combs []
  where
    combs r [] = Just r
    combs r (s:ss) = do { r' <- comb r s; combs r' ss }
    comb :: TySubst -> TySubst -> Maybe TySubst
    comb r [] = Just r
    comb r ((v, t):s) = do { r' <- comb1 v t r; comb r' s }
    comb1 v t r =
      case lookup v r of
        Nothing -> Just ((v, t) : r)
        Just t' -> matchType [] t' t

-- Get the best matches.  These are the matches with the smallest substitution.
-- Always prefer arguments rather than global instances.
getBestMatches :: [(Int, (Expr, [EConstraint], [Improve]))] -> [(Expr, [EConstraint], [Improve])]
getBestMatches [] = []
getBestMatches ams =
  let isDictArg (EVar i) = (adictPrefix ++ uniqIdentSep) `isPrefixOf` unIdent i
      isDictArg _ = True
      !(args, insts) = partition (\ (_, (ei, _, _)) -> isDictArg ei) ams
      pick ms =
        let b = minimum (map fst ms)         -- minimum substitution size
        in  [ ec | (s, ec) <- ms, s == b ]   -- pick out the smallest
  in  if null args then pick insts else pick args

-- Check that there are no unsolved constraints.
checkConstraints :: HasCallStack => T ()
checkConstraints = do
  cs <- gets constraints
  case cs of
    [] -> return ()
    (i, t) : _ -> do
      t' <- derefUVar t
--      is <- gets instTable
--      tcTrace $ "Cannot satisfy constraint: " ++ unlines (map (\ (i, ii) -> show i ++ ":\n" ++ showInstInfo ii) (M.toList is))
      tcError (getSLoc i) $ "Cannot satisfy constraint: " ++ show t'
                            ++ "\n     fully qualified: " ++ showExprRaw t'

-- Add a type equality constraint.
addEqConstraint :: SLoc -> EType -> EType -> T ()
addEqConstraint loc t1 t2 = do
  d <- newDictIdent loc
  addConstraint d (mkEqType loc t1 t2)

mkEqType :: SLoc -> EType -> EType -> EConstraint
mkEqType loc t1 t2 = eAppI2 (mkIdentSLoc loc nameTypeEq) t1 t2

mkCoercible :: SLoc -> EType -> EType -> EConstraint
mkCoercible loc t1 t2 = eAppI2 (mkIdentSLoc loc nameCoercible) t1 t2

-- Possibly solve a type equality.
-- Just normalize both types and compare.
solveEq :: TypeEqTable -> EType -> EType -> Maybe [(EType, EType)]
--solveEq eqs t1 t2 | trace ("solveEq: " ++ show (t1,t2) ++ show eqs) False = undefined
solveEq eqs t1 t2 | normTypeEq eqs t1 `eqEType` normTypeEq eqs t2 = Just []
                  | otherwise =
  case (t1, t2) of
    (EApp f1 a1, EApp f2 a2) -> Just [(f1, f2), (a1, a2)]
    _                        -> Nothing

-- Add the equality t1~t2.
-- Type equalities are saved as a rewrite system where all the rules
-- have the form
--   v -> rhs
-- where v is a type variable (rigid or skolem), and the rhs contains
-- only type constructors and type variables that are not the LHS of any rule.
-- To find out if two type are equal according to the known equalites,
-- we just normalize both type using the rewrite rules (which is just a substitution).
-- When we get TypeFamilies the LHS has to be a type expression as well.
-- And the rewrite rules can probably be obtained with Knuth-Bendix completion
-- of the equalities.
-- Note: there should be no meta variables in t1 and t2.
-- XXX This guaranteed by how it's called, but I'm not sure it always works properly.
addTypeEq :: EType -> EType -> TypeEqTable -> TypeEqTable
addTypeEq t1 t2 aeqs =
  let deref (EVar i) | Just t <- lookup i aeqs = t
      deref (ESign t _) = t
      deref t = t
      t1' = deref t1
      t2' = deref t2
      isVar = not . isConIdent
  in  case (t1', t2') of
        (EVar i1, EVar i2) | isVar i1 && isVar i2 ->
               if i1 < i2 then (i2, t1') : aeqs  -- always point smaller to bigger
          else if i1 > i2 then (i1, t2') : aeqs
          else                             aeqs
        (EVar i1, _) | isVar i1 -> (i1, t2') : aeqs
        (_, EVar i2) | isVar i2 -> (i2, t1') : aeqs
        (EApp f1 a1, EApp f2 a2) -> addTypeEq f1 f2 (addTypeEq a1 a2 aeqs)
        _ | t1' `eqEType` t2 -> aeqs
        _ -> errorMessage (getSLoc t1) $ "inconsisten type equality " ++ showEType t1' ++ " ~ " ++ showEType t2'

-- Normalize a type with the known type equalties.
-- For now, it's just substitution.
normTypeEq :: TypeEqTable -> EType -> EType
normTypeEq = subst

---------------------

-- Try adding all dictionaries that used to have meta variables.
addMetaDicts :: T ()
addMetaDicts = do
  ms <- gets metaTable
  putMetaTable []
  mapM_ addDict ms  -- Try adding them

-----------------------------
{-
showSymTab :: SymTab -> String
showSymTab (SymTab im ies) = showListS showIdent (map fst (M.toList im) ++ map fst ies)

showTModuleExps :: TModule a -> String
showTModuleExps (TModule mn _fxs tys _syns _clss _insts vals _defs) =
  showIdent mn ++ ":\n" ++
    unlines (map (("  " ++) . showValueExport) vals) ++
    unlines (map (("  " ++) . showTypeExport)  tys)

showValueExport :: ValueExport -> String
showValueExport (ValueExport i (Entry qi t)) =
  showIdent i ++ " = " ++ showExpr qi ++ " :: " ++ showEType t

showTypeExport :: TypeExport -> String
showTypeExport (TypeExport i (Entry qi t) vs) =
  showIdent i ++ " = " ++ showExpr qi ++ " :: " ++ showEType t ++ " assoc=" ++ showListS showValueExport vs

showIdentClassInfo :: (Ident, ClassInfo) -> String
showIdentClassInfo (i, (_vks, _ctx, cc, ms)) =
  showIdent i ++ " :: " ++ showEType cc ++
    " has " ++ showListS showIdent ms
-}

doDeriving :: EDef -> T [EDef]
doDeriving def@(Data    lhs cs ds) = (def:) . concat <$> mapM (deriveHdr lhs  cs) ds
doDeriving def@(Newtype lhs  c ds) = (def:) . concat <$> mapM (deriveHdr lhs [c]) ds
doDeriving def@(Deriving ct)       = (def:) <$> standaloneDeriving ct
doDeriving def                     = return [def]

standaloneDeriving :: EType -> T [EDef]
standaloneDeriving act = do
  (_vks, _ctx, cc) <- splitInst <$> expandSyn act
  dtable <- gets dataTable
--  traceM ("standaloneDeriving 1 " ++ show (ctx, cc))
  (cls, tname) <-
    case getAppM cc of
      Just (c, ts@(_:_)) |
        let t = last ts,
        Just (n, _) <- getAppM t -> return (tApps c (init ts), n)
      _ -> tcError (getSLoc act) "malformed standalone deriving"
--  traceM ("standaloneDeriving 2 " ++ show (act, cls, tname))
  (lhs, cs) <-
    case M.lookup tname dtable of
      Just (Newtype l  c _) -> return (l, [c])
      Just (Data    l xs _) -> return (l, xs)
      _ -> tcError (getSLoc act) ("not data/newtype " ++ showIdent tname)
  -- We want 'instance ctx => cls ty'
  deriveNoHdr act lhs cs cls