packages feed

glean-0.1.0.0: glean/db/Glean/Query/Vars.hs

{-
  Copyright (c) Meta Platforms, Inc. and affiliates.
  All rights reserved.

  This source code is licensed under the BSD-style license found in the
  LICENSE file in the root directory of this source tree.
-}

-- | Collecting the variables mentioned by a term
module Glean.Query.Vars (
    VarId,
    VarSet,
    WhichVars(..),
    VarsOf(..),
    vars,
    varsBound,
    varsUsed,
    Fresh(..),
    fresh,
    freshWild,
    freshWildVars,
    reWild,
    reWildGenerator,
    reWildQuery,
  ) where

import qualified Data.IntSet as IntSet
import Data.IntSet (IntSet)
import qualified Data.IntMap as IntMap
import Data.IntMap (IntMap)
import Data.List.NonEmpty (NonEmpty)

import Glean.Display
import Glean.Query.Codegen.Types
import Glean.RTS.Term
import Glean.RTS.Types as RTS

type VarId = Int
type VarSet = IntSet
type VarMap = IntMap VarId

vars :: VarsOf a => a -> VarSet
vars x = varsOf AllVars x IntSet.empty

varsBound :: VarsOf a => a -> VarSet
varsBound x = varsOf VarsBound x IntSet.empty

varsUsed :: VarsOf a => a -> VarSet
varsUsed x = varsOf VarsUsed x IntSet.empty

data WhichVars = VarsUsed | VarsBound | AllVars
  deriving Eq

class VarsOf a where
  varsOf :: WhichVars -> a -> VarSet -> VarSet

instance VarsOf Generator where
  varsOf w (FactGenerator _ key val _) r = varsOf w key $! varsOf w val r
  varsOf w (TermGenerator exp) r = varsOf w exp r
  varsOf w (DerivedFactGenerator _ key val) r = varsOf w key $! varsOf w val r
  varsOf w (ArrayElementGenerator _ arr) r = varsOf w arr r
  varsOf w (SetElementGenerator _ set) r = varsOf w set r
  varsOf w (PrimCall _ args _) r = varsOf w args r

instance (VarsOf a) => VarsOf [a] where
  varsOf w container r = foldr (varsOf w) r container

instance (VarsOf a) => VarsOf (NonEmpty a) where
  varsOf w container r = foldr (varsOf w) r container

instance VarsOf m => VarsOf (Term m) where
  varsOf w t r = case t of
    Byte{} -> r
    Nat{} -> r
    ByteArray{} -> r
    String{} -> r
    Ref x -> varsOf w x r
    Tuple xs -> varsOf w xs r
    Array xs -> varsOf w xs r
    Alt _ x -> varsOf w x r

instance VarsOf (Match () Var) where
  varsOf w m r = case m of
    MatchWild{} -> r
    MatchNever{} -> r
    MatchFid{} -> r
    MatchBind (Var _ v _) -> if w == VarsUsed then r else IntSet.insert v r
    MatchVar (Var _ v _) -> if w == VarsBound then r else IntSet.insert v r
    MatchAnd a b -> varsOf w a $! varsOf w b r
    MatchPrefix _ t -> varsOf w t r
    MatchArrayPrefix _ty pre all -> varsOf w pre (varsOf w all r)
    MatchExt{} -> r

instance VarsOf CgQuery where
  varsOf w (CgQuery head stmts) r = varsOf w head $! varsOf w stmts r

instance VarsOf CgStatement where
  varsOf w (CgStatement lhs gen) r = varsOf w lhs $! varsOf w gen r
  varsOf w (CgAllStatement (Var _ v _) expr stmts) r =
    varsOf w expr $! varsOf w stmts $!
    if w /= VarsUsed then IntSet.insert v r else r
  varsOf w (CgNegation stmts) r = varsOf w stmts r
  varsOf w (CgDisjunction stmtss) r = varsOf w stmtss r
  varsOf w (CgConditional cond then_ else_) r =
    varsOf w cond $! varsOf w then_ $! varsOf w else_ r

-- -----------------------------------------------------------------------------
-- Fresh variables

class Fresh m where
  peek :: m VarId
  alloc :: m VarId

fresh :: (Monad m, Fresh m) => Type -> m Var
fresh ty = do
  n <- alloc
  return (Var ty n Nothing)

-- -----------------------------------------------------------------------------
-- Replace wildcards with fresh variables

