packages feed

covenant 1.1.0 → 1.2.0

raw patch · 17 files changed

+1946/−473 lines, 17 filesdep +smashPVP ok

version bump matches the API change (PVP)

Dependencies added: smash

API changes (from Hackage documentation)

- Covenant.ASG: LambdaResultsInValType :: ValT AbstractTy -> CovenantTypeError
- Covenant.ASG: data ScopeInfo
- Covenant.ASG: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Vector.Vector (Data.Vector.Vector (Covenant.Internal.Type.ValT Covenant.Internal.Type.AbstractTy)), b GHC.Types.~ Data.Vector.Vector (Data.Vector.Vector (Covenant.Internal.Type.ValT Covenant.Internal.Type.AbstractTy))) => Optics.Label.LabelOptic "argumentInfo" k Covenant.ASG.ScopeInfo Covenant.ASG.ScopeInfo a b
- Covenant.ASG: pattern Return :: Ref -> CompNodeInfo
- Covenant.ASG: ret :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) => Ref -> m Id
+ Covenant.ASG: ASGEnv :: ScopeInfo -> Map TyName (DatatypeInfo AbstractTy) -> ASGEnv
+ Covenant.ASG: CataAlgebraWrongArity :: Int -> CovenantTypeError
+ Covenant.ASG: CataApplyToNonValT :: ASGNodeType -> CovenantTypeError
+ Covenant.ASG: CataNoBaseFunctorForType :: TyName -> CovenantTypeError
+ Covenant.ASG: CataNoSuchType :: TyName -> CovenantTypeError
+ Covenant.ASG: CataNonRigidAlgebra :: CompT AbstractTy -> CovenantTypeError
+ Covenant.ASG: CataNotAnAlgebra :: ASGNodeType -> CovenantTypeError
+ Covenant.ASG: CataUnsuitable :: CompT AbstractTy -> ValT AbstractTy -> CovenantTypeError
+ Covenant.ASG: CataWrongBuiltinType :: BuiltinFlatT -> CovenantTypeError
+ Covenant.ASG: CataWrongValT :: ValT AbstractTy -> CovenantTypeError
+ Covenant.ASG: CompNodeType :: CompT AbstractTy -> ASGNodeType
+ Covenant.ASG: ConstructorDoesNotExistForType :: TyName -> ConstructorName -> CovenantTypeError
+ Covenant.ASG: DatatypeInfoRenameError :: RenameError -> CovenantTypeError
+ Covenant.ASG: EncodingArgMismatch :: TyName -> ValT a -> EncodingArgErr a
+ Covenant.ASG: ErrorNodeType :: ASGNodeType
+ Covenant.ASG: FailedToRenameInstantiation :: RenameError -> CovenantTypeError
+ Covenant.ASG: IntroFormErrorNodeField :: TyName -> ConstructorName -> Vector Ref -> CovenantTypeError
+ Covenant.ASG: IntroFormWrongNumArgs :: TyName -> ConstructorName -> Int -> CovenantTypeError
+ Covenant.ASG: InvalidOpaqueField :: Set PlutusDataConstructor -> ConstructorName -> [ValT Renamed] -> CovenantTypeError
+ Covenant.ASG: InvalidScopeReference :: Int -> Index "tyvar" -> RenameError
+ Covenant.ASG: LambdaResultsInCompType :: CompT AbstractTy -> CovenantTypeError
+ Covenant.ASG: MatchErrorAsHandler :: Ref -> CovenantTypeError
+ Covenant.ASG: MatchNoBBForm :: TyName -> CovenantTypeError
+ Covenant.ASG: MatchNoDatatypeInfo :: TyName -> CovenantTypeError
+ Covenant.ASG: MatchNonDatatypeScrutinee :: ValT AbstractTy -> CovenantTypeError
+ Covenant.ASG: MatchNonThunkBBF :: ValT Renamed -> CovenantTypeError
+ Covenant.ASG: MatchNonValTy :: ASGNodeType -> CovenantTypeError
+ Covenant.ASG: MatchPolymorphicHandler :: ValT Renamed -> CovenantTypeError
+ Covenant.ASG: MatchRenameBBFail :: RenameError -> CovenantTypeError
+ Covenant.ASG: MatchRenameTyConArgFail :: RenameError -> CovenantTypeError
+ Covenant.ASG: NegativeDeBruijn :: Int -> UnRenameError
+ Covenant.ASG: OutOfScopeTyVar :: DeBruijn -> Index "tyvar" -> CovenantTypeError
+ Covenant.ASG: ScopeInfo :: Vector (Word32, Vector (ValT AbstractTy)) -> ScopeInfo
+ Covenant.ASG: TypeDoesNotExist :: TyName -> CovenantTypeError
+ Covenant.ASG: UnRenameWildCard :: Renamed -> UnRenameError
+ Covenant.ASG: UndeclaredOpaquePlutusDataCtor :: Set PlutusDataConstructor -> ConstructorName -> CovenantTypeError
+ Covenant.ASG: UndoRenameFailure :: UnRenameError -> CovenantTypeError
+ Covenant.ASG: ValNodeType :: ValT AbstractTy -> ASGNodeType
+ Covenant.ASG: boundTyVar :: (MonadError CovenantTypeError m, MonadReader ASGEnv m) => DeBruijn -> Index "tyvar" -> m BoundTyVar
+ Covenant.ASG: cata :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => Ref -> Ref -> m Id
+ Covenant.ASG: ctor :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => TyName -> ConstructorName -> Vector Ref -> Vector (Wedge BoundTyVar (ValT Void)) -> m Id
+ Covenant.ASG: data ASGEnv
+ Covenant.ASG: data ASGNodeType
+ Covenant.ASG: data BoundTyVar
+ Covenant.ASG: data EncodingArgErr a
+ Covenant.ASG: data UnRenameError
+ Covenant.ASG: dataConstructor :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => TyName -> ConstructorName -> Vector Ref -> m Id
+ Covenant.ASG: dtype :: TyName -> [ValT AbstractTy] -> ValT AbstractTy
+ Covenant.ASG: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Vector.Vector (GHC.Word.Word32, Data.Vector.Vector (Covenant.Internal.Type.ValT Covenant.Internal.Type.AbstractTy)), b GHC.Types.~ Data.Vector.Vector (GHC.Word.Word32, Data.Vector.Vector (Covenant.Internal.Type.ValT Covenant.Internal.Type.AbstractTy))) => Optics.Label.LabelOptic "argumentInfo" k Covenant.ASG.ScopeInfo Covenant.ASG.ScopeInfo a b
+ Covenant.ASG: instance GHC.Classes.Eq Covenant.ASG.BoundTyVar
+ Covenant.ASG: instance GHC.Classes.Ord Covenant.ASG.BoundTyVar
+ Covenant.ASG: instance GHC.Show.Show Covenant.ASG.BoundTyVar
+ Covenant.ASG: lazyLam :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => CompT AbstractTy -> m Ref -> m Id
+ Covenant.ASG: match :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => Ref -> Vector Ref -> m Id
+ Covenant.ASG: newtype ScopeInfo
+ Covenant.ASG: pattern Cata :: Ref -> Ref -> ValNodeInfo
+ Covenant.ASG: pattern DataConstructor :: TyName -> ConstructorName -> Vector Ref -> ValNodeInfo
+ Covenant.ASG: pattern Match :: Ref -> Vector Ref -> ValNodeInfo
+ Covenant.Data: mapValT :: (ValT a -> ValT a) -> ValT a -> ValT a
+ Covenant.Index: wordCount :: forall (ofWhat :: Symbol). Lens' (Count ofWhat) Word32
+ Covenant.Test: DebugASGBuilder :: ReaderT ASGEnv (ExceptT CovenantTypeError (HashConsT Id ASGNode Identity)) a -> DebugASGBuilder a
+ Covenant.Test: InvalidScopeReference :: Int -> Index "tyvar" -> RenameError
+ Covenant.Test: debugASGBuilder :: Map TyName (DatatypeInfo AbstractTy) -> DebugASGBuilder a -> Either CovenantError a
+ Covenant.Test: instance Control.Monad.Error.Class.MonadError Covenant.Internal.Term.CovenantTypeError Covenant.Test.DebugASGBuilder
+ Covenant.Test: instance Control.Monad.HashCons.MonadHashCons Covenant.Internal.Term.Id Covenant.Internal.Term.ASGNode Covenant.Test.DebugASGBuilder
+ Covenant.Test: instance Control.Monad.Reader.Class.MonadReader Covenant.ASG.ASGEnv Covenant.Test.DebugASGBuilder
+ Covenant.Test: instance GHC.Base.Applicative Covenant.Test.DebugASGBuilder
+ Covenant.Test: instance GHC.Base.Functor Covenant.Test.DebugASGBuilder
+ Covenant.Test: instance GHC.Base.Monad Covenant.Test.DebugASGBuilder
+ Covenant.Test: newtype DebugASGBuilder a
+ Covenant.Test: typeIdTest :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) => Id -> m (ValT AbstractTy)
+ Covenant.Test: undoRename :: Vector Word32 -> ValT Renamed -> Either UnRenameError (ValT AbstractTy)
- Covenant.ASG: app :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => Id -> Vector Ref -> m Id
+ Covenant.ASG: app :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => Id -> Vector Ref -> Vector (Wedge BoundTyVar (ValT Void)) -> m Id
- Covenant.ASG: lam :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => CompT AbstractTy -> m Id -> m Id
+ Covenant.ASG: lam :: (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) => CompT AbstractTy -> m Ref -> m Id
- Covenant.ASG: pattern Lam :: Id -> CompNodeInfo
+ Covenant.ASG: pattern Lam :: Ref -> CompNodeInfo
- Covenant.Test: runRenameM :: RenameM a -> Either RenameError a
+ Covenant.Test: runRenameM :: Vector Word32 -> RenameM a -> Either RenameError a

Files

