Skip to content

Commit dd41fd3

Browse files
authored
[Builtin] Make 'makeKnown' receive the term representation of the value (IntersectMBO#3957)
* [Builtin] Make 'makeKnown' receive the term representation of the value * [Builtin] Make 'readKnown' receive the term representation of the value
1 parent e49cb6f commit dd41fd3

File tree

17 files changed

+143
-169
lines changed

17 files changed

+143
-169
lines changed

plutus-core/generators/PlutusCore/Generators/Internal/TypeEvalCheck.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ typeEvalCheckBy
106106
-> TypeEvalCheckM uni fun (TermOf (Term TyName Name uni fun ()) (TypeEvalCheckResult uni fun))
107107
typeEvalCheckBy eval (TermOf term (x :: a)) = TermOf term <$> do
108108
let tyExpected = runQuote . normalizeType $ toTypeAst (Proxy @a)
109-
valExpected = makeKnownNoEmit x
109+
valExpected = makeKnownOrFail x
110110
tyActual <- runQuoteT $ do
111111
config <- getDefTypeCheckConfig ()
112112
inferType config term

plutus-core/generators/PlutusCore/Generators/Internal/TypedBuiltinGen.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ attachCoercedTerm
6464
=> GenT m a -> GenT m (TermOf term a)
6565
attachCoercedTerm a = do
6666
x <- a
67-
case makeKnownNoEmit x of
67+
case makeKnownOrFail x of
6868
-- I've attempted to implement support for generating 'EvaluationFailure',
6969
-- but it turned out to be too much of a pain for something that we do not really need.
7070
EvaluationFailure -> fail $ concat

plutus-core/plutus-core/examples/PlutusCore/Examples/Builtins.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ instance KnownTypeAst uni Void where
138138
a <- freshTyName "a"
139139
pure $ TyForall () a (Type ()) $ TyVar () a
140140
instance KnownType term Void where
141-
makeKnown = absurd
142-
readKnown = throwingWithCause _UnliftingError "Can't unlift a 'Void'" . Just
141+
makeKnown _ = absurd
142+
readKnown mayCause _ = throwingWithCause _UnliftingError "Can't unlift a 'Void'" mayCause
143143
type instance ToBinds Void = '[]
144144

145145
data BuiltinErrorCall = BuiltinErrorCall

plutus-core/plutus-core/src/PlutusCore/Constant/Typed.hs

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ module PlutusCore.Constant.Typed
3636
, HasConstantIn
3737
, KnownTypeAst (..)
3838
, KnownType (..)
39-
, makeKnownNoEmit
39+
, readKnownSelf
40+
, makeKnownOrFail
4041
, SomeConstant (..)
4142
, SomeConstantOf (..)
4243
) where
@@ -408,24 +409,24 @@ newtype Opaque term (rep :: GHC.Type) = Opaque
408409

409410
-- | Throw an 'UnliftingError' saying that the received argument is not a constant.
410411
throwNotAConstant
411-
:: (MonadError (ErrorWithCause err term) m, AsUnliftingError err)
412-
=> term -> m r
413-
throwNotAConstant = throwingWithCause _UnliftingError "Not a constant" . Just
412+
:: (MonadError (ErrorWithCause err cause) m, AsUnliftingError err)
413+
=> Maybe cause -> m r
414+
throwNotAConstant = throwingWithCause _UnliftingError "Not a constant"
414415

415416
class AsConstant term where
416417
-- | Unlift from the 'Constant' constructor throwing an 'UnliftingError' if the provided @term@
417418
-- is not a 'Constant'.
418419
asConstant
419-
:: (MonadError (ErrorWithCause err term) m, AsUnliftingError err)
420-
=> term -> m (Some (ValueOf (UniOf term)))
420+
:: (MonadError (ErrorWithCause err cause) m, AsUnliftingError err)
421+
=> Maybe cause -> term -> m (Some (ValueOf (UniOf term)))
421422

422423
class FromConstant term where
423424
-- | Wrap a Haskell value as a @term@.
424425
fromConstant :: Some (ValueOf (UniOf term)) -> term
425426

426427
instance AsConstant (Term TyName Name uni fun ann) where
427-
asConstant (Constant _ val) = pure val
428-
asConstant term = throwNotAConstant term
428+
asConstant _ (Constant _ val) = pure val
429+
asConstant mayCause _ = throwNotAConstant mayCause
429430

430431
instance FromConstant (Term tyname name uni fun ()) where
431432
fromConstant = Constant ()
@@ -461,35 +462,41 @@ latter breaks on @m a@
461462

