packages feed

covenant-1.2.0: src/Covenant/Internal/Term.hs

module Covenant.Internal.Term
  ( CovenantTypeError (..),
    Id (..),
    typeId,
    Arg (..),
    typeArg,
    Ref (..),
    typeRef,
    CompNodeInfo (..),
    ValNodeInfo (..),
    ASGNode (..),
    typeASGNode,
    ASGNodeType (..),
  )
where

import Control.Monad.Except (MonadError (throwError))
import Control.Monad.HashCons (MonadHashCons (lookupRef))
import Covenant.Constant (AConstant)
import Covenant.DeBruijn (DeBruijn)
import Covenant.Index (Index)
import Covenant.Internal.KindCheck (EncodingArgErr)
import Covenant.Internal.Rename (RenameError, UnRenameError)
import Covenant.Internal.Type
  ( AbstractTy,
    BuiltinFlatT,
    CompT,
    TyName,
    ValT,
  )
import Covenant.Internal.Unification (TypeAppError)
import Covenant.Prim (OneArgFunc, SixArgFunc, ThreeArgFunc, TwoArgFunc)
import Covenant.Type (ConstructorName, PlutusDataConstructor, Renamed)
import Data.Kind (Type)
import Data.Set qualified as Set
import Data.Vector (Vector)
import Data.Word (Word64)

