
-- ==========================================================--
-- === A type-checker -- v5        File: TypeCheck5.m (1) ===--
-- === Corrected version for 0.210a                       ===--
-- ==========================================================--

module TypeCheck5 where
import BaseDefs
import Utils
import MyUtils

import Data.List(nub) -- 1.3

-- ==========================================================--
-- === Formatting of results                              ===--
-- ==========================================================--

tcMapAnnExpr :: (a -> b) ->
                AnnExpr c a ->
                AnnExpr c b

tcMapAnnExpr f (ann, node)
   = (f ann, mapAnnExpr' node)
     where
        mapAnnExpr' (AVar v) = AVar v
        mapAnnExpr' (ANum n) = ANum n
        mapAnnExpr' (AConstr c) = AConstr c
        mapAnnExpr' (AAp ae1 ae2)
           = AAp (tcMapAnnExpr f ae1) (tcMapAnnExpr f ae2)
        mapAnnExpr' (ALet recFlag annDefs mainExpr)
           = ALet recFlag (map mapAnnDefn annDefs) (tcMapAnnExpr f mainExpr)
        mapAnnExpr' (ACase switchExpr annAlts)
           = ACase (tcMapAnnExpr f switchExpr) (map mapAnnAlt annAlts)
        mapAnnExpr' (ALam vs e) = ALam vs (tcMapAnnExpr f e)

        mapAnnDefn (naam, expr)
           = (naam, tcMapAnnExpr f expr)

        mapAnnAlt (naam, (pars, resExpr))
           = (naam, (pars, tcMapAnnExpr f resExpr))


-- ======================================================--
--
tcSubstAnnTree :: Subst ->
                  AnnExpr Naam TExpr ->
                  AnnExpr Naam TExpr

tcSubstAnnTree phi tree = tcMapAnnExpr (tcSub_type phi) tree


-- ======================================================--
--
tcTreeToEnv :: AnnExpr Naam TExpr ->
               TypeEnv

tcTreeToEnv tree
   = t2e tree
     where
        t2e (nodeType, node) = t2e' node

        t2e' (AVar v) = []
        t2e' (ANum n) = []
        t2e' (AConstr c) = []
        t2e' (AAp ae1 ae2) = (t2e ae1) ++ (t2e ae2)
        t2e' (ALam cs e) = t2e e
        t2e' (ALet rf dl me)
           = (concat (map aFN dl)) ++ (t2e me)
        t2e' (ACase sw alts)
           = (t2e sw) ++ (concat (map (t2e.second.second) alts))

        aFN (naam, (tijp, body))
          = (naam, tijp):(t2e' body)



-- ======================================================--
--
tcShowtExpr :: TExpr ->
               [Char]

tcShowtExpr t
   = pretty' False t
     where
       pretty' b (TVar tvname) = [' ', toEnum (96+(lookup tvname tvdict))]
       pretty' b (TCons "int" []) = " int"
       pretty' b (TCons "bool" []) = " bool"
       pretty' b (TCons "char" []) = " char"
       pretty' True (TArr t1 t2)
	   = " (" ++ (pretty' True t1) ++ " -> " ++
	     (pretty' False t2) ++ ")"
       pretty' False (TArr t1 t2)
	   = (pretty' True t1) ++ " -> " ++
	     (pretty' False t2)
       pretty' b (TCons notArrow cl)
	   = " (" ++ notArrow ++
	      concat (map (pretty' True) cl) ++ ")"
       lookup tvname []
          = panic "tcShowtExpr: Type name lookup failed"
       lookup tvname (t:ts) | t==tvname = 1
			    | otherwise = 1 + (lookup tvname ts)
       tvdict = nub (tvdict' t)
       tvdict' (TVar t) = [t]
       tvdict' (TCons c ts) = concat (map tvdict' ts)
       tvdict' (TArr t1 t2) = tvdict' t1 ++ tvdict' t2


-- ======================================================--
--
tcPretty :: (Naam, TExpr) ->
            [Char]

tcPretty (naam, tipe)
   = "\n   " ++ (ljustify 25 (naam ++ " :: ")) ++
            (tcShowtExpr tipe)


-- ======================================================--
tcCheck :: TcTypeEnv ->
           TypeNameSupply ->
           AtomicProgram ->
           ([Char],  Reply (AnnExpr Naam TExpr, TypeEnv) Message)

tcCheck baseTypes ns (tdefs, expr)
   = if good tcResult
         then (fullEnvWords,  Ok (rootTree, fullEnv))
         else ("",            Fail "No type")
     where
        tcResult = tc (tdefs++builtInTypes)
                   (baseTypes++finalConstrTypes) finalNs expr

        good (Ok x) = True
        good (Fail x2) = False

        (rootSubst, rootType, annoTree) = f tcResult where f (Ok x) = x

        rootTree = tcSubstAnnTree rootSubst annoTree

        rootEnv = tcTreeToEnv rootTree

        fullEnv = rootEnv ++ map f finalConstrTypes
                  where
                     f (naam, (Scheme vs t)) = (naam, t)

        fullEnvWords = concat (map tcPretty fullEnv)

        (finalNs, constrTypes) =
           mapAccuml tcConstrTypeSchemes ns (tdefs++builtInTypes)
        finalConstrTypes = concat constrTypes

        builtInTypes
           = [ ("bool", [], [("True", []), ("False", [])]) ]



-- ==========================================================--
-- === 9.2 Representation of type expressions             ===--
-- ==========================================================--

-- ======================================================--
--tcArrow :: TExpr ->
--           TExpr ->
--           TExpr
--
--tcArrow t1 t2 = TArr t1 t2



-- ======================================================--
tcInt :: TExpr

tcInt = TCons "int" []



-- ======================================================--
tcBool :: TExpr

tcBool = TCons "bool" []



-- ======================================================--
tcTvars_in :: TExpr ->
              [TVName]

tcTvars_in t = tvars_in' t []
               where
                  tvars_in' (TVar x) l = x:l
                  tvars_in' (TCons y ts) l = foldr tvars_in' l ts
                  tvars_in' (TArr t1 t2) l = tvars_in' t1 (tvars_in' t2 l)


-- ==========================================================--
-- === 9.41 Substitutions                                 ===--
-- ==========================================================--

-- ======================================================--
tcApply_sub :: Subst ->
               TVName ->
               TExpr

tcApply_sub phi tvn
   = if TVar tvn == lookUpResult
        then TVar tvn
        else tcSub_type phi lookUpResult
     where
        lookUpResult = utLookupDef phi tvn (TVar tvn)


-- ======================================================--
tcSub_type :: Subst ->
              TExpr ->
              TExpr

tcSub_type phi (TVar tvn) = tcApply_sub phi tvn

tcSub_type phi (TCons tcn ts) = TCons tcn (map (tcSub_type phi) ts)

tcSub_type phi (TArr t1 t2) = TArr (tcSub_type phi t1) (tcSub_type phi t2)


-- ======================================================--
tcScomp :: Subst ->
           Subst ->
           Subst

tcScomp sub2 sub1 = sub1 ++ sub2



-- ======================================================--
tcId_subst :: Subst

tcId_subst = []



-- ======================================================--
tcDelta :: TVName ->
           TExpr ->
           Subst
-- all TVar -> TVar substitutions lead downhill
tcDelta tvn (TVar tvn2)
   | tvn == tvn2   = []
   | tvn >  tvn2   = [(tvn, TVar tvn2)]
   | tvn <  tvn2   = [(tvn2, TVar tvn)]

tcDelta tvn non_var_texpr = [(tvn, non_var_texpr)]


-- ==========================================================--
-- === 9.42 Unification                                   ===--
-- ==========================================================--

-- ======================================================--
tcExtend :: Subst ->
            TVName ->
            TExpr ->
            Reply Subst Message

tcExtend phi tvn t
    | t == TVar tvn
    = Ok phi
    | tvn `notElem` (tcTvars_in t)
    = Ok ((tcDelta tvn t) `tcScomp` phi)
    | otherwise
    = myFail
         (   "Type error in source program:\n\n"         ++
             "Circular substitution:\n      "            ++
	      tcShowtExpr (TVar tvn)                     ++
              "\n   going to\n"                          ++
	      "      "                                   ++
              tcShowtExpr t                              ++
              "\n")



-- ======================================================--
tcUnify :: Subst ->
           (TExpr, TExpr) ->
           Reply Subst Message

tcUnify phi (TVar tvn, t)
  = if phitvn == TVar tvn
       then tcExtend phi tvn phit
       else tcUnify phi (phitvn, phit)
     where
	phitvn = tcApply_sub phi tvn
	phit = tcSub_type phi t

tcUnify phi (p@(TCons _ _), q@(TVar _))
   = tcUnify phi (q, p)

tcUnify phi (p@(TArr _ _), q@(TVar _))
   = tcUnify phi (q, p)

tcUnify phi (TArr t1 t2, TArr t1' t2')
   = tcUnifyl phi [(t1, t1'), (t2, t2')]

tcUnify phi (TCons tcn ts, TCons tcn' ts')
   | tcn == tcn'
   = tcUnifyl phi (ts `zip` ts')

tcUnify phi (t1, t2)
   = myFail
        (   "Type error in source program:\n\n"          ++
            "Cannot unify\n      "                       ++
            tcShowtExpr t1                               ++
            "\n   with\n      "                          ++
            tcShowtExpr t2                               ++
            "\n"
        )



-- ======================================================--
tcUnifyl :: Subst ->
            [(TExpr, TExpr)] ->
            Reply Subst Message

tcUnifyl phi eqns
   = foldr unify' (Ok phi) eqns
     where
	unify' eqn (Ok phi) = tcUnify phi eqn
	unify' eqn (Fail m) = Fail m



-- ==========================================================--
-- === 9.42.2 Merging of substitutions                    ===--
-- ==========================================================--

-- ======================================================--
tcMergeSubs :: Subst ->
               Subst

tcMergeSubs phi
   = if newBinds == []
        then unifiedOlds
        else tcMergeSubs (unifiedOlds ++ newBinds)
     where
        (newBinds, unifiedOlds) = tcMergeSubsMain phi



-- ======================================================--
tcMergeSubsMain :: Subst ->
                   (Subst, Subst)   -- pair of new binds, unified olds

tcMergeSubsMain phi
   = (concat newUnifiersChecked,
      zip oldVars (tcOldUnified newUnifiersChecked oldGroups))
     where
        oldVars = nub (utDomain phi)
        oldGroups = map (utLookupAll phi) oldVars
        newUnifiers = map (tcUnifySet tcId_subst) oldGroups
        newUnifiersChecked = map tcCheckUnifier newUnifiers



-- ======================================================--
tcCheckUnifier :: Reply Subst Message -> Subst

tcCheckUnifier (Ok r) = r
tcCheckUnifier (Fail m)
   = panic ("tcCheckUnifier: " ++ m)



-- ======================================================--
tcOldUnified :: [Subst] -> [[TExpr]] -> [TExpr]

tcOldUnified [] [] = []
tcOldUnified (u:us) (og:ogs)
      = (tcSub_type u (head og)): tcOldUnified us ogs


-- ==========================================================--
-- === 9.5 Keeping track of types                         ===--
-- ==========================================================--

-- ======================================================--
tcUnknowns_scheme :: TypeScheme ->
                     [TVName]

tcUnknowns_scheme (Scheme scvs t) = tcTvars_in t `tcBar` scvs



-- ======================================================--
tcBar :: (Eq a) => [a] ->
                   [a] ->
                   [a]

tcBar xs ys = [ x | x <- xs,  not (x `elem` ys)]



-- ======================================================--
tcSub_scheme :: Subst ->
                TypeScheme ->
                TypeScheme

tcSub_scheme phi (Scheme scvs t)
    = Scheme scvs (tcSub_type (tcExclude phi scvs) t)
      where
         tcExclude phi scvs = [(n,e) | (n,e) <- phi,  not (n `elem` scvs)]



-- ==========================================================--
-- === 9.53 Association lists                             ===--
-- ==========================================================--

-- ======================================================--
tcCharVal :: AList Naam b -> Naam -> b

tcCharVal al k
   = utLookupDef al k (panic ("tcCharVal: no such variable: " ++ k))


-- ======================================================--
tcUnknowns_te :: TcTypeEnv ->
                 [TVName]

tcUnknowns_te gamma = concat (map tcUnknowns_scheme (utRange gamma))



-- ======================================================--
tcSub_te :: Subst ->
            TcTypeEnv ->
            TcTypeEnv

tcSub_te phi gamma = [(x, tcSub_scheme phi st) | (x, st) <- gamma]


-- ==========================================================--
-- === 9.6 New variables                                  ===--
-- ==========================================================--

-- ======================================================--
tcNext_name :: TypeNameSupply ->
               TVName

tcNext_name ns@(f, s) = ns



-- ======================================================--
tcDeplete :: TypeNameSupply ->
             TypeNameSupply

tcDeplete (f, s) = (f, tcNSSucc s)



-- ======================================================--
tcSplit :: TypeNameSupply ->
           (TypeNameSupply, TypeNameSupply)

tcSplit (f, s) = ((f2, [0]), (tcNSSucc f2, [0]))
                 where f2 = tcNSDouble f



-- ======================================================--
tcName_sequence :: TypeNameSupply ->
                   [TVName]

tcName_sequence ns = tcNext_name ns: tcName_sequence (tcDeplete ns)


-- ======================================================--
tcNSSucc :: [Int] ->
            [Int]

tcNSSucc []     = [1]
tcNSSucc (n:ns) | n < tcNSslimit  = n+1: ns
                | otherwise       = 0: tcNSSucc ns


-- ======================================================--
tcNSDouble :: [Int] ->
              [Int]

tcNSDouble []   = []
tcNSDouble (n:ns)
    = 2*n': ns'
       where n' | n > tcNSdlimit  = n - tcNSdlimit
		| otherwise       = n
	     ns' | n' == n    = tcNSDouble ns
		 | otherwise  = tcNSSucc (tcNSDouble ns)


tcNSdlimit :: Int
tcNSdlimit = 2^30

tcNSslimit :: Int
tcNSslimit = tcNSdlimit + (tcNSdlimit - 1)


-- ==========================================================--
-- === 9.7 The type-checker                               ===--
-- ==========================================================--


-- ======================================================--
tc :: [TypeDef] ->
      TcTypeEnv ->
      TypeNameSupply ->
      CExpr ->
      Reply TypeInfo Message

tc tds gamma ns (ENum n)
   = Ok (tcId_subst, TCons "int" [], (TCons "int" [], ANum n))

tc tds gamma ns (EVar x)
   = tcvar tds gamma ns x

tc tds gamma ns (EConstr c)
   = tcvar tds gamma ns c

tc tds gamma ns (EAp e1 e2)
   = tcap tds gamma ns e1 e2

tc tds gamma ns (ELam [] e)
   = tc tds gamma ns e
tc tds gamma ns (ELam [x] e)
   = tclambda tds gamma ns x e
tc tds gamma ns (ELam (x:y:xs) e)
   = tclambda tds gamma ns x (ELam (y:xs) e)

tc tds gamma ns (ELet recursive dl e)
   = if not recursive
        then tclet tds gamma ns xs es e
        else tcletrec tds gamma ns xs es e
     where
       (xs, es) = unzip2 dl

tc tds gamma ns (ECase switch alts)
   = tccase tds gamma ns switch constructors arglists exprs
     where
        (constructors, alters) = unzip2 alts
        (arglists, exprs) = unzip2 alters


-- ==========================================================--
-- === 0.00 Type-checking case-expressions                ===--
-- ==========================================================--

tcConstrTypeSchemes :: TypeNameSupply ->
                       TypeDef ->
                       (TypeNameSupply, AList Naam TypeScheme)

tcConstrTypeSchemes ns (tn, stvs, cal)
   = (finalNameSupply, map2nd enScheme cAltsCurried)
     where
        -- associates new type vars with each poly var
        -- in the type
        newTVs = tcNewTypeVars (tn, stvs, cal) ns

        -- the actual type variables themselves
        tVs = map second newTVs

        -- the types of the constructor functions
        cAltsCurried = map2nd (foldr TArr tdSignature) cAltsXLated
        cAltsXLated = map2nd (map (tcTDefSubst newTVs)) cal
        tdSignature = TCons tn (map TVar tVs)
        enScheme texp = Scheme ((nub.tcTvars_in) texp) texp

        -- the revised name supply
        finalNameSupply = applyNtimes ( length tVs + 2) tcDeplete ns

        -- apply a function n times to an arg
        applyNtimes n func arg
           | n ==0       = arg
           | otherwise   = applyNtimes (n-1) func (func arg)



-- ======================================================--
--
tccase :: [TypeDef] ->         -- constructor type definitions
          TcTypeEnv ->         -- current type bindings
          TypeNameSupply ->    -- name supply
          CExpr ->             -- switch expression
          [Naam] ->            -- constructors
          [[Naam]] ->          -- argument lists
          [CExpr] ->           -- resulting expressions
          Reply TypeInfo Message


tccase tds gamma ns sw cs als res
-- get the type definition in use, & an association of
-- variables therein to type vars & pass
-- Also, reorder the argument lists
-- and resulting expressions so as to reflect the
-- sequence of constructors in the definition
 = if length tdCNames /=  length (nub cs)
      then  myFail
            "Error in source program: missing alternatives in CASE"
      else tccase1 tds gamma ns1 sw reOals reOres newTVs tdInUse
     where
        tdInUse = tcGetTypeDef tds cs
        newTVs = tcNewTypeVars tdInUse ns2
        (ns1, ns2) = tcSplit ns
        merge = zip cs (zip als res)
        tdCNames = map first (tcK33 tdInUse)
        (reOals, reOres) = unzip2 (tcReorder tdCNames merge)



-- ======================================================--
--
tcReorder :: [Naam] -> [(Naam,b)] -> [b]

tcReorder []     uol =  []
tcReorder (k:ks) uol
   = (utLookupDef uol k
        (myFail
            ("Error in source program: undeclared constructor '" ++ k ++
               "' in CASE") ) )
        : tcReorder ks uol


-- ======================================================--
-- Projection functions and similar rubbish.
tcDeOksel (Ok x) = x
tcDeOksel (Fail m) = panic ("tcDeOkSel: " ++ m)
tcOk13sel (Ok (a, b, c)) = a
tcOk13sel (Fail m) = panic ("tcOk13sel: " ++ m)
tcOk23sel (Ok (a, b, c)) = b
tcOk23sel (Fail m) = panic ("tcOk23sel: " ++ m)
tcOk33sel (Ok (a, b, c)) = c
tcOk33sel (Fail m) = panic ("tcOk33sel: " ++ m)
tcK31sel (a, b, c) = a
tcK33 (a,b,c) = c



-- ======================================================--
--
tccase1 :: [TypeDef] ->
           TcTypeEnv ->
           TypeNameSupply ->
           CExpr ->
           [[Naam]] ->
           [CExpr] ->
           AList Naam TVName ->
           TypeDef ->
           Reply TypeInfo Message

tccase1 tds gamma ns sw reOals reOres newTVs tdInUse
-- calculate all the gammas for the RHS's
-- call tc for each RHS, so as to gather all the
-- sigmas and types for each RHS, then pass on
   = tccase2 tds gamma ns2 sw reOals newTVs tdInUse rhsTcs
     where
        rhsGammas = tcGetAllGammas newTVs (tcK33 tdInUse) reOals
        rhsTcs = rhsTc1 ns1 rhsGammas reOres
        rhsTc1 nsl []     []     = []
        rhsTc1 nsl (g:gs) (r:rs)
           = tc tds (g++gamma) nsl1 r : rhsTc1 nsl2 gs rs
             where (nsl1, nsl2) = tcSplit nsl
        (ns1, ns2) = tcSplit ns


-- ======================================================--
--
tccase2 :: [TypeDef] ->
           TcTypeEnv ->
           TypeNameSupply ->
           CExpr ->
           [[Naam]] ->
           AList Naam TVName ->
           TypeDef ->
           [Reply TypeInfo Message] ->
           Reply TypeInfo Message

tccase2 tds gamma ns sw reOals newTVs tdInUse rhsTcs
-- get the unifiers for T1 to Tk and hence the unifier for all
-- type variables in the type definition.  Also compute the
-- unifier of the result types.
   = tccase3 tds gamma ns sw reOals newTVs tdInUse rhsTcs
             phi_1_to_n tau_1_to_n phi_rhs
     where
        phi_1_to_n = map tcOk13sel rhsTcs
        tau_1_to_n = map tcOk23sel rhsTcs
        phi_rhs = tcDeOksel (tcUnifySet tcId_subst tau_1_to_n)



-- ======================================================--
--
tccase3 :: [TypeDef] ->                    -- tds
           TcTypeEnv ->                    -- gamma
           TypeNameSupply ->               -- ns
           CExpr ->                        -- sw
           [[Naam]] ->                     -- reOals
           AList Naam TVName ->            -- newTVs
           TypeDef ->                      -- tdInUse
           [Reply TypeInfo Message] ->     -- rhsTcs
           [Subst] ->                      -- phi_1_to_n
           [TExpr] ->                      -- tau_1_to_n
           Subst ->                        -- phi_rhs
           Reply TypeInfo Message

tccase3 tds gamma ns sw reOals newTVs tdInUse rhsTcs
        phi_1_to_n tau_1_to_n phi_rhs
-- make up substitutions for each of the unknown tvars
-- merge the substitutions into one
-- apply the substitution to the typedef's signature to get the
-- most general allowable input type
-- call tc to get the type of the switch expression
-- check that this is an instance of the deduced input type
-- gather the new bindings from the RHSs and switch expression
-- return Ok (the big substitution, the result type, gathered bindings)
   = Ok (phi_Big, tau_final,
            (tau_final, ACase tree_s
                        (zip tdCNames (zip reOals annotatedRHSs))))
     where
        phi_sTau_sTree_s = tc tds gamma ns sw
        phi_s  = tcOk13sel phi_sTau_sTree_s
        tau_s  = tcOk23sel phi_sTau_sTree_s
        tree_s = tcOk33sel phi_sTau_sTree_s

        phi = tcMergeSubs (concat phi_1_to_n ++ phi_rhs ++ phi_s)

        tau_lhs = tcSub_type phi tdSignature

        phi_lhs = tcUnify tcId_subst (tau_lhs, tau_s) -- reverse these?

        phi_Big = tcMergeSubs (tcDeOksel phi_lhs ++ phi)

        tau_final = tcSub_type phi_Big (head (map tcOk23sel rhsTcs))

        annotatedRHSs = map tcOk33sel rhsTcs
        tVs = map second newTVs
        tdSignature = TCons (tcK31sel tdInUse) (map TVar tVs)
        tdCNames = map first (tcK33 tdInUse)


-- ======================================================--
--
tcUnifySet :: Subst ->
              [TExpr] ->
              Reply Subst Message

tcUnifySet sub (e1:[]) = Ok sub
tcUnifySet sub (e1:e2:[])
   = tcUnify sub (e1, e2)
tcUnifySet sub (e1:e2:e3:es)
   = tcUnifySet newSub (e2:e3:es)
     where
        newSub = tcDeOksel (tcUnify sub (e1, e2))


-- ======================================================--
--
tcNewTypeVars :: TypeDef ->
                 TypeNameSupply ->
                 AList Naam TVName

tcNewTypeVars (t, vl, c) ns = zip vl (tcName_sequence ns)



-- ======================================================--
--
tcGetGammaN :: AList Naam TVName ->
               ConstrAlt ->
               [Naam] ->
               AList Naam TypeScheme

tcGetGammaN tvl (cname, cal) cparams
   = zip cparams (map (Scheme [] . tcTDefSubst tvl) cal)



-- ======================================================--
--
tcTDefSubst :: AList Naam TVName ->
               TDefExpr ->
               TExpr

tcTDefSubst nameMap (TDefVar n)
   = f result
     where
        f (Just tvn) = TVar tvn
        f Nothing    = TCons n []
        result = utLookup nameMap n

tcTDefSubst nameMap (TDefCons c al)
   = TCons c (map (tcTDefSubst nameMap) al)


-- ======================================================--
--
tcGetAllGammas :: AList Naam TVName ->
                  [ConstrAlt] ->
                  [[Naam]] ->
                  [AList Naam TypeScheme]

tcGetAllGammas tvl []           [] = []
-- note param lists cparamss must be ordered in
-- accordance with calts
tcGetAllGammas tvl (calt:calts) (cparams:cparamss) =
      tcGetGammaN tvl calt cparams :
         tcGetAllGammas tvl calts cparamss


-- ======================================================--
--
tcGetTypeDef :: [TypeDef] ->    -- type definitions
                [Naam] ->       -- list of constructors used here
                TypeDef

tcGetTypeDef tds cs
   = if length tdefset == 0
        then myFail "Undeclared constructors in use"
     else if length tdefset > 1
        then myFail "CASE expression contains mixed constructors"
     else head tdefset
     where
        tdefset = nub
                  [ (tname, ftvs, cl) |
                    (tname, ftvs, cl) <- tds,
                    usedc <- cs,
                    usedc `elem` (map first cl) ]


-- ==========================================================--
-- === 9.71 Type-checking lists of expressions            ===--
-- ==========================================================--

-- ======================================================--
--
tcl :: [TypeDef] ->
       TcTypeEnv     ->
       TypeNameSupply  ->
       [CExpr]       ->
       Reply (Subst, [TExpr], [AnnExpr Naam TExpr]) Message

tcl tds gamma ns []
   = Ok (tcId_subst, [], [])
tcl tds gamma ns (e:es)
   = tcl1 tds gamma ns0 es (tc tds gamma ns1 e)
     where
        (ns0, ns1) = tcSplit ns


-- ======================================================--
--
tcl1 tds gamma ns es (Fail m) = Fail m
tcl1 tds gamma ns es (Ok (phi, t, annotatedE))
   = tcl2 phi t (tcl tds (tcSub_te phi gamma) ns es) annotatedE


-- ======================================================--
--
tcl2 phi t (Fail m) annotatedE = Fail m
tcl2 phi t (Ok (psi, ts, annotatedEs)) annotatedE
   = Ok (psi `tcScomp` phi, (tcSub_type psi t):ts,
         annotatedE:annotatedEs)


-- ==========================================================--
-- === 9.72 Type-checking variables                       ===--
-- ==========================================================--

-- ======================================================--
--
tcvar :: [TypeDef] ->
         TcTypeEnv     ->
         TypeNameSupply  ->
         Naam        ->
         Reply TypeInfo Message

tcvar tds gamma ns x = Ok (tcId_subst, finalType, (finalType, AVar x))
                       where
                          scheme = tcCharVal gamma x
                          finalType = tcNewinstance ns scheme


-- ======================================================--
--
tcNewinstance :: TypeNameSupply ->
                 TypeScheme ->
                 TExpr

tcNewinstance ns (Scheme scvs t) = tcSub_type phi t
                                   where
                                      al  = scvs `zip` (tcName_sequence ns)
                                      phi = tcAl_to_subst al


-- ======================================================--
--
tcAl_to_subst :: AList TVName TVName ->
                 Subst

tcAl_to_subst al = map2nd TVar al


-- ==========================================================--
-- === 9.73 Type-checking applications                    ===--
-- ==========================================================--

-- ======================================================--
--
tcap :: [TypeDef] ->
        TcTypeEnv     ->
        TypeNameSupply  ->
        CExpr         ->
        CExpr         ->
        Reply TypeInfo Message

tcap tds gamma ns e1 e2 = tcap1 tvn (tcl tds gamma ns' [e1, e2])
                          where
                             tvn = tcNext_name ns
                             ns' = tcDeplete ns


-- ======================================================--
--
tcap1 tvn (Fail m)
   = Fail m
tcap1 tvn (Ok (phi, [t1, t2], [ae1, ae2]))
   = tcap2 tvn (tcUnify phi (t1, t2 `TArr` (TVar tvn))) [ae1, ae2]


-- ======================================================--
--
tcap2 tvn (Fail m) [ae1, ae2]
   = Fail m
tcap2 tvn (Ok phi) [ae1, ae2]
   = Ok (phi, finalType, (finalType, AAp ae1 ae2))
     where
        finalType = tcApply_sub phi tvn


-- ==========================================================--
-- === 9.74 Type-checking lambda abstractions             ===--
-- ==========================================================--

-- ======================================================--
--
tclambda :: [TypeDef] ->
            TcTypeEnv     ->
            TypeNameSupply  ->
            Naam        ->
            CExpr         ->
            Reply TypeInfo Message

tclambda tds gamma ns x e = tclambda1 tvn x (tc tds gamma' ns' e)
                            where
                               ns' = tcDeplete ns
                               gamma' = tcNew_bvar (x, tvn): gamma
                               tvn = tcNext_name ns


-- ======================================================--
--
tclambda1 tvn x (Fail m) = Fail m

tclambda1 tvn x (Ok (phi, t, annotatedE)) =
   Ok (phi, finalType, (finalType, ALam [x] annotatedE))
   where
      finalType = (tcApply_sub phi tvn) `TArr` t


-- ======================================================--
--
tcNew_bvar (x, tvn) = (x, Scheme [] (TVar tvn))


-- ==========================================================--
-- === 9.75 Type-checking let-expressions                 ===--
-- ==========================================================--

-- ======================================================--
--
tclet :: [TypeDef] ->
         TcTypeEnv     ->
         TypeNameSupply  ->
         [Naam]       ->
         [CExpr]       ->
         CExpr         ->
         Reply TypeInfo Message

tclet tds gamma ns xs es e
   = tclet1 tds gamma ns0 xs e rhsTypes
     where
        (ns0, ns1) = tcSplit ns
        rhsTypes = tcl tds gamma ns1 es


-- ======================================================--
--
tclet1 tds gamma ns xs e (Fail m) = Fail m

tclet1 tds gamma ns xs e (Ok (phi, ts, rhsAnnExprs))
   = tclet2 phi xs False (tc tds gamma'' ns1 e) rhsAnnExprs
     where
        gamma'' = tcAdd_decls gamma' ns0 xs ts
        gamma'  = tcSub_te phi gamma
        (ns0, ns1) = tcSplit ns


-- ======================================================--
--
tclet2 phi xs recFlag (Fail m) rhsAnnExprs = Fail m

tclet2 phi xs recFlag (Ok (phi', t, annotatedE)) rhsAnnExprs
   = Ok (phi' `tcScomp` phi, t, (t, ALet recFlag (zip xs rhsAnnExprs) annotatedE))


-- ======================================================--
--
tcAdd_decls :: TcTypeEnv     ->
               TypeNameSupply  ->
               [Naam]       ->
               [TExpr]   ->
               TcTypeEnv

tcAdd_decls gamma ns xs ts = (xs `zip` schemes) ++ gamma
                             where
                                schemes = map (tcGenbar unknowns ns) ts
                                unknowns = tcUnknowns_te gamma


-- ======================================================--
--
tcGenbar unknowns ns t = Scheme (map second al) t'
                         where
                            al = scvs `zip` (tcName_sequence ns)
                            scvs = (nub (tcTvars_in t)) `tcBar` unknowns
                            t' = tcSub_type (tcAl_to_subst al) t



-- ==========================================================--
-- === 9.76 Type-checking letrec-expressions              ===--
-- ==========================================================--

-- ======================================================--
--
tcletrec :: [TypeDef] ->
            TcTypeEnv     ->
            TypeNameSupply  ->
            [Naam]       ->
            [CExpr]       ->
            CExpr         ->
            Reply TypeInfo Message

tcletrec tds gamma ns xs es e
   = tcletrec1 tds gamma ns0 xs nbvs e
               (tcl tds (nbvs ++ gamma) ns1 es)
     where
        (ns0, ns') = tcSplit ns
        (ns1, ns2) = tcSplit ns'
        nbvs = tcNew_bvars xs ns2


-- ======================================================--
--
tcNew_bvars xs ns = map tcNew_bvar (xs `zip` (tcName_sequence ns))



-- ======================================================--
--
tcletrec1 tds gamma ns xs nbvs e (Fail m) = (Fail m)

tcletrec1 tds gamma ns xs nbvs e (Ok (phi, ts, rhsAnnExprs))
   = tcletrec2 tds gamma' ns xs nbvs' e (tcUnifyl phi (ts `zip` ts')) rhsAnnExprs
     where
        ts' = map tcOld_bvar nbvs'
        nbvs' = tcSub_te phi nbvs
        gamma' = tcSub_te phi gamma


-- ======================================================--
--
tcOld_bvar (x, Scheme [] t) = t


-- ======================================================--
--
tcletrec2 tds gamma ns xs nbvs e (Fail m) rhsAnnExprs = (Fail m)

tcletrec2 tds gamma ns xs nbvs e (Ok phi) rhsAnnExprs
   = tclet2 phi xs True (tc tds gamma'' ns1 e) rhsAnnExprs
     where
        ts = map tcOld_bvar nbvs'
        nbvs' = tcSub_te phi nbvs
        gamma' = tcSub_te phi gamma
        gamma'' = tcAdd_decls gamma' ns0 (map first nbvs) ts
        (ns0, ns1) = tcSplit ns
        subnames = map first nbvs


-- ==========================================================--
-- === End                               TypeCheck5.m (1) ===--
-- ==========================================================--
