Skip to content

Commit

Permalink
chore(experimental): Improve variable not defined error message in co…
Browse files Browse the repository at this point in the history
…mptime interpreter (#4889)

# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## Summary\*

Slightly improves the error message for when a variable in the
interpreter isn't found.

Since all ids are already given by name resolution, the only time one
cannot be found is if it is not a comptime variable, so I've narrowed
the error message a bit.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
jfecher authored Apr 23, 2024
1 parent ce5e1a5 commit 6a66138
Show file tree
Hide file tree
Showing 16 changed files with 40 additions and 47 deletions.
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub enum StatementKind {
Break,
Continue,
/// This statement should be executed at compile-time
CompTime(Box<StatementKind>),
Comptime(Box<StatementKind>),
// This is an expression with a trailing semi-colon
Semi(Expression),
// This statement is the result of a recovered parse error.
Expand Down Expand Up @@ -87,10 +87,10 @@ impl StatementKind {
}
self
}
StatementKind::CompTime(mut statement) => {
StatementKind::Comptime(mut statement) => {
*statement =
statement.add_semicolon(semi, span, last_statement_in_block, emit_error);
StatementKind::CompTime(statement)
StatementKind::Comptime(statement)
}
// A semicolon on a for loop is optional and does nothing
StatementKind::For(_) => self,
Expand Down Expand Up @@ -685,7 +685,7 @@ impl Display for StatementKind {
StatementKind::For(for_loop) => for_loop.fmt(f),
StatementKind::Break => write!(f, "break"),
StatementKind::Continue => write!(f, "continue"),
StatementKind::CompTime(statement) => write!(f, "comptime {statement}"),
StatementKind::Comptime(statement) => write!(f, "comptime {statement}"),
StatementKind::Semi(semi) => write!(f, "{semi};"),
StatementKind::Error => write!(f, "Error"),
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{node_interner::DefinitionId, Type};
use crate::Type;
use acvm::FieldElement;
use noirc_errors::Location;

Expand All @@ -9,7 +9,7 @@ use super::value::Value;
pub enum InterpreterError {
ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location },
TypeMismatch { expected: Type, value: Value, location: Location },
NoValueForId { id: DefinitionId, location: Location },
NonComptimeVarReferenced { name: String, location: Location },
IntegerOutOfRangeForType { value: FieldElement, typ: Type, location: Location },
ErrorNodeEncountered { location: Location },
NonFunctionCalled { value: Value, location: Location },
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ impl StmtId {
HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)),
HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)),
HirStatement::Error => StatementKind::Error,
HirStatement::CompTime(statement) => {
StatementKind::CompTime(Box::new(statement.to_ast(interner).kind))
HirStatement::Comptime(statement) => {
StatementKind::Comptime(Box::new(statement.to_ast(interner).kind))
}
};

Expand Down
28 changes: 11 additions & 17 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,7 @@ impl<'a> Interpreter<'a> {
location: Location,
) -> IResult<()> {
self.type_check(typ, &argument, location)?;
for scope in self.scopes.iter_mut().rev() {
if let Entry::Occupied(mut entry) = scope.entry(id) {
entry.insert(argument);
return Ok(());
}
}
Err(InterpreterError::NoValueForId { id, location })
self.mutate(id, argument, location)
}

/// Mutate an existing variable, potentially from a prior scope
Expand All @@ -263,17 +257,12 @@ impl<'a> Interpreter<'a> {
return Ok(());
}
}
Err(InterpreterError::NoValueForId { id, location })
let name = self.interner.definition(id).name.clone();
Err(InterpreterError::NonComptimeVarReferenced { name, location })
}

fn lookup(&self, ident: &HirIdent) -> IResult<Value> {
for scope in self.scopes.iter().rev() {
if let Some(value) = scope.get(&ident.id) {
return Ok(value.clone());
}
}

Err(InterpreterError::NoValueForId { id: ident.id, location: ident.location })
self.lookup_id(ident.id, ident.location)
}

fn lookup_id(&self, id: DefinitionId, location: Location) -> IResult<Value> {
Expand All @@ -283,7 +272,12 @@ impl<'a> Interpreter<'a> {
}
}

