Skip to content

Commit ad81053

Browse files
authored
Migrate const-eval errors to new framework (#29413)
* Migrate const-eval errors to new framework Const eval shipped its own mini error framework that predated the recent Formatted combinator rework and mostly just carried strings around to be turned into a proper error later. The inner Span was dead (every wrapper supplied its own) and the LeoError::ConstEvalError variant existed only because crates/ast didn't own an error-code prefix. This drops the mini framework and produces Formatted errors directly from crates/ast/src/const_eval with a new CEV prefix. The only user-visible change is error codes shift from ESAZ037{4006,4007,4009} to ECEV037800{0,1,2}. * Add instrinsic evaluation fail test
1 parent 6ee408e commit ad81053

25 files changed

Lines changed: 330 additions & 428 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (C) 2019-2026 Provable Inc.
2+
// This file is part of the Leo library.
3+
4+
// The Leo library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// The Leo library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16+
17+
use std::fmt::Display;
18+
19+
use leo_errors::Formatted;
20+
use leo_span::Span;
21+
22+
use super::Value;
23+
use crate::{BinaryOperation, UnaryOperation};
24+
25+
const CODE_PREFIX: &str = "CEV";
26+
const CODE_MASK: i32 = 8000;
27+
28+
pub(crate) fn binary_op_failure(
29+
lhs: &Value,
30+
op: BinaryOperation,
31+
rhs: &Value,
32+
reason: impl Display,
33+
span: Span,
34+
) -> Formatted {
35+
Formatted::error(
36+
CODE_PREFIX,
37+
CODE_MASK,
38+
format!("Binary operation `{lhs} {op} {rhs}` failed at compile time: {reason}."),
39+
span,
40+
)
41+
}
42+
43+
pub(crate) fn unary_op_failure(value: &Value, op: UnaryOperation, reason: impl Display, span: Span) -> Formatted {
44+
Formatted::error(
45+
CODE_PREFIX,
46+
CODE_MASK + 1,
47+
format!("Unary operation `{value}.{op}()` failed at compile time: {reason}."),
48+
span,
49+
)
50+
}
51+
52+
pub(crate) fn intrinsic_failure(reason: impl Display, span: Span) -> Formatted {
53+
Formatted::error(
54+
CODE_PREFIX,
55+
CODE_MASK + 2,
56+
format!("Error during compile time evaluation of this intrinsic: {reason}."),
57+
span,
58+
)
59+
}

crates/ast/src/const_eval/evaluate.rs

Lines changed: 152 additions & 171 deletions
Large diffs are not rendered by default.

crates/ast/src/const_eval/intrinsic.rs

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,95 +19,107 @@ use snarkvm::{
1919
synthesizer::program::{DeserializeVariant, SerializeVariant},
2020
};
2121

22-
use crate::{
23-
ArrayType,
24-
Expression,
25-
Intrinsic,
26-
Type,
27-
const_eval::{ExpectTc, Value},
28-
};
29-
use leo_errors::{ConstEvalError, Result};
22+
use crate::{ArrayType, Expression, Intrinsic, Type, const_eval::Value};
23+
use leo_errors::Formatted;
3024
use leo_span::Span;
3125

32-
use super::*;
26+
use super::{errors, *};
27+
28+
fn pop_value(values: &mut Vec<Value>) -> Result<Value, String> {
29+
values.pop().ok_or_else(|| "value expected during constant evaluation".to_string())
30+
}
3331

34-
fn pop_value(values: &mut Vec<Value>) -> Result<Value> {
35-
match values.pop() {
36-
Some(v) => Ok(v),
37-
None => Err(ConstEvalError::new("value expected during constant evaluation".to_string()).into()),
38-
}
32+
fn type_fail<T, E>(r: Result<T, E>) -> Result<T, String> {
33+
r.map_err(|_| "type failure".to_string())
34+
}
35+
36+
fn snark<T>(r: Result<T, anyhow::Error>) -> Result<T, String> {
37+
r.map_err(|e| format!("{e}"))
3938
}
4039

4140
pub fn evaluate_intrinsic(
4241
values: &mut Vec<Value>,
4342
intrinsic: Intrinsic,
44-
_arguments: &[Expression],
43+
arguments: &[Expression],
4544
span: Span,
46-
) -> Result<Option<Value>> {
45+
) -> Result<Option<Value>, Formatted> {
46+
evaluate_intrinsic_inner(values, intrinsic, arguments).map_err(|reason| errors::intrinsic_failure(reason, span))
47+
}
48+
49+
fn evaluate_intrinsic_inner(
50+
values: &mut Vec<Value>,
51+
intrinsic: Intrinsic,
52+
_arguments: &[Expression],
53+
) -> Result<Option<Value>, String> {
4754
use snarkvm::{
4855
prelude::LiteralType,
4956
synthesizer::program::{CommitVariant, ECDSAVerifyVariant, HashVariant},
5057
};
5158

52-
let dohash = |values: &mut Vec<Value>, variant: HashVariant, typ: Type| -> Result<Value> {
53-
let input = pop_value(values)?.try_into().expect_tc(span)?;
54-
let value = snarkvm::synthesizer::program::evaluate_hash(variant, &input, &typ.to_snarkvm()?)?;
59+
let dohash = |values: &mut Vec<Value>, variant: HashVariant, typ: Type| -> Result<Value, String> {
60+
let input = type_fail(pop_value(values)?.try_into())?;
61+
let value = snark(snarkvm::synthesizer::program::evaluate_hash(variant, &input, &snark(typ.to_snarkvm())?))?;
5562
Ok(value.into())
5663
};
5764

58-
let docommit = |values: &mut Vec<Value>, variant: CommitVariant, typ: LiteralType| -> Result<Value> {
59-
let randomizer: Scalar = pop_value(values)?.try_into().expect_tc(span)?;
60-
let input: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
61-
let value = snarkvm::synthesizer::program::evaluate_commit(variant, &input, &randomizer, typ)?;
65+
let docommit = |values: &mut Vec<Value>, variant: CommitVariant, typ: LiteralType| -> Result<Value, String> {
66+
let randomizer: Scalar = type_fail(pop_value(values)?.try_into())?;
67+
let input: SvmValue = type_fail(pop_value(values)?.try_into())?;
68+
let value = snark(snarkvm::synthesizer::program::evaluate_commit(variant, &input, &randomizer, typ))?;
6269
Ok(value.into())
6370
};
6471

65-
let doschnorr = |values: &mut Vec<Value>| -> Result<Value> {
66-
let signature: Signature = pop_value(values)?.try_into().expect_tc(span)?;
67-
let address: Address = pop_value(values)?.try_into().expect_tc(span)?;
68-
let message: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
69-
let is_valid = snarkvm::synthesizer::program::evaluate_schnorr_verification(&signature, &address, &message)?;
72+
let doschnorr = |values: &mut Vec<Value>| -> Result<Value, String> {
73+
let signature: Signature = type_fail(pop_value(values)?.try_into())?;
74+
let address: Address = type_fail(pop_value(values)?.try_into())?;
75+
let message: SvmValue = type_fail(pop_value(values)?.try_into())?;
76+
let is_valid =
77+
snark(snarkvm::synthesizer::program::evaluate_schnorr_verification(&signature, &address, &message))?;
7078
Ok(Boolean::new(is_valid).into())
7179
};
7280

73-
let doecdsa = |values: &mut Vec<Value>, variant: ECDSAVerifyVariant| -> Result<Value> {
74-
let signature: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
75-
let public_key: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
76-
let message: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
77-
let is_valid =
78-
snarkvm::synthesizer::program::evaluate_ecdsa_verification(variant, &signature, &public_key, &message)?;
81+
let doecdsa = |values: &mut Vec<Value>, variant: ECDSAVerifyVariant| -> Result<Value, String> {
82+
let signature: SvmValue = type_fail(pop_value(values)?.try_into())?;
83+
let public_key: SvmValue = type_fail(pop_value(values)?.try_into())?;
84+
let message: SvmValue = type_fail(pop_value(values)?.try_into())?;
85+
let is_valid = snark(snarkvm::synthesizer::program::evaluate_ecdsa_verification(
86+
variant,
87+
&signature,
88+
&public_key,
89+
&message,
90+
))?;
7991
Ok(Boolean::new(is_valid).into())
8092
};
8193

82-
let doserialize = |values: &mut Vec<Value>, variant: SerializeVariant| -> Result<Value> {
83-
let input: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
94+
let doserialize = |values: &mut Vec<Value>, variant: SerializeVariant| -> Result<Value, String> {
95+
let input: SvmValue = type_fail(pop_value(values)?.try_into())?;
8496
let num_bits = match variant {
8597
SerializeVariant::ToBits => input.to_bits_le().len(),
8698
SerializeVariant::ToBitsRaw => input.to_bits_raw_le().len(),
8799
};
88100
let Ok(num_bits) = u32::try_from(num_bits) else {
89-
crate::halt_no_span2!("cannot serialize value with more than 2^32 bits");
101+
return Err("cannot serialize value with more than 2^32 bits".into());
90102
};
91-
let array_type = ArrayType::bit_array(num_bits).to_snarkvm()?;
92-
let value = snarkvm::synthesizer::program::evaluate_serialize(variant, &input, &array_type)?;
103+
let array_type = snark(ArrayType::bit_array(num_bits).to_snarkvm())?;
104+
let value = snark(snarkvm::synthesizer::program::evaluate_serialize(variant, &input, &array_type))?;
93105
Ok(value.into())
94106
};
95107

96-
let dodeserialize = |values: &mut Vec<Value>, variant: DeserializeVariant, type_: Type| -> Result<Value> {
97-
let value: SvmValue = pop_value(values)?.try_into().expect_tc(span)?;
108+
let dodeserialize = |values: &mut Vec<Value>, variant: DeserializeVariant, type_: Type| -> Result<Value, String> {
109+
let value: SvmValue = type_fail(pop_value(values)?.try_into())?;
98110
let bits = match value {
99-
SvmValue::Plaintext(plaintext) => plaintext.as_bit_array()?,
100-
_ => crate::halt_no_span2!("expected array for deserialization"),
111+
SvmValue::Plaintext(plaintext) => snark(plaintext.as_bit_array())?,
112+
_ => return Err("expected array for deserialization".into()),
101113
};
102114
let get_struct_fail = |_: &SvmIdentifier| anyhow::bail!("structs are not supported");
103115
let get_external_struct_fail = |_: &SvmLocator| anyhow::bail!("structs are not supported");
104-
let value = snarkvm::synthesizer::program::evaluate_deserialize(
116+
let value = snark(snarkvm::synthesizer::program::evaluate_deserialize(
105117
variant,
106118
&bits,
107-
&type_.to_snarkvm()?,
119+
&snark(type_.to_snarkvm())?,
108120
&get_struct_fail,
109121
&get_external_struct_fail,
110-
)?;
122+
))?;
111123
Ok(value.into())
112124
};
113125

@@ -123,11 +135,11 @@ pub fn evaluate_intrinsic(
123135
Intrinsic::Serialize(variant) => doserialize(values, variant)?,
124136
Intrinsic::Deserialize(variant, type_) => dodeserialize(values, variant, type_)?,
125137
Intrinsic::GroupToXCoordinate => {
126-
let g: Group = pop_value(values)?.try_into().expect_tc(span)?;
138+
let g: Group = type_fail(pop_value(values)?.try_into())?;
127139
g.to_x_coordinate().into()
128140
}
129141
Intrinsic::GroupToYCoordinate => {
130-
let g: Group = pop_value(values)?.try_into().expect_tc(span)?;
142+
let g: Group = type_fail(pop_value(values)?.try_into())?;
131143
g.to_y_coordinate().into()
132144
}
133145
Intrinsic::MappingGet

crates/ast/src/const_eval/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,4 @@ pub use evaluate::*;
2323
mod value;
2424
pub use value::*;
2525

26-
mod util;
27-
pub use util::*;
26+
mod errors;

crates/ast/src/const_eval/util.rs

Lines changed: 0 additions & 80 deletions
This file was deleted.

crates/errors/src/errors/const_eval.rs

Lines changed: 0 additions & 35 deletions
This file was deleted.

crates/errors/src/errors/mod.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
1616

17-
/// Contains the Constant Evaluation error definitions.
18-
mod const_eval;
19-
pub use self::const_eval::*;
20-
2117
/// The LeoError type that contains all sub error types.
2218
/// This allows a unified error type throughout the Leo crates.
2319
#[derive(Debug, Error)]
@@ -26,8 +22,6 @@ pub enum LeoError {
2622
Formatted(#[from] crate::Formatted),
2723
#[error(transparent)]
2824
Backtraced(#[from] crate::Backtraced),
29-
#[error(transparent)]
30-
ConstEvalError(#[from] ConstEvalError),
3125
#[error("")]
3226
LastErrorCode(i32),
3327
#[error(transparent)]
@@ -40,7 +34,6 @@ impl LeoError {
4034
match self {
4135
Formatted(e) => e.error_code(),
4236
Backtraced(e) => e.error_code(),
43-
ConstEvalError(_) => "Const Eval Error".to_string(),
4437
LastErrorCode(_) => unreachable!(),
4538
SnarkVM(_) => "SnarkVM Error".to_string(),
4639
}
@@ -51,7 +44,6 @@ impl LeoError {
5144
match self {
5245
Formatted(e) => e.exit_code(),
5346
Backtraced(e) => e.exit_code(),
54-
ConstEvalError(_) => 1,
5547
LastErrorCode(code) => *code,
5648
SnarkVM(_) => 11000,
5749
}
@@ -70,10 +62,7 @@ impl LeoError {
7062
pub fn diagnostic_view(&self) -> Option<crate::DiagnosticView<'_>> {
7163
match self {
7264
LeoError::Formatted(formatted) => Some(formatted.diagnostic_view()),
73-
LeoError::Backtraced(_)
74-
| LeoError::ConstEvalError(_)
75-
| LeoError::LastErrorCode(_)
76-
| LeoError::SnarkVM(_) => None,
65+
LeoError::Backtraced(_) | LeoError::LastErrorCode(_) | LeoError::SnarkVM(_) => None,
7766
}
7867
}
7968

0 commit comments

Comments
 (0)