Error handling in Statement processor (#2375)

This commit is contained in:
Gastón Zanitti
2025-01-27 07:57:42 -03:00
committed by GitHub
parent 070b284f8c
commit 6f1ba0a3b0
4 changed files with 138 additions and 107 deletions

View File

@@ -834,8 +834,8 @@ impl AnalysisASMFile {
pub fn get_machine(&self, ty: &AbsoluteSymbolPath) -> Option<&Machine> {
let mut path = ty.clone();
let name = path.pop().unwrap();
self.modules[&path].machines.get(&name)
let name = path.pop()?;
self.modules.get(&path)?.machines.get(&name)
}
}

View File

@@ -112,6 +112,13 @@ impl Error {
pub fn source_ref(&self) -> &SourceRef {
&self.source_ref
}
pub fn extend_message<F>(&self, f: F) -> Error
where
F: FnOnce(&str) -> String,
{
self.source_ref().with_error(f(self.message()))
}
}
pub fn handle_parse_error(

View File

@@ -14,8 +14,8 @@ use powdr_ast::parsed::asm::{
use powdr_ast::parsed::types::Type;
use powdr_ast::parsed::visitor::{AllChildren, Children};
use powdr_ast::parsed::{
self, FunctionKind, LambdaExpression, PILFile, PilStatement, SymbolCategory,
TraitImplementation, TypedExpression,
self, Expression as ParsedExpression, FunctionKind, LambdaExpression, PILFile, PilStatement,
SourceReference, SymbolCategory, TraitImplementation, TypedExpression,
};
use powdr_number::{FieldElement, GoldilocksField};
@@ -149,13 +149,20 @@ impl PILAnalyzer {
files = once(core).chain(files).collect();
}
let mut errors = Vec::new();
for PILFile(file) in files {
self.current_namespace = Default::default();
for statement in file {
self.handle_statement(statement);
if let Err(e) = self.handle_statement(statement) {
errors.push(e);
}
}
}
Ok(())
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
/// Adds core types if they are not present in the input.
@@ -472,7 +479,7 @@ impl PILAnalyzer {
}
}
fn handle_statement(&mut self, statement: PilStatement) {
fn handle_statement(&mut self, statement: PilStatement) -> Result<(), Error> {
match statement {
PilStatement::Include(_, _) => unreachable!(),
PilStatement::Namespace(_, name, degree) => self.handle_namespace(name, degree),
@@ -481,7 +488,7 @@ impl PILAnalyzer {
let mut counters = self.symbol_counters.take().unwrap();
let items =
StatementProcessor::new(self.driver(), &mut counters, self.polynomial_degree)
.handle_statement(statement);
.handle_statement(statement)?;
self.symbol_counters = Some(counters);
for item in items {
match item {
@@ -519,41 +526,49 @@ impl PILAnalyzer {
}
}
}
Ok(())
}
}
}
fn handle_namespace(&mut self, name: SymbolPath, degree: Option<parsed::NamespaceDegree>) {
let evaluate_degree_bound = |e| {
let e = match ExpressionProcessor::new(self.driver(), &Default::default())
fn handle_namespace(
&mut self,
name: SymbolPath,
degree: Option<parsed::NamespaceDegree>,
) -> Result<(), Error> {
let evaluate_degree_bound = |e: ParsedExpression| -> Result<u64, Error> {
let source = e.source_reference().clone();
let e = ExpressionProcessor::new(self.driver(), &Default::default())
.process_expression(e)
{
Ok(e) => e,
Err(e) => {
// TODO propagate this error up
panic!("Failed to evaluate degree bound: {e}");
}
};
u64::try_from(
evaluator::evaluate_expression::<GoldilocksField>(
&e,
&self.definitions,
&Default::default(),
)
.unwrap()
.try_to_integer()
.unwrap(),
.map_err(|err| {
err.extend_message(|m| format!("Failed to evaluate degree bound: {m}"))
})?;
let value = evaluator::evaluate_expression::<GoldilocksField>(
&e,
&self.definitions,
&Default::default(),
)
.unwrap()
.try_to_integer()
.map_err(|err| source.with_error(err.to_string()))?;
u64::try_from(value)
.map_err(|_| source.with_error("Degree bound too large".to_string()))
};
self.polynomial_degree = degree.map(|degree| DegreeRange {
min: evaluate_degree_bound(degree.min),
max: evaluate_degree_bound(degree.max),
});
self.current_namespace = AbsoluteSymbolPath::default().join(name);
}
self.polynomial_degree = degree
.map(|degree| -> Result<_, Error> {
Ok(DegreeRange {
min: evaluate_degree_bound(degree.min)?,
max: evaluate_degree_bound(degree.max)?,
})
})
.transpose()?;
self.current_namespace = AbsoluteSymbolPath::default().join(name);
Ok(())
}
fn driver(&self) -> Driver {
Driver(self)
}

View File

@@ -17,7 +17,7 @@ use powdr_ast::parsed::{
ArrayExpression, NamedExpression, StructDeclaration, SymbolCategory, TraitImplementation,
TypeDeclaration,
};
use powdr_parser_util::SourceRef;
use powdr_parser_util::{Error, SourceRef};
use std::str::FromStr;
use powdr_ast::analyzed::{
@@ -121,7 +121,7 @@ where
}
}
pub fn handle_statement(&mut self, statement: PilStatement) -> Vec<PILItem> {
pub fn handle_statement(&mut self, statement: PilStatement) -> Result<Vec<PILItem>, Error> {
match statement {
PilStatement::Include(_, _) => {
panic!("Includes must be handled outside the statement processor.")
@@ -130,7 +130,7 @@ where
panic!("Namespaces must be handled outside the statement processor.")
}
PilStatement::PolynomialDefinition(source, name, value) => {
let (name, ty) = self.name_and_type_from_polynomial_name(name, Type::Inter);
let (name, ty) = self.name_and_type_from_polynomial_name(name, Type::Inter)?;
self.handle_symbol_definition(
source,
name,
@@ -163,7 +163,7 @@ where
) => {
assert!(polynomials.len() == 1);
let (name, ty) =
self.name_and_type_from_polynomial_name(polynomials.pop().unwrap(), Type::Col);
self.name_and_type_from_polynomial_name(polynomials.pop().unwrap(), Type::Col)?;
self.handle_symbol_definition(
source,
@@ -197,15 +197,16 @@ where
Some(FunctionDefinition::TraitDeclaration(trait_decl.clone())),
),
PilStatement::TraitImplementation(_, trait_impl) => {
let trait_impl = self.process_trait_implementation(trait_impl);
vec![PILItem::TraitImplementation(trait_impl)]
let trait_impl = self.process_trait_implementation(trait_impl)?;
Ok(vec![PILItem::TraitImplementation(trait_impl)])
}
PilStatement::Expression(_, expr) => {
let new_expr = self
.expression_processor(&Default::default())
.process_expression(expr)?;
Ok(vec![PILItem::ProofItem(new_expr)])
}
PilStatement::Expression(_, expr) => vec![PILItem::ProofItem(
self.expression_processor(&Default::default())
.process_expression(expr)
// TODO propagate this error up
.expect("Expression processing failed"),
)],
PilStatement::StructDeclaration(source, struct_declaration) => self
.handle_symbol_definition(
source,
@@ -224,15 +225,16 @@ where
&mut self,
PolynomialName { name, array_size }: PolynomialName,
base_type: Type,
) -> (String, Option<TypeScheme>) {
) -> Result<(String, Option<TypeScheme>), Error> {
let ty = Some(match array_size {
None => base_type.into(),
Some(len) => {
let len = self
.expression_processor(&Default::default())
.process_expression(len)
// TODO propagate this error up
.expect("Failed to process length expression");
.map_err(|err| {
err.extend_message(|m| format!("Failed to process length expression: {m}"))
})?;
let length = untyped_evaluator::evaluate_expression_to_int(self.driver, len)
.map(|length| {
length
@@ -250,7 +252,7 @@ where
.into()
}
});
(name, ty)
Ok((name, ty))
}
fn handle_generic_definition(
@@ -259,7 +261,7 @@ where
name: String,
type_scheme: Option<TypeScheme<parsed::Expression>>,
value: Option<parsed::Expression>,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let type_scheme = type_scheme.map(|ts| {
let vars = ts.vars;
let duplicates = vars.vars().duplicates().collect::<Vec<_>>();
@@ -355,11 +357,11 @@ where
source: SourceRef,
stage: Option<u32>,
polynomials: Vec<PolynomialName>,
) -> Vec<PILItem> {
polynomials
) -> Result<Vec<PILItem>, Error> {
let result = polynomials
.into_iter()
.flat_map(|poly_name| {
let (name, ty) = self.name_and_type_from_polynomial_name(poly_name, Type::Col);
.map(|poly_name| {
let (name, ty) = self.name_and_type_from_polynomial_name(poly_name, Type::Col)?;
self.handle_symbol_definition(
source.clone(),
name,
@@ -369,7 +371,12 @@ where
None,
)
})
.collect()
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect();
Ok(result)
}
fn handle_symbol_definition(
@@ -380,7 +387,7 @@ where
stage: Option<u32>,
type_scheme: Option<TypeScheme>,
value: Option<FunctionDefinition>,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let length = type_scheme.as_ref().and_then(|t| {
if symbol_kind == SymbolKind::Other() {
None
@@ -424,7 +431,7 @@ where
Some(FunctionDefinition::Array(value)) => {
self.process_array_symbol(symbol, type_scheme, value)
}
None => vec![PILItem::Definition(symbol, None)],
None => Ok(vec![PILItem::Definition(symbol, None)]),
}
}
@@ -434,7 +441,7 @@ where
symbol: Symbol,
type_scheme: Option<TypeScheme>,
expr: parsed::Expression,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
if symbol_kind == SymbolKind::Poly(PolynomialType::Committed) {
// The only allowed value for a witness column is a query function.
assert!(matches!(
@@ -456,12 +463,11 @@ where
let value = FunctionValueDefinition::Expression(TypedExpression {
e: self
.expression_processor(&type_vars)
.process_expression(expr)
.expect("Failed to process expression"),
.process_expression(expr)?,
type_scheme,
});
vec![PILItem::Definition(symbol, Some(value))]
Ok(vec![PILItem::Definition(symbol, Some(value))])
}
fn process_array_symbol(
@@ -469,15 +475,14 @@ where
symbol: Symbol,
type_scheme: Option<TypeScheme>,
value: ArrayExpression,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let expression = self
.expression_processor(&Default::default())
.process_array_expression(value)
.expect("Failed to process array expression");
.process_array_expression(value)?;
assert!(type_scheme.is_none() || type_scheme == Some(Type::Col.into()));
let value = FunctionValueDefinition::Array(expression);
vec![PILItem::Definition(symbol, Some(value))]
Ok(vec![PILItem::Definition(symbol, Some(value))])
}
/// Given a list of (absolute_name, value) pairs, create PIL items for each of them.
@@ -510,32 +515,30 @@ where
poly: parsed::NamespacedPolynomialReference,
array_index: Option<parsed::Expression>,
index: parsed::Expression,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let id = self.counters.dispense_public_id();
let name = self.driver.resolve_decl(&name);
let polynomial = self
.expression_processor(&Default::default())
.process_namespaced_polynomial_reference(poly)
.expect("Failed to process polynomial reference");
.map_err(|err| source.with_error(err))?;
let type_vars = Default::default();
let mut expression_processor = self.expression_processor(&type_vars);
let array_index = array_index.map(|i| {
let i = expression_processor
.process_expression(i)
// TODO propagate this error up
.expect("Failed to process array index expression");
let index: u64 = untyped_evaluator::evaluate_expression_to_int(self.driver, i)
.unwrap()
.try_into()
.unwrap();
assert!(index <= usize::MAX as u64);
index as usize
});
let index = expression_processor
.process_expression(index) // TODO propagate this error up
.expect("Failed to process index");
vec![PILItem::PublicDeclaration(PublicDeclaration {
let array_index = array_index
.map(|i| {
let i = expression_processor.process_expression(i).map_err(|err| {
err.extend_message(|m| format!("Failed to process array index: {m}"))
})?;
let index: u64 = untyped_evaluator::evaluate_expression_to_int(self.driver, i)
.unwrap()
.try_into()
.unwrap();
assert!(index <= usize::MAX as u64);
Ok(index as usize)
})
.transpose()?;
let index = expression_processor.process_expression(index)?;
Ok(vec![PILItem::PublicDeclaration(PublicDeclaration {
id,
source,
name: name.to_string(),
@@ -545,7 +548,7 @@ where
.unwrap()
.try_into()
.unwrap(),
})]
})])
}
fn expression_processor<'b>(
@@ -565,7 +568,7 @@ where
name: String,
symbol: Symbol,
enum_decl: EnumDeclaration<parsed::Expression>,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let type_vars = enum_decl.type_vars.vars().collect();
let variants = enum_decl
.variants
@@ -596,14 +599,14 @@ where
.collect();
let var_items = self.process_inner_definitions(source, inner_items);
iter::once(PILItem::Definition(
Ok(iter::once(PILItem::Definition(
symbol,
Some(FunctionValueDefinition::TypeDeclaration(
TypeDeclaration::Enum(enum_decl.clone()),
)),
))
.chain(var_items)
.collect()
.collect())
}
fn process_enum_variant(
@@ -625,7 +628,7 @@ where
&mut self,
symbol: Symbol,
struct_decl: StructDeclaration<parsed::Expression>,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let StructDeclaration {
name,
type_vars,
@@ -647,13 +650,13 @@ where
fields,
};
iter::once(PILItem::Definition(
Ok(iter::once(PILItem::Definition(
symbol,
Some(FunctionValueDefinition::TypeDeclaration(
TypeDeclaration::Struct(struct_decl),
)),
))
.collect()
.collect())
}
fn process_trait_declaration(
@@ -662,7 +665,7 @@ where
name: String,
symbol: Symbol,
trait_decl: TraitDeclaration<parsed::Expression>,
) -> Vec<PILItem> {
) -> Result<Vec<PILItem>, Error> {
let type_vars = trait_decl.type_vars.iter().collect();
let functions = trait_decl
.functions
@@ -696,20 +699,20 @@ where
.collect();
let trait_functions = self.process_inner_definitions(source, inner_items);
iter::once(PILItem::Definition(
Ok(iter::once(PILItem::Definition(
symbol,
Some(FunctionValueDefinition::TraitDeclaration(
trait_decl.clone(),
)),
))
.chain(trait_functions)
.collect()
.collect())
}
fn process_trait_implementation(
&self,
trait_impl: parsed::TraitImplementation<parsed::Expression>,
) -> TraitImplementation<Expression> {
) -> Result<TraitImplementation<Expression>, Error> {
let type_vars: HashSet<_> = trait_impl.type_scheme.vars.vars().collect();
if !type_vars.is_empty() {
unimplemented!("Generic impls are not supported yet.");
@@ -717,15 +720,17 @@ where
let functions = trait_impl
.functions
.into_iter()
.map(|named| NamedExpression {
name: named.name,
body: Arc::new(
self.expression_processor(&type_vars)
.process_expression(Arc::try_unwrap(named.body).unwrap())
.expect("Failed to process expression inside trait"),
),
.map(|named| {
let processed_expr = self
.expression_processor(&type_vars)
.process_expression(Arc::try_unwrap(named.body).unwrap())?;
Ok(NamedExpression {
name: named.name,
body: Arc::new(processed_expr),
})
})
.collect();
.collect::<Result<Vec<NamedExpression<Arc<Expression>>>, Error>>()?;
let Type::Tuple(TupleType { items }) = trait_impl.type_scheme.ty.clone() else {
panic!("Type from trait scheme is not a tuple.")
@@ -743,11 +748,15 @@ where
&self
.driver
.resolve_ref(&trait_impl.name, SymbolCategory::TraitDeclaration)
.expect("TODO: Handle this up in the code"),
.map_err(|err| {
trait_impl
.source_ref
.with_error(format!("Cannot find trait {}: {}", trait_impl.name, err))
})?,
)
.unwrap();
TraitImplementation {
Ok(TraitImplementation {
name,
source_ref: trait_impl.source_ref,
type_scheme: TypeScheme {
@@ -757,6 +766,6 @@ where
}),
},
functions,
}
})
}
}