packages feed

alms-0.6.0: src/Statics/InstGen.hs

-- | Instantiation and generalization
module Statics.InstGen (
  -- * Instantiation operations
  instantiate, instantiateNeg, instAllEx, instAll, instEx,
  -- * Type-matching instantiation
  splitCon, splitRow,
  -- * Conditional generalization/instantiation
  Request(..), MkRequest(..),
  maybeGen, maybeInstGen, checkEscapingEx,
  -- * Instantiating type annotation variables
  withTVsOf,
) where

import Util
import qualified AST
import AST.TypeAnnotation
import qualified Syntax.Ppr as Ppr
import qualified Type.Rank as Rank
import Type
import Statics.Env
import Statics.Error
import Statics.Constraint

import Prelude ()
import qualified Data.Map as M

---
--- INSTANTIATION OPERATIONS
---

-- | To instantiate a prenex quantifier with fresh type variables.
instantiate ∷ MonadConstraint tv r m ⇒ Type tv → m (Type tv)
instantiate = instAllEx True True

-- | To instantiate a prenex quantifier with fresh type variables, in
--   a negative position
instantiateNeg ∷ MonadConstraint tv r m ⇒ Type tv → m (Type tv)
instantiateNeg = instAllEx False False

-- | Instantiate the outermost universal and existential quantifiers
--   at the given polarities.
instAllEx ∷ MonadConstraint tv r m ⇒ Bool → Bool → Type tv → m (Type tv)
instAllEx upos epos = subst >=> instEx epos >=> instAll upos

-- | Instantiate an outer universal quantifier.
--   PRECONDITION: σ is fully substituted.
instAll ∷ MonadConstraint tv r m ⇒ Bool → Type tv → m (Type tv)
instAll pos (TyQu Forall αqs σ) = do
  traceN 4 ("instAll/∀", pos, αqs, σ)
  instGeneric 0 (determineFlavor Forall pos) αqs σ
instAll pos (TyQu Exists αqs (TyQu Forall βqs σ)) = do
  traceN 4 ("instAll/∃∀", pos, αqs, βqs, σ)
  TyQu Exists αqs <$> instGeneric 1 (determineFlavor Forall pos) βqs σ
instAll _ σ = return σ

-- | Instantiate an outer existential quantifier.
--   PRECONDITION: σ is fully substituted.
instEx ∷ MonadConstraint tv r m ⇒ Bool → Type tv → m (Type tv)
instEx pos (TyQu Exists αqs σ) = do
  traceN 4 ("instEx", pos, αqs, σ)
  instGeneric 0 (determineFlavor Exists pos) αqs σ
instEx _ σ = return σ

-- | Instantiate type variables and use them to open a type, given
--   a flavor and list of qualifier literal bounds.  Along with the
--   instantiated type, returns any new type variables.
--   PRECONDITION: σ is fully substituted.
instGeneric ∷ MonadConstraint tv r m ⇒
              Int → Flavor → [(a, QLit)] → Type tv →
              m (Type tv)
