-
Notifications
You must be signed in to change notification settings - Fork 483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Errors] polish evaluation errors #6043
Changes from 2 commits
fb7095b
ac913dd
3847bae
1fde672
0ac3adc
d3d3d20
0b788fc
0aaf06f
b61398d
5484cd4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ module PlutusCore.Evaluation.Machine.Exception | |
, throwing_ | ||
, throwingWithCause | ||
, extractEvaluationResult | ||
, unsafeExtractEvaluationResult | ||
, unsafeToEvaluationResult | ||
) where | ||
|
||
import PlutusPrelude | ||
|
@@ -66,13 +66,10 @@ data MachineError fun | |
deriving stock (Show, Eq, Functor, Generic) | ||
deriving anyclass (NFData) | ||
|
||
-- | The type of errors (all of them) which can occur during evaluation | ||
-- (some are used-caused, some are internal). | ||
data EvaluationError user internal | ||
= InternalEvaluationError !internal | ||
-- ^ Indicates bugs. | ||
| UserEvaluationError !user | ||
-- ^ Indicates user errors. | ||
-- | The type of errors (all of them) which can occur during evaluation. TODO: explain | ||
effectfully marked this conversation as resolved.
Show resolved
Hide resolved
|
||
data EvaluationError operational structural | ||
= OperationalEvaluationError !operational | ||
| StructuralEvaluationError !structural | ||
deriving stock (Show, Eq, Functor, Generic) | ||
deriving anyclass (NFData) | ||
|
||
|
@@ -81,47 +78,50 @@ mtraverse makeClassyPrisms | |
, ''EvaluationError | ||
] | ||
|
||
instance internal ~ MachineError fun => AsMachineError (EvaluationError user internal) fun where | ||
_MachineError = _InternalEvaluationError | ||
instance AsUnliftingError internal => AsUnliftingError (EvaluationError user internal) where | ||
_UnliftingError = _InternalEvaluationError . _UnliftingError | ||
instance structural ~ MachineError fun => | ||
AsMachineError (EvaluationError operational structural) fun where | ||
_MachineError = _StructuralEvaluationError | ||
instance AsUnliftingError structural => | ||
AsUnliftingError (EvaluationError operational structural) where | ||
_UnliftingError = _StructuralEvaluationError . _UnliftingError | ||
instance AsUnliftingError (MachineError fun) where | ||
_UnliftingError = _UnliftingMachineError | ||
instance AsEvaluationFailure user => AsEvaluationFailure (EvaluationError user internal) where | ||
_EvaluationFailure = _UserEvaluationError . _EvaluationFailure | ||
instance AsEvaluationFailure operational => | ||
AsEvaluationFailure (EvaluationError operational structural) where | ||
_EvaluationFailure = _OperationalEvaluationError . _EvaluationFailure | ||
|
||
type EvaluationException user internal = | ||
ErrorWithCause (EvaluationError user internal) | ||
type EvaluationException operational structural = | ||
ErrorWithCause (EvaluationError operational structural) | ||
|
||
{- Note [Ignoring context in UserEvaluationError] | ||
The UserEvaluationError error has a term argument, but | ||
{- Note [Ignoring context in OperationalEvaluationError] | ||
The OperationalEvaluationError error has a term argument, but | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get what is happening here, but what I don't understand is why we do all this effort to distinquish between operational vs structural errors at actual script validation, by using From the user's point of view, validating a script would either EvaluationSuccess (validate) or EvaluationFailure (not validate). In what way does it matter if the non-validation was caused by operational or structural error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's explained in the Haddocks:
Basically, it's an extra level of safety for us. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it is an artifact of Tests alone, then perhaps EvaluationResult should belong to a testlib. Because taking into account other Plutus-based languages (Aiken,Plutarch,etc) there may not be a distinction between structural vs operational errors and instead they have just errors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe. I'm currently removing it from builtins where we have a much more appropriate
Perhaps, but I don't really care, I just want us to have that distinction for the same reasons I want us to have a type checker instead of juggling raw There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I mean, removing |
||
extractEvaluationResult just discards this and returns | ||
EvaluationFailure. This means that, for example, if we use the `plc` | ||
command to execute a program containing a division by zero, plc exits | ||
silently without reporting that anything has gone wrong (but returning | ||
a non-zero exit code to the shell via `exitFailure`). This is because | ||
UserEvaluationError is used in cases when a PLC program itself goes | ||
OperationalEvaluationError is used in cases when a PLC program itself goes | ||
wrong (for example, a failure due to `(error)`, a failure during | ||
builtin evaluation, or exceeding the gas limit). This is used to | ||
signal unsuccessful in validation and so is not regarded as a real | ||
error; in contrast, machine errors, typechecking failures, | ||
effectfully marked this conversation as resolved.
Show resolved
Hide resolved
|
||
and so on are genuine errors and we report their context if available. | ||
-} | ||
|
||
-- | Turn any 'UserEvaluationError' into an 'EvaluationFailure'. | ||
-- | Turn any 'OperationalEvaluationError' into an 'EvaluationFailure'. | ||
extractEvaluationResult | ||
:: Either (EvaluationException user internal term) a | ||
-> Either (ErrorWithCause internal term) (EvaluationResult a) | ||
:: Either (EvaluationException operational structural term) a | ||
-> Either (ErrorWithCause structural term) (EvaluationResult a) | ||
extractEvaluationResult (Right term) = Right $ EvaluationSuccess term | ||
extractEvaluationResult (Left (ErrorWithCause evalErr cause)) = case evalErr of | ||
InternalEvaluationError err -> Left $ ErrorWithCause err cause | ||
UserEvaluationError _ -> Right $ EvaluationFailure | ||
StructuralEvaluationError err -> Left $ ErrorWithCause err cause | ||
OperationalEvaluationError _ -> Right $ EvaluationFailure | ||
|
||
unsafeExtractEvaluationResult | ||
unsafeToEvaluationResult | ||
:: (PrettyPlc internal, PrettyPlc term, Typeable internal, Typeable term) | ||
=> Either (EvaluationException user internal term) a | ||
-> EvaluationResult a | ||
unsafeExtractEvaluationResult = unsafeFromEither . extractEvaluationResult | ||
unsafeToEvaluationResult = unsafeFromEither . extractEvaluationResult | ||
|
||
instance (HasPrettyDefaults config ~ 'True, Pretty fun) => | ||
PrettyBy config (MachineError fun) where | ||
|
@@ -148,13 +148,7 @@ instance (HasPrettyDefaults config ~ 'True, Pretty fun) => | |
|
||
instance | ||
( HasPrettyDefaults config ~ 'True | ||
, PrettyBy config internal, Pretty user | ||
) => PrettyBy config (EvaluationError user internal) where | ||
prettyBy config (InternalEvaluationError err) = fold | ||
[ "error:", hardline | ||
, prettyBy config err | ||
] | ||
prettyBy _ (UserEvaluationError err) = fold | ||
[ "User error:", hardline | ||
, pretty err | ||
] | ||
, Pretty operational, PrettyBy config structural | ||
) => PrettyBy config (EvaluationError operational structural) where | ||
prettyBy _ (OperationalEvaluationError operational) = pretty operational | ||
prettyBy config (StructuralEvaluationError structural) = prettyBy config structural | ||
Comment on lines
+171
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And indeed here you show that it does not matter if the error was operational or structural :P There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It matters when |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,8 +29,8 @@ import PlutusIR.Compiler qualified as PIR | |
import PlutusIR.Core qualified as PIR | ||
import PlutusIR.Parser (pTerm) | ||
import UntypedPlutusCore.Core qualified as UPLC | ||
import UntypedPlutusCore.Evaluation.Machine.Cek (CekValue, EvaluationResult (..), logEmitter, | ||
unsafeEvaluateCek) | ||
import UntypedPlutusCore.Evaluation.Machine.Cek (CekValue, EvaluationResult (..), evaluateCek, | ||
logEmitter, unsafeToEvaluationResult) | ||
import UntypedPlutusCore.Evaluation.Machine.Cek.CekMachineCosts (CekMachineCosts) | ||
|
||
pirTermFromFile | ||
|
@@ -66,7 +66,7 @@ compilePirProgramOrFail pirProgram = do | |
& runExceptT | ||
>>= \case | ||
Left (er :: PIR.Error DefaultUni DefaultFun (Provenance ())) -> fail $ show er | ||
Right p -> pure (void p) | ||
Right p -> pure (void p) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like |
||
|
||
compileTplcProgramOrFail | ||
:: (MonadFail m) | ||
|
@@ -83,7 +83,7 @@ evaluateUplcProgramWithTraces | |
:: UPLC.Program Name DefaultUni DefaultFun () | ||
-> (EvaluationResult (UPLC.Term Name DefaultUni DefaultFun ()), [Text]) | ||
evaluateUplcProgramWithTraces uplcProg = | ||
unsafeEvaluateCek logEmitter machineParameters (uplcProg ^. UPLC.progTerm) | ||
unsafeToEvaluationResult $ evaluateCek logEmitter machineParameters (uplcProg ^. UPLC.progTerm) | ||
where | ||
costModel :: CostModel CekMachineCosts BuiltinCostModel = | ||
CostModel defaultCekMachineCosts defaultBuiltinCostModel | ||
|
@@ -102,5 +102,5 @@ defaultCompilationCtx = do | |
handlePirErrorByFailing | ||
:: (Pretty ann, MonadFail m) => Either (PIR.Error DefaultUni DefaultFun ann) a -> m a | ||
handlePirErrorByFailing = \case | ||
Left e -> fail $ show e | ||
Left e -> fail $ show e | ||
Right x -> pure x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just to preserve the old behavior, I'm not really sure why we're using
hardline
in error messages.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will
hardline
be added twice in case ofJust cause
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but there will be pretty-printer
err
in-between, you can see how it gets rendered at the ~bottom of the diff. Anyways, I'm just preserving the old behavior newlines-wise.