-- | An error that can arise during the construction of an ASG by programmatic
-- means.
--
-- @since 1.0.0
data CovenantTypeError
  = -- | An 'Id' has no corresponding node. This error should not arise under
    -- normal circumstances: the most likely explanation is that you're using an
    -- 'Id' that was made by a different ASG builder computation.
    --
    -- @since 1.0.0
    BrokenIdReference Id
  | -- | Computation-typed nodes can't be forced, but we tried anyway.
    --
    -- @since 1.0.0
    ForceCompType (CompT AbstractTy)
  | -- | Value-typed nodes that aren't thunks can't be forced, but we tried anyway.
    --
    -- @since 1.0.0
    ForceNonThunk (ValT AbstractTy)
  | -- | Error nodes can't be forced, but we tried anyway.
    --
    -- @since 1.0.0
    ForceError
  | -- | Value-typed nodes can't be thunked, but we tried anyway.
    --
    -- @since 1.0.0
    ThunkValType (ValT AbstractTy)
  | -- | Error nodes can't be thunked, but we tried anyway.
    --
    -- @since 1.0.0
    ThunkError
  | -- | Arguments can't be applied to a value-typed node, but we tried anyway.
    --
    -- @since 1.0.0
    ApplyToValType (ValT AbstractTy)
  | -- | Arguments can't be applied to error nodes, but we tried anyway.
    --
    -- @since 1.0.0
    ApplyToError
  | -- | Computation-typed nodes can't be applied as arguments, but we tried anyway.
    --
    -- @since 1.0.0
    ApplyCompType (CompT AbstractTy)
  | -- | Renaming the function in an application failed.
    --
    -- @since 1.0.0
    RenameFunctionFailed (CompT AbstractTy) RenameError
  | -- | Renaming an argument in an application failed.
    --
    -- @since 1.0.0
    RenameArgumentFailed (ValT AbstractTy) RenameError
  | -- | We failed to unify an expected argument type with the type of the
    -- argument we were actually given.
    --
    -- @since 1.0.0
    UnificationError TypeAppError
  | -- | An argument was requested that doesn't exist.
    --
    -- @since 1.0.0
    NoSuchArgument DeBruijn (Index "arg")
  | -- | Can't return a computation-typed node, but we tried anyway.
    --
    -- @since 1.0.0
    ReturnCompType (CompT AbstractTy)
  | -- | The body of a lambda results in a value-typed node, which isn't allowed.
    --
    -- @since 1.2.0
    LambdaResultsInCompType (CompT AbstractTy)
  | -- | The body of a lambda results in a computation-typed node which isn't
    -- a return, which isn't allowed.
    --
    -- @since 1.0.0
    LambdaResultsInNonReturn (CompT AbstractTy)
  | -- | A lambda body's return is wrapping an error, instead of being directly
    -- an error. This should not happen under normal circumstances and is most
    -- certainly a bug.
    --
    -- @since 1.0.0
    ReturnWrapsError
  | -- | We tried to return a computation-typed node, but this isn't allowed.
    --
    -- @since 1.0.0
    ReturnWrapsCompType (CompT AbstractTy)
  | -- | The result of an application is not what the computation being
    -- applied expected.
    --
    -- First field is the expected type, the second is what we actually got.
    --
    -- @since 1.0.0
    WrongReturnType (ValT AbstractTy) (ValT AbstractTy)
  | -- | Wraps an encoding argument mismatch error from KindCheck
    --
    -- @since 1.1.0
    EncodingError (EncodingArgErr AbstractTy)
  | -- | The first argument to a catamorphism wasn't an algebra, as
    -- it had the wrong arity.
    --
    -- @since 1.2.0
    CataAlgebraWrongArity Int
  | -- | The first argument to a catamorphism wasn't an algebra.
    --
    -- @since 1.1.0
    CataNotAnAlgebra ASGNodeType
  | -- | The second argument to a catamorphism wasn't a value type.
    --
    -- @since 1.1.0
    CataApplyToNonValT ASGNodeType
  | -- The algebra given to this catamorphism is not rigid (that is, its
    -- computation type binds variables).
    --
    -- @since 1.2.0
    CataNonRigidAlgebra (CompT AbstractTy)
  | -- | The second argument to a catamorphism is a builtin type, but not one
    -- we can eliminate with a catamorphism.
    --
    -- @since 1.1.0
    CataWrongBuiltinType BuiltinFlatT
  | -- | The second argument to a catamorphism is a value type, but not one we
    -- can eliminate with a catamorphism. Usually, this means it's a variable.
    --
    -- @since 1.1.0
    CataWrongValT (ValT AbstractTy)
  | -- | We requested a catamorphism for a type that doesn't exist.
    --
    -- @since 1.2.0
    CataNoSuchType TyName
  | -- | We requested a catamorphism for a type without a base functor.
    --
    -- @since 1.2.0
    CataNoBaseFunctorForType TyName
  | -- | The provided algebra is not suitable for the given type.
    --
    -- @since 1.1.0
    CataUnsuitable (CompT AbstractTy) (ValT AbstractTy)
  | -- | Someone attempted to construct a tyvar using a DB index or argument position
    --   which refers to a scope (or argument) that does not exist.
    --
    -- @since 1.2.0
    OutOfScopeTyVar DeBruijn (Index "tyvar")
  | -- | We failed to rename an "instantiation type" supplied to 'Covenant.ASG.app'.
    --
    -- @since 1.2.0
    FailedToRenameInstantiation RenameError
  | -- | Un-renaming failed.
    --
    -- @since 1.2.0
    UndoRenameFailure UnRenameError
  | -- | We tried to look up the 'DatatypeInfo' corresponding to a 'TyName' and came up empty handed.
    --
    -- @since 1.2.0
    TypeDoesNotExist TyName
  | -- | We tried to rename a 'DatatypeInfo' and failed.
    --
    -- @since 1.2.0
    DatatypeInfoRenameError RenameError
  | -- | We tried to look up a constructor for a given type. The type exists, but the constructor does not.
    --
    -- @since 1.2.0
    ConstructorDoesNotExistForType TyName ConstructorName
  | -- | When using the helper function to construct an introduction form, the type and constructor exist but the
    --   number of fields provided as an argument does not match the number of declared fields.
    --   The 'Int' is the /incorrect/ number of /supplied/ fields.
    --
    -- @since 1.2.0
    IntroFormWrongNumArgs TyName ConstructorName Int
  | -- | The user passed an error node as an argument to a datatype into form. We return the arguments given
    --   to 'Covenant.ASG.dataConstructor' in the error.
    --
    -- @since 1.2.0
    IntroFormErrorNodeField TyName ConstructorName (Vector Ref)
  | -- | The user tried to construct an introduction form using a Plutus @Data@ constructor not found in the
    --   opaque datatype declaration.
    --
    -- @since 1.2.0
    UndeclaredOpaquePlutusDataCtor (Set.Set PlutusDataConstructor) ConstructorName
  | -- | The user tried to construct an introduction form with a valid Plutus @Data@ constructor, but
    --   supplied a 'Covenant.ASG.Ref' to a field of the wrong type.
    --
    -- @since 1.2.0
    InvalidOpaqueField (Set.Set PlutusDataConstructor) ConstructorName [ValT Renamed]
  | -- The user tried to match on (i.e. use as a scrutinee) a node that wasn't a value.
    --
    -- @since 1.2.0
    MatchNonValTy ASGNodeType
  | -- | Internal error: we found a base functor Boehm-Berrarducci form that isn't a thunk after instantiation
    --   during pattern matching.Somehow we got a BFBB that is something other than a thunk after instantiation during pattern matching.
    --
    --   This should not normally happen: let us know if you see this error!
    --
    -- @since 1.2.0
    MatchNonThunkBBF (ValT Renamed)
  | -- | We encountered a rename error during pattern matching. This will refer
    -- to either the Boehm-Berrarducci form, or the base functor Boehm-Berrarducci form, depending on what type we tried to match.
    --
    -- @since 1.2.0
    MatchRenameBBFail RenameError
  | -- | This indicates that we encountered an error when renaming the arguments to the type constructor of the
    --   /scrutinee type/ during pattern matching. That is, if we're matching on @Either a b@, this means that
    --   either @a@ or @b@ failed to rename.
    --
    --  This should not normally happen: let us know if you see this error!
    --
    -- @since 1.2.0
    MatchRenameTyConArgFail RenameError
  | -- | A user tried to use a polymorphic handler in a pattern match, which is not currently allowed.
    --
    -- @since 1.2.0
    MatchPolymorphicHandler (ValT Renamed)
  | -- | We tried to use an error node as a pattern match handler.
    --
    -- @since 1.2.0
    MatchErrorAsHandler Ref
  | -- | The non-recursive branch of a pattern match needs a Boehm-Berrarducci form for the given type
    -- name, but it doesn't exist.
    --
    -- @since 1.2.0
    MatchNoBBForm TyName
  | -- | Someone tried to match on something that isn't a datatype.
    --
    -- @since 1.2.0
    MatchNonDatatypeScrutinee (ValT AbstractTy)
  | -- | The scrutinee is a datatype, be don't have it in our datatype dictionary.
    --
    -- @since 1.2.0
    MatchNoDatatypeInfo TyName
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Show
    )

