Skip to content
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

feat: relate errors to macro built-ins errors #7609

Merged
merged 3 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 34 additions & 33 deletions compiler/noirc_frontend/src/elaborator/comptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ impl<'context> Elaborator<'context> {
pub fn elaborate_item_from_comptime_in_function<'a, T>(
&'a mut self,
current_function: Option<FuncId>,
reason: Option<ElaborateReason>,
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));
Expand All @@ -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<ElaborateReason>,
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;
Expand All @@ -76,6 +78,7 @@ impl<'context> Elaborator<'context> {

fn elaborate_item_from_comptime<'a, T>(
&'a mut self,
reason: Option<ElaborateReason>,
f: impl FnOnce(&mut Elaborator<'a>) -> T,
setup: impl FnOnce(&mut Elaborator<'a>),
) -> T {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
});
}
}
}
Expand Down Expand Up @@ -652,33 +655,31 @@ 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<CompilationError> {
self.elaborate_reasons.push_back((reason, location));
std::mem::take(&mut self.errors)
}
pub(crate) fn with_elaborate_reason<F, T>(&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<CompilationError>) {
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<CompilationError>) -> Vec<CompilationError> {
vecmap(errors, |error| self.wrap_error_in_macro_error(error))
}

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
}
Expand Down
19 changes: 10 additions & 9 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ElaborateReason>,
}

#[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 }
}
}
}
Expand Down Expand Up @@ -251,7 +252,7 @@ impl<'context> Elaborator<'context> {
crate_id: CrateId,
interpreter_call_stack: im::Vector<Location>,
options: ElaboratorOptions<'context>,
elaborate_reasons: im::Vector<(ElaborateReason, Location)>,
elaborate_reasons: im::Vector<ElaborateReason>,
) -> Self {
Self {
scopes: ScopeForest::default(),
Expand Down
17 changes: 12 additions & 5 deletions compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompilationError>, location: Location },
ErrorAddingItemToModule { error: Box<CompilationError>, location: Location },
ErrorRunningAttribute {
error: Box<CompilationError>,
location: Location,
},
ErrorEvaluatingComptimeCall {
method_name: &'static str,
error: Box<CompilationError>,
location: Location,
},
}

impl ComptimeError {
pub fn location(&self) -> Location {
match self {
ComptimeError::ErrorRunningAttribute { location, .. }
| ComptimeError::ErrorAddingItemToModule { location, .. } => *location,
| ComptimeError::ErrorEvaluatingComptimeCall { location, .. } => *location,
}
}
}
Expand All @@ -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
}
}
Expand Down
17 changes: 10 additions & 7 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

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;
Expand Down Expand Up @@ -191,7 +191,7 @@
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);
});

Expand All @@ -207,21 +207,23 @@
fn elaborate_in_function<T>(
&mut self,
function: Option<FuncId>,
reason: Option<ElaborateReason>,
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
}

fn elaborate_in_module<T>(
&mut self,
module: ModuleId,
reason: Option<ElaborateReason>,
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
}
Expand Down Expand Up @@ -253,7 +255,7 @@
}
} else {
let name = self.elaborator.interner.function_name(&function);
unreachable!("Non-builtin, lowlevel or oracle builtin fn '{name}'")

Check warning on line 258 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lowlevel)
}
}

Expand Down Expand Up @@ -1037,7 +1039,7 @@
}

/// Generate matches for bit shifting, which in Noir only accepts `u8` for RHS.
macro_rules! match_bitshift {

Check warning on line 1042 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (bitshift)
(($lhs_value:ident as $lhs:ident $op:literal $rhs_value:ident as $rhs:ident) => $expr:expr) => {
match_values! {
($lhs_value as $lhs $op $rhs_value as $rhs) {
Expand Down Expand Up @@ -1107,10 +1109,10 @@
BinaryOpKind::Xor => match_bitwise! {
(lhs_value as lhs "^" rhs_value as rhs) => lhs ^ rhs
},
BinaryOpKind::ShiftRight => match_bitshift! {

Check warning on line 1112 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (bitshift)
(lhs_value as lhs ">>" rhs_value as rhs) => lhs.checked_shr(rhs.into())
},
BinaryOpKind::ShiftLeft => match_bitshift! {

Check warning on line 1115 in compiler/noirc_frontend/src/hir/comptime/interpreter.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (bitshift)
(lhs_value as lhs "<<" rhs_value as rhs) => lhs.checked_shl(rhs.into())
},
BinaryOpKind::Modulo => match_integer! {
Expand Down Expand Up @@ -1325,9 +1327,10 @@
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.
Expand Down
42 changes: 28 additions & 14 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 })?;
Expand All @@ -873,8 +876,11 @@ fn quoted_as_type(
) -> IResult<Value> {
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))
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)))
}
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
Loading