packages feed

glean-0.1.0.0: glean/hs/Glean/RTS/Types.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.
-}

{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE UnboxedTuples #-}

module Glean.RTS.Types
  ( -- * fact types
    -- ** type
    Id
    -- ** newtype
  , Fid(..), invalidFid, lowestFid
    -- ** phantom type
  , Pid(..), invalidPid, lowestPid
    -- * Types
  , Type
  , Glean.RTS.Types.FieldDef
  , PidRef(..)
  , ExpandedType(..)
  , repType
  , sumLike
  , eqType
  , derefType
    -- ** Representation types
  , Rep(..)
) where

import Control.DeepSeq
import Data.Hashable
import qualified Data.Text as Text
import Compat.Prettyprinter
import Data.Vector.Primitive (Prim)
import Foreign

import Glean.Types (Id, fIRST_FREE_ID)
import Glean.Angle.Types hiding (Type)
import Glean.Display
import Glean.Schema.Util

-- | 'Pid' holds the glean Id corresponding to the definition of a predicate.
--
-- As a Fid that Id points to the glean fact that holds the predicate name and
-- key type and value type).
--
-- Querying for this Id (as prefix of key) gives all facts for this predicate.
newtype Pid = Pid { fromPid :: Int64 }
  deriving(Eq,Ord,Enum,Hashable,Storable,Show,NFData,Prim)

instance Pretty Pid where
  pretty (Pid n) = "{" <> pretty n <> "}"

instance Display Pid where
  display _ = pretty

invalidPid :: Pid
invalidPid = Pid 0

lowestPid :: Pid
lowestPid = Pid fIRST_FREE_ID

-- | Id of a fact that points to an entry in the database.
-- If this entry happens to be a predicate definition then this
-- is also logically a Pid.  If this entry is fact about some
-- predicate 'p' then this is also logically an 'IdOf p'.
newtype Fid = Fid { fromFid :: Id }
  deriving(Eq,Ord,Enum,Hashable,Storable,Show,NFData,Prim)

instance Pretty Fid where
  pretty (Fid n) = "{" <> pretty n <> "}"

instance Display Fid where
  display _ = pretty

invalidFid :: Fid
invalidFid = Fid 0

lowestFid :: Fid
lowestFid = Fid fIRST_FREE_ID

-- -----------------------------------------------------------------------------
-- Runtime types

data PidRef = PidRef Pid PredicateId
  deriving Show

instance Eq PidRef where
  PidRef a _ == PidRef b _ = a == b

instance Ord PidRef where
  compare (PidRef a _) (PidRef b _) = compare a b

instance Display PidRef where
  display opts (PidRef _ ref) = display opts ref
    -- we could add an option to display the Pids too

data ExpandedType = ExpandedType TypeId Type
  deriving (Show, Eq)

instance Display ExpandedType where
  display opts (ExpandedType ref _) = display opts ref

type Type = Type_ () PidRef ExpandedType
type FieldDef = FieldDef_ () PidRef ExpandedType


-- | Construct the representation of a Type
repType :: Type -> Rep Pid
repType ByteTy = ByteRep
repType NatTy = NatRep
repType StringTy = StringRep
repType (ArrayTy ty) = ArrayRep (repType ty)
repType (RecordTy fields) =
  TupleRep [ repType ty | FieldDef _ ty <- fields ]
repType (SumTy fields) =
  SumRep [ repType ty | FieldDef _ ty <- fields ]
repType (SetTy ty)  = SetRep (repType ty)
repType (PredicateTy _ (PidRef pid _)) = PredicateRep pid
repType (NamedTy _ (ExpandedType _ ty)) = repType ty
repType (MaybeTy ty) = repType (lowerMaybe ty)
repType (EnumeratedTy names) = repType (lowerEnum names)
repType BooleanTy = repType lowerBool
repType TyVar{} = error "repType: TyVar"
repType HasTy{} = error "repType: HasTy"
repType HasKey{} = error "repType: HasKey"
repType ElementsOf{} = error "repType: ElementsOf"

sumLike :: Type -> Maybe [Glean.RTS.Types.FieldDef]
sumLike (SumTy fs) = Just fs
sumLike (MaybeTy ty) = Just (maybeFields ty)
sumLike (EnumeratedTy names) = Just (enumFields names)
sumLike BooleanTy = Just boolFields
sumLike _ = Nothing

-- | Compare types for (structural) equality
eqType :: AngleVersion -> Type -> Type -> Bool
eqType version a b = case (a,b) of
  (ByteTy, ByteTy) -> True
  (NatTy, NatTy) -> True
  (StringTy, StringTy) -> True
  (ArrayTy a, ArrayTy b) -> eqType version a b
  (RecordTy as, RecordTy bs) ->
    let isTuple = all (Text.isInfixOf "tuplefield"  . fieldDefName)
        -- previous to version 7 records were always compared structurally
        compareStructurally =
          version < AngleVersion 7 || isTuple as || isTuple bs
        -- structural equality for tuples by ignoring field names.
        eqField fa fb = fa == fb
    in
    length as == length bs &&
    and [ eqType version a b && (compareStructurally || eqField fa fb)
        | (FieldDef fa a, FieldDef fb b) <- zip as bs ]
  (SumTy as, SumTy bs) ->
    length as == length bs &&
    and [ eqType version a b | (FieldDef _ a, FieldDef _ b) <- zip as bs ]
  (PredicateTy _ (PidRef p _), PredicateTy _ (PidRef q _)) -> p == q
  (NamedTy _ (ExpandedType n t), NamedTy _ (ExpandedType m u)) ->
    n == m || eqType version t u
  (NamedTy _ (ExpandedType _ t), u) -> eqType version t u
  (t, NamedTy _ (ExpandedType _ u)) -> eqType version t u
  (MaybeTy t, MaybeTy u) -> eqType version t u
  (MaybeTy t, u) ->  eqType version (lowerMaybe t) u
  (t, MaybeTy u) -> eqType version t (lowerMaybe u)
  (EnumeratedTy xs, EnumeratedTy ys) -> xs == ys
  (EnumeratedTy xs, t) -> eqType version (lowerEnum xs) t
  (t, EnumeratedTy xs) -> eqType version t (lowerEnum xs)
  (BooleanTy, BooleanTy) -> True
  (BooleanTy, t) -> eqType version lowerBool t
  (t, BooleanTy) -> eqType version t lowerBool
  _ -> False

-- | dereference NamedTy on the outside of a Type
derefType :: Type -> Type
derefType (NamedTy _ (ExpandedType _ ty)) = derefType ty
derefType ty = ty

-- | Type describing the raw representation of a value. This makes
-- fewer distinctions than Type, in particular: there are no field
-- names or named types, and sugar like maybe, bool and enumerations
-- are expanded into their low-level representations.
data Rep id
  = ByteRep
  | NatRep
  | ArrayRep (Rep id)
  | TupleRep [Rep id]
  | SumRep [Rep id]
  | SetRep (Rep id)
  | StringRep
  | PredicateRep id
  deriving (Eq, Show, Functor, Foldable, Traversable)

instance Pretty id => Pretty (Rep id) where
  pretty ByteRep = "byte#"
  pretty NatRep = "nat#"
  pretty (ArrayRep ty) = "[" <> pretty ty <> "]"
  pretty (TupleRep ty) = align $ encloseSep "(" ")" "," $ map pretty ty
  pretty (SumRep ty) = align $ encloseSep "(|" "|)" "," $ map pretty ty
  pretty (SetRep ty) = "set " <> pretty ty
  pretty StringRep = "string#"
  pretty (PredicateRep id) = pretty id