instGeneric k flav αqs σ = do
  αs ← zipWithM (newTV' <$$> (,flav,) . snd) αqs (inferKinds σ)
  return (openTy k (fvTy <$> αs) σ)

-- | What kind of type variable to create when instantiating
--   a given quantifier in a given polarity:
determineFlavor ∷ Quant → Bool → Flavor
determineFlavor Forall True  = Universal
determineFlavor Forall False = Skolem
determineFlavor Exists True  = Existential
determineFlavor Exists False = Universal

---
--- TYPE-MATCHING INSTANTIATION
---

-- | Given (maybe) a type, and a type constructor,
--   return a list of (maybe) parameter types and returns
--   a list of any new type variables.  The output types are @Nothing@
--   iff the input type is @Nothign@.  If the input type is a type
--   variable, it gets unified with the requested shape over fresh type
--   variables using the given type relation.
--   PRECONDITION: σ is fully substituted.
{-
Instantiates both ∀ and ∃ to univars:
  (λx.x) : A → A          ⇒       (λ(x:A). (x:A)) : A → A
  (λx.x) : ∀α. α → α      ⇒       (λ(x:β). (x:β)) : ∀α. α → α
  (λx.x) : ∀α. C α → C α  ⇒       (λ(x:C β). (x:C β)) : ∀α. C α → C α
  (λx.x) : ∃α. α → α      ⇒       (λ(x:β). (x:β)) : ∃α. α → α
  (λx.x) : ∃α. C α → C α  ⇒       (λ(x:C β). (x:C β)) : ∃α. C α → C α
-}
splitCon ∷ MonadConstraint tv r m ⇒
           -- | Type to split
           Maybe (Type tv) →
           -- | Expected type
           TyCon →
           m ([Maybe (Type tv)])
splitCon Nothing  tc = return (Nothing <$ tcArity tc)
splitCon (Just σ) tc = do
  traceN 4 ("splitCon", σ, tc)
  ρ ← instAllEx True False σ
  loop ρ
  where
  loop ρ = case ρ of
    TyApp tc' σs     | tc == tc'
      → return (Just <$> σs)
                     | Next ρ' ← headReduceType ρ
      → loop ρ'
    _ → return (Nothing <$ tcArity tc)

-- | Like 'splitCon', but for rows.
--   PRECONDITION: σ is fully substituted.
splitRow ∷ MonadConstraint tv r m ⇒
           -- | The type to split
           Maybe (Type tv) →
           -- | The row label that we're expecting
           RowLabel →
           m (Maybe (Type tv), Maybe (Type tv))
splitRow Nothing  _   = return (Nothing, Nothing)
splitRow (Just σ) lab = do
  traceN 4 ("splitRow", σ, lab)
  ρ ← instAllEx True False σ
  loop ρ
  where
  loop ρ = case ρ of
    TyRow lab' τ1 τ2 | lab' == lab
      → return (Just τ1, Just τ2)
                     | otherwise
      → do
        (mτ1, mτ2) ← loop τ2
        return (mτ1, TyRow lab' τ1 <$> mτ2)
    _ → return (Nothing, Nothing)

---
--- CONDITIONAL GENERALIZATION/INSTANTIATION
---

-- A system for specifying requested generalization/instantiation

-- | Used by 'infer' and helpers to specify a requested
--   generalization/instantiation state.
data Request tv
  = Request {
      -- | Request the type to have ∀ quantifiers generalized
      rqAll  ∷ !Bool,
      -- | Request the type to have ∃ quantifiers generalized
      rqEx   ∷ !Bool,
      -- | Require that the existential type variables among these
      --   be generalizable at the given ranks
      rqTVs  ∷ [(tv, Rank.Rank)],
      -- | Rank to which to generalize
      rqRank ∷ !Rank.Rank
    }

instance Ppr.Ppr tv ⇒ Ppr.Ppr (Request tv) where
  ppr φ = (if rqAll φ then Ppr.char '∀' else mempty)
          Ppr.<>
          (if rqEx φ then Ppr.char '∃' else mempty)
          Ppr.<>
          (if null (rqTVs φ)
             then mempty
             else Ppr.ppr (rqTVs φ) Ppr.<>
                  Ppr.char '/' Ppr.<> Ppr.ppr (rqRank φ))

-- | Defines a variadic function for building 'Request' states.  Minimal
--   definition: 'addToRequest'
class MkRequest r tv | r → tv where
  -- | Variadic function that constructs a 'Request' state given some
  --   number of parameters to modify it, as shown by instances below.
  request      ∷ r
  request      = addToRequest Request {
    rqAll   = False,
    rqEx    = False,
    rqTVs   = [],
    rqRank  = Rank.infinity
  }
  addToRequest ∷ Request tv → r

instance MkRequest (Request tv) tv where
  addToRequest = id

instance MkRequest r tv ⇒ MkRequest (Request tv → r) tv where
  addToRequest _ r' = addToRequest r'

instance (Tv tv, MkRequest r tv) ⇒ MkRequest (Γ tv' → [tv] → r) tv where
  addToRequest r γ αs = addToRequest r {
    rqTVs  = [(α, rank) | α ← αs, tvFlavorIs Existential α] ++ rqTVs r,
    rqRank = rank `min` rqRank r
  }
    where rank = rankΓ γ

instance MkRequest r tv ⇒ MkRequest (Rank.Rank → r) tv where
  addToRequest r rank = addToRequest r {
    rqRank = rank `min` rqRank r
  }

instance MkRequest r tv ⇒ MkRequest (Quant → r) tv where
  addToRequest r Forall = addToRequest r { rqAll = True }
  addToRequest r Exists = addToRequest r { rqEx = True }

-- 'maybeGen', 'maybeInst', and 'maybeInstGen' are the external
-- interface to conditional generalization.

-- | Generalize the requested flavors,·
maybeGen ∷ MonadConstraint tv r m ⇒
           AST.Expr R → Request tv → Γ tv → Type tv → m (Type tv)
maybeGen e0 φ γ σ = do
  let value = AST.syntacticValue e0
  traceN 4 ("maybeGen", value, φ, σ)
  checkEscapingEx φ
  (if rqAll φ then generalize value (Rank.inc (rankΓ γ)) else return)
    >=>
    (if rqEx φ then generalizeEx (rankΓ γ `min` rqRank φ) else return)
    >=>
    (if rqAll φ then generalize value (rankΓ γ) else return)
    $ σ

maybeInstGen ∷ MonadConstraint tv r m ⇒
               AST.Expr R → Request tv → Γ tv → Type tv → m (Type tv)
maybeInstGen e φ γ σ = do
  σ' ← case () of
     _ | AST.isAnnotated e → return σ
       | rqAll φ           → return σ
       | rqEx φ            → instAll True =<< subst σ
       | otherwise         → instantiate σ
  maybeGen e φ γ σ'

-- | Check for escaping existential type variables
checkEscapingEx ∷ MonadConstraint tv r m ⇒ Request tv → m ()
checkEscapingEx φ = do
  αrs ← filterM escapes (rqTVs φ)
  tassert (null αrs) $
    case αrs of
      [(α,_)] → [msg| Existential type variable $α escapes its context. |]
      _       → [msg| Existential type variables escape their context: $ul:1 |]
                  [ pprMsg α | (α, _) ← αrs ]
  where
    escapes (α, rank) = (rank >=) <$> getTVRank α

---
--- INSTANTIATING ANNOTATION TYPE VARIABLES
---

-- | Given the environments, a piece of syntax, and a continuation,
--   call the continuation with the type variable environment extended
--   with fresh type variables for any annotation type variables in the
--   piece of syntax.
withTVsOf ∷ (MonadConstraint tv r m, HasAnnotations a R) ⇒
            Δ tv → Γ tv → a → (Δ tv → m b) → m b
withTVsOf δ γ stx kont = do
  let (αs, κs) = unzip (tvsWithKinds γ stx)
  αs' ← zipWithM (\α κ → newTV' (AST.tvqual α, κ)) αs κs
  kont (δ =+= αs =:*= αs')

-- | Given an expression, get its type variables with their kinds
tvsWithKinds ∷ HasAnnotations a R ⇒
               Γ tv → a → [(AST.TyVar R, Kind)]
tvsWithKinds γ = M.toList . annotFtvMap var con cmb where
  var _      = KdType
  con n ix = case γ =..= n of
    Just tc
      | variance:_ ← drop ix (tcArity tc)
      , isQVariance variance
      → \_ → KdQual
    _ → id
  cmb KdQual KdQual = KdQual
  cmb _      _      = KdType