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: Implement Value::Type in comptime interpreter #5593

Merged
merged 7 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
19 changes: 9 additions & 10 deletions compiler/noirc_frontend/src/elaborator/comptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use std::mem::replace;

use crate::{
hir_def::expr::HirIdent,
macros_api::Expression,
node_interner::{DependencyId, ExprId, FuncId},
node_interner::{DependencyId, FuncId},
};

use super::{Elaborator, FunctionContext, ResolverMeta};
Expand All @@ -12,22 +11,22 @@ impl<'context> Elaborator<'context> {
/// Elaborate an expression from the middle of a comptime scope.
/// When this happens we require additional information to know
/// what variables should be in scope.
pub fn elaborate_expression_from_comptime(
pub fn elaborate_item_from_comptime<T>(
&mut self,
expr: Expression,
function: Option<FuncId>,
) -> ExprId {
current_function: Option<FuncId>,
f: impl FnOnce(&mut Self) -> T,
) -> T {
self.function_context.push(FunctionContext::default());
let old_scope = self.scopes.end_function();
self.scopes.start_function();
let function_id = function.map(DependencyId::Function);
let function_id = current_function.map(DependencyId::Function);
let old_item = replace(&mut self.current_item, function_id);

// Note: recover_generics isn't good enough here because any existing generics
// should not be in scope of this new function
let old_generics = std::mem::take(&mut self.generics);

let old_crate_and_module = function.map(|function| {
let old_crate_and_module = current_function.map(|function| {
let meta = self.interner.function_meta(&function);
let old_crate = replace(&mut self.crate_id, meta.source_crate);
let old_module = replace(&mut self.local_module, meta.source_module);
Expand All @@ -36,7 +35,7 @@ impl<'context> Elaborator<'context> {
});

self.populate_scope_from_comptime_scopes();
let expr = self.elaborate_expression(expr).0;
let result = f(self);

if let Some((old_crate, old_module)) = old_crate_and_module {
self.crate_id = old_crate;
Expand All @@ -48,7 +47,7 @@ impl<'context> Elaborator<'context> {
self.scopes.end_function();
self.scopes.0.push(old_scope);
self.check_and_pop_function_context();
expr
result
}

fn populate_scope_from_comptime_scopes(&mut self) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::{
use super::{Elaborator, LambdaContext};

impl<'context> Elaborator<'context> {
pub(super) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) {
pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) {
let (hir_expr, typ) = match expr.kind {
ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.span),
ExpressionKind::Block(block) => self.elaborate_block(block),
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub const WILDCARD_TYPE: &str = "_";

impl<'context> Elaborator<'context> {
/// Translates an UnresolvedType to a Type with a `TypeKind::Normal`
pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type {
pub fn resolve_type(&mut self, typ: UnresolvedType) -> Type {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let span = typ.span;
let resolved_type = self.resolve_type_inner(typ, &Kind::Normal);
if resolved_type.is_nested_slice() {
Expand Down
34 changes: 22 additions & 12 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,9 @@
perform_instantiation_bindings(&instantiation_bindings);
let impl_bindings =
perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?;
let old_function = self.current_function.replace(function);

let result = self.call_function_inner(function, arguments, location);

self.current_function = old_function;
undo_instantiation_bindings(impl_bindings);
undo_instantiation_bindings(instantiation_bindings);
result
Expand Down Expand Up @@ -109,9 +107,25 @@

if meta.kind != FunctionKind::Normal {
let return_type = meta.return_type().follow_bindings();
return self.call_builtin(function, arguments, return_type, location);
return self.call_special(function, arguments, return_type, location);
}

// Wait until after call_special to set the current function so that builtin functions like
// `.as_type()` still call the resolver in the caller's scope.
let old_function = self.current_function.replace(function);
let result = self.call_user_defined_function(function, arguments, location);
self.current_function = old_function;
result
}

/// Call a non-builtin function
fn call_user_defined_function(
&mut self,
function: FuncId,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let meta = self.elaborator.interner.function_meta(&function);
let parameters = meta.parameters.0.clone();
let previous_state = self.enter_function();

Expand All @@ -131,7 +145,7 @@
Ok(result)
}

fn call_builtin(
fn call_special(
jfecher marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
function: FuncId,
arguments: Vec<(Value, Location)>,
Expand All @@ -144,13 +158,7 @@

if let Some(builtin) = func_attrs.builtin() {
let builtin = builtin.clone();
builtin::call_builtin(
self.elaborator.interner,
&builtin,
arguments,
return_type,
location,
)
self.call_builtin(&builtin, arguments, return_type, location)
} else if let Some(foreign) = func_attrs.foreign() {
let item = format!("Comptime evaluation for foreign functions like {foreign}");
Err(InterpreterError::Unimplemented { item, location })
Expand All @@ -164,7 +172,7 @@
} else {
let name = self.elaborator.interner.function_name(&function);
unreachable!("Non-builtin, lowlevel or oracle builtin fn '{name}'")
}

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lowlevel)
}

fn call_closure(
Expand Down Expand Up @@ -1123,7 +1131,9 @@
let expr = result.into_expression(self.elaborator.interner, location)?;
let expr = self
.elaborator
.elaborate_expression_from_comptime(expr, self.current_function);
.elaborate_item_from_comptime(self.current_function, |elab| {
elab.elaborate_expression(expr).0

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)
});

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)
result = self.evaluate(expr)?;
}
Ok(result)
Expand Down
137 changes: 81 additions & 56 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,49 @@
QuotedType, Shared, Type,
};

pub(super) fn call_builtin(
interner: &mut NodeInterner,
name: &str,
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
match name {
"array_len" => array_len(interner, arguments, location),
"as_slice" => as_slice(interner, arguments, location),
"is_unconstrained" => Ok(Value::Bool(true)),
"modulus_be_bits" => modulus_be_bits(interner, arguments, location),
"modulus_be_bytes" => modulus_be_bytes(interner, arguments, location),
"modulus_le_bits" => modulus_le_bits(interner, arguments, location),
"modulus_le_bytes" => modulus_le_bytes(interner, arguments, location),
"modulus_num_bits" => modulus_num_bits(interner, arguments, location),
"slice_insert" => slice_insert(interner, arguments, location),
"slice_pop_back" => slice_pop_back(interner, arguments, location),
"slice_pop_front" => slice_pop_front(interner, arguments, location),
"slice_push_back" => slice_push_back(interner, arguments, location),
"slice_push_front" => slice_push_front(interner, arguments, location),
"slice_remove" => slice_remove(interner, arguments, location),
"struct_def_as_type" => struct_def_as_type(interner, arguments, location),
"struct_def_fields" => struct_def_fields(interner, arguments, location),
"struct_def_generics" => struct_def_generics(interner, arguments, location),
"trait_constraint_eq" => trait_constraint_eq(interner, arguments, location),
"trait_constraint_hash" => trait_constraint_hash(interner, arguments, location),
"trait_def_as_trait_constraint" => {
trait_def_as_trait_constraint(interner, arguments, location)
}
"quoted_as_trait_constraint" => quoted_as_trait_constraint(interner, arguments, location),
"zeroed" => zeroed(return_type, location),
_ => {
let item = format!("Comptime evaluation for builtin function {name}");
Err(InterpreterError::Unimplemented { item, location })
use super::Interpreter;

impl<'local, 'context> Interpreter<'local, 'context> {
pub(super) fn call_builtin(
&mut self,
name: &str,
arguments: Vec<(Value, Location)>,
return_type: Type,
location: Location,
) -> IResult<Value> {
let interner = &mut self.elaborator.interner;
match name {
"array_len" => array_len(interner, arguments, location),
"as_slice" => as_slice(interner, arguments, location),
"is_unconstrained" => Ok(Value::Bool(true)),
"modulus_be_bits" => modulus_be_bits(interner, arguments, location),
"modulus_be_bytes" => modulus_be_bytes(interner, arguments, location),
"modulus_le_bits" => modulus_le_bits(interner, arguments, location),
"modulus_le_bytes" => modulus_le_bytes(interner, arguments, location),
"modulus_num_bits" => modulus_num_bits(interner, arguments, location),
"slice_insert" => slice_insert(interner, arguments, location),
"slice_pop_back" => slice_pop_back(interner, arguments, location),
"slice_pop_front" => slice_pop_front(interner, arguments, location),
"slice_push_back" => slice_push_back(interner, arguments, location),
"slice_push_front" => slice_push_front(interner, arguments, location),
"slice_remove" => slice_remove(interner, arguments, location),
"struct_def_as_type" => struct_def_as_type(interner, arguments, location),
"struct_def_fields" => struct_def_fields(interner, arguments, location),
"struct_def_generics" => struct_def_generics(interner, arguments, location),
"trait_constraint_eq" => trait_constraint_eq(interner, arguments, location),
"trait_constraint_hash" => trait_constraint_hash(interner, arguments, location),
"trait_def_as_trait_constraint" => {
trait_def_as_trait_constraint(interner, arguments, location)
}
"quoted_as_trait_constraint" => {
quoted_as_trait_constraint(interner, arguments, location)
}
"quoted_as_type" => quoted_as_type(self, arguments, location),
"zeroed" => zeroed(return_type, location),
_ => {
let item = format!("Comptime evaluation for builtin function {name}");
Err(InterpreterError::Unimplemented { item, location })
}
}
}
}
Expand Down Expand Up @@ -179,36 +187,31 @@
Ok(Value::Slice(values, typ))
}

/// fn as_type(self) -> Quoted
/// fn as_type(self) -> Type
fn struct_def_as_type(
interner: &NodeInterner,
mut arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(1, &arguments, location)?;

let (struct_def, span) = match arguments.pop().unwrap() {
(Value::StructDefinition(id), location) => (id, location.span),
let struct_def = match arguments.pop().unwrap().0 {
Value::StructDefinition(id) => id,
value => {
let expected = Type::Quoted(QuotedType::StructDefinition);
return Err(InterpreterError::TypeMismatch { expected, location, value: value.0 });
return Err(InterpreterError::TypeMismatch { expected, location, value });
}
};

let struct_def = interner.get_struct(struct_def);
let struct_def = struct_def.borrow();
let make_token = |name| SpannedToken::new(Token::Ident(name), span);

let mut tokens = vec![make_token(struct_def.name.to_string())];
let struct_def_rc = interner.get_struct(struct_def);
let struct_def = struct_def_rc.borrow();

for (i, generic) in struct_def.generics.iter().enumerate() {
if i != 0 {
tokens.push(SpannedToken::new(Token::Comma, span));
}
tokens.push(make_token(generic.type_var.borrow().to_string()));
}
let generics = vecmap(&struct_def.generics, |generic| {
Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone())
});

Ok(Value::Code(Rc::new(Tokens(tokens))))
drop(struct_def);
Ok(Value::Type(Type::Struct(struct_def_rc, generics)))
}

/// fn generics(self) -> [Quoted]
Expand Down Expand Up @@ -239,7 +242,7 @@
Ok(Value::Slice(generics.collect(), typ))
}

/// fn fields(self) -> [(Quoted, Quoted)]
/// fn fields(self) -> [(Quoted, Type)]
/// Returns (name, type) pairs of each field of this StructDefinition
fn struct_def_fields(
interner: &mut NodeInterner,
Expand All @@ -266,15 +269,13 @@

for (name, typ) in struct_def.get_fields_as_written() {
let name = make_quoted(vec![make_token(name)]);
let id = interner.push_quoted_type(typ);
let typ = SpannedToken::new(Token::QuotedType(id), span);
let typ = Value::Code(Rc::new(Tokens(vec![typ])));
let typ = Value::Type(typ);
fields.push_back(Value::Tuple(vec![name, typ]));
}

let typ = Type::Slice(Box::new(Type::Tuple(vec![
Type::Quoted(QuotedType::Quoted),
Type::Quoted(QuotedType::Quoted),
Type::Quoted(QuotedType::Type),
])));
Ok(Value::Slice(fields, typ))
}
Expand Down Expand Up @@ -380,6 +381,30 @@
Ok(Value::TraitConstraint(trait_bound))
}

// fn as_type(quoted: Quoted) -> Type
fn quoted_as_type(
interpreter: &mut Interpreter,
mut arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(1, &arguments, location)?;

let tokens = get_quoted(arguments.pop().unwrap().0, location)?;
let quoted = tokens.as_ref().clone();

let typ = parser::parse_type().parse(quoted).map_err(|mut errors| {
let error = errors.swap_remove(0);
let rule = "a type";
InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file }
})?;

let typ = interpreter
.elaborator
.elaborate_item_from_comptime(interpreter.current_function, |elab| elab.resolve_type(typ));

Ok(Value::Type(typ))
}

// fn constraint_hash(constraint: TraitConstraint) -> Field
fn trait_constraint_hash(
_interner: &mut NodeInterner,
Expand All @@ -399,7 +424,7 @@

// fn constraint_eq(constraint_a: TraitConstraint, constraint_b: TraitConstraint) -> bool
fn trait_constraint_eq(
_interner: &mut NodeInterner,

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)

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

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elab)
mut arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
Expand Down
19 changes: 5 additions & 14 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use noirc_errors::Location;

use crate::{
hir::comptime::{errors::IResult, value::unwrap_rc, Value},
token::{SpannedToken, Token, Tokens},
hir::comptime::errors::IResult,
token::{Token, Tokens},
};

use super::Interpreter;
Expand All @@ -19,20 +19,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
let mut new_tokens = Vec::with_capacity(tokens.0.len());

for token in tokens.0 {
let span = token.to_span();
match token.token() {
Token::UnquoteMarker(id) => {
match self.evaluate(*id)? {
// If the value is already quoted we don't want to change the token stream by
// turning it into a Quoted block (which would add `quote`, `{`, and `}` tokens).
Value::Code(stream) => new_tokens.extend(unwrap_rc(stream).0),
value => {
let new_id =
value.into_hir_expression(self.elaborator.interner, location)?;
let new_token = Token::UnquoteMarker(new_id);
new_tokens.push(SpannedToken::new(new_token, span));
}
}
let value = self.evaluate(*id)?;
let tokens = value.into_tokens(self.elaborator.interner, location)?;
new_tokens.extend(tokens.0);
}
_ => new_tokens.push(token),
}
Expand Down
Loading
Loading