Skip to content

Commit

Permalink
feat(experimental): Add comptime keyword (#4840)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #4588

## Summary\*

Adds the `comptime` keyword which can be used around certain constructs:

- `comptime let foo = ...;` on local or global definitions
- `comptime if a { b } else { c }`
- `comptime for ... { ... }`
- `comptime { ... }`

This PR just adds these to parsing and forwards the new node types to
the frontend as necessary.

## Additional Context

The interpreter currently _does not_ handle these properly - this will
come in a separate PR. Eventually we want the interpreter to _only_
interpret comptime things where currently it interprets everything.

## Documentation\*

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

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
jfecher committed Apr 19, 2024
1 parent 6cfb328 commit 4dfd7f0
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 133 deletions.
2 changes: 2 additions & 0 deletions aztec_macros/src/utils/ast_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement {
pattern: mutable(name),
r#type: make_type(UnresolvedTypeData::Unspecified),
expression: assigned_to,
comptime: false,
attributes: vec![],
}))
}
Expand All @@ -90,6 +91,7 @@ pub fn assignment_with_type(
pattern: pattern(name),
r#type: make_type(typ),
expression: assigned_to,
comptime: false,
attributes: vec![],
}))
}
Expand Down
53 changes: 36 additions & 17 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub enum StatementKind {
For(ForLoopStatement),
Break,
Continue,
/// This statement should be executed at compile-time
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 All @@ -47,6 +49,19 @@ pub enum StatementKind {
}

impl Statement {
pub fn add_semicolon(
mut self,
semi: Option<Token>,
span: Span,
last_statement_in_block: bool,
emit_error: &mut dyn FnMut(ParserError),
) -> Self {
self.kind = self.kind.add_semicolon(semi, span, last_statement_in_block, emit_error);
self
}
}

impl StatementKind {
pub fn add_semicolon(
self,
semi: Option<Token>,
Expand All @@ -57,7 +72,7 @@ impl Statement {
let missing_semicolon =
ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span);

let kind = match self.kind {
match self {
StatementKind::Let(_)
| StatementKind::Constrain(_)
| StatementKind::Assign(_)
Expand All @@ -69,10 +84,15 @@ impl Statement {
if semi.is_none() {
emit_error(missing_semicolon);
}
self.kind
self
}
StatementKind::Comptime(mut statement) => {
*statement =
statement.add_semicolon(semi, span, last_statement_in_block, emit_error);
StatementKind::Comptime(statement)
}
// A semicolon on a for loop is optional and does nothing
StatementKind::For(_) => self.kind,
StatementKind::For(_) => self,

StatementKind::Expression(expr) => {
match (&expr.kind, semi, last_statement_in_block) {
Expand All @@ -92,9 +112,7 @@ impl Statement {
(_, None, true) => StatementKind::Expression(expr),
}
}
};

Statement { kind, span: self.span }
}
}
}

Expand All @@ -108,7 +126,13 @@ impl StatementKind {
pub fn new_let(
((pattern, r#type), expression): ((Pattern, UnresolvedType), Expression),
) -> StatementKind {
StatementKind::Let(LetStatement { pattern, r#type, expression, attributes: vec![] })
StatementKind::Let(LetStatement {
pattern,
r#type,
expression,
comptime: false,
attributes: vec![],
})
}

/// Create a Statement::Assign value, desugaring any combined operators like += if needed.
Expand Down Expand Up @@ -407,17 +431,9 @@ pub struct LetStatement {
pub r#type: UnresolvedType,
pub expression: Expression,
pub attributes: Vec<SecondaryAttribute>,
}

impl LetStatement {
pub fn new_let(
(((pattern, r#type), expression), attributes): (
((Pattern, UnresolvedType), Expression),
Vec<SecondaryAttribute>,
),
) -> LetStatement {
LetStatement { pattern, r#type, expression, attributes }
}
// True if this should only be run during compile-time
pub comptime: bool,
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -573,6 +589,7 @@ impl ForRange {
pattern: Pattern::Identifier(array_ident.clone()),
r#type: UnresolvedType::unspecified(),
expression: array,
comptime: false,
attributes: vec![],
}),
span: array_span,
Expand Down Expand Up @@ -616,6 +633,7 @@ impl ForRange {
pattern: Pattern::Identifier(identifier),
r#type: UnresolvedType::unspecified(),
expression: Expression::new(loop_element, array_span),
comptime: false,
attributes: vec![],
}),
span: array_span,
Expand Down Expand Up @@ -666,6 +684,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::Semi(semi) => write!(f, "{semi};"),
StatementKind::Error => write!(f, "Error"),
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ impl DebugInstrumenter {
pattern: ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)),
r#type: ast::UnresolvedType::unspecified(),
expression: ret_expr.clone(),
comptime: false,
attributes: vec![],
}),
span: ret_expr.span,
Expand Down Expand Up @@ -243,6 +244,7 @@ impl DebugInstrumenter {
kind: ast::StatementKind::Let(ast::LetStatement {
pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()),
r#type: ast::UnresolvedType::unspecified(),
comptime: false,
expression: ast::Expression {
kind: ast::ExpressionKind::Block(ast::BlockExpression {
statements: block_stmts,
Expand Down Expand Up @@ -275,6 +277,7 @@ impl DebugInstrumenter {
pattern: ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)),
r#type: ast::UnresolvedType::unspecified(),
expression: assign_stmt.expression.clone(),
comptime: false,
attributes: vec![],
});
let expression_span = assign_stmt.expression.span;
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl StmtId {
pattern,
r#type,
expression,
comptime: false,
attributes: Vec::new(),
})
}
Expand Down Expand Up @@ -64,6 +65,9 @@ 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))
}
};

Statement { kind, span }
Expand Down
13 changes: 13 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub(crate) struct Interpreter<'interner> {
changed_globally: bool,

in_loop: bool,

/// True if we're currently in a compile-time context.
/// If this is false code is skipped over instead of executed.
in_comptime_context: bool,
}

#[allow(unused)]
Expand Down Expand Up @@ -121,6 +125,7 @@ impl<'a> Interpreter<'a> {
changed_functions: HashSet::default(),
changed_globally: false,
in_loop: false,
in_comptime_context: false,
}
}