-- | A unique identifier for a node in a Covenant program.
--
-- @since 1.0.0
newtype Id = Id Word64
  deriving
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Bounded,
      -- | Needed for internal reasons, even though this type class is terrible.
      --
      -- @since 1.0.0
      Enum
    )
    via Word64
  deriving stock
    ( -- | @since 1.0.0
      Show
    )

-- Get the type of an `Id`, or fail.
typeId ::
  forall (m :: Type -> Type).
  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) =>
  Id ->
  m ASGNodeType
typeId i = do
  lookedUp <- lookupRef i
  case lookedUp of
    Nothing -> throwError . BrokenIdReference $ i
    Just node -> pure . typeASGNode $ node

-- | An argument passed to a function in a Covenant program.
--
-- @since 1.0.0
data Arg = Arg DeBruijn (Index "arg") (ValT AbstractTy)
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )

-- Helper to get the type of an argument.
typeArg :: Arg -> ValT AbstractTy
typeArg (Arg _ _ t) = t

-- | A general reference in a Covenant program.
--
-- @since 1.0.0
data Ref
  = -- | A function argument.
    --
    -- @since 1.0.0
    AnArg Arg
  | -- | A link to an ASG node.
    --
    -- @since 1.0.0
    AnId Id
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )

-- Helper for getting a type for any reference.
typeRef ::
  forall (m :: Type -> Type).
  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) =>
  Ref ->
  m ASGNodeType
typeRef = \case
  AnArg arg -> pure . ValNodeType . typeArg $ arg
  AnId i -> typeId i

-- | Computation-term-specific node information.
--
-- @since 1.0.0
data CompNodeInfo
  = Builtin1Internal OneArgFunc
  | Builtin2Internal TwoArgFunc
  | Builtin3Internal ThreeArgFunc
  | Builtin6Internal SixArgFunc
  | LamInternal Ref
  | ForceInternal Ref
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )

-- | Value-term-specific node information.
--
-- @since 1.0.0
data ValNodeInfo
  = LitInternal AConstant
  | AppInternal Id (Vector Ref)
  | ThunkInternal Id
  | -- | @since 1.1.0
    CataInternal Ref Ref
  | -- | @since 1.2.0
    DataConstructorInternal TyName ConstructorName (Vector Ref)
  | -- | @since 1.2.0
    MatchInternal Ref (Vector Ref)
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )

-- | A single node in a Covenant ASG. Where appropriate, these carry their
-- types.
--
-- @since 1.0.0
data ASGNode
  = -- | A computation-typed node.
    --
    -- @since 1.0.0
    ACompNode (CompT AbstractTy) CompNodeInfo
  | -- | A value-typed node
    --
    -- @since 1.0.0
    AValNode (ValT AbstractTy) ValNodeInfo
  | -- | An error node.
    --
    -- @since 1.0.0
    AnError
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )

-- | Produces the type of any ASG node.
--
-- @since 1.0.0
typeASGNode :: ASGNode -> ASGNodeType
typeASGNode = \case
  ACompNode t _ -> CompNodeType t
  AValNode t _ -> ValNodeType t
  AnError -> ErrorNodeType

-- | Helper data type representing the type of any ASG node whatsoever.
--
-- @since 1.0.0
data ASGNodeType
  = CompNodeType (CompT AbstractTy)
  | ValNodeType (ValT AbstractTy)
  | ErrorNodeType
  deriving stock
    ( -- | @since 1.0.0
      Eq,
      -- | @since 1.0.0
      Ord,
      -- | @since 1.0.0
      Show
    )