diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 7091f7b261c..6c556d6781f 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -49,9 +49,10 @@ impl<'context> Elaborator<'context> { pub fn elaborate_item_from_comptime_in_function<'a, T>( &'a mut self, current_function: Option, + reason: Option, f: impl FnOnce(&mut Elaborator<'a>) -> T, ) -> T { - self.elaborate_item_from_comptime(f, |elaborator| { + self.elaborate_item_from_comptime(reason, f, |elaborator| { if let Some(function) = current_function { let meta = elaborator.interner.function_meta(&function); elaborator.current_item = Some(DependencyId::Function(function)); @@ -65,9 +66,10 @@ impl<'context> Elaborator<'context> { pub fn elaborate_item_from_comptime_in_module<'a, T>( &'a mut self, module: ModuleId, + reason: Option, f: impl FnOnce(&mut Elaborator<'a>) -> T, ) -> T { - self.elaborate_item_from_comptime(f, |elaborator| { + self.elaborate_item_from_comptime(reason, f, |elaborator| { elaborator.current_item = None; elaborator.crate_id = module.krate; elaborator.local_module = module.local_id; @@ -76,6 +78,7 @@ impl<'context> Elaborator<'context> { fn elaborate_item_from_comptime<'a, T>( &'a mut self, + reason: Option, f: impl FnOnce(&mut Elaborator<'a>) -> T, setup: impl FnOnce(&mut Elaborator<'a>), ) -> T { @@ -104,7 +107,14 @@ impl<'context> Elaborator<'context> { let result = f(&mut elaborator); elaborator.check_and_pop_function_context(); - self.errors.append(&mut elaborator.errors); + let mut errors = std::mem::take(&mut elaborator.errors); + if let Some(reason) = reason { + errors = vecmap(errors, |error| { + CompilationError::ComptimeError(reason.to_macro_error(error)) + }); + }; + + self.errors.extend(errors); result } @@ -342,14 +352,11 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, location: Location, ) { - let previous_errors = - self.push_elaborate_reason_and_take_errors(ElaborateReason::RunningAttribute, location); - - for item in items { - self.add_item(item, generated_items, location); - } - - self.pop_elaborate_reason(previous_errors); + self.with_elaborate_reason(ElaborateReason::RunningAttribute(location), |elaborator| { + for item in items { + elaborator.add_item(item, generated_items, location); + } + }); } pub(crate) fn add_item( @@ -558,14 +565,10 @@ impl<'context> Elaborator<'context> { }); if !generated_items.is_empty() { - let previous_errors = self.push_elaborate_reason_and_take_errors( - ElaborateReason::RunningAttribute, - location, - ); - - self.elaborate_items(generated_items); - - self.pop_elaborate_reason(previous_errors); + let reason = ElaborateReason::RunningAttribute(location); + self.with_elaborate_reason(reason, |elaborator| { + elaborator.elaborate_items(generated_items); + }); } } } @@ -652,24 +655,22 @@ impl<'context> Elaborator<'context> { } } - /// Pushes an ElaborateReason but also `std::mem::take`s the current errors and returns them. - pub(crate) fn push_elaborate_reason_and_take_errors( - &mut self, - reason: ElaborateReason, - location: Location, - ) -> Vec { - self.elaborate_reasons.push_back((reason, location)); - std::mem::take(&mut self.errors) - } + pub(crate) fn with_elaborate_reason(&mut self, reason: ElaborateReason, f: F) -> T + where + F: FnOnce(&mut Elaborator) -> T, + { + self.elaborate_reasons.push_back(reason); + let previous_errors = std::mem::take(&mut self.errors); + + let value = f(self); - /// Pops en ElaborateREason. Receives the errors that were returned by `push_elaborate_reason` - /// so they are restored, while also wrapping errors in the current Elaborator in a ComptimeError. - pub(crate) fn pop_elaborate_reason(&mut self, previous_errors: Vec) { let new_errors = std::mem::take(&mut self.errors); let new_errors = self.wrap_errors_in_macro_error(new_errors); self.errors = previous_errors; self.push_errors(new_errors); self.elaborate_reasons.pop_back(); + + value } fn wrap_errors_in_macro_error(&self, errors: Vec) -> Vec { @@ -677,8 +678,8 @@ impl<'context> Elaborator<'context> { } fn wrap_error_in_macro_error(&self, mut error: CompilationError) -> CompilationError { - for (reason, location) in self.elaborate_reasons.iter().rev() { - error = CompilationError::ComptimeError(reason.to_macro_error(error, *location)); + for reason in self.elaborate_reasons.iter().rev() { + error = CompilationError::ComptimeError(reason.to_macro_error(error)); } error } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 23a9ec5246e..035e2e8b400 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -199,25 +199,26 @@ pub struct Elaborator<'context> { /// Sometimes items are elaborated because a function attribute ran and generated items. /// The Elaborator keeps track of these reasons so that when an error is produced it will /// be wrapped in another error that will include this reason. - pub(crate) elaborate_reasons: im::Vector<(ElaborateReason, Location)>, + pub(crate) elaborate_reasons: im::Vector, } #[derive(Copy, Clone)] pub enum ElaborateReason { /// A function attribute generated an item that's being elaborated. - RunningAttribute, - /// Evaluating `Module::add_item` - AddingItemToModule, + RunningAttribute(Location), + /// Evaluating a comptime call like `Module::add_item` + EvaluatingComptimeCall(&'static str, Location), } impl ElaborateReason { - fn to_macro_error(self, error: CompilationError, location: Location) -> ComptimeError { + fn to_macro_error(self, error: CompilationError) -> ComptimeError { match self { - ElaborateReason::RunningAttribute => { + ElaborateReason::RunningAttribute(location) => { ComptimeError::ErrorRunningAttribute { error: Box::new(error), location } } - ElaborateReason::AddingItemToModule => { - ComptimeError::ErrorAddingItemToModule { error: Box::new(error), location } + ElaborateReason::EvaluatingComptimeCall(method_name, location) => { + let error = Box::new(error); + ComptimeError::ErrorEvaluatingComptimeCall { method_name, error, location } } } } @@ -251,7 +252,7 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, interpreter_call_stack: im::Vector, options: ElaboratorOptions<'context>, - elaborate_reasons: im::Vector<(ElaborateReason, Location)>, + elaborate_reasons: im::Vector, ) -> Self { Self { scopes: ScopeForest::default(), diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 27440069c02..52b50d54566 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -703,15 +703,22 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { /// comptime call or macro "something" that eventually led to that error. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ComptimeError { - ErrorRunningAttribute { error: Box, location: Location }, - ErrorAddingItemToModule { error: Box, location: Location }, + ErrorRunningAttribute { + error: Box, + location: Location, + }, + ErrorEvaluatingComptimeCall { + method_name: &'static str, + error: Box, + location: Location, + }, } impl ComptimeError { pub fn location(&self) -> Location { match self { ComptimeError::ErrorRunningAttribute { location, .. } - | ComptimeError::ErrorAddingItemToModule { location, .. } => *location, + | ComptimeError::ErrorEvaluatingComptimeCall { location, .. } => *location, } } } @@ -724,9 +731,9 @@ impl<'a> From<&'a ComptimeError> for CustomDiagnostic { diagnostic.add_secondary("While running this function attribute".into(), *location); diagnostic } - ComptimeError::ErrorAddingItemToModule { error, location } => { + ComptimeError::ErrorEvaluatingComptimeCall { method_name, error, location } => { let mut diagnostic = CustomDiagnostic::from(&**error); - diagnostic.add_secondary("While interpreting `Module::add_item`".into(), *location); + diagnostic.add_secondary(format!("While evaluating `{method_name}`"), *location); diagnostic } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 5c87e70949a..91d81f7b10d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashMap as HashMap; use crate::TypeVariable; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness, UnaryOp}; -use crate::elaborator::Elaborator; +use crate::elaborator::{ElaborateReason, Elaborator}; use crate::graph::CrateId; use crate::hir::def_map::ModuleId; use crate::hir::type_check::TypeCheckError; @@ -191,7 +191,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Some(body) => Ok(body), None => { if matches!(&meta.function_body, FunctionBody::Unresolved(..)) { - self.elaborate_in_function(None, |elaborator| { + self.elaborate_in_function(None, None, |elaborator| { elaborator.elaborate_function(function); }); @@ -207,10 +207,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn elaborate_in_function( &mut self, function: Option, + reason: Option, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime_in_function(function, f); + let result = self.elaborator.elaborate_item_from_comptime_in_function(function, reason, f); self.rebind_generics_from_previous_function(); result } @@ -218,10 +219,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn elaborate_in_module( &mut self, module: ModuleId, + reason: Option, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime_in_module(module, f); + let result = self.elaborator.elaborate_item_from_comptime_in_module(module, reason, f); self.rebind_generics_from_previous_function(); result } @@ -1325,9 +1327,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut result = self.call_function(function_id, arguments, bindings, location)?; if call.is_macro_call { let expr = result.into_expression(self.elaborator, location)?; - let expr = self.elaborate_in_function(self.current_function, |elaborator| { - elaborator.elaborate_expression(expr).0 - }); + let expr = + self.elaborate_in_function(self.current_function, None, |elaborator| { + elaborator.elaborate_expression(expr).0 + }); result = self.evaluate(expr)?; // Macro calls are typed as type variables during type checking. diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 34a5535f63c..5b7d7b4edba 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -833,8 +833,9 @@ fn quoted_as_module( parse(interpreter.elaborator, argument, Parser::parse_path_no_turbofish_or_error, "a path") .ok(); let option_value = path.and_then(|path| { - let module = interpreter - .elaborate_in_function(interpreter.current_function, |elaborator| { + let reason = Some(ElaborateReason::EvaluatingComptimeCall("Quoted::as_module", location)); + let module = + interpreter.elaborate_in_function(interpreter.current_function, reason, |elaborator| { elaborator.resolve_module_by_path(path) }); module.map(Value::ModuleDefinition) @@ -856,8 +857,10 @@ fn quoted_as_trait_constraint( Parser::parse_trait_bound_or_error, "a trait constraint", )?; + let reason = + Some(ElaborateReason::EvaluatingComptimeCall("Quoted::as_trait_constraint", location)); let bound = interpreter - .elaborate_in_function(interpreter.current_function, |elaborator| { + .elaborate_in_function(interpreter.current_function, reason, |elaborator| { elaborator.resolve_trait_bound(&trait_bound) }) .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; @@ -873,8 +876,11 @@ fn quoted_as_type( ) -> IResult { let argument = check_one_argument(arguments, location)?; let typ = parse(interpreter.elaborator, argument, Parser::parse_type_or_error, "a type")?; - let typ = interpreter - .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); + let reason = Some(ElaborateReason::EvaluatingComptimeCall("Quoted::as_type", location)); + let typ = + interpreter.elaborate_in_function(interpreter.current_function, reason, |elaborator| { + elaborator.resolve_type(typ) + }); Ok(Value::Type(typ)) } @@ -2292,7 +2298,9 @@ fn expr_resolve( interpreter.current_function }; - interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { + let reason = Some(ElaborateReason::EvaluatingComptimeCall("Expr::resolve", location)); + interpreter.elaborate_in_function(function_to_resolve_in, reason, |elaborator| match expr_value + { ExprValue::Expression(expression_kind) => { let expr = Expression { kind: expression_kind, location: self_argument_location }; let (expr_id, _) = elaborator.elaborate_expression(expr); @@ -2426,7 +2434,14 @@ fn function_def_as_typed_expr( let hir_expr = HirExpression::Ident(hir_ident.clone(), generics.clone()); let expr_id = interpreter.elaborator.interner.push_expr(hir_expr); interpreter.elaborator.interner.push_expr_location(expr_id, location); - let typ = interpreter.elaborator.type_check_variable(hir_ident, expr_id, generics); + let reason = Some(ElaborateReason::EvaluatingComptimeCall( + "FunctionDefinition::as_typed_expr", + location, + )); + let typ = + interpreter.elaborate_in_function(interpreter.current_function, reason, |elaborator| { + elaborator.type_check_variable(hir_ident, expr_id, generics) + }); interpreter.elaborator.interner.push_expr_type(expr_id, typ); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } @@ -2632,7 +2647,10 @@ fn function_def_set_parameters( "a pattern", )?; - let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| { + let reason = + ElaborateReason::EvaluatingComptimeCall("FunctionDefinition::set_parameters", location); + let reason = Some(reason); + let hir_pattern = interpreter.elaborate_in_function(Some(func_id), reason, |elaborator| { elaborator.elaborate_pattern_and_store_ids( parameter_pattern, parameter_type.clone(), @@ -2747,10 +2765,8 @@ fn module_add_item( let parser = Parser::parse_top_level_items; let top_level_statements = parse(interpreter.elaborator, item, parser, "a top-level item")?; - interpreter.elaborate_in_module(module_id, |elaborator| { - let previous_errors = elaborator - .push_elaborate_reason_and_take_errors(ElaborateReason::AddingItemToModule, location); - + let reason = Some(ElaborateReason::EvaluatingComptimeCall("Module::add_item", location)); + interpreter.elaborate_in_module(module_id, reason, |elaborator| { let mut generated_items = CollectedItems::default(); for top_level_statement in top_level_statements { @@ -2760,8 +2776,6 @@ fn module_add_item( if !generated_items.is_empty() { elaborator.elaborate_items(generated_items); } - - elaborator.pop_elaborate_reason(previous_errors); }); Ok(Value::Unit)