Err(InterpreterError::NoValueForId { id, location })
// Justification for `NonComptimeVarReferenced`:
// If we have an id to lookup at all that means name resolution successfully
// found another variable in scope for this name. If the name is in scope
// but unknown by the interpreter it must be because it was not a comptime variable.
let name = self.interner.definition(id).name.clone();
Err(InterpreterError::NonComptimeVarReferenced { name, location })
}

fn type_check(&self, typ: &Type, value: &Value, location: Location) -> IResult<()> {
Expand Down Expand Up @@ -1023,7 +1017,7 @@ impl<'a> Interpreter<'a> {
HirStatement::Break => self.evaluate_break(statement),
HirStatement::Continue => self.evaluate_continue(statement),
HirStatement::Expression(expression) => self.evaluate(expression),
HirStatement::CompTime(statement) => self.evaluate_comptime(statement),
HirStatement::Comptime(statement) => self.evaluate_comptime(statement),
HirStatement::Semi(expression) => {
self.evaluate(expression)?;
Ok(Value::Unit)
Expand Down
10 changes: 5 additions & 5 deletions compiler/noirc_frontend/src/hir/comptime/scan.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//! This module is for the scanning of the Hir by the interpreter.
//! In this initial step, the Hir is scanned for `CompTime` nodes
//! In this initial step, the Hir is scanned for `Comptime` nodes
//! without actually executing anything until such a node is found.
//! Once such a node is found, the interpreter will call the relevant
//! evaluate method on that node type, insert the result into the Ast,
//! and continue scanning the rest of the program.
//!
//! Since it mostly just needs to recur on the Hir looking for CompTime
//! Since it mostly just needs to recur on the Hir looking for Comptime
//! nodes, this pass is fairly simple. The only thing it really needs to
//! ensure to do is to push and pop scopes on the interpreter as needed
//! so that any variables defined within e.g. an `if` statement containing
//! a `CompTime` block aren't accessible outside of the `if`.
//! a `Comptime` block aren't accessible outside of the `if`.
use crate::{
hir_def::{
expr::{
Expand All @@ -30,7 +30,7 @@ use super::{

#[allow(dead_code)]
impl<'interner> Interpreter<'interner> {
/// Scan through a function, evaluating any CompTime nodes found.
/// Scan through a function, evaluating any Comptime nodes found.
/// These nodes will be modified in place, replaced with the
/// result of their evaluation.
pub fn scan_function(&mut self, function: FuncId) -> IResult<()> {
Expand Down Expand Up @@ -185,7 +185,7 @@ impl<'interner> Interpreter<'interner> {
HirStatement::Expression(expression) => self.scan_expression(expression),
HirStatement::Semi(semi) => self.scan_expression(semi),
HirStatement::Error => Ok(()),
HirStatement::CompTime(comptime) => {
HirStatement::Comptime(comptime) => {
let location = self.interner.statement_location(comptime);
let new_expr =
self.evaluate_comptime(comptime)?.into_expression(self.interner, location)?;
Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use rustc_hash::FxHashMap as HashMap;

use super::errors::{IResult, InterpreterError};

#[allow(unused)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
Unit,
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1272,9 +1272,9 @@ impl<'a> Resolver<'a> {
HirStatement::Continue
}
StatementKind::Error => HirStatement::Error,
StatementKind::CompTime(statement) => {
StatementKind::Comptime(statement) => {
let statement = self.resolve_stmt(*statement, span);
HirStatement::CompTime(self.interner.push_stmt(statement))
HirStatement::Comptime(self.interner.push_stmt(statement))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/type_check/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl<'interner> TypeChecker<'interner> {
HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt),
HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id),
HirStatement::For(for_loop) => self.check_for_loop(for_loop),
HirStatement::CompTime(statement) => return self.check_statement(&statement),
HirStatement::Comptime(statement) => return self.check_statement(&statement),
HirStatement::Break | HirStatement::Continue | HirStatement::Error => (),
}
Type::Unit
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir_def/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub enum HirStatement {
Continue,
Expression(ExprId),
Semi(ExprId),
CompTime(StmtId),
Comptime(StmtId),
Error,
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ pub enum Keyword {
Break,
CallData,
Char,
CompTime,
Comptime,
Constrain,
Continue,
Contract,
Expand Down Expand Up @@ -856,7 +856,7 @@ impl fmt::Display for Keyword {
Keyword::Break => write!(f, "break"),
Keyword::Char => write!(f, "char"),
Keyword::CallData => write!(f, "call_data"),
Keyword::CompTime => write!(f, "comptime"),
Keyword::Comptime => write!(f, "comptime"),
Keyword::Constrain => write!(f, "constrain"),
Keyword::Continue => write!(f, "continue"),
Keyword::Contract => write!(f, "contract"),
Expand Down Expand Up @@ -904,7 +904,7 @@ impl Keyword {
"break" => Keyword::Break,
"call_data" => Keyword::CallData,
"char" => Keyword::Char,
"comptime" => Keyword::CompTime,
"comptime" => Keyword::Comptime,
"constrain" => Keyword::Constrain,
"continue" => Keyword::Continue,
"contract" => Keyword::Contract,
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ impl<'interner> Monomorphizer<'interner> {
HirStatement::Error => unreachable!(),

// All `comptime` statements & expressions should be removed before runtime.
HirStatement::CompTime(_) => unreachable!("comptime statement in runtime code"),
HirStatement::Comptime(_) => unreachable!("comptime statement in runtime code"),
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/noir_parser.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ extern {
"break" => BorrowedToken::Keyword(noir_token::Keyword::Break),
"call_data" => BorrowedToken::Keyword(noir_token::Keyword::CallData),
"char" => BorrowedToken::Keyword(noir_token::Keyword::Char),
"comptime" => BorrowedToken::Keyword(noir_token::Keyword::CompTime),
"comptime" => BorrowedToken::Keyword(noir_token::Keyword::Comptime),
"constrain" => BorrowedToken::Keyword(noir_token::Keyword::Constrain),
"continue" => BorrowedToken::Keyword(noir_token::Keyword::Continue),
"contract" => BorrowedToken::Keyword(noir_token::Keyword::Contract),
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,23 +532,23 @@ where
P2: ExprParser + 'a,
S: NoirParser<StatementKind> + 'a,
{
keyword(Keyword::CompTime)
keyword(Keyword::Comptime)
.ignore_then(choice((
declaration(expr),
for_loop(expr_no_constructors, statement.clone()),
block(statement).map_with_span(|block, span| {
StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span))
}),
)))
.map(|statement| StatementKind::CompTime(Box::new(statement)))
.map(|statement| StatementKind::Comptime(Box::new(statement)))
}

/// Comptime in an expression position only accepts entire blocks
fn comptime_expr<'a, S>(statement: S) -> impl NoirParser<ExpressionKind> + 'a
where
S: NoirParser<StatementKind> + 'a,
{
keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::Comptime)
keyword(Keyword::Comptime).ignore_then(block(statement)).map(ExpressionKind::Comptime)
}

fn declaration<'a, P>(expr_parser: P) -> impl NoirParser<StatementKind> + 'a
Expand Down Expand Up @@ -733,7 +733,7 @@ fn optional_distinctness() -> impl NoirParser<Distinctness> {
}

fn maybe_comp_time() -> impl NoirParser<bool> {
keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| {
keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| {
if opt.is_some() {
emit(ParserError::with_reason(
ParserErrorReason::ExperimentalFeature("comptime"),
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/parser/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use chumsky::prelude::*;
use noirc_errors::Span;

fn maybe_comp_time() -> impl NoirParser<()> {
keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| {
keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| {
if opt.is_some() {
emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span));
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ mod test {
HirStatement::Error => panic!("Invalid HirStatement!"),
HirStatement::Break => panic!("Unexpected break"),
HirStatement::Continue => panic!("Unexpected continue"),
HirStatement::CompTime(_) => panic!("Unexpected comptime"),
HirStatement::Comptime(_) => panic!("Unexpected comptime"),
};
let expr = interner.expression(&expr_id);

Expand Down
2 changes: 1 addition & 1 deletion tooling/nargo_fmt/src/visitor/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl super::FmtVisitor<'_> {
StatementKind::Error => unreachable!(),
StatementKind::Break => self.push_rewrite("break;".into(), span),
StatementKind::Continue => self.push_rewrite("continue;".into(), span),
StatementKind::CompTime(statement) => self.visit_stmt(*statement, span, is_last),
StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last),
}
}
}

0 comments on commit 6a66138

Please sign in to comment.