freshWildVars :: (Monad m, Fresh m) => (a -> m a) -> a -> m (a, VarSet)
freshWildVars freshen thing = do
  v0 <- peek
  pat' <- freshen thing
  v1 <- peek
  return (pat', IntSet.fromList [v0..v1])

-- | Instantiate all the wildcards in a pattern with fresh
-- variables. This makes the pattern usable when we substitute it, for
-- two reasons: (1) it might occur in multiple places, and we must
-- ensure that it matches the same term in all places, and (2) it
-- might occur in an expression (E where ...) where wildcards don't
-- make sense.
freshWild :: forall m . (Monad m, Fresh m) => Pat -> m Pat
freshWild pat = mapM freshWildMatch pat
  where
  freshWildMatch :: Match a Var -> m (Match a Var)
  freshWildMatch m = case m of
    MatchWild ty -> MatchBind <$> fresh ty
    MatchPrefix str rest -> MatchPrefix str <$> mapM freshWildMatch rest
    MatchArrayPrefix ty pre all ->
      MatchArrayPrefix ty
        <$> (mapM.mapM) freshWildMatch pre
        <*> mapM freshWildMatch all
    MatchNever ty -> return (MatchNever ty)
    MatchFid f -> return (MatchFid f)
    MatchBind v -> return (MatchVar v)  -- also make all variables MatchVar
    MatchVar v -> return (MatchVar v)
    MatchAnd x y -> MatchAnd <$> mapM freshWildMatch x <*> mapM freshWildMatch y
    MatchExt _ -> error "freshWildMatch"

-- | Replace unused variables with wildcards and remap variable numbers
reWild :: VarMap -> Pat -> Pat
reWild used pat = fmap reWildMatch pat
  where
  reWildMatch :: Match () Var -> Match () Var
  reWildMatch m = case m of
    MatchWild ty -> MatchWild ty
    MatchBind (Var ty n x) -> case IntMap.lookup n used of
      Nothing -> MatchWild ty
      Just new -> MatchBind (Var ty new x)
    MatchVar (Var ty n x) -> case IntMap.lookup n used of
      Nothing -> error $ "reWild: " <> show (displayVerbose m)
      Just new -> MatchVar (Var ty new x)
    MatchPrefix str rest -> MatchPrefix str (fmap reWildMatch rest)
    MatchArrayPrefix ty pre all ->
      MatchArrayPrefix ty ((fmap . fmap) reWildMatch pre) (fmap reWildMatch all)
    MatchAnd x y -> MatchAnd (fmap reWildMatch x) (fmap reWildMatch y)
    MatchNever ty -> MatchNever ty
    MatchFid f -> MatchFid f
    MatchExt _ -> error "reWild"

reWildGenerator :: VarMap -> Generator -> Generator
reWildGenerator used gen = case gen of
  FactGenerator pid key val sec ->
    FactGenerator pid (reWild used key) (reWild used val) sec
  TermGenerator t -> TermGenerator (reWild used t)
  DerivedFactGenerator pid key val ->
    DerivedFactGenerator pid (reWild used key) (reWild used val)
  ArrayElementGenerator ty exp ->
    ArrayElementGenerator ty (reWild used exp)
  SetElementGenerator ty exp ->
    SetElementGenerator ty (reWild used exp)
  PrimCall op args ty ->
    PrimCall op (map (reWild used) args) ty

reWildStatement :: VarMap -> CgStatement -> CgStatement
reWildStatement used (CgStatement lhs rhs) =
  CgStatement (reWild used lhs) (reWildGenerator used rhs)
reWildStatement used s@(CgAllStatement (Var ty n x) expr stmts) =
  case IntMap.lookup n used of
    Nothing -> error $
      "reWildStatement: var " <> show n <>
      " not in scope in " <> show (displayVerbose s) <>
      "\nVarMap: " <> show used
    Just new ->
      CgAllStatement
        (Var ty new x) (reWild used expr)
        (map (reWildStatement used) stmts)
reWildStatement used (CgNegation stmts) =
  CgNegation (map (reWildStatement used) stmts)
reWildStatement used (CgDisjunction stmtss) =
  CgDisjunction (map (map (reWildStatement used)) stmtss)
reWildStatement used (CgConditional cond then_ else_) =
  CgConditional
    (map (reWildStatement used) cond)
    (map (reWildStatement used) then_)
    (map (reWildStatement used) else_)

reWildQuery :: VarMap -> CgQuery -> CgQuery
reWildQuery used (CgQuery head stmts) =
  CgQuery (reWild used head) (map (reWildStatement used) stmts)