CHANGELOG.md view
@@ -4,7 +4,16 @@  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). -## UNRELEASED+## 1.2.0 -- 2025-08-27++* Helper function for safely retrieving an in-scope type variable when constructing the ASG  +* Removed Return nodes, associated pattern synonyms, and the `ret` ASGBuilder function+* Reworked the renamer to be context sensitive+* Added new error types for un-renaming+* Modified ASG helper functions and updated tests to conform with renamer rework+* Added support for introduction forms (ValNode stuff, ASG helper function, tests)+* Added support for catamorphism elimination forms+* Added support for pattern matching elimination form  ## 1.1.0 -- 2025-07-11 
covenant.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: covenant-version: 1.1.0+version: 1.2.0 synopsis: Standalone IR for Cardano scripts. description:   A library describing a call-by-push-value, Turner-total IR. Includes the ability to build up the IR programmatically.@@ -84,6 +84,7 @@     nonempty-vector ==0.2.4,     optics-core ==0.4.1.1,     prettyprinter ==1.7.1,+    smash ==0.1.0.0,     tasty ==1.5.3,     tasty-expected-failure ==0.12.3,     tasty-hunit ==0.10.2,@@ -92,7 +93,8 @@  common bench-lang   import: lang-  ghc-options: -O2+  ghc-options:+    -O2  -- Primary library library@@ -135,6 +137,7 @@     prettyprinter ==1.7.1,     quickcheck-instances ==0.3.32,     quickcheck-transformer ==0.3.1.2,+    smash ==0.1.0.0,     tasty-hunit ==0.10.2,     text >=2.1.1 && <2.2,     transformers >=0.6.1.0 && <0.7.0.0,
src/Covenant/ASG.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE PatternSynonyms #-}  -- |@@ -37,11 +38,11 @@         Builtin3,         Builtin6,         Lam,-        Force,-        Return+        Force       ),-    ValNodeInfo (Lit, App, Thunk),+    ValNodeInfo (Lit, App, Thunk, Cata, DataConstructor, Match),     ASGNode (..),+    ASGNodeType (..),      -- ** Functions     typeASGNode,@@ -50,36 +51,54 @@      -- ** Types     CovenantError (..),-    ScopeInfo,+    ScopeInfo (..),     ASGBuilder,     TypeAppError (..),     RenameError (..),+    UnRenameError (..),+    EncodingArgErr (..),     CovenantTypeError (..),+    BoundTyVar,      -- ** Introducers+    boundTyVar,     arg,     builtin1,     builtin2,     builtin3,     builtin6,     force,-    ret,     lam,     err,     lit,     thunk,+    dataConstructor,++    -- ** Eliminators     app,+    cata,+    match, -    -- ** Elimination+    -- ** Helpers+    ctor,+    lazyLam,+    dtype,      -- *** Environment     defaultDatatypes,      -- *** Function     runASGBuilder,+    -- only for tests+    ASGEnv (..),   ) where +#if __GLASGOW_HASKELL__==908+import Data.Foldable (foldl')+#endif++import Control.Monad (foldM, join, unless, zipWithM) import Control.Monad.Except   ( ExceptT,     MonadError (throwError),@@ -98,15 +117,18 @@   ) import Covenant.Constant (AConstant, typeConstant) import Covenant.Data (DatatypeInfo, mkDatatypeInfo)-import Covenant.DeBruijn (DeBruijn, asInt)-import Covenant.Index (Index, count0, intIndex)-import Covenant.Internal.KindCheck (checkEncodingArgs)+import Covenant.DeBruijn (DeBruijn (S, Z), asInt)+import Covenant.Index (Count, Index, count0, intCount, intIndex, wordCount)+import Covenant.Internal.KindCheck (EncodingArgErr (EncodingArgMismatch), checkEncodingArgs) import Covenant.Internal.Ledger (ledgerTypes) import Covenant.Internal.Rename   ( RenameError-      ( InvalidAbstractionReference+      ( InvalidAbstractionReference,+        InvalidScopeReference       ),+    UnRenameError (NegativeDeBruijn, UnRenameWildCard),     renameCompT,+    renameDatatypeInfo,     renameValT,     runRenameM,     undoRename,@@ -121,21 +143,45 @@         Builtin3Internal,         Builtin6Internal,         ForceInternal,-        LamInternal,-        ReturnInternal+        LamInternal       ),     CovenantTypeError       ( ApplyCompType,         ApplyToError,         ApplyToValType,         BrokenIdReference,+        CataAlgebraWrongArity,+        CataApplyToNonValT,+        CataNoBaseFunctorForType,+        CataNoSuchType,+        CataNonRigidAlgebra,+        CataNotAnAlgebra,+        CataUnsuitable,+        CataWrongBuiltinType,+        CataWrongValT,+        ConstructorDoesNotExistForType,+        DatatypeInfoRenameError,         EncodingError,+        FailedToRenameInstantiation,         ForceCompType,         ForceError,         ForceNonThunk,+        IntroFormErrorNodeField,+        IntroFormWrongNumArgs,+        InvalidOpaqueField,+        LambdaResultsInCompType,         LambdaResultsInNonReturn,-        LambdaResultsInValType,+        MatchErrorAsHandler,+        MatchNoBBForm,+        MatchNoDatatypeInfo,+        MatchNonDatatypeScrutinee,+        MatchNonThunkBBF,+        MatchNonValTy,+        MatchPolymorphicHandler,+        MatchRenameBBFail,+        MatchRenameTyConArgFail,         NoSuchArgument,+        OutOfScopeTyVar,         RenameArgumentFailed,         RenameFunctionFailed,         ReturnCompType,@@ -143,24 +189,29 @@         ReturnWrapsError,         ThunkError,         ThunkValType,+        TypeDoesNotExist,+        UndeclaredOpaquePlutusDataCtor,+        UndoRenameFailure,         UnificationError,         WrongReturnType       ),     Id,     Ref (AnArg, AnId),-    ValNodeInfo (AppInternal, LitInternal, ThunkInternal),+    ValNodeInfo (AppInternal, CataInternal, DataConstructorInternal, LitInternal, MatchInternal, ThunkInternal),     typeASGNode,     typeId,     typeRef,   ) import Covenant.Internal.Type-  ( AbstractTy,+  ( AbstractTy (BoundAt),+    BuiltinFlatT (ByteStringT, IntegerT),     CompT (CompT),     CompTBody (CompTBody),-    DataDeclaration,+    DataDeclaration (DataDeclaration),     Renamed,     TyName,-    ValT (ThunkT),+    ValT (BuiltinFlat, Datatype, ThunkT),+    arity,   ) import Covenant.Internal.Unification   ( TypeAppError@@ -174,7 +225,13 @@         NoBBForm,         NoDatatypeInfo       ),+    UnifyM,     checkApp,+    fixUp,+    reconcile,+    runUnifyM,+    substitute,+    unify,   ) import Covenant.Prim   ( OneArgFunc,@@ -186,27 +243,50 @@     typeThreeArgFunc,     typeTwoArgFunc,   )+import Covenant.Type+  ( CompT (Comp0),+    CompTBody (ReturnT),+    Constructor,+    ConstructorName,+    DataDeclaration (OpaqueData),+    PlutusDataConstructor (PlutusB, PlutusConstr, PlutusI, PlutusList, PlutusMap),+    Renamed (Unifiable),+    TyName (TyName),+    ValT (Abstraction),+    tyvar,+  ) import Data.Bimap (Bimap) import Data.Bimap qualified as Bimap import Data.Coerce (coerce) import Data.Functor.Identity (Identity, runIdentity) import Data.Kind (Type)+import Data.List (find) import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, isJust, mapMaybe)+import Data.Set qualified as Set+import Data.Text qualified as T import Data.Vector (Vector) import Data.Vector qualified as Vector import Data.Vector.NonEmpty qualified as NonEmpty+import Data.Void (Void, vacuous)+import Data.Wedge (Wedge, wedge)+import Data.Word (Word32) import Optics.Core   ( A_Lens,     LabelOptic (labelOptic),+    at,+    folded,     ix,     lens,     over,     preview,     review,+    toListOf,     view,     (%),+    _1,+    _2,   )  -- | A fully-assembled Covenant ASG.@@ -249,8 +329,14 @@ nodeAt :: Id -> ASG -> ASGNode nodeAt i (ASG (_, mappings)) = fromJust . Map.lookup i $ mappings +-- | The environment used when \'building up\' an 'ASG'. This type is exposed+-- only for testing, or debugging, and should /not/ be used in general by those+-- who just want to build an 'ASG'.+--+-- @since 1.2.0 data ASGEnv = ASGEnv ScopeInfo (Map TyName (DatatypeInfo AbstractTy)) +-- | @since 1.2.0 instance   (k ~ A_Lens, a ~ ScopeInfo, b ~ ScopeInfo) =>   LabelOptic "scopeInfo" k ASGEnv ASGEnv a b@@ -261,6 +347,7 @@       (\(ASGEnv si _) -> si)       (\(ASGEnv _ dti) si -> ASGEnv si dti) +-- | @since 1.2.0 instance   (k ~ A_Lens, a ~ Map TyName (DatatypeInfo AbstractTy), b ~ Map TyName (DatatypeInfo AbstractTy)) =>   LabelOptic "datatypeInfo" k ASGEnv ASGEnv a b@@ -281,8 +368,8 @@ -- the functionality provided by this module is not recommended, unless you know -- /exactly/ what you're doing. ----- @since 1.0.0-newtype ScopeInfo = ScopeInfo (Vector (Vector (ValT AbstractTy)))+-- @since 1.2.0+newtype ScopeInfo = ScopeInfo (Vector (Word32, Vector (ValT AbstractTy)))   deriving stock     ( -- | @since 1.0.0       Eq,@@ -296,9 +383,9 @@ -- enclosing scope, 2 is the enclosing scope of our enclosing scope, etc. The -- \'inner\' 'Vector's are positional lists of argument types. ----- @since 1.0.0+-- @since 1.2.0 instance-  (k ~ A_Lens, a ~ Vector (Vector (ValT AbstractTy)), b ~ Vector (Vector (ValT AbstractTy))) =>+  (k ~ A_Lens, a ~ Vector (Word32, Vector (ValT AbstractTy)), b ~ Vector (Word32, Vector (ValT AbstractTy))) =>   LabelOptic "argumentInfo" k ScopeInfo ScopeInfo a b   where   {-# INLINEABLE labelOptic #-}@@ -334,19 +421,13 @@ pattern Force :: Ref -> CompNodeInfo pattern Force r <- ForceInternal r --- | Produce the result of a computation.------ @since 1.0.0-pattern Return :: Ref -> CompNodeInfo-pattern Return r <- ReturnInternal r- -- | A lambda. ----- @since 1.0.0-pattern Lam :: Id -> CompNodeInfo-pattern Lam i <- LamInternal i+-- @since 1.2.0+pattern Lam :: Ref -> CompNodeInfo+pattern Lam r <- LamInternal r -{-# COMPLETE Builtin1, Builtin2, Builtin3, Builtin6, Force, Return, Lam #-}+{-# COMPLETE Builtin1, Builtin2, Builtin3, Builtin6, Force, Lam #-}  -- | A compile-time literal of a flat builtin type. --@@ -367,8 +448,26 @@ pattern Thunk :: Id -> ValNodeInfo pattern Thunk i <- ThunkInternal i -{-# COMPLETE Lit, App, Thunk #-}+-- | \'Tear down\' a self-recursive value with an algebra.+--+-- @since 1.0.0+pattern Cata :: Ref -> Ref -> ValNodeInfo+pattern Cata algebraRef valRef <- CataInternal algebraRef valRef +-- | Inject (zero or more) fields into a data constructor+--+-- @since 1.2.0+pattern DataConstructor :: TyName -> ConstructorName -> Vector Ref -> ValNodeInfo+pattern DataConstructor tyName ctorName fields <- DataConstructorInternal tyName ctorName fields++-- | Deconstruct a value of a data type using the supplied handlers for each arm+--+-- @since 1.2.0+pattern Match :: Ref -> Vector Ref -> ValNodeInfo+pattern Match scrutinee handlers <- MatchInternal scrutinee handlers++{-# COMPLETE Lit, App, Thunk, Cata, DataConstructor, Match #-}+ -- | Any problem that might arise when building an ASG programmatically. -- -- @since 1.0.0@@ -467,7 +566,7 @@ arg scope index = do   let scopeAsInt = review asInt scope   let indexAsInt = review intIndex index-  lookedUp <- asks (preview (#scopeInfo % #argumentInfo % ix scopeAsInt % ix indexAsInt))+  lookedUp <- asks (preview (#scopeInfo % #argumentInfo % ix scopeAsInt % _2 % ix indexAsInt))   case lookedUp of     Nothing -> throwError . NoSuchArgument scope $ index     Just t -> pure . Arg scope index $ t@@ -538,24 +637,6 @@     CompNodeType t -> throwError . ForceCompType $ t     ErrorNodeType -> throwError ForceError --- | Given the result of a function body (either a value or an error), construct--- the return for it. Will fail if that reference aims at a computation node.------ @since 1.0.0-ret ::-  forall (m :: Type -> Type).-  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) =>-  Ref ->-  m Id-ret r = do-  refT <- typeRef r-  case refT of-    ValNodeType t -> do-      let t' = CompT count0 . CompTBody . NonEmpty.singleton $ t-      refTo . ACompNode t' . ReturnInternal $ r-    CompNodeType t -> throwError . ReturnCompType $ t-    ErrorNodeType -> err- -- | Given a desired type, and a computation which will construct a lambda body -- when executed (with the scope extended with the arguments the functions can -- expect), construct a lambda.@@ -567,37 +648,34 @@ -- \'bottom-up\', whereas function arguments (and their scopes) are necessarily -- top-down. Thus, we need to \'delay\' the construction of a lambda's body to -- ensure that proper scoped argument information can be given to it, hence why--- the argument being passed is an @m Id@.+-- the argument being passed is an @m Ref@. ----- @since 1.0.0+-- @since 1.2.0 lam ::   forall (m :: Type -> Type).   (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>   CompT AbstractTy ->-  m Id ->+  m Ref ->   m Id-lam expectedT@(CompT _ (CompTBody xs)) bodyComp = do+lam expectedT@(CompT cnt (CompTBody xs)) bodyComp = do   let (args, resultT) = NonEmpty.unsnoc xs-  bodyId <- local (over (#scopeInfo % #argumentInfo) (Vector.cons args)) bodyComp-  bodyNode <- lookupRef bodyId-  case bodyNode of-    Nothing -> throwError . BrokenIdReference $ bodyId-    -- This unifies with anything, so we're fine-    Just AnError -> refTo . ACompNode expectedT . LamInternal $ bodyId-    Just (ACompNode t specs) -> case specs of-      ReturnInternal r -> do-        rT <- typeRef r-        case rT of-          -- Note (Koz, 17/04/2025): I am not 100% sure about this, but I can't-          -- see how anything else would make sense.-          ValNodeType actualT ->-            if resultT == actualT-              then refTo . ACompNode expectedT . LamInternal $ bodyId-              else throwError . WrongReturnType resultT $ actualT-          ErrorNodeType -> throwError ReturnWrapsError -- Should be impossible-          CompNodeType t' -> throwError . ReturnWrapsCompType $ t'-      _ -> throwError . LambdaResultsInNonReturn $ t-    Just (AValNode t _) -> throwError . LambdaResultsInValType $ t+      cntW = view wordCount cnt+  bodyRef <- local (over (#scopeInfo % #argumentInfo) (Vector.cons (cntW, args))) bodyComp+  case bodyRef of+    AnArg (Arg _ _ argTy) -> do+      if argTy == resultT+        then refTo . ACompNode expectedT . LamInternal $ bodyRef+        else throwError . WrongReturnType resultT $ argTy+    AnId bodyId ->+      lookupRef bodyId >>= \case+        Nothing -> throwError . BrokenIdReference $ bodyId+        -- This unifies with anything, so we're fine+        Just AnError -> refTo . ACompNode expectedT . LamInternal . AnId $ bodyId+        Just (AValNode ty _) -> do+          if ty == resultT+            then refTo . ACompNode expectedT . LamInternal . AnId $ bodyId+            else throwError . WrongReturnType resultT $ ty+        Just (ACompNode t _) -> throwError $ LambdaResultsInCompType t  -- | Construct the error node. --@@ -608,37 +686,245 @@   m Id err = refTo AnError --- | Given an 'Id' referring to a computation, and a 'Vector' of 'Ref's to the--- desired arguments, construct the application of the arguments to that--- computation. This can fail for a range of reasons:+-- | Performs both term and type application. More precisely, given: --+-- * An 'Id' referring to a computation; and+-- * A 'Vector' of 'Ref's for the desired term arguments to the computation, in+--   order; and+-- * A 'Vector' of (optional) type arguments to the computation, also in order.+--+-- we produce the result of that application.+--+-- This can fail for a range of reasons:+-- -- * Type mismatch between what the computation expects and what it's given -- * Too many or too few arguments -- * Not a computation type for 'Id' argument -- * Not value types for 'Ref's+-- * Renaming failures (likely due to a malformed function or argument type) ----- @since 1.0.0+-- = Note+--+-- We use the 'Wedge' data type to designate type arguments, as it can represent+-- the three possibilities we need:+--+-- * \'Infer this argument\', specified as 'Nowhere'.+-- * \'Use this type variable in our scope\', specified as 'Here'.+-- * \'Use this concrete type\', specified as 'There'.+--+-- @since 1.2.0 app ::   forall (m :: Type -> Type).   (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>   Id ->   Vector Ref ->+  Vector (Wedge BoundTyVar (ValT Void)) ->   m Id-app fId argRefs = do+app fId argRefs instTys = do   lookedUp <- typeId fId+  let rawSubs = mkSubstitutions instTys+  subs <- renameSubs rawSubs+  scopeInfo <- askScope   case lookedUp of-    CompNodeType fT -> case runRenameM . renameCompT $ fT of+    CompNodeType fT -> case runRenameM scopeInfo . renameCompT $ fT of       Left err' -> throwError . RenameFunctionFailed fT $ err'       Right renamedFT -> do+        instantiatedFT <- instantiate subs renamedFT         renamedArgs <- traverse renameArg argRefs         tyDict <- asks (view #datatypeInfo)-        result <- either (throwError . UnificationError) pure $ checkApp tyDict renamedFT (Vector.toList renamedArgs)-        let restored = undoRename result+        result <- either (throwError . UnificationError) pure $ checkApp tyDict instantiatedFT (Vector.toList renamedArgs)+        restored <- undoRenameM result         checkEncodingWithInfo tyDict restored         refTo . AValNode restored . AppInternal fId $ argRefs     ValNodeType t -> throwError . ApplyToValType $ t     ErrorNodeType -> throwError ApplyToError+  where+    mkSubstitutions :: Vector (Wedge BoundTyVar (ValT Void)) -> [(Index "tyvar", ValT AbstractTy)]+    mkSubstitutions =+      Vector.ifoldl'+        ( \acc i' w ->+            let i = fromJust . preview intIndex $ i'+             in wedge+                  acc+                  (\(BoundTyVar dbIx posIx) -> (i, tyvar dbIx posIx) : acc)+                  (\v -> (i, vacuous v) : acc)+                  w+        )+        [] +    renameSubs :: [(Index "tyvar", ValT AbstractTy)] -> m [(Index "tyvar", ValT Renamed)]+    renameSubs subs =+      askScope >>= \scope -> case traverse (traverse (runRenameM scope . renameValT)) subs of+        Left err' -> throwError $ FailedToRenameInstantiation err'+        Right res -> pure res++    instantiate :: [(Index "tyvar", ValT Renamed)] -> CompT Renamed -> m (CompT Renamed)+    instantiate [] fn = pure fn+    instantiate subs fn = do+      instantiated <- liftUnifyM . fixUp $ foldr (\(i, t) f -> substitute i t f) (ThunkT fn) subs+      case instantiated of+        ThunkT res -> pure res+        other ->+          throwError . UnificationError . ImpossibleHappened $+            "Impossible happened: Result of tyvar instantiation should be a thunk, but is: "+              <> T.pack (show other)++-- | Introduce a data constructor.+--+-- The first argument is a type name (for example, @\"Maybe\"@). The second+-- argument is a constructor of that type (for example, @\"Just\"@ or+-- @\"Nothing\"@). The third argument are the values to \'fill in\' all the fields+-- of the constructor requested.+--+-- = Note+--+-- 'dataConstructor' yields thunks, which must be forced, and then possibly have+-- type arguments applied to them. The reason for this is subtle, but important.+-- Consider the @Nothing@ constructor of @Maybe@: as this has no fields, we+-- cannot use the field type to determine what the type argument to @Maybe@+-- should be in this case. As datatype terms are values, they do not bind type+-- variables, and thus, we cannot have a return type that makes sense in this+-- case.+--+-- We resolve this problem by returning a thunk. In the case of our example,+-- @Nothing@ would produce @<forall a . !Maybe a>@.+--+-- @since 1.2.0+dataConstructor ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  TyName ->+  ConstructorName ->+  Vector Ref ->+  m Id+dataConstructor tyName ctorName fields = do+  thisTyInfo <- lookupDatatypeInfo+  let thisTyDecl = view #originalDecl thisTyInfo+  renamedFieldTypes <-+    traverse renameArg fields+      >>= ( \case+              Nothing -> throwError $ IntroFormErrorNodeField tyName ctorName fields+              Just ok -> pure ok+          )+        . sequence+  {- The procedures for handling a typed declared as Opaque and a "normal" type are totally different.++     For Opaque types, we just have to check that the "ConstructorName" we get corresponds to a constructor of+     PlutusData, then validate that the arguments conform with that PlutusData constructor.+  -}+  case thisTyDecl of+    OpaqueData _ opaqueCtorSet -> do+      checkOpaqueArgs opaqueCtorSet renamedFieldTypes+      refTo $ AValNode (Datatype tyName mempty) (DataConstructorInternal tyName ctorName fields)+    DataDeclaration _ count ctors _ -> do+      -- First we check that the arity of the constructor is equal to the number of fields in the decl.+      checkFieldArity (Vector.length fields) thisTyInfo+      -- Then we resolve the supplied field Refs, rename, and throw an error if we're passed an error node.++      -- Then we construct the return type.+      resultThunk <- mkResultThunk count ctors renamedFieldTypes+      -- Then we undo the renaming.+      restored <- undoRenameM resultThunk+      -- Then we check the compatibility of the arguments with the datatype's encoding.+      asks (view #datatypeInfo) >>= \dti -> checkEncodingWithInfo dti restored+      -- Finally, if nothing has thrown an error, we return a reference to our result node, decorated with the+      -- return type we constructed.+      refTo $ AValNode restored (DataConstructorInternal tyName ctorName fields)+  where+    {- Constructs the result type of the introduction form. Arguments are:+         1. The count (number of tyvars) from the data declaration.+         2. The vector of constructors from the data declaration.+         3. The (renamed and resolved) vector of supplied arguments.++       The procedure goes like:+         1. Extract the argument types from the constructor in the declaration. Any tyvars here+            *have* to be Unifiable (unless something has slipped past the kind checker) -+            data declarations have atomic, independent scopes.+         2. Unify those with the actual, supplied field types, yielding a set of substitutions.+         3. Construct a fully abstract (i.e. parameterized only by unifiable type variables) representation of the+            type constructor.+         4. Apply those substitutions to the abstract type constructor.+         5. Wrap the "concretified" type constructor in a thunk and use `fixUp` to sort out the `Count` and+            indices.+    -}+    mkResultThunk :: Count "tyvar" -> Vector (Constructor Renamed) -> Vector (ValT Renamed) -> m (ValT Renamed)+    mkResultThunk count' declCtors fieldArgs' = do+      declCtorFields <- Vector.toList . view #constructorArgs <$> findConstructor declCtors+      subs <- unifyFields declCtorFields fieldArgs+      let tyConAbstractArgs = mapMaybe (fmap (Abstraction . Unifiable) . preview intIndex) [0, 1 .. (count - 1)]+          tyConAbstract = Datatype tyName (Vector.fromList tyConAbstractArgs)+      let tyConConcrete = Map.foldlWithKey' (\acc i t -> substitute i t acc) tyConAbstract subs+      liftUnifyM . fixUp . ThunkT . Comp0 . ReturnT $ tyConConcrete+      where+        count :: Int+        count = review intCount count'++        fieldArgs :: [ValT Renamed]+        fieldArgs = Vector.toList fieldArgs'++    {- Unifies the declaration fields (which may be abstract) with the supplied fields+       (which will be "concrete", in the sense that "they have to be rigid if they're tyvars").++       Returns a (reconciled) set of substitutions which can be applied to a fully-abstract (i.e.+       parameterized only by Unifiable tyVars) to yield the concrete, applied type constructor.+    -}+    unifyFields :: [ValT Renamed] -> [ValT Renamed] -> m (Map (Index "tyvar") (ValT Renamed))+    unifyFields declFields suppliedFields = liftUnifyM $ do+      rawSubs <- zipWithM unify declFields suppliedFields+      foldM reconcile Map.empty rawSubs++    {- Checks that the number of fields supplied as arguments is equal to the+       number of fields in the corresponding constructor of the data declaration.++       This is needed because `zipWithM unifyFields` won't throw an error in the case that they are not equal.+    -}+    checkFieldArity :: Int -> DatatypeInfo Renamed -> m ()+    checkFieldArity actualNumFields dtInfo = do+      let ctors = toListOf (#originalDecl % #datatypeConstructors % folded) dtInfo+      expectedNumFields <- Vector.length . view #constructorArgs <$> findConstructor ctors+      unless (actualNumFields == expectedNumFields) $+        throwError $+          IntroFormWrongNumArgs tyName ctorName actualNumFields++    checkOpaqueArgs :: Set.Set PlutusDataConstructor -> Vector (ValT Renamed) -> m ()+    checkOpaqueArgs declCtors fieldArgs' = case ctorName of+      "I" -> opaqueCheck PlutusI [BuiltinFlat IntegerT]+      "B" -> opaqueCheck PlutusB [BuiltinFlat ByteStringT]+      "List" -> opaqueCheck PlutusList [Datatype "List" (Vector.fromList [Datatype "Data" mempty])]+      "Map" -> opaqueCheck PlutusMap [Datatype "Map" (Vector.fromList [Datatype "Data" mempty, Datatype "Data" mempty])]+      "Constr" -> opaqueCheck PlutusConstr [BuiltinFlat IntegerT, Datatype "List" (Vector.fromList [Datatype "Data" mempty])]+      _ -> throwError $ UndeclaredOpaquePlutusDataCtor declCtors ctorName+      where+        fieldArgs :: [ValT Renamed]+        fieldArgs = Vector.toList fieldArgs'+        opaqueCheck :: PlutusDataConstructor -> [ValT Renamed] -> m ()+        opaqueCheck setMustHaveThis fieldShouldBeThis = do+          unless (setMustHaveThis `Set.member` declCtors) $ throwError (UndeclaredOpaquePlutusDataCtor declCtors ctorName)+          unless (fieldArgs == fieldShouldBeThis) $ throwError (InvalidOpaqueField declCtors ctorName fieldArgs)++    -- convenience helpers++    -- Looks up a constructor in a foldable container of constructors (which is probably always a vector but w/e)+    -- Exists to avoid duplicating this code in a few places.+    findConstructor ::+      forall (t :: Type -> Type) (a :: Type).+      (Foldable t) =>+      t (Constructor a) ->+      m (Constructor a)+    findConstructor xs = case find (\x -> view #constructorName x == ctorName) xs of+      Nothing -> throwError $ ConstructorDoesNotExistForType tyName ctorName+      Just ctor' -> pure ctor'++    -- Looks up the DatatypeInfo for the type argument supplied+    -- and also renames (and rethrows the rename error if renaming fails)+    lookupDatatypeInfo :: m (DatatypeInfo Renamed)+    lookupDatatypeInfo =+      asks (preview (#datatypeInfo % ix tyName)) >>= \case+        Nothing -> throwError $ TypeDoesNotExist tyName+        Just infoAbstract -> case renameDatatypeInfo infoAbstract of+          Left e -> throwError $ DatatypeInfoRenameError e+          Right infoRenamed -> pure infoRenamed+ -- | Construct a node corresponding to the given constant. -- -- @since 1.0.0@@ -665,20 +951,260 @@     ValNodeType t -> throwError . ThunkValType $ t     ErrorNodeType -> throwError ThunkError +-- | Given a 'Ref' to an algebra (that is, something taking a base functor and+-- producing some result), and a 'Ref' to a value associated with that base+-- functor, build a catamorphism to tear it down. This can fail for a range of+-- reasons:+--+-- * First 'Ref' is not a thunk taking one argument+-- * The argument to the thunk isn't a base functor, or isn't a suitable base+--   functor for the second argument+-- * Second argument is not a value type+--+-- = Note+--+-- 'cata' cannot work with /non-rigid/ algebras; that is, all algebras must be+-- functions that bind no type variables of their own.+--+-- @since 1.1.0+cata ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  Ref ->+  Ref ->+  m Id+cata rAlg rVal =+  typeRef rVal >>= \case+    ValNodeType valT ->+      typeRef rAlg >>= \case+        t@(ValNodeType (ThunkT algT)) -> case algT of+          Comp0 (CompTBody nev) -> do+            let algebraArity = arity algT+            unless (algebraArity == 1) (throwError . CataAlgebraWrongArity $ algebraArity)+            case nev NonEmpty.! 0 of+              Datatype bfName bfTyArgs -> do+                -- If we got this far, we know at minimum that we have somewhat+                -- sensical arguments. Now we have to make sure that we have a+                -- suitable type for the algebra, and a suitable thing to tear+                -- down.+                --+                -- After verifying this, we use `tryApply` so the unification+                -- machinery can produce the type we expect with proper+                -- concretifications.+                unless (Vector.length bfTyArgs > 0) (throwError . CataNotAnAlgebra $ t)+                let lastTyArg = Vector.last bfTyArgs+                unless (nev NonEmpty.! 1 == lastTyArg) (throwError . CataNotAnAlgebra $ t)+                appliedArgT <- case valT of+                  BuiltinFlat bT -> case bT of+                    ByteStringT -> do+                      unless (bfName == "ByteString_F") (throwError . CataUnsuitable algT $ valT)+                      pure $ Datatype "ByteString_F" . Vector.singleton $ lastTyArg+                    IntegerT -> do+                      let isSuitableBaseFunctor = bfName == "Natural_F" || bfName == "Negative_F"+                      unless isSuitableBaseFunctor (throwError . CataUnsuitable algT $ valT)+                      pure $ Datatype bfName . Vector.singleton $ lastTyArg+                    _ -> throwError . CataWrongBuiltinType $ bT+                  Datatype tyName tyVars -> do+                    lookedUp <- asks (view (#datatypeInfo % at tyName))+                    case lookedUp of+                      Nothing -> throwError . CataNoSuchType $ tyName+                      Just info -> case view #baseFunctor info of+                        Just (DataDeclaration actualBfName _ _ _, _) -> do+                          unless (bfName == actualBfName) (throwError . CataUnsuitable algT $ valT)+                          let lastTyArg' = stepDownDB lastTyArg+                          pure . Datatype bfName . Vector.snoc tyVars $ lastTyArg'+                        _ -> throwError . CataNoBaseFunctorForType $ tyName+                  _ -> throwError . CataWrongValT $ valT+                resultT <- tryApply algT appliedArgT+                refTo . AValNode resultT . CataInternal rAlg $ rVal+              _ -> throwError . CataNotAnAlgebra $ t+          _ -> throwError . CataNonRigidAlgebra $ algT+        t -> throwError . CataNotAnAlgebra $ t+    t -> throwError . CataApplyToNonValT $ t++-- | Perform a pattern match. The first argument is the value to be matched on,+-- and the second argument is a 'Vector' of \'handlers\' for each possible+-- \'arm\' of the type of the value to be matched on.+--+-- All handlers must be thunks, and must all return the same (concrete) result.+-- Polymorphic \'handlers\' (that is, thunks whose computation binds type+-- variables of its own) will fail to compile.+--+-- @since 1.2.0+match ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  Ref ->+  Vector Ref ->+  m Id+match scrutinee handlers = do+  scrutNodeTy <- typeRef scrutinee+  case scrutNodeTy of+    ValNodeType scrutTy@(Datatype tn args) ->+      isRecursive scrutTy >>= \case+        True -> goRecursive tn args+        False -> goNonRecursive tn args+    ValNodeType other -> throwError $ MatchNonDatatypeScrutinee other+    other -> throwError $ MatchNonValTy other+  where+    isRecursive :: ValT AbstractTy -> m Bool+    isRecursive (Datatype tyName _) = do+      datatypeInfoExists <- asks (isJust . preview (#datatypeInfo % ix tyName))+      if datatypeInfoExists+        then asks (isJust . join . preview (#datatypeInfo % ix tyName % #baseFunctor))+        else throwError $ MatchNoDatatypeInfo tyName+    isRecursive _ = pure False++    goRecursive :: TyName -> Vector (ValT AbstractTy) -> m Id+    goRecursive tn@(TyName rawTn) tyConArgs = do+      -- This fromJust is safe b/c the presence of absence of base functor data is the condition that+      -- determines whether we're in this branch or the non-recursive one+      rawBFBB <- asks (snd . fromJust . join . preview (#datatypeInfo % ix tn % #baseFunctor))+      bfbb <- instantiateBFBB rawBFBB+      handlers' <- Vector.toList <$> traverse cleanupHandler handlers+      tyDict <- asks (view #datatypeInfo)+      case checkApp tyDict bfbb (Just <$> handlers') of+        Right appliedBfbb -> do+          result <- undoRenameM appliedBfbb+          refTo $ AValNode result (MatchInternal scrutinee handlers)+        Left err' -> throwError . UnificationError $ err'+      where+        instantiateBFBB :: ValT AbstractTy -> m (CompT Renamed)+        instantiateBFBB bfbb = do+          -- we have a BFBB like:+          -- listBB :: forall a r . r -> <a -> r -> !r> -> !r+          -- And we need to:+          --   1. Instantiate all of the type arguments to the original datatype (e.g. the 'a' in List a)+          --      into the BFBB+          --   2. Instantiate the *last* tyvar bound by the BBBF to the type of the original datatype+          --      giving us, e.g. ListF a (List a)+          scope <- askScope+          renamedBFBB <- case runRenameM scope (renameValT bfbb) of+            Left err' -> throwError $ MatchRenameBBFail err'+            Right res -> pure res+          -- The type constructor for the base-functor variant of the scrutinee type.+          let scrut = Datatype tn tyConArgs+          let scrutF = Datatype (TyName $ rawTn <> "_F") (Vector.snoc tyConArgs scrut)+          -- These are arguments to the original type constructor plus the snoc'd original type.+          -- E.g. if we have:+          --      Scrutinee: List Int+          --   this should be:+          --   [Int, List Int]+          let bfInstArgs = Vector.snoc tyConArgs scrutF+          renamedArgs <- case runRenameM scope (traverse renameValT bfInstArgs) of+            Left err' -> throwError $ MatchRenameTyConArgFail err'+            Right res -> pure res+          let subs :: Vector (Index "tyvar", ValT Renamed)+              subs = Vector.imap (\i v -> (fromJust . preview intIndex $ i, v)) renamedArgs+              subbed = foldl' (\bbf (i, v) -> substitute i v bbf) renamedBFBB subs+          case subbed of+            ThunkT bfComp -> pure bfComp+            other -> throwError $ MatchNonThunkBBF other++    -- Unwraps a thunk handler if it is a handler for a nullary constructor.+    cleanupHandler :: Ref -> m (ValT Renamed)+    cleanupHandler r =+      renameArg r >>= \case+        Nothing ->+          throwError $ MatchErrorAsHandler r+        Just hVal -> case hVal of+          hdlr@(ThunkT (CompT cnt (ReturnT v)))+            | cnt == count0 -> pure v+            | otherwise -> throwError $ MatchPolymorphicHandler hdlr+          other -> pure other++    goNonRecursive :: TyName -> Vector (ValT AbstractTy) -> m Id+    goNonRecursive tn tyConArgs = do+      rawBBF <- asks (fromJust . preview (#datatypeInfo % ix tn % #bbForm))+      (instantiatedBBF :: CompT Renamed) <- instantiateBB rawBBF tyConArgs+      handlers' <- Vector.toList <$> traverse cleanupHandler handlers+      tyDict <- asks (view #datatypeInfo)+      case checkApp tyDict instantiatedBBF (Just <$> handlers') of+        Right appliedBBF -> do+          result <- undoRenameM appliedBBF+          refTo $ AValNode result (MatchInternal scrutinee handlers)+        Left err' -> throwError . UnificationError $ err'+      where+        instantiateBB :: Maybe (ValT AbstractTy) -> Vector (ValT AbstractTy) -> m (CompT Renamed)+        instantiateBB Nothing _ = throwError $ MatchNoBBForm tn+        instantiateBB (Just bb) tyArgs = do+          scope <- askScope+          renamedBB <- case runRenameM scope (renameValT bb) of+            Left err' -> throwError $ MatchRenameBBFail err'+            Right res -> pure res+          renamedArgs <- case runRenameM scope (traverse renameValT tyArgs) of+            Left err' -> throwError $ MatchRenameTyConArgFail err'+            Right res -> pure res+          let subs :: Vector (Index "tyvar", ValT Renamed)+              subs = Vector.imap (\i v -> (fromJust . preview intIndex $ i, v)) renamedArgs+              subbed = foldl' (\bbf (i, v) -> substitute i v bbf) renamedBB subs+          case subbed of+            ThunkT bbComp -> pure bbComp+            other -> throwError $ MatchNonThunkBBF other+ -- Helpers +-- Note (Koz, 13/08/2025): We need this procedure specifically for `cata`. The+-- reason for this has to do with how we construct the 'base functor form' of+-- the value to be torn down by the catamorphism, in order to use the+-- unification machinery to get the type of the final result.+--+-- To be specific, suppose we have `<List_F r (Maybe r) -> !Maybe r>` as our algebra+-- argument (where `r` is some rigid), and `List r` as the value to be torn+-- down. If we assume the rigid is bound one scope away, `r`'s DeBruijn index+-- will be `S Z` for+-- the value to be torn down, but `S (S Z)` for the algebra argument. The way+-- our approach works is:+--+-- 1. Look at the algebra argument, specifically the base functor type. Take its+--    last type argument, which we will call `last`.+-- 2. Determine the base functor for the value to be torn down. Cook up a new+--    instance of the base functor type, copying all the type arguments from the+--    value to be torn down in the same order. Then put `last` at the end.+-- 3. Force the algebra argument thunk, then try and apply the result of Step 2+--    to that.+--+-- Following the steps above for our example, we would proceed as follows:+--+-- 1. Set `last` as `Maybe r`.+-- 2. Cook up `List_F r (Maybe r)`. Note that this matches what the algebra+--    expects.+-- 3. Use the unifier with `List_F r (Maybe r) -> !Maybe r`, applying the+--    argument `List_F r (Maybe r)` from Step 2.+--+-- However, if `last` is a rigid, we have an 'off by one error'. To see why,+-- consider the form of the algebra argument:+--+-- `ThunkT . Comp0 $ Datatype "List_F" [tyvar (S (S Z)) ix0, ....`+--+-- However, `tyvar (S (S Z)) ix0` is not valid in the scope of the value to be+-- torn down: that same rigid would have DeBruijn index `S Z` there instead.+-- This applies the same if the tyvar is part of a datatype.+--+-- As we prohibit non-rigid algebras, this requires us to lower the DeBruijn+-- index by one for our process.+stepDownDB :: ValT AbstractTy -> ValT AbstractTy+stepDownDB = \case+  Abstraction (BoundAt db i) -> case db of+    -- This is impossible, so we just return it unmodified+    Z -> Abstraction (BoundAt db i)+    (S db') -> Abstraction (BoundAt db' i)+  Datatype tyName tyArgs -> Datatype tyName . fmap stepDownDB $ tyArgs+  x -> x+ renameArg ::   forall (m :: Type -> Type).-  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) =>+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>   Ref ->   m (Maybe (ValT Renamed)) renameArg r =-  typeRef r >>= \case-    CompNodeType t -> throwError . ApplyCompType $ t-    ValNodeType t -> case runRenameM . renameValT $ t of-      Left err' -> throwError . RenameArgumentFailed t $ err'-      Right renamed -> pure . Just $ renamed-    ErrorNodeType -> pure Nothing+  askScope >>= \scope ->+    typeRef r >>= \case+      CompNodeType t -> throwError . ApplyCompType $ t+      ValNodeType t -> case runRenameM scope . renameValT $ t of+        Left err' -> throwError . RenameArgumentFailed t $ err'+        Right renamed -> pure . Just $ renamed+      ErrorNodeType -> pure Nothing  checkEncodingWithInfo ::   forall (a :: Type) (m :: Type -> Type).@@ -689,3 +1215,146 @@ checkEncodingWithInfo tyDict valT = case checkEncodingArgs (view (#originalDecl % #datatypeEncoding)) tyDict valT of   Left encErr -> throwError $ EncodingError encErr   Right {} -> pure ()++tryApply ::+  forall (m :: Type -> Type).+  (MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  CompT AbstractTy ->+  ValT AbstractTy ->+  m (ValT AbstractTy)+tryApply algebraT argT =+  askScope >>= \scope -> case runRenameM scope . renameCompT $ algebraT of+    Left err' -> throwError . RenameFunctionFailed algebraT $ err'+    Right renamedAlgebraT -> case runRenameM scope . renameValT $ argT of+      Left err' -> throwError . RenameArgumentFailed argT $ err'+      Right renamedArgT -> do+        tyDict <- asks (view #datatypeInfo)+        case checkApp tyDict renamedAlgebraT [Just renamedArgT] of+          Left err' -> throwError . UnificationError $ err'+          Right resultT -> undoRenameM resultT++-- Putting this here to reduce chance of annoying manual merge (will move later)++-- | Wrapper around an `Arg` that we know represents an in-scope type variable.+-- @since 1.2.0+data BoundTyVar = BoundTyVar DeBruijn (Index "tyvar")+  deriving stock+    ( -- @since 1.2.0+      Show,+      -- @since 1.2.0+      Eq,+      -- @since 1.2.0+      Ord+    )++-- | Given a DeBruijn index (designating scope) and positional index (designating+-- which variable in that scope we are interested in), retrieve an in-scope type+-- variable.+--+-- This will error if we request a type variable in a scope that doesn't exist,+-- or at a position that doesn't exist in that scope.+--+-- @since 1.2.0+boundTyVar ::+  forall (m :: Type -> Type).+  (MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  DeBruijn ->+  Index "tyvar" ->+  m BoundTyVar+boundTyVar scope index = do+  let scopeAsInt = review asInt scope+      indexAsWord :: Word32+      indexAsWord = fromIntegral $ review intIndex index+  tyVarInScope <-+    asks (preview (#scopeInfo % #argumentInfo % ix scopeAsInt % _1)) >>= \case+      Nothing -> pure False+      Just varsBoundAtScope ->+        -- varsBoundAtScope is the count of the CompT binding context verbatim+        if varsBoundAtScope <= 0+          then pure False+          else pure $ indexAsWord < varsBoundAtScope+  if tyVarInScope+    then pure (BoundTyVar scope index)+    else throwError $ OutOfScopeTyVar scope index++-- To avoid annoying code duplication++-- Helper to avoid having to manually catch and rethrow the error+undoRenameM ::+  forall (m :: Type -> Type).+  (MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  ValT Renamed ->+  m (ValT AbstractTy)+undoRenameM val = do+  scope <- asks (fmap fst . view (#scopeInfo % #argumentInfo))+  case undoRename scope val of+    Left err' -> throwError $ UndoRenameFailure err'+    Right renamed -> pure renamed++askScope ::+  forall (m :: Type -> Type).+  (MonadReader ASGEnv m) =>+  m (Vector Word32)+askScope = asks (fmap fst . view (#scopeInfo % #argumentInfo))++-- Runs a UnifyM computation in our abstract monad. Again, largely to avoid superfluous code+-- duplication.+liftUnifyM ::+  forall (m :: Type -> Type) (a :: Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  UnifyM a ->+  m a+liftUnifyM act = do+  tyDict <- asks (view #datatypeInfo)+  case runUnifyM tyDict act of+    Left e -> throwError $ UnificationError e+    Right res -> pure res++-- Utility functions for ASG construction. These are not strictly necessary, but are extremely convenient.++-- | Constructs a datatype value at given constructor. This is different to+-- 'dataConstructor', as it doesn't produce a thunk.+--+-- The third argument is a 'Vector' of values to \'fill in\' all the fields+-- required by the stated constructor. The fourth argument is a 'Vector' of+-- \'type instantiations\', which allow \'concretification\' of any lingering+-- polymorphic type variables which are not determined by the field values given+-- as the third argument.+--+-- = Example+--+-- Consider @Left 3@. In this case, the field only determines the first type+-- argument to the @Either@ data type, and if we used 'dataConstructor', we+-- would be left with a thunk of type @<forall a . !Either Integer a>@. Using+-- 'ctor', we can immediately specify what @a@ should be, and unwrap the thunk.+--+-- @since 1.2.0+ctor ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  TyName ->+  ConstructorName ->+  Vector.Vector Ref ->+  Vector.Vector (Wedge BoundTyVar (ValT Void)) ->+  m Id+ctor tn cn args instTys = do+  dataThunk <- dataConstructor tn cn args+  dataForced <- force (AnId dataThunk)+  app dataForced mempty instTys++-- | As 'lam', but produces a thunk value instead of a computation.+--+-- @since 1.2.0+lazyLam ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m, MonadReader ASGEnv m) =>+  CompT AbstractTy ->+  m Ref ->+  m Id+lazyLam expected bodyComp = lam expected bodyComp >>= thunk++-- | Helper to avoid using 'Vector.fromList' when defining data types.+--+-- @since 1.2.0+dtype :: TyName -> [ValT AbstractTy] -> ValT AbstractTy+dtype tn = Datatype tn . Vector.fromList
src/Covenant/Data.hs view
@@ -35,6 +35,7 @@     isRecursiveChildOf,     hasRecursive,     everythingOf,+    mapValT,   ) where 
src/Covenant/Index.hs view
@@ -22,6 +22,7 @@     count2,     ix3,     count3,+    wordCount,   ) where @@ -31,6 +32,7 @@ import Data.Semigroup (Semigroup (sconcat, stimes), Sum (Sum)) import Data.Word (Word32) import GHC.TypeLits (Symbol)+import Optics.Core (Lens', lens) import Optics.Prism (Prism', prism) import Test.QuickCheck (Arbitrary) @@ -146,6 +148,11 @@   prism     (fromIntegral . coerce @_ @Word32)     (\i -> maybe (Left i) (Right . Count) . toIntegralSized $ i)++-- | We use the Word32 directly during renaming, and a Lens is more appropriate+-- than a Prism if we're working with Word32s+wordCount :: forall (ofWhat :: Symbol). Lens' (Count ofWhat) Word32+wordCount = lens (\(Count x) -> x) (\_ w -> Count w)  -- | Helper for a count of zero items. --
src/Covenant/Internal/KindCheck.hs view
@@ -186,8 +186,18 @@  -} --- First arg is the name of the type constructor w/ a bad argument, second arg is the bad argument.-data EncodingArgErr a = EncodingArgMismatch TyName (ValT a)+-- | Indicates that we tried to instantiate a polymorphic data type using a type+-- whose encoding is incompatible. The most common case of this is when we have+-- a \'polymorphic\' data type that uses some kind of @Data@ encoding and we try+-- to instantiate it with a type which has a SOP encoding.+--+-- @since 1.2.0+data EncodingArgErr a+  = -- | First field is a constructor name for the type we tried to instantiate,+    -- second field is the bad instantiator.+    --+    -- @since 1.2.0+    EncodingArgMismatch TyName (ValT a)   deriving stock (Show, Eq)  -- | Verifies that a datatype (third argument) is valid according to its stated
src/Covenant/Internal/Rename.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE MultiWayIf #-}+ module Covenant.Internal.Rename   ( RenameM,     RenameError (..),@@ -7,17 +9,21 @@     renameCompT,     undoRename,     renameDatatypeInfo,+    UnRenameM,+    UnRenameError (..),+    runUnRenameM,   ) where -import Control.Monad (unless) import Control.Monad.Except   ( ExceptT,+    MonadError,     runExceptT,     throwError,   ) import Control.Monad.Reader-  ( Reader,+  ( MonadReader,+    Reader,     asks,     local,     runReader,@@ -29,8 +35,8 @@     modify,   ) import Covenant.Data (DatatypeInfo (DatatypeInfo))-import Covenant.DeBruijn (DeBruijn (S, Z), asInt)-import Covenant.Index (Count, Index, intCount, intIndex)+import Covenant.DeBruijn (DeBruijn (Z), asInt)+import Covenant.Index (Count, Index, intIndex, wordCount) import Covenant.Internal.Type   ( AbstractTy (BoundAt),     CompT (CompT),@@ -43,17 +49,16 @@ import Data.Bitraversable (Bitraversable (bitraverse)) import Data.Coerce (coerce) import Data.Kind (Type)-import Data.Tuple.Optics (_1) import Data.Vector (Vector) import Data.Vector qualified as Vector import Data.Vector.NonEmpty qualified as NonEmpty-import Data.Word (Word64)+import Data.Word (Word32, Word64) import Optics.Core   ( A_Lens,     LabelOptic (labelOptic),-    ix,     lens,     over,+    preview,     review,     set,     to,@@ -62,12 +67,14 @@   )  -- Used during renaming. Contains a source of fresh indices for wildcards, as--- well as tracking:+-- well as: ----- 1. How many variables are bound by each scope;--- 2. Which of these variables have been noted as used; and--- 3. A unique identifier for each scope (for wildcards).-data RenameState = RenameState Word64 (Vector (Vector Bool, Word64))+-- 1. The first Word64 argument is the "source of freshness" for WildCards+-- 2. The second Word64 argument is the inherited scope size+-- 3. The *size* of the vector tracks the current scope size (the enclosing scope is inherited, but it may grow during renaming)+-- 4. The first element of the tuple in the vector is the *count* of TyVars bound in each scope. (Note: It is therefore 1 greater than the index)+-- 5. The second element of the tuple in the vector is the unique identifier for wildcards in each scope.+data RenameState = RenameState Word64 Word32 (Vector (Word32, Word64))   deriving stock (Eq, Show)  -- Note (Koz, 11/04/2025): We need this field as a source of unique identifiers@@ -94,35 +101,65 @@   {-# INLINEABLE labelOptic #-}   labelOptic =     lens-      (\(RenameState x _) -> x)-      (\(RenameState _ y) x' -> RenameState x' y)+      (\(RenameState x _ _) -> x)+      (\(RenameState _ b c) a' -> RenameState a' b c) --- The 'outer' vector represents a stack of scopes. Each entry is a combination--- of a vector of used variables (length is equal to the number of variables--- bound by that scope), together with a unique identifier not only for that--- scope, but also the `step` into that scope, as required by wildcard renaming. instance-  (k ~ A_Lens, a ~ Vector (Vector Bool, Word64), b ~ Vector (Vector Bool, Word64)) =>+  (k ~ A_Lens, a ~ Word32, b ~ Word32) =>+  LabelOptic "inheritedScope" k RenameState RenameState a b+  where+  {-# INLINEABLE labelOptic #-}+  labelOptic =+    lens+      (\(RenameState _ x _) -> x)+      (\(RenameState a _ c) b' -> RenameState a b' c)++instance+  (k ~ A_Lens, a ~ Vector (Word32, Word64), b ~ Vector (Word32, Word64)) =>   LabelOptic "tracker" k RenameState RenameState a b   where   {-# INLINEABLE labelOptic #-}   labelOptic =     lens-      (\(RenameState _ y) -> y)-      (\(RenameState x _) y' -> RenameState x y')+      (\(RenameState _ _ y) -> y)+      (\(RenameState x y _) z' -> RenameState x y z')  -- | Ways in which the renamer can fail. -- -- @since 1.1.0 data RenameError   = -- | An attempt to reference an abstraction in a scope where this-    -- abstraction doesn't exist. First field is the true level, second is-    -- the index that was requested.+    -- abstraction doesn't exist, but where the scope itself /does/ exist.+    -- Put another way: This gets thrown when the argument index of an+    -- abstraction inconsistent with the `Count` of the scope its DB index refers to.+    -- First field is the true level, second is the index that was requested.     ---    -- @since 1.0.0+    -- @since 1.2.0     InvalidAbstractionReference Int (Index "tyvar")+  | -- | An abstraction refers to a scope which does not exist. That is: The abstraction's+    -- DeBruijn index points to a scope "higher than" the top-level scope.+    --+    -- @since 1.2.0+    InvalidScopeReference Int (Index "tyvar")   deriving stock (Eq, Show) +-- | Ways in which the un-renamer can fail.+--+-- @since 1.2.0+data UnRenameError+  = -- | We tried to un-rename a wildcard. This means something has gone very wrong internally.+    -- @since 1.2.0+    UnRenameWildCard Renamed+  | -- | We received a negative DeBruijn in our true level calculation. This is impossible, and indicates another+    --   internal malfunction or bug+    NegativeDeBruijn Int+  deriving stock+    ( -- | @since 1.2.0+      Eq,+      -- | @since 1.2.0+      Show+    )+ -- | A \'renaming monad\' which allows us to convert type representations from -- ones that use /relative/ abstraction labelling to /absolute/ abstraction -- labelling.@@ -156,15 +193,74 @@     )     via (ExceptT RenameError (State RenameState)) +-- | The portions of the RenameState needed for unrenaming. Lacks the unique indicator for+-- wildcards, since trying to un-rename a wildcard is an error.+data UnRenameCxt = UnRenameCxt Word32 (Vector Word32)+  deriving stock+    ( -- @since 1.2.0+      Show,+      -- @since 1.2.0+      Eq,+      -- @since 1.2.0+      Ord+    )++instance+  (k ~ A_Lens, a ~ Word32, b ~ Word32) =>+  LabelOptic "inheritedScopeSize" k UnRenameCxt UnRenameCxt a b+  where+  {-# INLINEABLE labelOptic #-}+  labelOptic =+    lens+      (\(UnRenameCxt x _) -> x)+      (\(UnRenameCxt _ y) x' -> UnRenameCxt x' y)++instance+  (k ~ A_Lens, a ~ Vector Word32, b ~ Vector Word32) =>+  LabelOptic "scopeInfo" k UnRenameCxt UnRenameCxt a b+  where+  {-# INLINEABLE labelOptic #-}+  labelOptic =+    lens+      (\(UnRenameCxt _ y) -> y)+      (\(UnRenameCxt x _) y' -> UnRenameCxt x y')++-- | @since 1.2.0+newtype UnRenameM (a :: Type) = UnRenameM (ExceptT UnRenameError (Reader UnRenameCxt) a)+  deriving+    ( -- | @since 1.2.0+      Functor,+      -- | @since 1.2.0+      Applicative,+      -- | @since 1.2.0+      Monad,+      -- | @since 1.2.0+      MonadReader UnRenameCxt,+      -- | @since 1.2.0+      MonadError UnRenameError+    )+    via (ExceptT UnRenameError (Reader UnRenameCxt))+ -- | Execute a renaming computation. ----- @since 1.0.0+-- @since 1.2.0 runRenameM ::   forall (a :: Type).+  Vector Word32 ->   RenameM a ->   Either RenameError a-runRenameM (RenameM comp) = evalState (runExceptT comp) . RenameState 0 $ Vector.empty+runRenameM scopeInfo (RenameM comp) =+  evalState (runExceptT comp)+    . RenameState 0 (fromIntegral $ Vector.length scopeInfo)+    $ Vector.map (,0) scopeInfo +runUnRenameM ::+  forall (a :: Type).+  UnRenameM a ->+  Vector Word32 ->+  Either UnRenameError a+runUnRenameM (UnRenameM comp) inherited = runReader (runExceptT comp) $ UnRenameCxt (fromIntegral $ Vector.length inherited) inherited+ -- | Rename a computation type. -- -- @since 1.0.0@@ -211,8 +307,10 @@     renameCtor :: Constructor AbstractTy -> RenameM (Constructor Renamed)     renameCtor (Constructor cn args) = Constructor cn <$> traverse renameValT args +-- REVIEW: I am not sure if we really want the scope arg to runRenameM to be `mempty`.+--         If something breaks w/ BB forms or datatypes, look here. renameDatatypeInfo :: DatatypeInfo AbstractTy -> Either RenameError (DatatypeInfo Renamed)-renameDatatypeInfo (DatatypeInfo ogDecl baseFStuff bb) = runRenameM $ do+renameDatatypeInfo (DatatypeInfo ogDecl baseFStuff bb) = runRenameM mempty $ do   ogDecl' <- renameDataDecl ogDecl   baseFStuff' <- traverse (bitraverse renameDataDecl renameValT) baseFStuff   bb' <- traverse renameValT bb@@ -221,60 +319,65 @@ -- A way of 'undoing' the renaming process. This is meant to be used only after -- applications, and assumes that what is being un-renamed is the result of a -- computation.-undoRename :: ValT Renamed -> ValT AbstractTy-undoRename t = runReader (go t) 1+--+-- @since 1.2.0+undoRename :: Vector Word32 -> ValT Renamed -> Either UnRenameError (ValT AbstractTy)+undoRename scope t = runUnRenameM (go t) scope   where-    go :: ValT Renamed -> Reader Int (ValT AbstractTy)+    go :: ValT Renamed -> UnRenameM (ValT AbstractTy)     go = \case       Abstraction t' ->         Abstraction <$> case t' of-          Unifiable index -> BoundAt <$> trueLevelToDB 1 <*> pure index-          Rigid trueLevel index -> BoundAt <$> trueLevelToDB trueLevel <*> pure index-          Wildcard _ trueLevel index -> BoundAt <$> trueLevelToDB trueLevel <*> pure index+          Rigid trueLevel index -> do+            db <- unTrueLevel trueLevel+            pure $ BoundAt db index+          w@(Wildcard {}) -> throwError $ UnRenameWildCard w+          Unifiable index -> pure $ BoundAt Z index       ThunkT (CompT abses (CompTBody xs)) ->-        ThunkT . CompT abses . CompTBody <$> local (+ 1) (traverse go xs)+        ThunkT+          . CompT abses+          . CompTBody+          <$> local (over #scopeInfo (Vector.cons $ view wordCount abses)) (traverse go xs)       BuiltinFlat t' -> pure . BuiltinFlat $ t'       Datatype tn args -> Datatype tn <$> traverse go args --- Helpers--trueLevelToDB :: Int -> Reader Int DeBruijn-trueLevelToDB trueLevel = asks (go . subtract trueLevel)-  where-    go :: Int -> DeBruijn-    go = \case-      0 -> Z-      n -> S . go $ n - 1+    unTrueLevel :: Int -> UnRenameM DeBruijn+    unTrueLevel tl = do+      trackerLen <- asks (Vector.length . view #scopeInfo)+      inheritedSize <- asks (fromIntegral . view #inheritedScopeSize)+      let db = trackerLen - 1 - inheritedSize - tl+      case preview asInt db of+        Nothing -> throwError $ NegativeDeBruijn db+        Just res -> pure res  renameAbstraction :: AbstractTy -> RenameM Renamed renameAbstraction (BoundAt scope index) = RenameM $ do-  trueLevel <- gets (\x -> view (#tracker % to Vector.length) x - review asInt scope)+  inheritedScopeSize <- gets (fromIntegral . view #inheritedScope)+  trueLevel <- gets (\x -> view (#tracker % to Vector.length) x - 1 - inheritedScopeSize - review asInt scope)   scopeInfo <- gets (\x -> view #tracker x Vector.!? review asInt scope)   let asIntIx = review intIndex index   case scopeInfo of-    -- This variable is bound in a scope that encloses the renaming scope. Thus,-    -- the variable is rigid.-    Nothing -> pure . Rigid trueLevel $ index-    Just (occursTracker, uniqueScopeId) -> case occursTracker Vector.!? asIntIx of-      Nothing -> throwError . InvalidAbstractionReference trueLevel $ index-      Just beenUsed -> do-        -- Note that this variable has occurred-        unless beenUsed (modify (noteUsed scope index))-        pure $-          if trueLevel == 1-            -- This is a unifiable variable-            then Unifiable index-            -- This is a wildcard variable-            else Wildcard uniqueScopeId trueLevel index+    Nothing -> throwError . InvalidScopeReference trueLevel $ index+    Just (occursTracker, uniqueScopeId) ->+      if+        | not (checkVarIxExists asIntIx occursTracker) -> throwError . InvalidAbstractionReference trueLevel $ index+        | trueLevel == 0 -> pure $ Unifiable index+        | trueLevel < 0 -> pure $ Rigid trueLevel index+        | otherwise -> pure $ Wildcard uniqueScopeId trueLevel index+  where+    checkVarIxExists :: Int -> Word32 -> Bool+    checkVarIxExists i wCount = fromIntegral i < wCount +-- Helpers+ -- Given a number of abstractions bound by a scope, modify the state to track -- that scope. stepUpScope :: Count "tyvar" -> RenameState -> RenameState stepUpScope abses x =   let fresh = view #idSource x-      absesI = review intCount abses+      absesW = view wordCount abses       -- Label (speculatively) the current scope 'step' with a unique value.-      entry = (Vector.replicate absesI False, fresh)+      entry = (absesW, fresh)    in -- Ensure that our source of fresh identifiers is incremented       over #tracker (Vector.cons entry) . set #idSource (fresh + 1) $ x @@ -286,9 +389,3 @@ -- achieve our goal of renaming wildcards. dropDownScope :: RenameState -> RenameState dropDownScope = over #tracker Vector.tail---- Given a pair of DeBruijn index and positional index for a variable, note that--- we've seen this variable.-noteUsed :: DeBruijn -> Index "tyvar" -> RenameState -> RenameState-noteUsed scope index =-  set (#tracker % ix (review asInt scope) % _1 % ix (review intIndex index)) True
src/Covenant/Internal/Term.hs view
@@ -20,11 +20,19 @@ import Covenant.DeBruijn (DeBruijn) import Covenant.Index (Index) import Covenant.Internal.KindCheck (EncodingArgErr)-import Covenant.Internal.Rename (RenameError)-import Covenant.Internal.Type (AbstractTy, CompT, ValT)+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) @@ -94,8 +102,8 @@     ReturnCompType (CompT AbstractTy)   | -- | The body of a lambda results in a value-typed node, which isn't allowed.     ---    -- @since 1.0.0-    LambdaResultsInValType (ValT AbstractTy)+    -- @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.     --@@ -118,10 +126,141 @@     --     -- @since 1.0.0     WrongReturnType (ValT AbstractTy) (ValT AbstractTy)-  | -- @since 1.1.0--    -- | Wraps an encoding argument mismatch error from KindCheck+  | -- | 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,@@ -219,9 +358,8 @@   | Builtin2Internal TwoArgFunc   | Builtin3Internal ThreeArgFunc   | Builtin6Internal SixArgFunc-  | LamInternal Id+  | LamInternal Ref   | ForceInternal Ref-  | ReturnInternal Ref   deriving stock     ( -- | @since 1.0.0       Eq,@@ -238,6 +376,12 @@   = 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,
src/Covenant/Internal/Type.hs view
@@ -21,6 +21,7 @@     naturalBaseFunctor,     negativeBaseFunctor,     byteStringBaseFunctor,+    arity,   ) where @@ -158,7 +159,9 @@       -- | @since 1.0.0       Ord,       -- | @since 1.0.0-      Show+      Show,+      -- | @since 1.2.0+      Functor     )  -- | @since 1.0.0@@ -179,7 +182,9 @@       -- | @since 1.0.0       Ord,       -- | @since 1.0.0-      Show+      Show,+      -- | @since 1.2.0+      Functor     )  -- | @since 1.0.0@@ -192,6 +197,13 @@ instance Pretty (CompT Renamed) where   pretty = runPrettyM . prettyCompTWithContext +-- | Determine the arity of a computation type: that is, how many arguments a+-- function of this type must be given.+--+-- @since 1.0.0+arity :: forall (a :: Type). CompT a -> Int+arity (CompT _ (CompTBody xs)) = NonEmpty.length xs - 1+ -- | The name of a data type. This refers specifically to non-\'flat\' types -- either provided by the ledger, or defined by the user. --@@ -230,7 +242,9 @@       -- | @since 1.0.0       Ord,       -- | @since 1.0.0-      Show+      Show,+      -- | @since 1.2.0+      Functor     )  -- | @since 1.0.0
src/Covenant/Internal/Unification.hs view
@@ -1,10 +1,16 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE MultiWayIf #-}  module Covenant.Internal.Unification   ( TypeAppError (..),     checkApp,     runUnifyM,     UnifyM,+    -- These are exported for use with ASG helpers, largely (but not exclusively) the intro forms helper+    unify,+    substitute,+    fixUp,+    reconcile,   ) where @@ -15,7 +21,7 @@ #endif import Control.Monad.Except (MonadError, catchError, throwError) import Control.Monad.Reader (MonadReader, ReaderT (runReaderT), ask)-import Covenant.Data (DatatypeInfo)+import Covenant.Data (DatatypeInfo, mkDatatypeInfo) import Covenant.Index (Index, intCount, intIndex) import Covenant.Internal.Rename (RenameError, renameDatatypeInfo) import Covenant.Internal.Type@@ -24,8 +30,11 @@     CompT (CompT),     CompTBody (CompTBody),     Renamed (Rigid, Unifiable, Wildcard),-    TyName,+    TyName (TyName),     ValT (Abstraction, BuiltinFlat, Datatype, ThunkT),+    byteStringBaseFunctor,+    naturalBaseFunctor,+    negativeBaseFunctor,   ) import Data.Kind (Type) import Data.Map (Map)@@ -35,6 +44,7 @@ import Data.Set (Set) import Data.Set qualified as Set import Data.Text (Text)+import Data.Text qualified as Text import Data.Vector (Vector) import Data.Vector qualified as Vector import Data.Vector.NonEmpty (NonEmptyVector)@@ -76,6 +86,9 @@     --     -- @since 1.1.0     ImpossibleHappened Text+  | -- Could not reconcile two assignments with the same index+    -- @since 1.2.0+    CouldNotReconcile (Index "tyvar") (ValT Renamed) (ValT Renamed)   deriving stock     ( -- | @since 1.0.0       Eq,@@ -101,10 +114,42 @@ lookupDatatypeInfo ::   TyName ->   UnifyM (DatatypeInfo Renamed)-lookupDatatypeInfo tn =+lookupDatatypeInfo tn@(TyName rawTyName) =   ask >>= \tyDict -> case preview (ix tn) tyDict of-    Nothing -> throwError $ NoDatatypeInfo tn-    Just dti -> either (throwError . DatatypeInfoRenameFailed tn) pure $ renameDatatypeInfo dti+    Nothing -> checkForBaseFunctor tyDict+    Just dti -> renamedToUnify . renameDatatypeInfo $ dti+  where+    checkForBaseFunctor :: Map TyName (DatatypeInfo AbstractTy) -> UnifyM (DatatypeInfo Renamed)+    checkForBaseFunctor tyDict = case Text.stripSuffix "_F" rawTyName of+      Nothing -> throwError . NoDatatypeInfo $ tn+      Just rawTyNameStub ->+        if+          -- Note (Koz, 12/08/2025): None of these specific cases should _ever_+          -- fail. Thus, `fromRight` is safe here.+          | rawTyNameStub == "Natural" ->+              renamedToUnify . renameDatatypeInfo . fromRight . mkDatatypeInfo $ naturalBaseFunctor+          | rawTyNameStub == "Negative" ->+              renamedToUnify . renameDatatypeInfo . fromRight . mkDatatypeInfo $ negativeBaseFunctor+          | rawTyNameStub == "ByteString" ->+              renamedToUnify . renameDatatypeInfo . fromRight . mkDatatypeInfo $ byteStringBaseFunctor+          -- We have something that _looks_ like a base functor, but not a+          -- special builtin case. We thus need to ask the environment for the+          -- recursive type it stands for, if it exists.+          | otherwise -> do+              let standinTyName = TyName rawTyNameStub+              case preview (ix standinTyName) tyDict of+                -- Now we have _truly_ missed.+                Nothing -> throwError . NoDatatypeInfo $ tn+                Just dti -> case view #baseFunctor dti of+                  Nothing -> throwError . NoDatatypeInfo $ tn+                  -- Since this is generated, it can't fail to rename+                  Just (bfDd, _) -> renamedToUnify . renameDatatypeInfo . fromRight . mkDatatypeInfo $ bfDd+    renamedToUnify :: Either RenameError (DatatypeInfo Renamed) -> UnifyM (DatatypeInfo Renamed)+    renamedToUnify = either (throwError . DatatypeInfoRenameFailed tn) pure+    fromRight :: forall a b. (Show a) => Either a b -> b+    fromRight = \case+      Left err -> error . show $ err+      Right x -> x  lookupBBForm :: TyName -> UnifyM (ValT Renamed) lookupBBForm tn =@@ -210,8 +255,6 @@  fixUp :: ValT Renamed -> UnifyM (ValT Renamed) fixUp = \case-  -- We have a result that's effectively `forall a . a` but not an error-  Abstraction (Unifiable index) -> throwError . LeakingUnifiable $ index   -- We're doing the equivalent of failing the `ST` trick   Abstraction (Wildcard scopeId trueLevel index) -> throwError . LeakingWildcard scopeId trueLevel $ index   -- We may have a result with fewer unifiables than we started with@@ -344,28 +387,30 @@         go :: [(Index "tyvar", ValT Renamed)] -> ValT Renamed -> ValT Renamed         go subs arg = foldl' (\val (i, concrete) -> substitute i concrete val) arg subs     concretify _ _ = throwError $ ImpossibleHappened "bbForm is not a thunk"-    reconcile ::-      Map (Index "tyvar") (ValT Renamed) ->-      Map (Index "tyvar") (ValT Renamed) ->-      UnifyM (Map (Index "tyvar") (ValT Renamed))-    -- Note (Koz, 14/04/2025): This utter soup means the following:-    ---    -- - If the old map and the new map don't have any overlapping assignments,-    --   just union them.-    -- - Otherwise, for any assignment to a unifiable that is present in both-    --   maps, ensure they assign to the same thing; if they do, it's fine,-    --   otherwise we have a problem.-    reconcile =-      Merge.mergeA-        Merge.preserveMissing-        Merge.preserveMissing-        (Merge.zipWithAMatched combineBindings)++reconcile ::+  Map (Index "tyvar") (ValT Renamed) ->+  Map (Index "tyvar") (ValT Renamed) ->+  UnifyM (Map (Index "tyvar") (ValT Renamed))+-- Note (Koz, 14/04/2025): This utter soup means the following:+--+-- - If the old map and the new map don't have any overlapping assignments,+--   just union them.+-- - Otherwise, for any assignment to a unifiable that is present in both+--   maps, ensure they assign to the same thing; if they do, it's fine,+--   otherwise we have a problem.+reconcile =+  Merge.mergeA+    Merge.preserveMissing+    Merge.preserveMissing+    (Merge.zipWithAMatched combineBindings)+  where     combineBindings :: Index "tyvar" -> ValT Renamed -> ValT Renamed -> UnifyM (ValT Renamed)-    combineBindings _ old new =+    combineBindings i old new =       if old == new         then pure old         else case old of           Abstraction (Unifiable _) -> pure new           _ -> case new of             Abstraction (Unifiable _) -> pure old-            _ -> unificationError+            _ -> throwError $ CouldNotReconcile i old new
src/Covenant/Test.hs view
@@ -57,6 +57,12 @@      -- *** Elimination     runRenameM,+    undoRename,++    -- ** ASG+    DebugASGBuilder (..),+    debugASGBuilder,+    typeIdTest,   ) where @@ -65,6 +71,9 @@ #endif import Control.Applicative ((<|>)) import Control.Monad (void)+import Control.Monad.Error.Class (MonadError)+import Control.Monad.HashCons (HashConsT, MonadHashCons, runHashConsT)+import Control.Monad.Reader (MonadReader, ReaderT, runReaderT) import Control.Monad.State.Strict   ( MonadState (get, put),     State,@@ -73,6 +82,8 @@     modify,   ) import Control.Monad.Trans (MonadTrans (lift))+import Control.Monad.Trans.Except (ExceptT, runExceptT)+import Covenant.ASG (ASGEnv (ASGEnv), ASGNode, CovenantError (TypeError), CovenantTypeError, Id, ScopeInfo (ScopeInfo)) import Covenant.Data   ( DatatypeInfo,     mkDatatypeInfo,@@ -106,17 +117,19 @@   ) import Covenant.Internal.PrettyPrint (ScopeBoundary) import Covenant.Internal.Rename-  ( RenameError (InvalidAbstractionReference),+  ( RenameError (InvalidAbstractionReference, InvalidScopeReference),     RenameM,     renameCompT,     renameDataDecl,     renameValT,     runRenameM,+    undoRename,   ) import Covenant.Internal.Strategy   ( DataEncoding (PlutusData, SOP),     PlutusDataStrategy (ConstrData),   )+import Covenant.Internal.Term (ASGNodeType (CompNodeType, ValNodeType), typeId) import Covenant.Internal.Type   ( AbstractTy (BoundAt),     BuiltinFlatT@@ -143,6 +156,7 @@   ) import Covenant.Util (prettyStr) import Data.Coerce (coerce)+import Data.Functor.Identity (Identity (runIdentity)) import Data.Kind (Type) import Data.Map.Strict (Map) import Data.Map.Strict qualified as M@@ -325,7 +339,9 @@ -- @since 1.1.0 chooseInt ::   forall (m :: Type -> Type).-  (MonadGen m) => (Int, Int) -> m Int+  (MonadGen m) =>+  (Int, Int) ->+  m Int chooseInt bounds = GT.liftGen $ QC.chooseInt bounds  -- | The same as 'QC.scale', but lifted to work in any 'MonadGen'.@@ -333,7 +349,10 @@ -- @since 1.1.0 scale ::   forall (m :: Type -> Type) (a :: Type).-  (MonadGen m) => (Int -> Int) -> m a -> m a+  (MonadGen m) =>+  (Int -> Int) ->+  m a ->+  m a scale f g = GT.sized (\n -> GT.resize (f n) g)  -- | If the argument is a 'Right', pass the assertion; otherwise, fail the@@ -828,9 +847,64 @@ genDataList :: forall (a :: Type). DataGenM a -> Gen [a] genDataList = runDataGenM . GT.listOf +-- ASG Stuff++-- | This is a @newtype@ over 'ASGBuilder' to clearly indicate that it should be used only for testing, as it is+-- useful to have a variant of the 'ASGBuilder' monad which has a \'runner\'.+--+-- @since 1.2.0+newtype DebugASGBuilder (a :: Type)+  = DebugASGBuilder (ReaderT ASGEnv (ExceptT CovenantTypeError (HashConsT Id ASGNode Identity)) a)+  deriving+    ( -- | @since 1.0.0+      Functor,+      -- | @since 1.0.0+      Applicative,+      -- | @since 1.0.0+      Monad,+      -- | @since 1.1.0+      MonadReader ASGEnv,+      -- | @since 1.0.0+      MonadError CovenantTypeError,+      -- | @since 1.0.0+      MonadHashCons Id ASGNode+    )+    via ReaderT ASGEnv (ExceptT CovenantTypeError (HashConsT Id ASGNode Identity))++-- | \'Runner\' for 'DebugASGBuilder'.+--+-- @since 1.2.0+debugASGBuilder ::+  forall (a :: Type).+  Map TyName (DatatypeInfo AbstractTy) ->+  DebugASGBuilder a ->+  Either CovenantError a+debugASGBuilder tyDict (DebugASGBuilder comp) =+  case runIdentity . runHashConsT . runExceptT . runReaderT comp $ ASGEnv (ScopeInfo Vector.empty) tyDict of+    (result, bm) -> case result of+      Left err' -> Left . TypeError bm $ err'+      Right a -> pure a++-- | Looks up the type of a node, wrapping computation node types into a thunk.+--+-- This is /only/ for use in testing!+--+-- @since 1.2.0+typeIdTest ::+  forall (m :: Type -> Type).+  (MonadHashCons Id ASGNode m, MonadError CovenantTypeError m) =>+  Id ->+  m (ValT AbstractTy)+typeIdTest i =+  typeId i >>= \case+    ValNodeType t -> pure t+    -- FIXME: This is quick & dirty but I need it for something+    CompNodeType t -> pure $ ThunkT t+    other -> error $ "Expected a ValT but got: " <> show other+ -- For convenience. Don't remove this, necessary for efficient development on future work unsafeRename :: forall (a :: Type). RenameM a -> a-unsafeRename act = case runRenameM act of+unsafeRename act = case runRenameM mempty act of   Left err -> error $ show err   Right res -> res @@ -855,4 +929,4 @@       (PlutusData ConstrData)  testDatatypes :: [DataDeclaration AbstractTy]-testDatatypes = [maybeT, eitherT, unitT, pair]+testDatatypes = [maybeT, eitherT, unitT, pair, list]
src/Covenant/Type.hs view
@@ -105,6 +105,7 @@     Renamed (Rigid, Unifiable, Wildcard),     TyName (TyName),     ValT (Abstraction, BuiltinFlat, Datatype, ThunkT),+    arity,   ) import Data.Coerce (coerce) import Data.Kind (Type)@@ -176,13 +177,6 @@ {-# COMPLETE ArgsAndResult #-}  {-# COMPLETE ReturnT, (:--:>) #-}---- | Determine the arity of a computation type: that is, how many arguments a--- function of this type must be given.------ @since 1.0.0-arity :: forall (a :: Type). CompT a -> Int-arity (CompT _ (CompTBody xs)) = NonEmpty.length xs - 1  -- | A computation type that does not bind any type variables. Use this like a -- data constructor.
test/asg/Main.hs view
@@ -1,30 +1,36 @@+{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE PatternSynonyms #-}  module Main (main) where  import Control.Applicative ((<|>))-import Control.Monad (guard)+import Control.Monad (guard, void) import Covenant.ASG   ( ASG,     ASGBuilder,-    ASGNode (ACompNode, AValNode),+    ASGNode (ACompNode, AValNode, AnError),     CompNodeInfo       ( Builtin1,         Builtin2,-        Builtin3,-        Return+        Builtin3       ),-    CovenantError (EmptyASG, TopLevelError, TopLevelValue, TypeError),+    CovenantError+      ( EmptyASG,+        TopLevelError,+        TopLevelValue,+        TypeError+      ),     CovenantTypeError       ( ApplyCompType,         ApplyToError,         ApplyToValType,+        CataNoBaseFunctorForType,+        CataNonRigidAlgebra,         ForceCompType,         ForceError,         ForceNonThunk,-        LambdaResultsInValType,         NoSuchArgument,-        ReturnCompType,+        OutOfScopeTyVar,         ThunkError,         ThunkValType       ),@@ -33,34 +39,54 @@     ValNodeInfo (Lit),     app,     arg,+    boundTyVar,     builtin1,     builtin2,     builtin3,+    cata,+    ctor,+    dataConstructor,+    defaultDatatypes,+    dtype,     err,     force,     lam,+    lazyLam,     lit,-    nodeAt,-    ret,+    match,     runASGBuilder,     thunk,     topLevelNode,   )-import Covenant.Constant (typeConstant)-import Covenant.DeBruijn (DeBruijn (Z))-import Covenant.Index (Index, intIndex, ix0)+import Covenant.Constant+  ( AConstant (AUnit, AnInteger),+    typeConstant,+  )+import Covenant.DeBruijn (DeBruijn (S, Z))+import Covenant.Index (Index, intIndex, ix0, ix1) import Covenant.Prim   ( typeOneArgFunc,     typeThreeArgFunc,     typeTwoArgFunc,   )-import Covenant.Test (Concrete (Concrete))+import Covenant.Test+  ( Concrete (Concrete),+    DebugASGBuilder,+    debugASGBuilder,+    tyAppTestDatatypes,+    typeIdTest,+  ) import Covenant.Type   ( AbstractTy,-    CompT (Comp0, CompN),-    CompTBody (ArgsAndResult, ReturnT),-    ValT,+    BuiltinFlatT (IntegerT, UnitT),+    CompT (Comp0, Comp1, Comp2, CompN),+    CompTBody (ArgsAndResult, ReturnT, (:--:>)),+    ValT (BuiltinFlat, Datatype, ThunkT),     arity,+    boolT,+    byteStringT,+    integerT,+    tyvar,   ) import Covenant.Util (pattern ConsV, pattern NilV) import Data.Coerce (coerce)@@ -68,6 +94,7 @@ import Data.Map qualified as M import Data.Maybe (fromJust) import Data.Vector qualified as Vector+import Data.Wedge (Wedge (Here, Nowhere, There), wedgeLeft) import Optics.Core (preview, review) import Test.QuickCheck   ( Gen,@@ -82,8 +109,8 @@     shrink,     (===),   )-import Test.Tasty (adjustOption, defaultMain, testGroup)-import Test.Tasty.HUnit (assertEqual, assertFailure, testCase)+import Test.Tasty (TestTree, adjustOption, defaultMain, testGroup)+import Test.Tasty.HUnit (Assertion, assertEqual, assertFailure, testCase) import Test.Tasty.QuickCheck (QuickCheckTests, testProperty)  main :: IO ()@@ -97,9 +124,7 @@       testProperty "toplevel one-arg builtin compiles and has the right type" propTopLevelBuiltin1,       testProperty "toplevel two-arg builtin compiles and has the right type" propTopLevelBuiltin2,       testProperty "toplevel three-arg builtin compiles and has the right type" propTopLevelBuiltin3,-      testProperty "toplevel return compiles and has the right type" propTopLevelReturn,       testProperty "forcing a thunk has the same type as what the thunk wraps" propForceThunk,-      testProperty "applying zero arguments to a return has the same type as what the return wraps" propApplyReturn,       testProperty "forcing a computation type does not compile" propForceComp,       testProperty "forcing a non-thunk value type does not compile" propForceNonThunk,       testProperty "thunking a value type does not compile" propThunkValType,@@ -108,8 +133,33 @@       testProperty "passing computations as arguments does not compile" propApplyComp,       testProperty "requesting a non-existent argument does not compile" propNonExistentArg,       testProperty "requesting an argument that exists compiles" propExistingArg,-      testProperty "returning a computation from a lambda does not compile" propReturnComp,-      testProperty "a lambda body having a value type does not compile" propLambdaValBody+      testCase "db indices are well behaved (non-datatype case)" newLamTest1,+      testCase "db indices are well behaved (datatype case)" newLamTest2,+      testCase "calling down an in-scope tyvar works" boundTyVarHappy,+      testCase "calling down an out-of-scope tyvar fails" boundTyVarShouldFail,+      nothingIntro,+      justConcreteIntro,+      justRigidIntro,+      justNothingIntro,+      testGroup+        "Catamorphisms"+        [ testCase "Natural_F can tear down an Integer" unitCataNaturalF,+          testCase "Negative_F can tear down an Integer" unitCataNegativeF,+          testCase "ByteString_F can tear down a ByteString" unitCataByteStringF,+          testCase "Non-recursive type cata should fail" unitCataMaybeF,+          testCase "Cata with non-rigid algebra should fail" unitCataNonRigidF,+          testCase "<List_F Integer Bool -> !Bool> with List Integer should be Bool" unitCataListInteger,+          testCase "<List_F Integer r -> !r> with List Integer should be r" unitCataListIntegerRigid,+          testCase "<List_F r Integer -> !Integer> with List r should be Integer" unitCataListRigid,+          testCase "<List_F r (Maybe r) -> !Maybe r> with List r should be Maybe r" unitCataListMaybeRigid,+          testCase "introduction then cata elimination" unitCataIntroThenEliminate+        ],+      testGroup+        "Matching"+        [ matchMaybe,+          matchList,+          maybeToList+        ]     ]   where     moreTests :: QuickCheckTests -> QuickCheckTests@@ -147,11 +197,182 @@     Left (TypeError _ ThunkError) -> pure ()     _ -> assertFailure $ "Unexpected result: " <> show result +-- Construct a function of type `<Natural_F Bool -> !Bool> -> Integer -> !Bool`, whose+-- body performs a cata over its second argument using its first argument. This+-- should compile, and type as expected.+unitCataNaturalF :: IO ()+unitCataNaturalF = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "Natural_F" [boolT] :--:> ReturnT boolT+  let ty = Comp0 $ thunkTy :--:> integerT :--:> ReturnT boolT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `<Negative_F Bool -> !Bool> -> Integer -> !Bool`, whose+-- body performs a cata over its second argument using its first argument. This+-- should compile, and type as expected.+unitCataNegativeF :: IO ()+unitCataNegativeF = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "Negative_F" [boolT] :--:> ReturnT boolT+  let ty = Comp0 $ thunkTy :--:> integerT :--:> ReturnT boolT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `<ByteString_F Integer -> !Integer> -> ByteString+-- -> !Bool`, whose body performs a cata over its second argument using its+-- first argument. This should compile, and type as expected.+unitCataByteStringF :: IO ()+unitCataByteStringF = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "ByteString_F" [integerT] :--:> ReturnT integerT+  let ty = Comp0 $ thunkTy :--:> byteStringT :--:> ReturnT integerT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `forall a . <Maybe_F a Integer -> !Integer> -> Maybe+-- a -> !Integer`, whose body performs a cata over its second argument+-- using its first argument. This should fail to compile, indicating that+-- `Maybe` doesn't have a base functor.+unitCataMaybeF :: IO ()+unitCataMaybeF = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "Maybe_F" [tyvar (S Z) ix0, integerT] :--:> ReturnT integerT+  let ty = Comp1 $ thunkTy :--:> Datatype "Maybe" [tyvar Z ix0] :--:> ReturnT integerT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationFailureUnit comp $ \case+    TypeError _ (CataNoBaseFunctorForType tyName) -> assertEqual "" "Maybe" tyName+    err' -> assertFailure $ "Failed with unexpected type of error: " <> show err'++-- Construct a function of type `<forall a . ListF a (Maybe a) -> !Maybe a> -> List+-- Integer -> !Maybe Integer`, whose body performs a cata over its second+-- argument using its first argument. This should fail to compile due to a+-- non-rigid algebra.+unitCataNonRigidF :: IO ()+unitCataNonRigidF = do+  let nonRigidCompT = Comp1 $ Datatype "List_F" [tyvar Z ix0, Datatype "Maybe" [tyvar Z ix0]] :--:> ReturnT (Datatype "Maybe" [tyvar Z ix0])+  let thunkTy = ThunkT nonRigidCompT+  let ty = Comp0 $ thunkTy :--:> Datatype "List" [integerT] :--:> ReturnT (Datatype "Maybe" [integerT])+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationFailureUnit comp $ \case+    TypeError _ (CataNonRigidAlgebra t) -> assertEqual "" nonRigidCompT t+    err' -> assertFailure $ "Failed with unexpected type of error: " <> show err'++-- Construct a function of type `<List_F Integer Bool -> !Bool> -> List Integer+-- -> !Bool`, whose body performs a cata over its second argument using its+-- first argument. This should compile, and type as expected.+unitCataListInteger :: IO ()+unitCataListInteger = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "List_F" [integerT, boolT] :--:> ReturnT boolT+  let ty = Comp0 $ thunkTy :--:> Datatype "List" [integerT] :--:> ReturnT boolT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `forall a . <List_F Integer a -> !a> -> List+-- Integer -> !a`, whose body performs a cata over its second argument using its+-- first argument. This should compile, and type as expected.+unitCataListIntegerRigid :: IO ()+unitCataListIntegerRigid = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "List_F" [integerT, tyvar (S Z) ix0] :--:> ReturnT (tyvar (S Z) ix0)+  let ty = Comp1 $ thunkTy :--:> Datatype "List" [integerT] :--:> ReturnT (tyvar Z ix0)+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `forall a . <List_F a Integer -> !Integer> -> List+-- a -> !Integer`, whose body performs a cata over its second argument using its+-- first argument. This should compile, and type as expected.+unitCataListRigid :: IO ()+unitCataListRigid = do+  let thunkTy = ThunkT $ Comp0 $ Datatype "List_F" [tyvar (S Z) ix0, integerT] :--:> ReturnT integerT+  let ty = Comp1 $ thunkTy :--:> Datatype "List" [tyvar Z ix0] :--:> ReturnT integerT+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `forall a . <List_F a (Maybe a) -> !Maybe a> ->+-- List a -> !Maybe a`, whose body performs a cata over its second argument+-- using its first argument. This should compile, and type as expected.+unitCataListMaybeRigid :: IO ()+unitCataListMaybeRigid = do+  let thunkTy =+        ThunkT $+          Comp0 $+            Datatype "List_F" [tyvar (S Z) ix0, Datatype "Maybe" [tyvar (S Z) ix0]]+              :--:> ReturnT (Datatype "Maybe" [tyvar (S Z) ix0])+  let ty =+        Comp1 $+          thunkTy+            :--:> Datatype "List" [tyvar Z ix0]+            :--:> ReturnT (Datatype "Maybe" [tyvar Z ix0])+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        result <- cata (AnArg alg) (AnArg x)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty++-- Construct a function of type `forall a b . <List_F a (Maybe b) -> !Maybe b> ->+-- a -> !Maybe b`. In its body, we construct a singleton list, then eliminate it+-- using a cata with the first argument as the algebra. THis should compile and+-- type as expected.+unitCataIntroThenEliminate :: IO ()+unitCataIntroThenEliminate = do+  let thunkTy =+        ThunkT $+          Comp0 $+            Datatype "List_F" [tyvar (S Z) ix0, Datatype "Maybe" [tyvar (S Z) ix1]]+              :--:> ReturnT (Datatype "Maybe" [tyvar (S Z) ix1])+  let ty =+        Comp2 $+          thunkTy+            :--:> tyvar Z ix0+            :--:> ReturnT (Datatype "Maybe" [tyvar Z ix1])+  let comp = lam ty $ do+        alg <- arg Z ix0+        x <- arg Z ix1+        nilThunk <- dataConstructor "List" "Nil" []+        nilForced <- force (AnId nilThunk)+        aT <- boundTyVar Z ix0+        nilApplied <- app nilForced [] [Here aT]+        singleThunk <- dataConstructor "List" "Cons" [AnArg x, AnId nilApplied]+        singleForced <- force (AnId singleThunk)+        singleApplied <- app singleForced [] [Nowhere]+        result <- cata (AnArg alg) (AnId singleApplied)+        pure . AnId $ result+  withCompilationSuccessUnit comp $ matchesType ty+ -- Properties  propTopLevelConstant :: Property propTopLevelConstant = forAllShrinkShow arbitrary shrink show $ \c ->-  let builtUp = lit c+  let builtUp = AnId <$> lit c    in withCompilationFailure builtUp $ \case         TopLevelValue _ t info -> case info of           Lit c' ->@@ -201,25 +422,6 @@                 ]             _ -> failUnexpectedCompNodeInfo info -propTopLevelReturn :: Property-propTopLevelReturn = forAllShrinkShow arbitrary shrink show $ \c ->-  let builtUp = lit c >>= \i -> ret (AnId i)-   in withCompilationSuccess builtUp $ \asg ->-        withToplevelCompNode asg $ \t info ->-          case info of-            Return r -> withExpectedId r $ \i ->-              withExpectedValNode i asg $ \t' info' ->-                case info' of-                  Lit c' ->-                    let cT = typeConstant c-                     in conjoin-                          [ c' === c,-                            cT === t',-                            t === Comp0 (ReturnT cT)-                          ]-                  _ -> failUnexpectedValNodeInfo info'-            _ -> failUnexpectedCompNodeInfo info- -- We use builtins only for this test, but this should demonstrate the -- properties well enough propForceThunk :: Property@@ -247,30 +449,15 @@             force (AnId thunkI)        in (comp, forceThunkComp) --- As we can't build toplevel value ASGs, this has to be a bit roundabout-propApplyReturn :: Property-propApplyReturn = forAllShrinkShow arbitrary shrink show $ \c ->-  let comp = do-        i <- lit c-        ret (AnId i)-      applyReturnComp = do-        i <- comp-        applied <- app i Vector.empty-        ret (AnId applied)-   in withCompilationSuccess comp $ \expectedASG ->-        withCompilationSuccess applyReturnComp $ \applyReturnASG ->-          withToplevelCompNode expectedASG $ \expectedT _ ->-            withToplevelCompNode applyReturnASG $ \actualT _ ->-              expectedT === actualT- propForceComp :: Property propForceComp = forAllShrinkShow arbitrary shrink show $ \x ->-  let comp = do-        i <- case x of-          Left bi1 -> builtin1 bi1-          Right (Left bi2) -> builtin2 bi2-          Right (Right bi3) -> builtin3 bi3-        force (AnId i)+  let comp =+        AnId <$> do+          i <- case x of+            Left bi1 -> builtin1 bi1+            Right (Left bi2) -> builtin2 bi2+            Right (Right bi3) -> builtin3 bi3+          force (AnId i)       expectedT = case x of         Left bi1 -> typeOneArgFunc bi1         Right (Left bi2) -> typeTwoArgFunc bi2@@ -282,9 +469,10 @@  propForceNonThunk :: Property propForceNonThunk = forAllShrinkShow arbitrary shrink show $ \c ->-  let comp = do-        i <- lit c-        force (AnId i)+  let comp =+        AnId <$> do+          i <- lit c+          force (AnId i)    in withCompilationFailure comp $ \case         TypeError _ (ForceNonThunk actualT) -> typeConstant c === actualT         TypeError _ err' -> failWrongTypeError err'@@ -292,9 +480,10 @@  propThunkValType :: Property propThunkValType = forAllShrinkShow arbitrary shrink show $ \c ->-  let comp = do-        i <- lit c-        thunk i+  let comp =+        AnId <$> do+          i <- lit c+          thunk i    in withCompilationFailure comp $ \case         TypeError _ (ThunkValType actualT) -> typeConstant c === actualT         TypeError _ err' -> failWrongTypeError err'@@ -302,10 +491,11 @@  propApplyToVal :: Property propApplyToVal = forAllShrinkShow arbitrary shrink show $ \c args ->-  let comp = do-        args' <- traverse (fmap AnId . lit) args-        i <- lit c-        app i args'+  let comp =+        AnId <$> do+          args' <- traverse (fmap AnId . lit) args+          i <- lit c+          app i args' mempty    in withCompilationFailure comp $ \case         TypeError _ (ApplyToValType t) -> typeConstant c === t         TypeError _ err' -> failWrongTypeError err'@@ -313,10 +503,11 @@  propApplyToError :: Property propApplyToError = forAllShrinkShow arbitrary shrink show $ \args ->-  let comp = do-        args' <- traverse (fmap AnId . lit) args-        i <- err-        app i args'+  let comp =+        AnId <$> do+          args' <- traverse (fmap AnId . lit) args+          i <- err+          app i args' mempty    in withCompilationFailure comp $ \case         TypeError _ ApplyToError -> property True         TypeError _ err' -> failWrongTypeError err'@@ -332,13 +523,14 @@         Right (Left bi2) -> typeTwoArgFunc bi2         Right (Right bi3) -> typeThreeArgFunc bi3 -      comp = do-        i <- builtin1 f-        arg' <- case arg1 of-          Left bi1 -> builtin1 bi1-          Right (Left bi2) -> builtin2 bi2-          Right (Right bi3) -> builtin3 bi3-        app i (Vector.singleton . AnId $ arg')+      comp =+        AnId <$> do+          i <- builtin1 f+          arg' <- case arg1 of+            Left bi1 -> builtin1 bi1+            Right (Left bi2) -> builtin2 bi2+            Right (Right bi3) -> builtin3 bi3+          app i (Vector.singleton . AnId $ arg') mempty    in withCompilationFailure comp $ \case         TypeError _ (ApplyCompType actualT) -> t === actualT         TypeError _ err' -> failWrongTypeError err'@@ -346,7 +538,7 @@  propNonExistentArg :: Property propNonExistentArg = forAllShrinkShow arbitrary shrink show $ \(db, index) ->-  let comp = arg db index >>= \i -> ret (AnArg i)+  let comp = arg db index >>= \i -> pure $ AnArg i    in withCompilationFailure comp $ \case         TypeError _ (NoSuchArgument db' index') -> conjoin [db === db', index === index']         TypeError _ err' -> failWrongTypeError err'@@ -359,7 +551,7 @@ propExistingArg = forAllShrinkShow gen shr show $ \(t, index) ->   let comp = lam t $ do         arg1 <- arg Z index-        ret (AnArg arg1)+        pure (AnArg arg1)    in withCompilationSuccess comp $ \asg ->         withToplevelCompNode asg $ \t' _ ->           t' === t@@ -399,41 +591,217 @@                   Just (Concrete res') -> pure (Comp0 (ArgsAndResult (coerce args') res'), index)            in shrinkOnIndex <|> shrinkOnArgs -propReturnComp :: Property-propReturnComp = forAllShrinkShow arbitrary shrink show $ \x ->-  let t = case x of-        Left bi1 -> typeOneArgFunc bi1-        Right (Left bi2) -> typeTwoArgFunc bi2-        Right (Right bi3) -> typeThreeArgFunc bi3-      comp = do-        i <- case x of-          Left bi1 -> builtin1 bi1-          Right (Left bi2) -> builtin2 bi2-          Right (Right bi3) -> builtin3 bi3-        ret (AnId i)-   in withCompilationFailure comp $ \case-        TypeError _ (ReturnCompType actualT) -> t === actualT-        TypeError _ err' -> failWrongTypeError err'-        err' -> failWrongError err'+boundTyVarHappy :: Assertion+boundTyVarHappy = run $ do+  lam lamTy $ do+    arg1 <- AnArg <$> arg Z ix0+    void $ boundTyVar Z ix0+    pure arg1+  where+    lamTy :: CompT AbstractTy+    lamTy = Comp1 $ tyvar Z ix0 :--:> ReturnT (tyvar Z ix0) -propLambdaValBody :: Property-propLambdaValBody = forAllShrinkShow arbitrary shrink show $ \(Concrete t, c) ->-  let resultT = typeConstant c-      comp = lam (Comp0 (ArgsAndResult (Vector.singleton t) resultT)) $ lit c-   in withCompilationFailure comp $ \case-        TypeError _ (LambdaResultsInValType actualT) -> resultT === actualT-        TypeError _ err' -> failWrongTypeError err'-        err' -> failWrongError err'+    run :: forall (a :: Type). ASGBuilder a -> IO ()+    run act = case runASGBuilder M.empty act of+      Left err' -> assertFailure . show $ err'+      Right {} -> pure () +boundTyVarShouldFail :: Assertion+boundTyVarShouldFail = run $ boundTyVar Z ix0+  where+    run :: forall (a :: Type). (Show a) => ASGBuilder a -> IO ()+    run act = case runASGBuilder M.empty act of+      Left (TypeError _ (OutOfScopeTyVar db argpos)) ->+        if db == Z && argpos == ix0+          then pure ()+          else assertFailure $ "Expected OutOfScopeTyVar error for Z, ix0 but got: " <> show db <> ", " <> show argpos+      Left err' -> assertFailure $ "Expected an OutofScopeTyVar error, but got: " <> show err'+      Right x -> assertFailure $ "Expected boundTyVar to fail, but got: " <> show x++-- TODO: better name+newLamTest1 :: Assertion+newLamTest1 = case runASGBuilder M.empty fn of+  Left err' -> assertFailure (show err')+  Right {} -> pure ()+  where+    fn :: ASGBuilder Id+    fn = lam expected $ do+      f <- arg Z ix0 >>= force . AnArg+      a <- AnArg <$> arg Z ix1+      AnId <$> app f (Vector.singleton a) mempty++    expected :: CompT AbstractTy+    expected =+      Comp2 $+        ThunkT (Comp0 $ tyvar (S Z) ix0 :--:> ReturnT (tyvar (S Z) ix1))+          :--:> tyvar Z ix0+          :--:> ReturnT (tyvar Z ix1)++newLamTest2 :: Assertion+newLamTest2 = case runASGBuilder tyAppTestDatatypes fn of+  Left err' -> assertFailure (show err')+  Right {} -> pure ()+  where+    fn :: ASGBuilder Id+    fn = lam expected $ do+      f <- arg Z ix0 >>= force . AnArg+      a <- AnArg <$> arg Z ix1+      AnId <$> app f (Vector.singleton a) mempty++    expected :: CompT AbstractTy+    expected =+      Comp2 $+        ThunkT (Comp0 $ Datatype "Maybe" (Vector.singleton $ tyvar (S Z) ix0) :--:> ReturnT (tyvar (S Z) ix1))+          :--:> Datatype "Maybe" (Vector.singleton $ tyvar Z ix0)+          :--:> ReturnT (tyvar Z ix1)++-- Intro form tests++nothingIntro :: TestTree+nothingIntro =+  runIntroFormTest "nothing" expectNothingThunk $+    dataConstructor "Maybe" "Nothing" mempty >>= typeIdTest+  where+    expectNothingThunk :: ValT AbstractTy+    expectNothingThunk = ThunkT . Comp1 . ReturnT $ Datatype "Maybe" (Vector.fromList [tyvar Z ix0])++justConcreteIntro :: TestTree+justConcreteIntro = runIntroFormTest "justConcreteIntro" expected $ do+  argRef <- AnId <$> lit AUnit+  dataConstructor "Maybe" "Just" (Vector.singleton argRef) >>= typeIdTest+  where+    expected :: ValT AbstractTy+    expected = ThunkT . Comp0 . ReturnT $ Datatype "Maybe" (Vector.singleton $ BuiltinFlat UnitT)++justRigidIntro :: TestTree+justRigidIntro = runIntroFormTest "justRigidIntro" expected $ do+  lamId <- lam lamTy $ do+    arg1 <- AnArg <$> arg Z ix0+    justRigid <- dataConstructor "Maybe" "Just" (Vector.singleton arg1)+    pure (AnId justRigid)+  lamThunked <- thunk lamId+  typeIdTest lamThunked+  where+    lamTy :: CompT AbstractTy+    lamTy =+      Comp1 $+        tyvar Z ix0+          :--:> ReturnT+            ( ThunkT+                . Comp0+                . ReturnT+                $ Datatype "Maybe"+                $ Vector.singleton (tyvar (S Z) ix0)+            )++    expected :: ValT AbstractTy+    expected = ThunkT lamTy++justNothingIntro :: TestTree+justNothingIntro = runIntroFormTest "justNothingIntro" expectedThunk $ do+  thunkL <- lam expectedComp $ do+    nothingThunk <- dataConstructor "Maybe" "Nothing" mempty+    var <- boundTyVar Z ix0+    nothingForced <- force (AnId nothingThunk)+    nothingApplied <- app nothingForced mempty (Vector.singleton . wedgeLeft . Just $ var)+    justNothing <- dataConstructor "Maybe" "Just" (Vector.singleton (AnId nothingApplied))+    justNothingForced <- force (AnId justNothing)+    justNothingApplied <- app justNothingForced mempty (Vector.singleton Nowhere)+    pure (AnId justNothingApplied)+  typeIdTest thunkL+  where+    expectedComp :: CompT AbstractTy+    expectedComp =+      Comp1+        . ReturnT+        . Datatype "Maybe"+        . Vector.singleton+        . Datatype "Maybe"+        $ Vector.singleton+        $ tyvar Z ix0++    expectedThunk :: ValT AbstractTy+    expectedThunk = ThunkT expectedComp++-- pattern matching++{- Construct a pattern match on 'Maybe Unit' that returns an integer.++   This is effectively the simplest possible pattern matching test: The type is non-recursive and the+   parameters to the type constructor are all concrete.+-}+matchMaybe :: TestTree+matchMaybe = runIntroFormTest "matchMaybe" (BuiltinFlat IntegerT) $ do+  unit <- AnId <$> lit AUnit+  scrutinee <- ctor "Maybe" "Just" (Vector.singleton unit) (Vector.singleton Nowhere)+  nothingHandler <- lazyLam (Comp0 $ ReturnT (BuiltinFlat IntegerT)) (AnId <$> lit (AnInteger 0))+  justHandler <- lazyLam (Comp0 $ BuiltinFlat UnitT :--:> ReturnT (BuiltinFlat IntegerT)) (AnId <$> lit (AnInteger 1))+  result <- match (AnId scrutinee) (AnId <$> Vector.fromList [justHandler, nothingHandler])+  typeIdTest result++{- Construct a pattern match on 'List Unit' that returns an integer.++   A simple test for pattern matches on values of recursive types.+-}+matchList :: TestTree+matchList = runIntroFormTest "matchList" (BuiltinFlat IntegerT) $ do+  unit <- AnId <$> lit AUnit+  nilUnit <- ctor "List" "Nil" mempty (Vector.singleton $ There (BuiltinFlat UnitT))+  scrutinee <- ctor "List" "Cons" (Vector.fromList [unit, AnId nilUnit]) (Vector.singleton Nowhere)+  let nilHandlerTy = Comp0 $ ReturnT (BuiltinFlat IntegerT)+      consHandlerTy =+        Comp0 $+          BuiltinFlat UnitT+            :--:> Datatype "List_F" (Vector.fromList [BuiltinFlat UnitT, Datatype "List" (Vector.singleton $ BuiltinFlat UnitT)])+            :--:> ReturnT (BuiltinFlat IntegerT)+  nilHandler <- lazyLam nilHandlerTy (AnId <$> lit (AnInteger 0))+  consHandler <- lazyLam consHandlerTy (AnId <$> lit (AnInteger 0))+  result <- match (AnId scrutinee) (AnId <$> Vector.fromList [nilHandler, consHandler])+  typeIdTest result++{- This differs from the two above tests in that we're using pattern matching to construct the+   'maybeToList :: forall a. Maybe a -> List a' function. This is very useful, because if successful, it provides good evidence that:+     1. Pattern matching works on datatypes with rigid parameters.+     2. Pattern matching works inside the body of a lambda.+     3. Nothing breaks renaming anywhere.+-}+maybeToList :: TestTree+maybeToList = runIntroFormTest "maybeToList" maybeToListTy $ do+  thonk <- lazyLam maybeToListCompTy $ do+    let nothingHandlerTy = Comp0 $ ReturnT (dtype "List" [tyvar (S Z) ix0])+        justHandlerTy = Comp0 $ tyvar (S Z) ix0 :--:> ReturnT (dtype "List" [tyvar (S Z) ix0])+    nothingHandler <- lazyLam nothingHandlerTy $ do+      tvA <- boundTyVar (S Z) ix0+      AnId <$> ctor "List" "Nil" mempty (Vector.singleton (Here tvA))+    justHandler <- lazyLam justHandlerTy $ do+      tvA <- boundTyVar (S Z) ix0+      vA <- AnArg <$> arg Z ix0+      nil <- AnId <$> ctor "List" "Nil" mempty (Vector.singleton (Here tvA))+      AnId <$> ctor "List" "Cons" (Vector.fromList [vA, nil]) (Vector.singleton Nowhere)+    scrutinee <- AnArg <$> arg Z ix0+    AnId <$> match scrutinee (AnId <$> Vector.fromList [justHandler, nothingHandler])+  typeIdTest thonk+  where+    maybeToListCompTy :: CompT AbstractTy+    maybeToListCompTy = Comp1 (dtype "Maybe" [tyvar Z ix0] :--:> ReturnT (dtype "List" [tyvar Z ix0]))++    maybeToListTy :: ValT AbstractTy+    maybeToListTy = ThunkT maybeToListCompTy+ -- Helpers +runIntroFormTest :: String -> ValT AbstractTy -> DebugASGBuilder (ValT AbstractTy) -> TestTree+runIntroFormTest nm expectedTy act = testCase nm $ case debugASGBuilder tyAppTestDatatypes act of+  Left err' -> assertFailure ("ASG Error: " <> show err')+  Right actualTy -> assertEqual nm expectedTy actualTy+ failWrongTypeError :: CovenantTypeError -> Property failWrongTypeError err' = failWithCounterExample ("Unexpected type error: " <> show err')  failWrongError :: CovenantError -> Property failWrongError err' = failWithCounterExample ("Unexpected error: " <> show err') -withCompilationFailure :: ASGBuilder Id -> (CovenantError -> Property) -> Property+withCompilationFailure :: ASGBuilder Ref -> (CovenantError -> Property) -> Property withCompilationFailure comp cb = case runASGBuilder M.empty comp of   Left err' -> cb err'   Right asg -> failWithCounterExample ("Unexpected success: " <> show asg)@@ -459,6 +827,23 @@ failUnexpectedValNodeInfo info =   failWithCounterExample ("Unexpected ValNodeInfo: " <> show info) +withCompilationSuccessUnit :: ASGBuilder Id -> (ASG -> IO ()) -> IO ()+withCompilationSuccessUnit comp cb = case runASGBuilder defaultDatatypes comp of+  Left err' -> assertFailure $ "Did not compile: " <> show err'+  Right asg -> cb asg++withCompilationFailureUnit :: ASGBuilder Id -> (CovenantError -> IO ()) -> IO ()+withCompilationFailureUnit comp cb = case runASGBuilder defaultDatatypes comp of+  Left err' -> cb err'+  Right asg -> assertFailure $ "Unexpected compilation success: " <> show asg++matchesType :: CompT AbstractTy -> ASG -> IO ()+matchesType expectedTy asg = case topLevelNode asg of+  ACompNode actualTy _ -> assertEqual "" expectedTy actualTy+  u@(AValNode _ _) -> assertFailure $ "Got a value node: " <> show u+  AnError -> assertFailure "Got an error node"++{- NOTE: Not 100% sure I won't need these withExpectedId :: Ref -> (Id -> Property) -> Property withExpectedId r cb = case r of   AnId i -> cb i@@ -468,3 +853,4 @@ withExpectedValNode i asg cb = case nodeAt i asg of   AValNode t info -> cb t info   node -> failWithCounterExample ("Unexpected node: " <> show node)+--}
test/bb/Main.hs view
@@ -2,8 +2,6 @@  module Main (main) where --- import Data.Either (isRight)- import Control.Exception (throwIO) import Control.Monad ((<=<)) import Covenant.Data@@ -69,7 +67,7 @@     Right (catMaybes -> bbfDecls) ->       let results =             mapM-              ( \valT -> case runRenameM . renameValT $ valT of+              ( \valT -> case runRenameM mempty . renameValT $ valT of                   Left err -> Left (err, valT)                   Right res -> Right res               )@@ -149,8 +147,8 @@ testMonotypeBB :: TestTree testMonotypeBB = testCase "unitBbf" $ do   let expected = Comp1 $ Abstraction (BoundAt Z ix0) :--:> ReturnT (Abstraction $ BoundAt Z ix0)-  expected' <- failLeft . runRenameM . renameCompT $ expected+  expected' <- failLeft . runRenameM mempty . renameCompT $ expected   actual <- case fromJust . (view #bbForm <=< M.lookup "Unit") $ tyAppTestDatatypes of-    ThunkT inner -> either (throwIO . userError . show) pure . runRenameM $ renameCompT inner+    ThunkT inner -> either (throwIO . userError . show) pure . runRenameM mempty $ renameCompT inner     _ -> assertFailure "BB form not a thunk!"   assertEqual "unit bbf" expected' actual
test/primops/Main.hs view
@@ -292,7 +292,7 @@   Property mkRenameProp typingFun = forAll arbitrary $ \f ->   let t = typingFun f-      result = runRenameM . renameCompT $ t+      result = runRenameM mempty . renameCompT $ t    in case result of         Left err -> counterexample (show err) False         Right renamed -> property $ liftEq eqRenamedVar t renamed@@ -309,7 +309,7 @@   CompT AbstractTy ->   (CompT Renamed -> IO ()) ->   IO ()-withRenamedComp t f = case runRenameM . renameCompT $ t of+withRenamedComp t f = case runRenameM mempty . renameCompT $ t of   Left err -> assertFailure $ "Could not rename: " <> show err   Right t' -> f t' @@ -319,7 +319,7 @@   t (ValT AbstractTy) ->   (t (ValT Renamed) -> IO ()) ->   IO ()-withRenamedVals vals f = case runRenameM . traverse renameValT $ vals of+withRenamedVals vals f = case runRenameM mempty . traverse renameValT $ vals of   Left err -> assertFailure $ "Could not rename: " <> show err   Right vals' -> f vals' 
test/renaming/Main.hs view
@@ -81,14 +81,14 @@ testFlat :: BuiltinFlatT -> IO () testFlat t = do   let input = BuiltinFlat t-  let result = runRenameM . renameValT $ input+  let result = runRenameM mempty . renameValT $ input   assertRight (assertBool "" . liftEq (\_ _ -> False) input) result  -- Checks that for any 'fully concretified' type (nested or not), renaming -- changes nothing. propNestedConcrete :: Property propNestedConcrete = forAllShrinkShow arbitrary shrink show $ \(Concrete t) ->-  let result = runRenameM . renameValT $ t+  let result = runRenameM mempty . renameValT $ t    in case result of         Left _ -> False         Right actual -> liftEq (\_ _ -> False) t actual@@ -101,7 +101,7 @@         Comp1 $           Abstraction (Unifiable ix0)             :--:> ReturnT (Abstraction (Unifiable ix0))-  let result = runRenameM . renameCompT $ idT+  let result = runRenameM mempty . renameCompT $ idT   assertRight (assertEqual "" expected) result  -- Checks that `forall a b . a -> b -> !a` correctly renames.@@ -113,7 +113,7 @@           Abstraction (Unifiable ix0)             :--:> Abstraction (Unifiable ix1)             :--:> ReturnT (Abstraction (Unifiable ix0))-  let result = runRenameM . renameCompT $ constT+  let result = runRenameM mempty . renameCompT $ constT   assertRight (assertEqual "" expected) result  -- Checks that `forall a . a -> !(forall b . b -> !a)` correctly renames.@@ -126,22 +126,23 @@           Abstraction (Unifiable ix0)             :--:> ReturnT               ( ThunkT . Comp1 $-                  Abstraction (Wildcard 1 2 ix0)+                  Abstraction (Wildcard 1 1 ix0)                     :--:> ReturnT (Abstraction (Unifiable ix0))               )-  let result = runRenameM . renameCompT $ constT+  let result = runRenameM mempty . renameCompT $ constT   assertRight (assertEqual "" expected) result  -- Checks that `forall a . b -> !a` triggers the variable indexing checker. testIndexingIdT :: IO () testIndexingIdT = do-  let t = Comp1 $ tyvar Z ix0 :--:> ReturnT (tyvar Z ix1)-  let result = runRenameM . renameCompT $ t+  let t = Comp1 $ tyvar Z ix1 :--:> ReturnT (tyvar Z ix0)+  let result = runRenameM mempty . renameCompT $ t+   case result of     Left (InvalidAbstractionReference trueLevel ix) -> do-      assertEqual "" trueLevel 1+      assertEqual "" trueLevel 0       assertEqual "" ix ix1-    _ -> assertBool "renaming succeeded when it should have failed" False+    _ -> assertBool ("renaming succeeded when it should have failed: " <> show result) False  -- Helpers 
test/type-applications/Main.hs view
@@ -3,7 +3,6 @@ module Main (main) where  import Control.Applicative ((<|>))-import Control.Monad (guard) import Covenant.ASG   ( TypeAppError       ( DoesNotUnify,@@ -14,6 +13,7 @@ import Covenant.DeBruijn (DeBruijn (S, Z), asInt) import Covenant.Index   ( Index,+    intIndex,     ix0,     ix1,     ix2,@@ -43,27 +43,35 @@ import Data.Functor.Identity (Identity (Identity)) import Data.Kind (Type) import Data.Map qualified as M+import Data.Maybe (fromJust)+import Data.Vector (Vector) import Data.Vector qualified as Vector-import Optics.Core (review)+import Data.Word (Word32)+import Optics.Core (preview, review) import Test.QuickCheck   ( Gen,     Property,     arbitrary,+    chooseInt,     counterexample,     discard,     elements,+    forAll,     forAllShrink,+    getNonZero,     getSize,+    liftArbitrary,     liftShrink,     oneof,     shrink,     suchThat,+    suchThatMap,     vectorOf,     (===),   ) import Test.Tasty (TestTree, adjustOption, defaultMain, testGroup) import Test.Tasty.HUnit (assertEqual, assertFailure, testCase)-import Test.Tasty.QuickCheck (QuickCheckTests, testProperty)+import Test.Tasty.QuickCheck (QuickCheckMaxSize, QuickCheckTests, testProperty)  main :: IO () main =@@ -79,13 +87,13 @@       testGroup         "Unification"         [ testProperty "concrete expected, concrete actual" propUnifyConcrete,-          testProperty "rigid expected, concrete actual" propUnifyRigidConcrete,+          adjustOption smallerTests . testProperty "rigid expected, concrete actual" $ propUnifyRigidConcrete,           testProperty "wildcard expected, concrete actual" propUnifyWildcardConcrete,           testProperty "wildcard expected, unifiable actual" propUnifyWildcardUnifiable,-          testProperty "concrete expected, rigid actual" propUnifyConcreteRigid,-          testProperty "unifiable expected, rigid actual" propUnifyUnifiableRigid,+          adjustOption smallerTests . testProperty "concrete expected, rigid actual" $ propUnifyConcreteRigid,+          adjustOption smallerTests . testProperty "unifiable expected, rigid actual" $ propUnifyUnifiableRigid,           testProperty "rigid expected, rigid actual" propUnifyRigid,-          testProperty "wildcard expected, rigid actual" propUnifyWildcardRigid,+          adjustOption smallerTests . testProperty "wildcard expected, rigid actual" $ propUnifyWildcardRigid,           testProperty "thunk with unifiable result" propThunkWithUnifiableResult         ],       testGroup@@ -110,14 +118,20 @@     moreTests :: QuickCheckTests -> QuickCheckTests     moreTests = max 10_000 +    -- fewerTests :: QuickCheckTests -> QuickCheckTests+    -- fewerTests = const 100++    smallerTests :: QuickCheckMaxSize -> QuickCheckMaxSize+    smallerTests = (`div` 4)+ -- Units and properties  -- Try to apply more than one argument to `forall a . a -> !a`. -- Result should indicate excess arguments. propTooManyArgs :: Property propTooManyArgs = forAllShrink gen shr $ \excessArgs ->-  withRenamedComp idT $ \renamedIdT ->-    withRenamedVals excessArgs $ \renamedExcessArgs ->+  withRenamedComp mempty idT $ \renamedIdT ->+    withRenamedVals mempty excessArgs $ \renamedExcessArgs ->       case renamedExcessArgs of         [] -> discard -- should be impossible         _ : extraArgs ->@@ -147,7 +161,7 @@ -- insufficient arguments. unitInsufficientArgs :: IO () unitInsufficientArgs = do-  renamedIdT <- failLeft . runRenameM . renameCompT $ idT+  renamedIdT <- failLeft . runRenameM mempty . renameCompT $ idT   let expected = Left $ InsufficientArgs 0 renamedIdT []   let actual = checkApp M.empty renamedIdT []   assertEqual "" expected actual@@ -156,8 +170,8 @@ -- that type. propIdConcrete :: Property propIdConcrete = forAllShrink arbitrary shrink $ \(Concrete t) ->-  withRenamedComp idT $ \renamedIdT ->-    withRenamedVals (Identity t) $ \(Identity t') ->+  withRenamedComp mempty idT $ \renamedIdT ->+    withRenamedVals mempty (Identity t) $ \(Identity t') ->       let expected = Right t'           actual = checkApp M.empty renamedIdT [Just t']        in expected === actual@@ -166,8 +180,8 @@ -- Result should be that type. propConst2Same :: Property propConst2Same = forAllShrink arbitrary shrink $ \(Concrete t) ->-  withRenamedComp const2T $ \renamedConst2T ->-    withRenamedVals (Identity t) $ \(Identity t') ->+  withRenamedComp mempty const2T $ \renamedConst2T ->+    withRenamedVals mempty (Identity t) $ \(Identity t') ->       let expected = Right t'           actual = checkApp M.empty renamedConst2T [Just t', Just t']        in expected === actual@@ -178,9 +192,9 @@ propConst2Different = forAllShrink arbitrary shrink $ \(Concrete t1, Concrete t2) ->   if t1 == t2     then discard-    else withRenamedComp const2T $ \renamedConst2T ->-      withRenamedVals (Identity t1) $ \(Identity t1') ->-        withRenamedVals (Identity t2) $ \(Identity t2') ->+    else withRenamedComp mempty const2T $ \renamedConst2T ->+      withRenamedVals mempty (Identity t1) $ \(Identity t1') ->+        withRenamedVals mempty (Identity t2) $ \(Identity t2') ->           let expected = Right t1'               actual = checkApp M.empty renamedConst2T [Just t1', Just t2']            in expected === actual@@ -191,8 +205,8 @@ -- unification error otherwise. propUnifyConcrete :: Property propUnifyConcrete = forAllShrink gen shr $ \(tA, mtB) ->-  withRenamedComp (Comp0 $ tA :--:> ReturnT integerT) $ \f ->-    withRenamedVals (Identity tA) $ \(Identity tA') ->+  withRenamedComp mempty (Comp0 $ tA :--:> ReturnT integerT) $ \f ->+    withRenamedVals mempty (Identity tA) $ \(Identity tA') ->       case mtB of         Nothing ->           let expected = Right integerT@@ -201,7 +215,7 @@         Just tB ->           if tA == tB             then discard-            else withRenamedVals (Identity tB) $ \(Identity arg) ->+            else withRenamedVals mempty (Identity tB) $ \(Identity arg) ->               let expected = Left . DoesNotUnify tA' $ arg                   actual = checkApp M.empty f [Just arg]                in expected === actual@@ -226,26 +240,27 @@ -- !Integer` to `b`. Result should fail to unify. propUnifyRigidConcrete :: Property propUnifyRigidConcrete = forAllShrink arbitrary shrink $ \(Concrete t, scope, ix) ->-  withRenamedComp (Comp0 $ tyvar (S scope) ix :--:> ReturnT integerT) $ \f ->-    withRenamedVals (Identity t) $ \(Identity t') ->-      -- This is a little confusing, as we would expect that the true level will-      -- be based on `S scope`, since that's what's in the computation type.-      -- However, we actually have to reduce it by 1, as we have a 'scope-      -- stepdown' for `f` even though we bind no variables.-      let trueLevel = negate . review asInt $ scope-          expected = Left . DoesNotUnify (Abstraction . Rigid trueLevel $ ix) $ t'-          actual = checkApp M.empty f [Just t']-       in expected === actual+  let mockScope = Vector.replicate (review asInt scope + 1) (fromIntegral $ review intIndex ix + 1)+   in withRenamedComp mockScope (Comp0 $ tyvar (S scope) ix :--:> ReturnT integerT) $ \f ->+        withRenamedVals mockScope (Identity t) $ \(Identity t') ->+          -- This is a little confusing, as we would expect that the true level will+          -- be based on `S scope`, since that's what's in the computation type.+          -- However, we actually have to reduce it by 1, as we have a 'scope+          -- stepdown' for `f` even though we bind no variables.+          let trueLevel = ezTrueLevel mockScope scope ix+              expected = Left . DoesNotUnify (Abstraction . Rigid trueLevel $ ix) $ t'+              actual = checkApp M.empty f [Just t']+           in expected === actual  -- Randomly pick a concrete type A, then try to apply `(forall a . a -> -- !Integer) -> !Integer` to `(A -> !Integer)`. Result should fail to unify. propUnifyWildcardConcrete :: Property propUnifyWildcardConcrete = forAllShrink arbitrary shrink $ \(Concrete t) ->   let thunk = ThunkT . Comp1 $ tyvar Z ix0 :--:> ReturnT integerT-   in withRenamedComp (Comp0 $ thunk :--:> ReturnT integerT) $ \f ->+   in withRenamedComp mempty (Comp0 $ thunk :--:> ReturnT integerT) $ \f ->         let argT = ThunkT . Comp0 $ t :--:> ReturnT integerT-         in withRenamedVals (Identity argT) $ \(Identity argT') ->-              let lhs = ThunkT . Comp1 $ Abstraction (Wildcard 1 2 ix0) :--:> ReturnT integerT+         in withRenamedVals mempty (Identity argT) $ \(Identity argT') ->+              let lhs = ThunkT . Comp1 $ Abstraction (Wildcard 1 1 ix0) :--:> ReturnT integerT                   expected = Left . DoesNotUnify lhs $ argT'                   actual = checkApp M.empty f [Just argT']                in expected === actual@@ -255,102 +270,100 @@ -- to `A`. propUnifyWildcardUnifiable :: Property propUnifyWildcardUnifiable = forAllShrink arbitrary shrink $ \(Concrete t) ->-  withRenamedComp (Comp0 $ ThunkT (Comp1 $ tyvar Z ix0 :--:> ReturnT t) :--:> ReturnT t) $ \f ->-    withRenamedVals (Identity t) $ \(Identity t') ->-      withRenamedVals (Identity . ThunkT . Comp1 $ tyvar Z ix0 :--:> ReturnT t) $ \(Identity arg) ->-        let expected = Right t'-            actual = checkApp M.empty f [Just arg]-         in expected === actual+  let mockScope = Vector.singleton 1+   in withRenamedComp mockScope (Comp0 $ ThunkT (Comp1 $ tyvar Z ix0 :--:> ReturnT t) :--:> ReturnT t) $ \f ->+        withRenamedVals mockScope (Identity t) $ \(Identity t') ->+          withRenamedVals mockScope (Identity . ThunkT . Comp1 $ tyvar Z ix0 :--:> ReturnT t) $ \(Identity arg) ->+            let expected = Right t'+                actual = checkApp M.empty f [Just arg]+             in expected === actual  -- Randomly generate a concrete type A, and a rigid type B, then try to apply `A -- -> !Integer` to `B`. Result should fail to unify. propUnifyConcreteRigid :: Property propUnifyConcreteRigid = forAllShrink arbitrary shrink $ \(Concrete aT, scope, index) ->-  withRenamedComp (Comp0 $ aT :--:> ReturnT integerT) $ \f ->-    withRenamedVals (Identity $ tyvar scope index) $ \(Identity arg) ->-      withRenamedVals (Identity aT) $ \(Identity aT') ->-        let level = negate . review asInt $ scope-            expected = Left . DoesNotUnify aT' . Abstraction . Rigid level $ index-            actual = checkApp M.empty f [Just arg]-         in expected === actual+  let mockScope = Vector.replicate (review asInt scope + 1) (fromIntegral $ review intIndex index + 1)+   in withRenamedComp mockScope (Comp0 $ aT :--:> ReturnT integerT) $ \f ->+        withRenamedVals mockScope (Identity $ tyvar scope index) $ \(Identity arg) ->+          withRenamedVals mockScope (Identity aT) $ \(Identity aT') ->+            let level = ezTrueLevel mockScope scope index+                expected = Left . DoesNotUnify aT' . Abstraction . Rigid level $ index+                actual = checkApp M.empty f [Just arg]+             in expected === actual  -- Randomly generate a rigid type A, then try to apply `forall a . a -> !a` to -- `A`. Result should unify to `A`. propUnifyUnifiableRigid :: Property propUnifyUnifiableRigid = forAllShrink arbitrary shrink $ \(scope, index) ->-  withRenamedComp idT $ \f ->-    withRenamedVals (Identity $ tyvar scope index) $ \(Identity arg) ->-      let expected = Right arg-          actual = checkApp M.empty f [Just arg]-       in expected === actual+  let mockScope = Vector.replicate (review asInt scope + 1) (fromIntegral $ review intIndex index + 1)+   in withRenamedComp mockScope idT $ \f ->+        withRenamedVals mockScope (Identity $ tyvar scope index) $ \(Identity arg) ->+          let expected = Right arg+              actual = checkApp M.empty f [Just arg]+           in expected === actual --- Randomly generate a scope S and an index I, then another scope S' and another--- index I', that may or may not be different to S and/or I respectively. Let--- `T` be the rigid type that results from `S` and `I`, and `U` be the rigid--- type that results from `S'` and `I'`. Attempt to unify `T -> !Integer` with--- `U`. This should unify to `Integer` if, and only if, `T == U`; otherwise, it--- should fail to unify.+-- Randomly generate a scope stack of height at least 2, then two indexes `I`+-- and `J`, both valid in that scope stack. `I` and `J` may be different or the+-- same, with equal probability. Let `T` be the rigid type corresponding to some+-- variable index in the scope stack at the position for `I`, and `U` be the+-- rigid type corresponding to some variable index in the scope stack at the+-- position for `J`. Attempt to unify `T -> !Integer` with `U`. This should+-- unify to `Integer` if, and only if, `T == U`; otherwise, it should fail to+-- unify. propUnifyRigid :: Property-propUnifyRigid = forAllShrink gen shr $ \testData ->-  withTestData testData $ \(f, arg, expected) ->-    let actual = checkApp M.empty f [Just arg]-     in expected === actual+propUnifyRigid = forAll gen $ \(scopeStack, t, u, same) ->+  withRenamedComp scopeStack (Comp0 $ t :--:> ReturnT integerT) $ \fun ->+    withRenamedVals scopeStack (Identity u) $ \(Identity arg) ->+      case checkApp mempty fun [Just arg] of+        Left err -> counterexample ("Identical rigids, but got " <> show err) $ not same+        Right res -> counterexample ("Different rigids, but unified to " <> show res) same   where-    gen :: Gen (DeBruijn, Index "tyvar", Maybe (Either DeBruijn (Index "tyvar")))+    gen :: Gen (Vector Word32, ValT AbstractTy, ValT AbstractTy, Bool)     gen = do-      db <- arbitrary-      index <- arbitrary-      (db,index,)-        <$> oneof-          [ pure Nothing,-            Just . Left <$> suchThat arbitrary (db /=),-            Just . Right <$> suchThat arbitrary (index /=)-          ]-    shr ::-      (DeBruijn, Index "tyvar", Maybe (Either DeBruijn (Index "tyvar"))) ->-      [(DeBruijn, Index "tyvar", Maybe (Either DeBruijn (Index "tyvar")))]-    shr (db, index, mrest) = do-      db' <- shrink db-      index' <- shrink index-      case mrest of-        Nothing -> pure (db', index, Nothing) <|> pure (db, index', Nothing)-        Just (Left db2) -> do-          db2' <- shrink db2-          (db', index, Just (Left db2)) <$ guard (db' /= db2)-            <|> pure (db, index', Just (Left db2))-            <|> (db, index, Just (Left db2')) <$ guard (db /= db2')-        Just (Right index2) -> do-          index2' <- shrink index2-          pure (db', index, Just (Right index2))-            <|> (db, index', Just (Right index2)) <$ guard (index' /= index2)-            <|> (db, index, Just (Right index2')) <$ guard (index /= index2')-    withTestData ::-      (DeBruijn, Index "tyvar", Maybe (Either DeBruijn (Index "tyvar"))) ->-      ((CompT Renamed, ValT Renamed, Either TypeAppError (ValT Renamed)) -> Property) ->-      Property-    withTestData (db, index, mrest) f =-      withRenamedComp (Comp0 $ tyvar (S db) index :--:> ReturnT integerT) $ \fun ->-        case mrest of-          Nothing -> withRenamedVals (Identity . tyvar db $ index) $ \(Identity arg) ->-            f (fun, arg, Right integerT)-          Just rest ->-            let level = negate . review asInt $ db-                lhs = Abstraction . Rigid level $ index-             in case rest of-                  Left db2 -> withRenamedVals (Identity . tyvar db2 $ index) $ \(Identity arg) ->-                    f (fun, arg, Left . DoesNotUnify lhs $ arg)-                  Right index2 -> withRenamedVals (Identity . tyvar db $ index2) $ \(Identity arg) ->-                    f (fun, arg, Left . DoesNotUnify lhs $ arg)+      -- Note (Koz, 08/08/2025): We have to use this rather odd method to ensure+      -- that we never have any scope stack smaller than 2 elements. If we have+      -- an empty stack, we loop forever as we can't find a valid index, and if+      -- the scope stack is a singleton, we can never hit the 'different' case.+      --+      -- Furthermore, we must ensure every scope stack has at least 1 available+      -- variable, as otherwise, our subsequent generator can 'miss'.+      firstScope <- getNonZero <$> arbitrary+      secondScope <- getNonZero <$> arbitrary+      restOfScopes <- liftArbitrary (getNonZero <$> arbitrary)+      let scopeStack = Vector.cons firstScope . Vector.cons secondScope $ restOfScopes+      (t, u, same) <- genAbstractions scopeStack+      pure (scopeStack, t, u, same)+    genAbstractions :: Vector Word32 -> Gen (ValT AbstractTy, ValT AbstractTy, Bool)+    genAbstractions scopeStack = do+      let len = Vector.length scopeStack+      iPosition <- chooseInt (0, len - 1)+      jPosition <- suchThat (chooseInt (0, len - 1)) (/= iPosition)+      let iDB = fromJust . preview asInt $ iPosition+      let jDB = fromJust . preview asInt $ jPosition+      let iVarsAvailable = fromIntegral $ scopeStack Vector.! iPosition+      let jVarsAvailable = fromIntegral $ scopeStack Vector.! jPosition+      iIx <- suchThatMap (chooseInt (0, iVarsAvailable - 1)) (preview intIndex)+      jIx <- suchThatMap (chooseInt (0, jVarsAvailable - 1)) (preview intIndex)+      -- Note (Koz, 08/08/2025): We have to offset `t` by 1, because it's being+      -- bundled directly into a `Comp0`, which means that to refer to the same+      -- position in the scope stack, it needs to be one higher.+      elements+        [ -- 'Same' option.+          (tyvar (S iDB) iIx, tyvar iDB iIx, True),+          -- 'Different' option.+          (tyvar (S iDB) iIx, tyvar jDB jIx, False)+        ]  -- Randomly pick a rigid type A, then try to apply `(forall a . a -> !Integer) -- -> !Integer` to `(A -> !Integer)`. Result should fail to unify. propUnifyWildcardRigid :: Property propUnifyWildcardRigid = forAllShrink arbitrary shrink $ \(scope, index) ->   let thunk = ThunkT . Comp1 $ tyvar Z ix0 :--:> ReturnT integerT-   in withRenamedComp (Comp0 $ thunk :--:> ReturnT integerT) $ \f ->+      mockScope = Vector.replicate (review asInt scope + 1) (fromIntegral $ review intIndex index + 1)+   in withRenamedComp mockScope (Comp0 $ thunk :--:> ReturnT integerT) $ \f ->         let argT = ThunkT . Comp0 $ tyvar (S scope) index :--:> ReturnT integerT-         in withRenamedVals (Identity argT) $ \(Identity argT') ->-              let lhs = ThunkT . Comp1 $ Abstraction (Wildcard 1 2 ix0) :--:> ReturnT integerT+         in withRenamedVals mockScope (Identity argT) $ \(Identity argT') ->+              let lhs = ThunkT . Comp1 $ Abstraction (Wildcard 1 1 ix0) :--:> ReturnT integerT                   expected = Left . DoesNotUnify lhs $ argT'                   actual = checkApp M.empty f [Just argT']                in expected === actual@@ -363,9 +376,9 @@   let funThunkArgT = ThunkT $ Comp0 $ aT :--:> bT :--:> ReturnT (tyvar (S Z) ix0)       funT = Comp1 $ funThunkArgT :--:> ReturnT (tyvar Z ix0)       thunkT = ThunkT $ Comp0 $ aT :--:> bT :--:> ReturnT aT-   in withRenamedComp funT $ \f ->-        withRenamedVals (Identity thunkT) $ \(Identity argT) ->-          withRenamedVals (Identity aT) $ \(Identity aT') ->+   in withRenamedComp mempty funT $ \f ->+        withRenamedVals mempty (Identity thunkT) $ \(Identity argT) ->+          withRenamedVals mempty (Identity aT) $ \(Identity aT') ->             let expected = Right aT'                 actual = checkApp M.empty f [Just argT]              in expected === actual@@ -382,7 +395,7 @@       arg3 = Datatype "Either" . Vector.fromList $ [BuiltinFlat UnitT, BuiltinFlat BoolT]        expected = BuiltinFlat IntegerT-  defaultLeftRenamed <- failLeft . runRenameM . renameCompT $ defaultLeft+  defaultLeftRenamed <- failLeft . runRenameM mempty . renameCompT $ defaultLeft   actual <-     either (assertFailure . show) pure $       checkApp@@ -411,8 +424,8 @@           tyvar Z ix0             :--:> ThunkT (Comp0 (BuiltinFlat BoolT :--:> ReturnT (tyvar (S Z) ix0)))             :--:> ReturnT (tyvar Z ix0)-  fnRenamed <- failLeft . runRenameM . renameCompT $ testFn-  argRenamed <- failLeft . runRenameM . renameValT $ testArg+  fnRenamed <- failLeft . runRenameM mempty . renameCompT $ testFn+  argRenamed <- failLeft . runRenameM mempty . renameValT $ testArg   result <-     either (assertFailure . show) pure $       checkApp tyAppTestDatatypes fnRenamed [Just argRenamed]@@ -428,7 +441,7 @@   let arg1 = Abstraction $ Unifiable ix0       arg2 = ThunkT (Comp0 $ BuiltinFlat BoolT :--:> ReturnT (BuiltinFlat IntegerT))       arg3 = Datatype "Either" . Vector.fromList $ [arg1, BuiltinFlat BoolT]-  fnRenamed <- failLeft . runRenameM . renameCompT $ defaultLeft+  fnRenamed <- failLeft . runRenameM mempty . renameCompT $ defaultLeft   actual <-     either (assertFailure . show) pure $       checkApp tyAppTestDatatypes fnRenamed (pure <$> [arg1, arg2, arg3])@@ -445,7 +458,7 @@       arg2 = BuiltinFlat BoolT       arg3 = ThunkT $ Comp0 $ Abstraction (Rigid 1 ix0) :--:> BuiltinFlat BoolT :--:> ReturnT (BuiltinFlat IntegerT)       arg4 = Datatype "Pair" (Vector.fromList [arg1, BuiltinFlat BoolT])-  fnRenamed <- failLeft . runRenameM . renameCompT $ defaultPair+  fnRenamed <- failLeft . runRenameM mempty . renameCompT $ defaultPair   actual <-     either (assertFailure . show) pure $       checkApp tyAppTestDatatypes fnRenamed (pure <$> [arg1, arg2, arg3, arg4])@@ -459,7 +472,7 @@           Datatype "Maybe" (Vector.fromList [tyvar Z ix0])             :--:> ReturnT (BuiltinFlat IntegerT)       testArg = Datatype "Maybe" (Vector.fromList [BuiltinFlat BoolT])-  fnRenamed <- failLeft . runRenameM . renameCompT $ testFn+  fnRenamed <- failLeft . runRenameM mempty . renameCompT $ testFn   result <-     either (assertFailure . catchInsufficientArgs) pure $       checkApp tyAppTestDatatypes fnRenamed [Just testArg]@@ -478,7 +491,7 @@         Comp1 $           Datatype "Maybe" (Vector.singleton $ tyvar Z ix0)             :--:> ReturnT (Datatype "Maybe" (Vector.singleton . Datatype "Maybe" . Vector.singleton $ tyvar Z ix0))-  fnRenamed <- failLeft . runRenameM . renameCompT $ fn+  fnRenamed <- failLeft . runRenameM mempty . renameCompT $ fn   let arg = Datatype "Maybe" . Vector.singleton $ integerT   let expected = Datatype "Maybe" . Vector.singleton . Datatype "Maybe" . Vector.singleton $ integerT   case checkApp tyAppTestDatatypes fnRenamed [Just arg] of@@ -494,9 +507,9 @@       funThunkArg = ThunkT $ Comp0 $ aT :--:> maybeT :--:> ReturnT (tyvar (S Z) ix0)       funT = Comp1 $ funThunkArg :--:> ReturnT (tyvar Z ix0)       thunkT = ThunkT $ Comp0 $ aT :--:> maybeT :--:> ReturnT aT-   in withRenamedComp funT $ \f ->-        withRenamedVals (Identity thunkT) $ \(Identity argT) ->-          withRenamedVals (Identity aT) $ \(Identity aT') ->+   in withRenamedComp mempty funT $ \f ->+        withRenamedVals mempty (Identity thunkT) $ \(Identity argT) ->+          withRenamedVals mempty (Identity aT) $ \(Identity aT') ->             let expected = Right aT'                 actual = checkApp tyAppTestDatatypes f [Just argT]              in expected === actual@@ -510,9 +523,9 @@       funThunkArg = ThunkT $ Comp0 $ aT :--:> maybeT :--:> ReturnT aT       funT = Comp0 $ funThunkArg :--:> ReturnT aT       thunkT = ThunkT $ Comp0 $ aT :--:> maybeT :--:> ReturnT aT-   in withRenamedComp funT $ \f ->-        withRenamedVals (Identity thunkT) $ \(Identity argT) ->-          withRenamedVals (Identity aT) $ \(Identity aT') ->+   in withRenamedComp mempty funT $ \f ->+        withRenamedVals mempty (Identity thunkT) $ \(Identity argT) ->+          withRenamedVals mempty (Identity aT) $ \(Identity aT') ->             let expected = Right aT'                 actual = checkApp tyAppTestDatatypes f [Just argT]              in expected === actual@@ -526,9 +539,9 @@       funThunkArg = ThunkT $ Comp1 $ tyvar (S Z) ix0 :--:> maybeT :--:> ReturnT aT       funT = Comp1 $ funThunkArg :--:> ReturnT aT       thunkT = ThunkT $ Comp0 $ aT :--:> maybeT :--:> ReturnT aT-   in withRenamedComp funT $ \f ->-        withRenamedVals (Identity thunkT) $ \(Identity argT) ->-          withRenamedVals (Identity aT) $ \(Identity aT') ->+   in withRenamedComp mempty funT $ \f ->+        withRenamedVals mempty (Identity thunkT) $ \(Identity argT) ->+          withRenamedVals mempty (Identity aT) $ \(Identity aT') ->             let expected = Right aT'                 actual = checkApp tyAppTestDatatypes f [Just argT]              in expected === actual@@ -543,9 +556,9 @@       funThunkArg = ThunkT $ Comp0 $ maybeTConcrete :--:> ReturnT (tyvar (S Z) ix0)       funT = Comp1 $ funThunkArg :--:> ReturnT (tyvar Z ix0)       thunkT = ThunkT $ Comp0 $ maybeTConcrete :--:> ReturnT aT-   in withRenamedComp funT $ \f ->-        withRenamedVals (Identity thunkT) $ \(Identity argT) ->-          withRenamedVals (Identity aT) $ \(Identity aT') ->+   in withRenamedComp mempty funT $ \f ->+        withRenamedVals mempty (Identity thunkT) $ \(Identity argT) ->+          withRenamedVals mempty (Identity aT) $ \(Identity aT') ->             let expected = Right aT'                 actual = checkApp tyAppTestDatatypes f [Just argT]              in expected === actual@@ -581,19 +594,27 @@       :--:> ReturnT (tyvar Z ix2)  withRenamedComp ::+  Vector.Vector Word32 ->   CompT AbstractTy ->   (CompT Renamed -> Property) ->   Property-withRenamedComp t f = case runRenameM . renameCompT $ t of+withRenamedComp scope t f = case runRenameM scope . renameCompT $ t of   Left err -> counterexample (show err) False   Right t' -> f t'  withRenamedVals ::   forall (t :: Type -> Type).   (Traversable t) =>+  Vector.Vector Word32 ->   t (ValT AbstractTy) ->   (t (ValT Renamed) -> Property) ->   Property-withRenamedVals vals f = case runRenameM . traverse renameValT $ vals of+withRenamedVals scope vals f = case runRenameM scope . traverse renameValT $ vals of   Left err -> counterexample (show err) False   Right vals' -> f vals'++ezTrueLevel :: Vector.Vector Word32 -> DeBruijn -> Index "tyvar" -> Int+ezTrueLevel inherited db ix = case runRenameM inherited . renameValT $ tyvar db ix of+  Left err' -> error ("ezTrueLevel: " <> show err')+  Right (Abstraction (Rigid res _)) -> res+  other -> error $ "ezTrueLevel didn't get a rigid, but got: " <> show other