Expand Down Expand Up @@ -1074,6 +1079,7 @@ impl<'a> Interpreter<'a> {
HirStatement::Break => self.evaluate_break(),
HirStatement::Continue => self.evaluate_continue(),
HirStatement::Expression(expression) => self.evaluate(expression),
HirStatement::Comptime(statement) => self.evaluate_comptime(statement),
HirStatement::Semi(expression) => {
self.evaluate(expression)?;
Ok(Value::Unit)
Expand Down Expand Up @@ -1246,6 +1252,13 @@ impl<'a> Interpreter<'a> {
Err(InterpreterError::ContinueNotInLoop)
}
}

fn evaluate_comptime(&mut self, statement: StmtId) -> IResult<Value> {
let was_in_comptime = std::mem::replace(&mut self.in_comptime_context, true);
let result = self.evaluate_statement(statement);
self.in_comptime_context = was_in_comptime;
result
}
}

impl Value {
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,10 @@ impl<'a> Resolver<'a> {
HirStatement::Continue
}
StatementKind::Error => HirStatement::Error,
StatementKind::Comptime(statement) => {
let statement = self.resolve_stmt(*statement, span);
HirStatement::Comptime(self.interner.push_stmt(statement))
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/hir/type_check/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +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::Break | HirStatement::Continue | HirStatement::Error => (),
}
Type::Unit
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/hir_def/stmt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::expr::HirIdent;
use crate::macros_api::SecondaryAttribute;
use crate::node_interner::ExprId;
use crate::node_interner::{ExprId, StmtId};
use crate::{Ident, Type};
use fm::FileId;
use noirc_errors::{Location, Span};
Expand All @@ -19,6 +19,7 @@ pub enum HirStatement {
Continue,
Expression(ExprId),
Semi(ExprId),
Comptime(StmtId),
Error,
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,9 @@ impl<'interner> Monomorphizer<'interner> {
HirStatement::Break => Ok(ast::Expression::Break),
HirStatement::Continue => Ok(ast::Expression::Continue),
HirStatement::Error => unreachable!(),

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

Expand Down

0 comments on commit 4dfd7f0

Please sign in to comment.