462463
-- See Note [KnownType's defaults].
463464
-- | Haskell types known to exist on the PLC side.
465+
-- Both the methods take a @Maybe cause@ argument to report the cause of a potential failure.
466+
-- @cause@ is different to @term@ to support evaluators that distinguish between terms and values
467+
-- (@makeKnown@ normally constructs a value, but it's convenient to report the cause of a failure
468+
-- as a term). Note that an evaluator might require the cause to be computed lazily for best
469+
-- performance on the happy path and @Maybe@ ensures that even if we somehow force the argument,
470+
-- the cause stored in it is not forced due to @Maybe@ being a lazy data type.
464471
class KnownTypeAst (UniOf term) a => KnownType term a where
465472
-- | Convert a Haskell value to the corresponding PLC term.
466473
-- The inverse of 'readKnown'.
467474
makeKnown
468-
:: ( MonadEmitter m, MonadError err m, AsEvaluationFailure err
475+
:: ( MonadEmitter m, MonadError (ErrorWithCause err cause) m, AsEvaluationFailure err
469476
)
470-
=> a -> m term
477+
=> Maybe cause -> a -> m term
471478
default makeKnown
472-
:: ( MonadError err m
479+
:: ( MonadError (ErrorWithCause err cause) m
473480
, KnownBuiltinType term a
474481
)
475-
=> a -> m term
482+
=> Maybe cause -> a -> m term
476483
-- Forcing the value to avoid space leaks. Note that the value is only forced to WHNF,
477484
-- so care must be taken to ensure that every value of a type from the universe gets forced
478485
-- to NF whenever it's forced to WHNF.
479-
makeKnown x = pure . fromConstant . someValue $! x
486+
makeKnown _ x = pure . fromConstant . someValue $! x
480487

481488
-- | Convert a PLC term to the corresponding Haskell value.
482489
-- The inverse of 'makeKnown'.
483490
readKnown
484-
:: ( MonadError (ErrorWithCause err term) m, AsUnliftingError err, AsEvaluationFailure err
491+
:: ( MonadError (ErrorWithCause err cause) m, AsUnliftingError err, AsEvaluationFailure err
485492
)
486-
=> term -> m a
493+
=> Maybe cause -> term -> m a
487494
default readKnown
488-
:: ( MonadError (ErrorWithCause err term) m, AsUnliftingError err
495+
:: ( MonadError (ErrorWithCause err cause) m, AsUnliftingError err
489496
, KnownBuiltinType term a
490497
)
491-
=> term -> m a
492-
readKnown term = asConstant term >>= \case
498+
=> Maybe cause -> term -> m a
499+
readKnown mayCause term = asConstant mayCause term >>= \case
493500
Some (ValueOf uniAct x) -> do
494501
let uniExp = knownUni @_ @(UniOf term) @a
495502
case uniAct `geq` uniExp of
@@ -500,34 +507,57 @@ class KnownTypeAst (UniOf term) a => KnownType term a where
500507
, "expected: " ++ gshow uniExp
501508
, "; actual: " ++ gshow uniAct
502509
]
503-
throwingWithCause _UnliftingError err $ Just term
504-
505-
makeKnownNoEmit :: (KnownType term a, MonadError err m, AsEvaluationFailure err) => a -> m term
506-
makeKnownNoEmit = unNoEmitterT . makeKnown
510+
throwingWithCause _UnliftingError err mayCause
511+
512+
-- | Same as 'readKnown', but the cause of a potential failure is the provided term itself.
513+
readKnownSelf
514+
:: ( KnownType term a
515+
, MonadError (ErrorWithCause err term) m, AsUnliftingError err, AsEvaluationFailure err
516+
)
517+
=> term -> m a
518+
readKnownSelf term = readKnown (Just term) term
519+
520+
-- | A transformer for fitting a monad not carrying the cause of a failure into 'makeKnown'.
521+
newtype NoCauseT (term :: GHC.Type) m a = NoCauseT
522+
{ unNoCauseT :: m a
523+
} deriving newtype (Functor, Applicative, Monad)
524+
525+
instance (MonadError err m, AsEvaluationFailure err) =>
526+
MonadError (ErrorWithCause err term) (NoCauseT term m) where
527+
throwError _ = NoCauseT $ throwError evaluationFailure
528+
NoCauseT a `catchError` h =
529+
NoCauseT $ a `catchError` \err ->
530+
unNoCauseT . h $ ErrorWithCause err Nothing
531+
532+
-- | Same as 'makeKnown', but allows for neither emitting nor storing the cause of a failure.
533+
-- For example the monad can be simply 'EvaluationResult'.
534+
makeKnownOrFail :: (KnownType term a, MonadError err m, AsEvaluationFailure err) => a -> m term
535+
makeKnownOrFail = unNoCauseT . unNoEmitterT . makeKnown Nothing
507536

508537
instance KnownTypeAst uni a => KnownTypeAst uni (EvaluationResult a) where
509538
toTypeAst _ = toTypeAst $ Proxy @a
510539

511540
instance (KnownTypeAst (UniOf term) a, KnownType term a) =>
512541
KnownType term (EvaluationResult a) where
513-
makeKnown EvaluationFailure = throwError evaluationFailure
514-
makeKnown (EvaluationSuccess x) = makeKnown x
542+
makeKnown mayCause EvaluationFailure = throwingWithCause _EvaluationFailure () mayCause
543+
makeKnown mayCause (EvaluationSuccess x) = makeKnown mayCause x
515544

516545
-- Catching 'EvaluationFailure' here would allow *not* to short-circuit when 'readKnown' fails
517546
-- to read a Haskell value of type @a@. Instead, in the denotation of the builtin function
518547
-- the programmer would be given an explicit 'EvaluationResult' value to handle, which means
519548
-- that when this value is 'EvaluationFailure', a PLC 'Error' was caught.
520549
-- I.e. it would essentially allow us to catch errors and handle them in a programmable way.
521550
-- We forbid this, because it complicates code and isn't supported by evaluation engines anyway.
522-
readKnown = throwingWithCause _UnliftingError "Error catching is not supported" . Just
551+
readKnown mayCause _ =
552+
throwingWithCause _UnliftingError "Error catching is not supported" mayCause
523553

524554
instance KnownTypeAst uni a => KnownTypeAst uni (Emitter a) where
525555
toTypeAst _ = toTypeAst $ Proxy @a
526556

527557
instance KnownType term a => KnownType term (Emitter a) where
528-
makeKnown = unEmitter >=> makeKnown
558+
makeKnown mayCause = unEmitter >=> makeKnown mayCause
529559
-- TODO: we really should tear 'KnownType' apart into two separate type classes.
530-
readKnown = throwingWithCause _UnliftingError "Can't unlift an 'Emitter'" . Just
560+
readKnown mayCause _ = throwingWithCause _UnliftingError "Can't unlift an 'Emitter'" mayCause
531561

532562
-- | For unlifting from the 'Constant' constructor. For cases where we care about having a type tag
533563
-- in the denotation of a builtin rather than full unlifting to a specific built-in type.
@@ -543,8 +573,8 @@ instance (uni ~ uni', KnownTypeAst uni rep) => KnownTypeAst uni (SomeConstant un
543573

544574
instance (HasConstantIn uni term, KnownTypeAst uni rep) =>
545575
KnownType term (SomeConstant uni rep) where
546-
makeKnown = pure . fromConstant . unSomeConstant
547-
readKnown = fmap SomeConstant . asConstant
576+
makeKnown _ = pure . fromConstant . unSomeConstant
577+
readKnown mayCause = fmap SomeConstant . asConstant mayCause
548578

549579
{- | 'SomeConstantOf' is similar to 'SomeConstant': while the latter is for unlifting any
550580
constants, the former is for unlifting constants of a specific polymorphic built-in type
@@ -606,18 +636,18 @@ data ReadSomeConstantOf m uni f reps =
606636

607637
instance (KnownBuiltinTypeIn uni term f, All (KnownTypeAst uni) reps, HasUniApply uni) =>
608638
KnownType term (SomeConstantOf uni f reps) where
609-
makeKnown = pure . fromConstant . runSomeConstantOf
639+
makeKnown _ = pure . fromConstant . runSomeConstantOf
610640

611-
readKnown term = asConstant term >>= \case
641+
readKnown (mayCause :: Maybe cause) term = asConstant mayCause term >>= \case
612642
Some (ValueOf uni xs) -> do
613643
let uniF = knownUni @_ @_ @f
614644
err = fromString $ concat
615645
[ "Type mismatch: "
616646
, "expected an application of: " ++ gshow uniF
617647
, "; but got the following type: " ++ gshow uni
618648
]
619-
wrongType :: (MonadError (ErrorWithCause err term) m, AsUnliftingError err) => m a
620-
wrongType = throwingWithCause _UnliftingError err $ Just term
649+
wrongType :: (MonadError (ErrorWithCause err cause) m, AsUnliftingError err) => m a
650+
wrongType = throwingWithCause _UnliftingError err mayCause
621651
-- In order to prove that the type of @xs@ is an application of @f@ we need to
622652
-- peel all type applications off in the type of @xs@ until we get to the head and then
623653
-- check that the head is indeed @f@. Each peeled type application becomes a
@@ -663,12 +693,12 @@ instance KnownTypeAst uni rep => KnownTypeAst uni (Opaque term rep) where
663693
toTypeAst _ = toTypeAst $ Proxy @rep
664694

665695
instance (term ~ term', KnownTypeAst (UniOf term) rep) => KnownType term (Opaque term' rep) where
666-
makeKnown = pure . unOpaque
667-
readKnown = pure . Opaque
696+
makeKnown _ = pure . unOpaque
697+
readKnown _ = pure . Opaque
668698

669699
instance uni `Contains` Integer => KnownTypeAst uni Integer
670700
instance uni `Contains` BS.ByteString => KnownTypeAst uni BS.ByteString
671-
instance uni `Contains` Text.Text => KnownTypeAst uni Text.Text
701+
instance uni `Contains` Text.Text => KnownTypeAst uni Text.Text
672702
instance uni `Contains` () => KnownTypeAst uni ()
673703
instance uni `Contains` Bool => KnownTypeAst uni Bool
674704
instance uni `Contains` [a] => KnownTypeAst uni [a]
@@ -677,7 +707,7 @@ instance uni `Contains` Data => KnownTypeAst uni Data
677707

678708
instance KnownBuiltinType term Integer => KnownType term Integer
679709
instance KnownBuiltinType term BS.ByteString => KnownType term BS.ByteString
680-
instance KnownBuiltinType term Text.Text => KnownType term Text.Text
710+
instance KnownBuiltinType term Text.Text => KnownType term Text.Text
681711
instance KnownBuiltinType term () => KnownType term ()
682712
instance KnownBuiltinType term Bool => KnownType term Bool
683713
instance KnownBuiltinType term [a] => KnownType term [a]
@@ -697,11 +727,11 @@ instance uni `Includes` Integer => KnownTypeAst uni Int where
697727

698728
-- See Note [Int as Integer].
699729
instance KnownBuiltinType term Integer => KnownType term Int where
700-
makeKnown = makeKnown . toInteger
701-
readKnown term = do
702-
i :: Integer <- readKnown term
730+
makeKnown mayCause = makeKnown mayCause . toInteger
731+
readKnown mayCause term = do
732+
i :: Integer <- readKnown mayCause term
703733
unless (fromIntegral (minBound :: Int) <= i && i <= fromIntegral (maxBound :: Int)) $
704-
throwingWithCause _EvaluationFailure () $ Just term
734+
throwingWithCause _EvaluationFailure () mayCause
705735
pure $ fromIntegral i
706736

707737
-- Utils

plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Ck.hs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import PlutusCore.Name
3636
import PlutusCore.Pretty (PrettyConfigPlc, PrettyConst)
3737

3838
import Control.Monad.Except
39-
import Control.Monad.Morph
4039
import Control.Monad.Reader
4140
import Control.Monad.ST
4241
import Data.Array
@@ -69,7 +68,7 @@ evalBuiltinApp
6968
-> BuiltinRuntime (CkValue uni fun)
7069
-> CkM uni fun s (CkValue uni fun)
7170
evalBuiltinApp term runtime@(BuiltinRuntime sch x _) = case sch of
72-
TypeSchemeResult _ -> makeKnown x
71+
TypeSchemeResult _ -> makeKnown (Just term) x
7372
_ -> pure $ VBuiltin term runtime
7473

7574
ckValueToTerm :: CkValue uni fun -> Term TyName Name uni fun ()
@@ -95,29 +94,22 @@ data CkUserError =
9594
CkEvaluationFailure -- Error has been called or a builtin application has failed
9695
deriving (Show, Eq, Generic, NFData)
9796

98-
-- | The CK machine-specific 'EvaluationException' parameterized over @term@.
99-
type CkEvaluationExceptionCarrying term fun =
100-
EvaluationException CkUserError (MachineError fun) term
97+
-- | The CK machine-specific 'EvaluationException'.
98+
type CkEvaluationException uni fun =
99+
EvaluationException CkUserError (MachineError fun) (Term TyName Name uni fun ())
101100

102-
-- See Note [Being generic over @term@ in 'CekM']
103-
type CkCarryingM term uni fun s =
101+
type CkM uni fun s =
104102
ReaderT (CkEnv uni fun s)
105-
(ExceptT (CkEvaluationExceptionCarrying term fun)
103+
(ExceptT (CkEvaluationException uni fun)
106104
(ST s))
107105

108-
-- | The CK machine-specific 'EvaluationException'.
109-
type CkEvaluationException uni fun =
110-
CkEvaluationExceptionCarrying (Term TyName Name uni fun ()) fun
111-
112106
instance AsEvaluationFailure CkUserError where
113107
_EvaluationFailure = _EvaluationFailureVia CkEvaluationFailure
114108

115109
instance Pretty CkUserError where
116110
pretty CkEvaluationFailure = "The provided Plutus code called 'error'."
117111

118-
type CkM uni fun s = CkCarryingM (Term TyName Name uni fun ()) uni fun s
119-
120-
instance MonadEmitter (CkCarryingM term uni fun s) where
112+
instance MonadEmitter (CkM uni fun s) where
121113
emit str = do
122114
mayLogsRef <- asks ckEnvMayEmitRef
123115
case mayLogsRef of
@@ -130,8 +122,8 @@ instance FromConstant (CkValue uni fun) where
130122
fromConstant = VCon
131123

132124
instance AsConstant (CkValue uni fun) where
133-
asConstant (VCon val) = pure val
134-
asConstant term = throwNotAConstant term
125+
asConstant _ (VCon val) = pure val
126+
asConstant mayCause _ = throwNotAConstant mayCause
135127

136128
data Frame uni fun
137129
= FrameApplyFun (CkValue uni fun) -- ^ @[V _]@
@@ -304,15 +296,13 @@ applyEvaluate
304296
-> CkM uni fun s (Term TyName Name uni fun ())
305297
applyEvaluate stack (VLamAbs name _ body) arg = stack |> substituteDb name (ckValueToTerm arg) body
306298
applyEvaluate stack (VBuiltin term (BuiltinRuntime sch f _)) arg = do
307-
let term' = Apply () term $ ckValueToTerm arg
299+
let argTerm = ckValueToTerm arg
300+
term' = Apply () term argTerm
308301
case sch of
309302
-- It's only possible to apply a builtin application if the builtin expects a term
310303
-- argument next.
311304
TypeSchemeArrow _ schB -> do
312-
-- The builtin application machinery wants to be able to throw a 'CekValue' rather
313-
-- than a 'Term', hence 'withErrorDischarging'.
314-
let dischargeError = hoist $ withExceptT $ mapCauseInMachineException ckValueToTerm
315-
x <- dischargeError $ readKnown arg
305+
x <- readKnown (Just argTerm) arg
316306
let noCosting = error "The CK machine does not support costing"
317307
runtime' = BuiltinRuntime schB (f x) noCosting
318308
res <- evalBuiltinApp term' runtime'
@@ -374,4 +364,4 @@ readKnownCk
374364
=> BuiltinsRuntime fun (CkValue uni fun)
375365
-> Term TyName Name uni fun ()
376366
-> Either (CkEvaluationException uni fun) a
377-
readKnownCk runtime = evaluateCkNoEmit runtime >=> readKnown
367+
readKnownCk runtime = evaluateCkNoEmit runtime >=> readKnownSelf

plutus-core/plutus-ir/src/PlutusIR/Core/Type.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ data Term tyname name uni fun a =
125125
type instance UniOf (Term tyname name uni fun ann) = uni
126126

127127
instance AsConstant (Term tyname name uni fun ann) where
128-
asConstant (Constant _ val) = pure val
129-
asConstant term = throwNotAConstant term
128+
asConstant _ (Constant _ val) = pure val
129+
asConstant mayCause _ = throwNotAConstant mayCause
130130

131131
instance FromConstant (Term tyname name uni fun ()) where
132132
fromConstant = Constant ()

plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Core/Type.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ instance TermLike (Term name uni fun) TPLC.TyName name uni fun where
9696
error = \ann _ -> Error ann
9797

9898
instance TPLC.AsConstant (Term name uni fun ann) where
99-
asConstant (Constant _ val) = pure val
100-
asConstant term = TPLC.throwNotAConstant term
99+
asConstant _ (Constant _ val) = pure val
100+
asConstant mayCause _ = TPLC.throwNotAConstant mayCause
101101

102102
instance TPLC.FromConstant (Term name uni fun ()) where
103103
fromConstant = Constant ()

0 commit comments

Comments
 (0)