From 237418f63b809a4d3f60dce3fa3f81a73e4eb116 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 10 Apr 2024 16:02:59 -0400 Subject: [PATCH 01/21] Add Hir -> Ast conversion --- .../noirc_frontend/src/hir/comptime/mod.rs | 143 ++++++++++++++++++ compiler/noirc_frontend/src/hir/mod.rs | 1 + compiler/noirc_frontend/src/node_interner.rs | 4 + 3 files changed, 148 insertions(+) create mode 100644 compiler/noirc_frontend/src/hir/comptime/mod.rs diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs new file mode 100644 index 00000000000..7b0947bbbd1 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -0,0 +1,143 @@ +use iter_extended::vecmap; +use noirc_errors::Spanned; + +use crate::{ConstrainKind, LetStatement, Pattern, Ident, Type, Path, UnresolvedType, UnresolvedTypeData}; +use crate::node_interner::{NodeInterner, StmtId, ExprId}; +use crate::ast::{ Expression, Statement, StatementKind, ConstrainStatement }; +use crate::hir_def::expr::{HirExpression, HirIdent}; +use crate::hir_def::stmt::{HirStatement, HirPattern}; + +// TODO: +// - Full path for idents & types +// - Assert/AssertEq information lost +// - The type name span is lost in constructor patterns & expressions +// - All type spans are lost + +impl StmtId { + #[allow(unused)] + fn to_ast(&self, interner: &NodeInterner) -> Statement { + let statement = interner.statement(self); + let span = interner.statement_span(self); + + let kind = match statement { + HirStatement::Let(let_stmt) => { + let pattern = let_stmt.pattern.to_ast(interner); + let r#type = interner.id_type(let_stmt.expression).to_ast(interner); + let expression = let_stmt.expression.to_ast(interner); + StatementKind::Let(LetStatement { pattern, r#type, expression }) + }, + HirStatement::Constrain(constrain) => { + let expr = constrain.0.to_ast(interner); + let message = constrain.2.map(|message| message.to_ast(interner)); + + // TODO: Find difference in usage between Assert & AssertEq + StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) + }, + HirStatement::Assign(_) => todo!(), + HirStatement::For(_) => todo!(), + HirStatement::Break => todo!(), + HirStatement::Continue => todo!(), + HirStatement::Expression(_) => todo!(), + HirStatement::Semi(_) => todo!(), + HirStatement::Error => todo!(), + }; + + Statement { kind, span } + } +} + +impl ExprId { + #[allow(unused)] + fn to_ast(&self, interner: &NodeInterner) -> Expression { + let expression = interner.expression(self); + let location = interner.expr_span(self); + + match expression { + HirExpression::Ident(_) => todo!(), + HirExpression::Literal(_) => todo!(), + HirExpression::Block(_) => todo!(), + HirExpression::Prefix(_) => todo!(), + HirExpression::Infix(_) => todo!(), + HirExpression::Index(_) => todo!(), + HirExpression::Constructor(_) => todo!(), + HirExpression::MemberAccess(_) => todo!(), + HirExpression::Call(_) => todo!(), + HirExpression::MethodCall(_) => todo!(), + HirExpression::Cast(_) => todo!(), + HirExpression::If(_) => todo!(), + HirExpression::Tuple(_) => todo!(), + HirExpression::Lambda(_) => todo!(), + HirExpression::Error => todo!(), + HirExpression::Quote(_) => todo!(), + } + } +} + +impl HirPattern { + fn to_ast(self, interner: &NodeInterner) -> Pattern { + match self { + HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), + HirPattern::Mutable(pattern, location) => { + let pattern = Box::new(pattern.to_ast(interner)); + Pattern::Mutable(pattern, location.span, false) + }, + HirPattern::Tuple(patterns, location) => { + let patterns = vecmap(patterns, |pattern| pattern.to_ast(interner)); + Pattern::Tuple(patterns, location.span) + }, + HirPattern::Struct(typ, patterns, location) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name, pattern.to_ast(interner)) + }); + let name = match typ.follow_bindings() { + Type::Struct(struct_def, _) => { + let struct_def = struct_def.borrow(); + struct_def.name.0.contents.clone() + }, + // This pass shouldn't error so if the type isn't a struct we just get a string + // representation of any other type and use that. We're relying on name + // resolution to fail later when this Ast is re-converted to Hir. + other => other.to_string(), + }; + // The name span is lost here + let path = Path::from_single(name, location.span); + Pattern::Struct(path, patterns, location.span) + }, + } + } +} + +impl HirIdent { + fn to_ast(&self, interner: &NodeInterner) -> Ident { + let name = interner.definition_name(self.id).to_owned(); + Ident(Spanned::from(self.location.span, name)) + } +} + +impl Type { + fn to_ast(&self, interner: &NodeInterner) -> UnresolvedType { + let typ = match self { + Type::FieldElement => todo!(), + Type::Array(_, _) => todo!(), + Type::Slice(_) => todo!(), + Type::Integer(_, _) => todo!(), + Type::Bool => todo!(), + Type::String(_) => todo!(), + Type::FmtString(_, _) => todo!(), + Type::Unit => todo!(), + Type::Tuple(_) => todo!(), + Type::Struct(_, _) => todo!(), + Type::Alias(_, _) => todo!(), + Type::TypeVariable(_, _) => todo!(), + Type::TraitAsType(_, _, _) => todo!(), + Type::NamedGeneric(_, _) => todo!(), + Type::Function(_, _, _) => todo!(), + Type::MutableReference(_) => todo!(), + Type::Forall(_, _) => todo!(), + Type::Constant(_) => todo!(), + Type::Code => todo!(), + Type::Error => UnresolvedTypeData::Error, + }; + UnresolvedType { typ, span: todo!() } + } +} diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 00bcb0cdebf..5f983657c31 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -1,3 +1,4 @@ +pub mod comptime; pub mod def_collector; pub mod def_map; pub mod resolution; diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index dcfceccdb57..7e880fb2bef 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -907,6 +907,10 @@ impl NodeInterner { self.id_location(expr_id) } + pub fn statement_span(&self, stmt_id: &StmtId) -> Span { + self.id_location(stmt_id).span + } + pub fn get_struct(&self, id: StructId) -> Shared { self.structs[&id].clone() } From 88fbae44ee40ff52be5157c71fa5cc9af5d14a70 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Apr 2024 11:56:14 -0400 Subject: [PATCH 02/21] Move file --- .../src/hir/comptime/hir_to_ast.rs | 208 ++++++++++++++++++ .../noirc_frontend/src/hir/comptime/mod.rs | 144 +----------- 2 files changed, 209 insertions(+), 143 deletions(-) create mode 100644 compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs new file mode 100644 index 00000000000..56558a1dad3 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -0,0 +1,208 @@ +use iter_extended::vecmap; +use noirc_errors::{Spanned, Span}; + +use crate::{ConstrainKind, LetStatement, Pattern, Ident, Type, Path, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; +use crate::node_interner::{NodeInterner, StmtId, ExprId}; +use crate::ast::{ Expression, Statement, StatementKind, ConstrainStatement }; +use crate::hir_def::expr::{HirExpression, HirIdent}; +use crate::hir_def::stmt::{HirStatement, HirPattern}; + +// TODO: +// - Full path for idents & types +// - Assert/AssertEq information lost +// - The type name span is lost in constructor patterns & expressions +// - All type spans are lost + +impl StmtId { + #[allow(unused)] + fn to_ast(&self, interner: &NodeInterner) -> Statement { + let statement = interner.statement(self); + let span = interner.statement_span(self); + + let kind = match statement { + HirStatement::Let(let_stmt) => { + let pattern = let_stmt.pattern.to_ast(interner); + let r#type = interner.id_type(let_stmt.expression).to_ast(interner); + let expression = let_stmt.expression.to_ast(interner); + StatementKind::Let(LetStatement { pattern, r#type, expression }) + }, + HirStatement::Constrain(constrain) => { + let expr = constrain.0.to_ast(interner); + let message = constrain.2.map(|message| message.to_ast(interner)); + + // TODO: Find difference in usage between Assert & AssertEq + StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) + }, + HirStatement::Assign(_) => todo!(), + HirStatement::For(_) => todo!(), + HirStatement::Break => todo!(), + HirStatement::Continue => todo!(), + HirStatement::Expression(_) => todo!(), + HirStatement::Semi(_) => todo!(), + HirStatement::Error => todo!(), + }; + + Statement { kind, span } + } +} + +impl ExprId { + #[allow(unused)] + fn to_ast(&self, interner: &NodeInterner) -> Expression { + let expression = interner.expression(self); + let location = interner.expr_span(self); + + match expression { + HirExpression::Ident(_) => todo!(), + HirExpression::Literal(_) => todo!(), + HirExpression::Block(_) => todo!(), + HirExpression::Prefix(_) => todo!(), + HirExpression::Infix(_) => todo!(), + HirExpression::Index(_) => todo!(), + HirExpression::Constructor(_) => todo!(), + HirExpression::MemberAccess(_) => todo!(), + HirExpression::Call(_) => todo!(), + HirExpression::MethodCall(_) => todo!(), + HirExpression::Cast(_) => todo!(), + HirExpression::If(_) => todo!(), + HirExpression::Tuple(_) => todo!(), + HirExpression::Lambda(_) => todo!(), + HirExpression::Error => todo!(), + HirExpression::Quote(_) => todo!(), + } + } +} + +impl HirPattern { + fn to_ast(self, interner: &NodeInterner) -> Pattern { + match self { + HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), + HirPattern::Mutable(pattern, location) => { + let pattern = Box::new(pattern.to_ast(interner)); + Pattern::Mutable(pattern, location.span, false) + }, + HirPattern::Tuple(patterns, location) => { + let patterns = vecmap(patterns, |pattern| pattern.to_ast(interner)); + Pattern::Tuple(patterns, location.span) + }, + HirPattern::Struct(typ, patterns, location) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name, pattern.to_ast(interner)) + }); + let name = match typ.follow_bindings() { + Type::Struct(struct_def, _) => { + let struct_def = struct_def.borrow(); + struct_def.name.0.contents.clone() + }, + // This pass shouldn't error so if the type isn't a struct we just get a string + // representation of any other type and use that. We're relying on name + // resolution to fail later when this Ast is re-converted to Hir. + other => other.to_string(), + }; + // The name span is lost here + let path = Path::from_single(name, location.span); + Pattern::Struct(path, patterns, location.span) + }, + } + } +} + +impl HirIdent { + fn to_ast(&self, interner: &NodeInterner) -> Ident { + let name = interner.definition_name(self.id).to_owned(); + Ident(Spanned::from(self.location.span, name)) + } +} + +impl Type { + fn to_ast(&self, interner: &NodeInterner) -> UnresolvedType { + let typ = match self { + Type::FieldElement => UnresolvedTypeData::FieldElement, + Type::Array(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_ast(interner)); + UnresolvedTypeData::Array(length, element) + } + Type::Slice(element) => { + let element = Box::new(element.to_ast(interner)); + UnresolvedTypeData::Slice(element) + }, + Type::Integer(sign, bit_size) => { + UnresolvedTypeData::Integer(*sign, *bit_size) + }, + Type::Bool => UnresolvedTypeData::Bool, + Type::String(length) => { + let length = length.to_type_expression(); + UnresolvedTypeData::String(Some(length)) + }, + Type::FmtString(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_ast(interner)); + UnresolvedTypeData::FormatString(length, element) + }, + Type::Unit => UnresolvedTypeData::Unit, + Type::Tuple(fields) => { + let fields = vecmap(fields, |field| field.to_ast(interner)); + UnresolvedTypeData::Tuple(fields) + }, + Type::Struct(def, generics) => { + let struct_def = def.borrow(); + let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let name = Path::from_ident(struct_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + }, + Type::Alias(type_def, generics) => { + // Keep the alias name instead of expanding this in case the + // alias' definition was changed + let type_def = type_def.borrow(); + let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let name = Path::from_ident(type_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + }, + Type::TypeVariable(_, _) => todo!(), + Type::TraitAsType(_, name, generics) => { + let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, generics) + }, + Type::NamedGeneric(_, name) => { + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, Vec::new()) + }, + Type::Function(args, ret, env) => { + let args = vecmap(args, |arg| arg.to_ast(interner)); + let ret = Box::new(ret.to_ast(interner)); + let env = Box::new(env.to_ast(interner)); + UnresolvedTypeData::Function(args, ret, env) + }, + Type::MutableReference(element) => { + let element = Box::new(element.to_ast(interner)); + UnresolvedTypeData::MutableReference(element) + }, + // Type::Forall is only for generic functions which don't store a type + // in their Ast so they don't need to call to_ast for their Forall type. + // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use + // this to ignore this case since it shouldn't be needed anyway. + Type::Forall(_, typ) => return typ.to_ast(interner), + Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), + Type::Code => UnresolvedTypeData::Code, + Type::Error => UnresolvedTypeData::Error, + }; + + UnresolvedType { typ, span: None } + } + + fn to_type_expression(&self) -> UnresolvedTypeExpression { + let span = Span::default(); + + match self.follow_bindings() { + Type::Constant(length) => UnresolvedTypeExpression::Constant(length, span), + Type::NamedGeneric(_, name) => { + let path = Path::from_single(name.as_ref().clone(), span); + UnresolvedTypeExpression::Variable(path) + }, + // TODO: This should be turned into a proper error. + other => panic!("Cannot represent {other:?} as type expression"), + } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 7b0947bbbd1..91621c857cf 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,143 +1 @@ -use iter_extended::vecmap; -use noirc_errors::Spanned; - -use crate::{ConstrainKind, LetStatement, Pattern, Ident, Type, Path, UnresolvedType, UnresolvedTypeData}; -use crate::node_interner::{NodeInterner, StmtId, ExprId}; -use crate::ast::{ Expression, Statement, StatementKind, ConstrainStatement }; -use crate::hir_def::expr::{HirExpression, HirIdent}; -use crate::hir_def::stmt::{HirStatement, HirPattern}; - -// TODO: -// - Full path for idents & types -// - Assert/AssertEq information lost -// - The type name span is lost in constructor patterns & expressions -// - All type spans are lost - -impl StmtId { - #[allow(unused)] - fn to_ast(&self, interner: &NodeInterner) -> Statement { - let statement = interner.statement(self); - let span = interner.statement_span(self); - - let kind = match statement { - HirStatement::Let(let_stmt) => { - let pattern = let_stmt.pattern.to_ast(interner); - let r#type = interner.id_type(let_stmt.expression).to_ast(interner); - let expression = let_stmt.expression.to_ast(interner); - StatementKind::Let(LetStatement { pattern, r#type, expression }) - }, - HirStatement::Constrain(constrain) => { - let expr = constrain.0.to_ast(interner); - let message = constrain.2.map(|message| message.to_ast(interner)); - - // TODO: Find difference in usage between Assert & AssertEq - StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) - }, - HirStatement::Assign(_) => todo!(), - HirStatement::For(_) => todo!(), - HirStatement::Break => todo!(), - HirStatement::Continue => todo!(), - HirStatement::Expression(_) => todo!(), - HirStatement::Semi(_) => todo!(), - HirStatement::Error => todo!(), - }; - - Statement { kind, span } - } -} - -impl ExprId { - #[allow(unused)] - fn to_ast(&self, interner: &NodeInterner) -> Expression { - let expression = interner.expression(self); - let location = interner.expr_span(self); - - match expression { - HirExpression::Ident(_) => todo!(), - HirExpression::Literal(_) => todo!(), - HirExpression::Block(_) => todo!(), - HirExpression::Prefix(_) => todo!(), - HirExpression::Infix(_) => todo!(), - HirExpression::Index(_) => todo!(), - HirExpression::Constructor(_) => todo!(), - HirExpression::MemberAccess(_) => todo!(), - HirExpression::Call(_) => todo!(), - HirExpression::MethodCall(_) => todo!(), - HirExpression::Cast(_) => todo!(), - HirExpression::If(_) => todo!(), - HirExpression::Tuple(_) => todo!(), - HirExpression::Lambda(_) => todo!(), - HirExpression::Error => todo!(), - HirExpression::Quote(_) => todo!(), - } - } -} - -impl HirPattern { - fn to_ast(self, interner: &NodeInterner) -> Pattern { - match self { - HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), - HirPattern::Mutable(pattern, location) => { - let pattern = Box::new(pattern.to_ast(interner)); - Pattern::Mutable(pattern, location.span, false) - }, - HirPattern::Tuple(patterns, location) => { - let patterns = vecmap(patterns, |pattern| pattern.to_ast(interner)); - Pattern::Tuple(patterns, location.span) - }, - HirPattern::Struct(typ, patterns, location) => { - let patterns = vecmap(patterns, |(name, pattern)| { - (name, pattern.to_ast(interner)) - }); - let name = match typ.follow_bindings() { - Type::Struct(struct_def, _) => { - let struct_def = struct_def.borrow(); - struct_def.name.0.contents.clone() - }, - // This pass shouldn't error so if the type isn't a struct we just get a string - // representation of any other type and use that. We're relying on name - // resolution to fail later when this Ast is re-converted to Hir. - other => other.to_string(), - }; - // The name span is lost here - let path = Path::from_single(name, location.span); - Pattern::Struct(path, patterns, location.span) - }, - } - } -} - -impl HirIdent { - fn to_ast(&self, interner: &NodeInterner) -> Ident { - let name = interner.definition_name(self.id).to_owned(); - Ident(Spanned::from(self.location.span, name)) - } -} - -impl Type { - fn to_ast(&self, interner: &NodeInterner) -> UnresolvedType { - let typ = match self { - Type::FieldElement => todo!(), - Type::Array(_, _) => todo!(), - Type::Slice(_) => todo!(), - Type::Integer(_, _) => todo!(), - Type::Bool => todo!(), - Type::String(_) => todo!(), - Type::FmtString(_, _) => todo!(), - Type::Unit => todo!(), - Type::Tuple(_) => todo!(), - Type::Struct(_, _) => todo!(), - Type::Alias(_, _) => todo!(), - Type::TypeVariable(_, _) => todo!(), - Type::TraitAsType(_, _, _) => todo!(), - Type::NamedGeneric(_, _) => todo!(), - Type::Function(_, _, _) => todo!(), - Type::MutableReference(_) => todo!(), - Type::Forall(_, _) => todo!(), - Type::Constant(_) => todo!(), - Type::Code => todo!(), - Type::Error => UnresolvedTypeData::Error, - }; - UnresolvedType { typ, span: todo!() } - } -} +mod hir_to_ast; From d4512ace829a217a11a706d04d76c598dffdd2e8 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 11 Apr 2024 15:13:10 -0400 Subject: [PATCH 03/21] Add Hir -> Ast pass --- compiler/noirc_frontend/src/ast/mod.rs | 4 + .../src/hir/comptime/hir_to_ast.rs | 299 +++++++++++++----- .../src/hir/resolution/resolver.rs | 1 + tooling/nargo_fmt/src/rewrite/typ.rs | 1 + 4 files changed, 224 insertions(+), 81 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 4547dc2a176..254ec4a7590 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -112,6 +112,9 @@ pub enum UnresolvedTypeData { /*env:*/ Box, ), + // The type of quoted code for metaprogramming + Code, + Unspecified, // This is for when the user declares a variable without specifying it's type Error, } @@ -200,6 +203,7 @@ impl std::fmt::Display for UnresolvedTypeData { } } MutableReference(element) => write!(f, "&mut {element}"), + Code => write!(f, "Code"), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 56558a1dad3..e91f6d50302 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -1,45 +1,64 @@ use iter_extended::vecmap; -use noirc_errors::{Spanned, Span}; +use noirc_errors::{Span, Spanned}; -use crate::{ConstrainKind, LetStatement, Pattern, Ident, Type, Path, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; -use crate::node_interner::{NodeInterner, StmtId, ExprId}; -use crate::ast::{ Expression, Statement, StatementKind, ConstrainStatement }; -use crate::hir_def::expr::{HirExpression, HirIdent}; -use crate::hir_def::stmt::{HirStatement, HirPattern}; +use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; +use crate::hir_def::expr::{HirArrayLiteral, HirExpression, HirIdent}; +use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; +use crate::macros_api::HirLiteral; +use crate::node_interner::{ExprId, NodeInterner, StmtId}; +use crate::{ + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, + ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, + IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, Type, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, +}; // TODO: // - Full path for idents & types // - Assert/AssertEq information lost // - The type name span is lost in constructor patterns & expressions // - All type spans are lost +// - Type::TypeVariable has no equivalent in the Ast impl StmtId { #[allow(unused)] - fn to_ast(&self, interner: &NodeInterner) -> Statement { - let statement = interner.statement(self); - let span = interner.statement_span(self); - + fn to_ast(self, interner: &NodeInterner) -> Statement { + let statement = interner.statement(&self); + let span = interner.statement_span(&self); + let kind = match statement { HirStatement::Let(let_stmt) => { - let pattern = let_stmt.pattern.to_ast(interner); - let r#type = interner.id_type(let_stmt.expression).to_ast(interner); + let pattern = let_stmt.pattern.into_ast(interner); + let r#type = interner.id_type(let_stmt.expression).to_ast(); let expression = let_stmt.expression.to_ast(interner); StatementKind::Let(LetStatement { pattern, r#type, expression }) - }, + } HirStatement::Constrain(constrain) => { let expr = constrain.0.to_ast(interner); let message = constrain.2.map(|message| message.to_ast(interner)); // TODO: Find difference in usage between Assert & AssertEq StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) - }, - HirStatement::Assign(_) => todo!(), - HirStatement::For(_) => todo!(), - HirStatement::Break => todo!(), - HirStatement::Continue => todo!(), - HirStatement::Expression(_) => todo!(), - HirStatement::Semi(_) => todo!(), - HirStatement::Error => todo!(), + } + HirStatement::Assign(assign) => StatementKind::Assign(AssignStatement { + lvalue: assign.lvalue.into_ast(interner), + expression: assign.expression.to_ast(interner), + }), + HirStatement::For(for_stmt) => StatementKind::For(ForLoopStatement { + identifier: for_stmt.identifier.to_ast(interner), + range: ForRange::Range( + for_stmt.start_range.to_ast(interner), + for_stmt.end_range.to_ast(interner), + ), + block: for_stmt.block.to_ast(interner), + span, + }), + HirStatement::Break => StatementKind::Break, + HirStatement::Continue => StatementKind::Continue, + HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), + HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), + HirStatement::Error => StatementKind::Error, }; Statement { kind, span } @@ -48,52 +67,129 @@ impl StmtId { impl ExprId { #[allow(unused)] - fn to_ast(&self, interner: &NodeInterner) -> Expression { - let expression = interner.expression(self); - let location = interner.expr_span(self); - - match expression { - HirExpression::Ident(_) => todo!(), - HirExpression::Literal(_) => todo!(), - HirExpression::Block(_) => todo!(), - HirExpression::Prefix(_) => todo!(), - HirExpression::Infix(_) => todo!(), - HirExpression::Index(_) => todo!(), - HirExpression::Constructor(_) => todo!(), - HirExpression::MemberAccess(_) => todo!(), - HirExpression::Call(_) => todo!(), - HirExpression::MethodCall(_) => todo!(), - HirExpression::Cast(_) => todo!(), - HirExpression::If(_) => todo!(), - HirExpression::Tuple(_) => todo!(), - HirExpression::Lambda(_) => todo!(), - HirExpression::Error => todo!(), - HirExpression::Quote(_) => todo!(), - } + fn to_ast(self, interner: &NodeInterner) -> Expression { + let expression = interner.expression(&self); + let span = interner.expr_span(&self); + + let kind = match expression { + HirExpression::Ident(ident) => { + let path = Path::from_ident(ident.to_ast(interner)); + ExpressionKind::Variable(path) + } + HirExpression::Literal(HirLiteral::Array(array)) => { + let array = array.into_ast(interner, span); + ExpressionKind::Literal(Literal::Array(array)) + } + HirExpression::Literal(HirLiteral::Slice(array)) => { + let array = array.into_ast(interner, span); + ExpressionKind::Literal(Literal::Slice(array)) + } + HirExpression::Literal(HirLiteral::Bool(value)) => { + ExpressionKind::Literal(Literal::Bool(value)) + } + HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + ExpressionKind::Literal(Literal::Integer(value, sign)) + } + HirExpression::Literal(HirLiteral::Str(string)) => { + ExpressionKind::Literal(Literal::Str(string)) + } + HirExpression::Literal(HirLiteral::FmtStr(string, _exprs)) => { + // TODO: Is throwing away the exprs here valid? + ExpressionKind::Literal(Literal::FmtStr(string)) + } + HirExpression::Literal(HirLiteral::Unit) => ExpressionKind::Literal(Literal::Unit), + HirExpression::Block(expr) => { + let statements = vecmap(expr.statements, |statement| statement.to_ast(interner)); + ExpressionKind::Block(BlockExpression { statements }) + } + HirExpression::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { + operator: prefix.operator, + rhs: prefix.rhs.to_ast(interner), + })), + HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { + lhs: infix.lhs.to_ast(interner), + operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + rhs: infix.rhs.to_ast(interner), + })), + HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { + collection: index.collection.to_ast(interner), + index: index.index.to_ast(interner), + })), + HirExpression::Constructor(constructor) => { + let type_name = constructor.r#type.borrow().name.to_string(); + let type_name = Path::from_single(type_name, span); + let fields = + vecmap(constructor.fields, |(name, expr)| (name, expr.to_ast(interner))); + + ExpressionKind::Constructor(Box::new(ConstructorExpression { type_name, fields })) + } + HirExpression::MemberAccess(access) => { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: access.lhs.to_ast(interner), + rhs: access.rhs, + })) + } + HirExpression::Call(call) => { + let func = Box::new(call.func.to_ast(interner)); + let arguments = vecmap(call.arguments, |arg| arg.to_ast(interner)); + ExpressionKind::Call(Box::new(CallExpression { func, arguments })) + } + HirExpression::MethodCall(method_call) => { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: method_call.object.to_ast(interner), + method_name: method_call.method, + arguments: vecmap(method_call.arguments, |arg| arg.to_ast(interner)), + })) + } + HirExpression::Cast(cast) => { + let lhs = cast.lhs.to_ast(interner); + let r#type = cast.r#type.to_ast(); + ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type })) + } + HirExpression::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { + condition: if_expr.condition.to_ast(interner), + consequence: if_expr.consequence.to_ast(interner), + alternative: if_expr.alternative.map(|expr| expr.to_ast(interner)), + })), + HirExpression::Tuple(fields) => { + ExpressionKind::Tuple(vecmap(fields, |field| field.to_ast(interner))) + } + HirExpression::Lambda(lambda) => { + let parameters = vecmap(lambda.parameters, |(pattern, typ)| { + (pattern.into_ast(interner), typ.to_ast()) + }); + let return_type = lambda.return_type.to_ast(); + let body = lambda.body.to_ast(interner); + ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) + } + HirExpression::Quote(block) => ExpressionKind::Quote(block), + HirExpression::Error => ExpressionKind::Error, + }; + + Expression::new(kind, span) } } impl HirPattern { - fn to_ast(self, interner: &NodeInterner) -> Pattern { + fn into_ast(self, interner: &NodeInterner) -> Pattern { match self { HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), HirPattern::Mutable(pattern, location) => { - let pattern = Box::new(pattern.to_ast(interner)); + let pattern = Box::new(pattern.into_ast(interner)); Pattern::Mutable(pattern, location.span, false) - }, + } HirPattern::Tuple(patterns, location) => { - let patterns = vecmap(patterns, |pattern| pattern.to_ast(interner)); + let patterns = vecmap(patterns, |pattern| pattern.into_ast(interner)); Pattern::Tuple(patterns, location.span) - }, + } HirPattern::Struct(typ, patterns, location) => { - let patterns = vecmap(patterns, |(name, pattern)| { - (name, pattern.to_ast(interner)) - }); + let patterns = + vecmap(patterns, |(name, pattern)| (name, pattern.into_ast(interner))); let name = match typ.follow_bindings() { Type::Struct(struct_def, _) => { let struct_def = struct_def.borrow(); struct_def.name.0.contents.clone() - }, + } // This pass shouldn't error so if the type isn't a struct we just get a string // representation of any other type and use that. We're relying on name // resolution to fail later when this Ast is re-converted to Hir. @@ -102,7 +198,7 @@ impl HirPattern { // The name span is lost here let path = Path::from_single(name, location.span); Pattern::Struct(path, patterns, location.span) - }, + } } } } @@ -115,75 +211,73 @@ impl HirIdent { } impl Type { - fn to_ast(&self, interner: &NodeInterner) -> UnresolvedType { + fn to_ast(&self) -> UnresolvedType { let typ = match self { Type::FieldElement => UnresolvedTypeData::FieldElement, Type::Array(length, element) => { let length = length.to_type_expression(); - let element = Box::new(element.to_ast(interner)); + let element = Box::new(element.to_ast()); UnresolvedTypeData::Array(length, element) } Type::Slice(element) => { - let element = Box::new(element.to_ast(interner)); + let element = Box::new(element.to_ast()); UnresolvedTypeData::Slice(element) - }, - Type::Integer(sign, bit_size) => { - UnresolvedTypeData::Integer(*sign, *bit_size) - }, + } + Type::Integer(sign, bit_size) => UnresolvedTypeData::Integer(*sign, *bit_size), Type::Bool => UnresolvedTypeData::Bool, Type::String(length) => { let length = length.to_type_expression(); UnresolvedTypeData::String(Some(length)) - }, + } Type::FmtString(length, element) => { let length = length.to_type_expression(); - let element = Box::new(element.to_ast(interner)); + let element = Box::new(element.to_ast()); UnresolvedTypeData::FormatString(length, element) - }, + } Type::Unit => UnresolvedTypeData::Unit, Type::Tuple(fields) => { - let fields = vecmap(fields, |field| field.to_ast(interner)); + let fields = vecmap(fields, |field| field.to_ast()); UnresolvedTypeData::Tuple(fields) - }, + } Type::Struct(def, generics) => { let struct_def = def.borrow(); - let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let generics = vecmap(generics, |generic| generic.to_ast()); let name = Path::from_ident(struct_def.name.clone()); UnresolvedTypeData::Named(name, generics, false) - }, + } Type::Alias(type_def, generics) => { // Keep the alias name instead of expanding this in case the // alias' definition was changed let type_def = type_def.borrow(); - let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let generics = vecmap(generics, |generic| generic.to_ast()); let name = Path::from_ident(type_def.name.clone()); UnresolvedTypeData::Named(name, generics, false) - }, - Type::TypeVariable(_, _) => todo!(), + } + Type::TypeVariable(_, _) => todo!("Convert Type::TypeVariable Hir -> Ast"), Type::TraitAsType(_, name, generics) => { - let generics = vecmap(generics, |generic| generic.to_ast(interner)); + let generics = vecmap(generics, |generic| generic.to_ast()); let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::TraitAsType(name, generics) - }, + } Type::NamedGeneric(_, name) => { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::TraitAsType(name, Vec::new()) - }, + } Type::Function(args, ret, env) => { - let args = vecmap(args, |arg| arg.to_ast(interner)); - let ret = Box::new(ret.to_ast(interner)); - let env = Box::new(env.to_ast(interner)); + let args = vecmap(args, |arg| arg.to_ast()); + let ret = Box::new(ret.to_ast()); + let env = Box::new(env.to_ast()); UnresolvedTypeData::Function(args, ret, env) - }, + } Type::MutableReference(element) => { - let element = Box::new(element.to_ast(interner)); + let element = Box::new(element.to_ast()); UnresolvedTypeData::MutableReference(element) - }, + } // Type::Forall is only for generic functions which don't store a type // in their Ast so they don't need to call to_ast for their Forall type. // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use // this to ignore this case since it shouldn't be needed anyway. - Type::Forall(_, typ) => return typ.to_ast(interner), + Type::Forall(_, typ) => return typ.to_ast(), Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), Type::Code => UnresolvedTypeData::Code, Type::Error => UnresolvedTypeData::Error, @@ -200,9 +294,52 @@ impl Type { Type::NamedGeneric(_, name) => { let path = Path::from_single(name.as_ref().clone(), span); UnresolvedTypeExpression::Variable(path) - }, + } // TODO: This should be turned into a proper error. other => panic!("Cannot represent {other:?} as type expression"), } } } + +impl HirLValue { + fn into_ast(self, interner: &NodeInterner) -> LValue { + match self { + HirLValue::Ident(ident, _) => LValue::Ident(ident.to_ast(interner)), + HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { + let object = Box::new(object.into_ast(interner)); + LValue::MemberAccess { object, field_name, span: location.span } + } + HirLValue::Index { array, index, typ: _, location } => { + let array = Box::new(array.into_ast(interner)); + let index = index.to_ast(interner); + LValue::Index { array, index, span: location.span } + } + HirLValue::Dereference { lvalue, element_type: _, location } => { + let lvalue = Box::new(lvalue.into_ast(interner)); + LValue::Dereference(lvalue, location.span) + } + } + } +} + +impl HirArrayLiteral { + fn into_ast(self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + match self { + HirArrayLiteral::Standard(elements) => { + ArrayLiteral::Standard(vecmap(elements, |element| element.to_ast(interner))) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let repeated_element = Box::new(repeated_element.to_ast(interner)); + let length = match length { + Type::Constant(length) => { + let literal = Literal::Integer((length as u128).into(), false); + let kind = ExpressionKind::Literal(literal); + Box::new(Expression::new(kind, span)) + } + other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + }; + ArrayLiteral::Repeated { repeated_element, length } + } + } + } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f2b8212db7a..cb15510ca28 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -501,6 +501,7 @@ impl<'a> Resolver<'a> { let fields = self.resolve_type_inner(*fields, new_variables); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } + Code => Type::Code, Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 922337cdb74..980d02ee5dc 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -64,6 +64,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::String(_) | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::Code | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), UnresolvedTypeData::Error => unreachable!(), } From 064a0745961d826c1f6ac6690959ef3a64aa302c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 12 Apr 2024 10:09:58 -0400 Subject: [PATCH 04/21] Add weird attributes field even though it was compiling locally without it --- compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index e91f6d50302..8ffcbce7d62 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -32,7 +32,12 @@ impl StmtId { let pattern = let_stmt.pattern.into_ast(interner); let r#type = interner.id_type(let_stmt.expression).to_ast(); let expression = let_stmt.expression.to_ast(interner); - StatementKind::Let(LetStatement { pattern, r#type, expression }) + StatementKind::Let(LetStatement { + pattern, + r#type, + expression, + attributes: Vec::new(), + }) } HirStatement::Constrain(constrain) => { let expr = constrain.0.to_ast(interner); From 99b7839153b8df116bdc0314279acf9413dada89 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 12 Apr 2024 15:42:40 -0400 Subject: [PATCH 05/21] Start work on interpreter --- Cargo.lock | 1 + Cargo.toml | 1 + compiler/noirc_evaluator/Cargo.toml | 4 +- compiler/noirc_frontend/Cargo.toml | 1 + compiler/noirc_frontend/src/ast/expression.rs | 5 + .../src/hir/comptime/interpreter.rs | 613 ++++++++++++++++++ .../noirc_frontend/src/hir/comptime/mod.rs | 1 + .../noirc_frontend/src/hir/type_check/mod.rs | 3 +- .../noirc_frontend/src/hir_def/function.rs | 4 +- compiler/noirc_frontend/src/node_interner.rs | 4 + 10 files changed, 631 insertions(+), 6 deletions(-) create mode 100644 compiler/noirc_frontend/src/hir/comptime/interpreter.rs diff --git a/Cargo.lock b/Cargo.lock index e62f966b8fd..2cfa14cf795 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3086,6 +3086,7 @@ dependencies = [ "base64 0.21.2", "chumsky", "fm", + "im", "iter-extended", "noirc_errors", "noirc_printable_type", diff --git a/Cargo.toml b/Cargo.toml index 5dd453415aa..dc15f3dac6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,6 +124,7 @@ tempfile = "3.6.0" jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" +im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" tracing-web = "0.1.3" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index fad7c3c309e..fb2f003aa56 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -15,7 +15,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" -im = { version = "15.1", features = ["serde"] } +im.workspace = true serde.workspace = true tracing.workspace = true -chrono = "0.4.37" \ No newline at end of file +chrono = "0.4.37" diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 03b92e15032..4f2fff91ccb 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -17,6 +17,7 @@ iter-extended.workspace = true chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true +im.workspace = true serde_json.workspace = true serde.workspace = true rustc-hash = "1.1.0" diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 0e5919bf7db..755739af8fe 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -387,6 +387,9 @@ pub struct FunctionDefinition { /// True if this function was defined with the 'unconstrained' keyword pub is_unconstrained: bool, + /// True if this function was defined with the 'comptime' keyword + pub is_comptime: bool, + /// Indicate if this function was defined with the 'pub' keyword pub visibility: ItemVisibility, @@ -679,10 +682,12 @@ impl FunctionDefinition { span: ident.span().merge(unresolved_type.span.unwrap()), }) .collect(); + FunctionDefinition { name: name.clone(), attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, visibility: ItemVisibility::Private, generics: generics.clone(), parameters: p, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs new file mode 100644 index 00000000000..7d09f6cf493 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -0,0 +1,613 @@ +use std::rc::Rc; + +use acvm::FieldElement; +use im::Vector; +use iter_extended::try_vecmap; +use noirc_errors::Location; +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{Shared, node_interner::{DefinitionId, FuncId, ExprId}, macros_api::{NodeInterner, HirExpression, HirLiteral}, BlockExpression, Type, hir_def::{stmt::HirPattern, expr::{HirArrayLiteral, HirBlockExpression, HirPrefixExpression, HirInfixExpression, HirIndexExpression, HirConstructorExpression, HirMemberAccess, HirCallExpression, HirMethodCallExpression, HirCastExpression, HirIfExpression, HirLambda}}, FunctionKind, IntegerBitSize, Signedness, BinaryOpKind}; + + +struct Interpreter<'interner> { + /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner + interner: &'interner mut NodeInterner, + + /// Each value currently in scope in the interpreter. + /// Each element of the Vec represents a scope with every scope together making + /// up all currently visible definitions. + scopes: Vec>, + + /// True if we've expanded any macros into any functions and will need + /// to redo name resolution & type checking for that function. + changed_functions: FxHashSet, + + /// True if we've expanded any macros into global scope and will need + /// to redo name resolution & type checking for everything. + changed_globally: bool, +} + +#[derive(Debug, Clone)] +enum Value { + Unit, + Bool(bool), + Field(FieldElement), + I8(i8), + I32(i32), + I64(i64), + U8(u8), + U32(u32), + U64(u64), + String(Rc), + Function(FuncId), + Tuple(Vec), + Struct(FxHashMap, Value>), + Pointer(Shared), + Array(Vector), + Slice(Vector), + Code(Rc), +} + +enum InterpreterError { + ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, + TypeMismatch { expected: Type, value: Value }, + NoValueForId(DefinitionId), + IntegerOutOfRangeForType(FieldElement, Type), + UnableToEvaluateTypeToInteger(Type), + ErrorNodeEncountered { location: Location }, +} + +type IResult = std::result::Result; + +impl<'a> Interpreter<'a> { + fn call_function(&mut self, function: FuncId, arguments: Vec, call_location: Location) -> IResult { + let modifiers = self.interner.function_modifiers(&function); + assert!(modifiers.is_comptime, "Tried to evaluate non-comptime function!"); + + self.push_scope(); + + let meta = self.interner.function_meta(&function); + + if meta.parameters.len() != arguments.len() { + return Err(InterpreterError::ArgumentCountMismatch { expected: meta.parameters.len(), actual: arguments.len(), call_location }); + } + + for ((parameter, typ, _), argument) in meta.parameters.0.iter().zip(arguments) { + self.define_pattern(parameter, typ, argument); + } + + match meta.kind { + FunctionKind::Normal => (), + other => todo!("Evaluation for {:?} is unimplemented", meta.kind), + } + + let function_body = self.interner.function(&function).as_expr(); + let result = self.evaluate(function_body)?; + + self.pop_scope(); + Ok(result) + } + + fn push_scope(&mut self) { + self.scopes.push(FxHashMap::default()); + } + + fn pop_scope(&mut self) { + self.scopes.pop(); + } + + fn current_scope(&mut self) -> &mut FxHashMap { + self.scopes.last_mut().unwrap() + } + + fn define_pattern(&self, pattern: &HirPattern, typ: &Type, argument: Value) -> IResult<()> { + match pattern { + HirPattern::Identifier(identifier) => self.define(identifier.id, typ, argument), + HirPattern::Mutable(pattern, _) => self.define_pattern(pattern, typ, argument), + HirPattern::Tuple(pattern_fields, _) => { + self.type_check(typ, &argument)?; + + if let (Value::Tuple(fields), Type::Tuple(type_fields)) = (argument, typ) { + // The type check already ensures fields.len() == type_fields.len() + if fields.len() == pattern_fields.len() { + for ((pattern, typ), argument) in pattern_fields.iter().zip(type_fields).zip(fields) { + self.define_pattern(pattern, typ, argument)?; + } + return Ok(()); + } + } + + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) + }, + HirPattern::Struct(struct_type, pattern_fields, _) => { + self.type_check(typ, &argument)?; + self.type_check(struct_type, &argument)?; + + if let (Value::Struct(fields), Type::Struct(struct_def, generics)) = (argument, typ) { + let struct_def = struct_def.borrow(); + + // The type check already ensures fields.len() == type_fields.len() + if fields.len() == pattern_fields.len() { + for (field_name, field_pattern) in pattern_fields { + let field = fields.get(&field_name.0.contents).unwrap_or_else(|| { + panic!("Expected Struct value {argument:?} to have a field named '{field_name}'") + }); + + let field_type = struct_def.get_field(&field_name.0.contents, generics).unwrap_or_else(|| { + panic!("Expected struct type {typ} to have a field named '{field_name}'") + }).0; + + self.define_pattern(field_pattern, &field_type, field.clone())?; + } + return Ok(()); + } + } + + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) + }, + } + } + + fn define(&self, id: DefinitionId, typ: &Type, argument: Value) -> IResult<()> { + self.type_check(typ, &argument)?; + self.current_scope().insert(id, argument); + Ok(()) + } + + fn lookup(&self, id: DefinitionId) -> IResult { + self.current_scope().get(&id).cloned().ok_or_else(|| { + InterpreterError::NoValueForId(id) + }) + } + + /// Do a quick, shallow type check to catch some obviously wrong cases. + /// The interpreter generally relies on expressions to already be well-typed + /// but this helps catch bugs. It is also worth noting expression types may not + /// correlate 1-1 with non-comptime code. For example, comptime code also allows + /// pointers and unsized data types like strings and (unbounded) vectors. + fn type_check(&self, typ: &Type, value: &Value) -> IResult<()> { + let typ = typ.follow_bindings(); + use crate::Signedness::*; + use crate::IntegerBitSize::*; + + match (value, &typ) { + (Value::Unit, Type::Unit) => (), + (Value::Bool(_), Type::Bool) => (), + (Value::Field(_), Type::FieldElement) => (), + (Value::I8(_), Type::Integer(Signed, Eight)) => (), + (Value::I32(_), Type::Integer(Signed, ThirtyTwo)) => (), + (Value::I64(_), Type::Integer(Signed, SixtyFour)) => (), + (Value::U8(_), Type::Integer(Unsigned, Eight)) => (), + (Value::U32(_), Type::Integer(Unsigned, ThirtyTwo)) => (), + (Value::U64(_), Type::Integer(Unsigned, SixtyFour)) => (), + (Value::String(_), Type::String(_)) => (), + (Value::Function(_), Type::Function(..)) => (), + (Value::Tuple(fields1), Type::Tuple(fields2)) if fields1.len() == fields2.len() => (), + (Value::Struct(_), _) => (), + (Value::Array(_), Type::Array(..)) => (), + (Value::Slice(_), Type::Slice(_)) => (), + (Value::Pointer(_), _) => (), + (Value::Code(_), Type::Code) => (), + _ => return Err(InterpreterError::TypeMismatch { expected: typ, value: value.clone() }), + } + + Ok(()) + } + + /// Evaluate an expression and return the result + fn evaluate(&mut self, id: ExprId) -> IResult { + match self.interner.expression(&id) { + HirExpression::Ident(ident) => self.lookup(ident.id), + HirExpression::Literal(literal) => self.evaluate_literal(literal, id), + HirExpression::Block(block) => self.evaluate_block(block), + HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix), + HirExpression::Infix(infix) => self.evaluate_infix(infix), + HirExpression::Index(index) => self.evaluate_index(index), + HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor), + HirExpression::MemberAccess(access) => self.evaluate_access(access), + HirExpression::Call(call) => self.evaluate_call(call), + HirExpression::MethodCall(call) => self.evaluate_method_call(call), + HirExpression::Cast(cast) => self.evaluate_cast(cast), + HirExpression::If(if_) => self.evaluate_if(if_), + HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), + HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda), + HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), + HirExpression::Error => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::ErrorNodeEncountered { location }) + } + } + } + + fn evaluate_literal(&mut self, literal: HirLiteral, id: ExprId) -> IResult { + match literal { + HirLiteral::Unit => Ok(Value::Unit), + HirLiteral::Bool(value) => Ok(Value::Bool(value)), + HirLiteral::Integer(value, is_negative) => self.evaluate_integer(value, is_negative, id), + HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), + HirLiteral::FmtStr(_, _) => todo!("Evaluate format strings"), + HirLiteral::Array(array) => self.evaluate_array(array), + HirLiteral::Slice(array) => self.evaluate_slice(array), + } + } + + fn evaluate_integer(&self, value: FieldElement, is_negative: bool, id: ExprId) -> IResult { + let typ = self.interner.id_type(id).follow_bindings(); + if let Type::Integer(sign, bit_size) = &typ { + match (sign, bit_size) { + (Signedness::Unsigned, IntegerBitSize::One) => panic!("u1 is not supported by the interpreter"), + (Signedness::Unsigned, IntegerBitSize::Eight) => { + let value: u8 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; + Ok(Value::U8(value)) + }, + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { + let value: u32 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; + Ok(Value::U32(value)) + }, + (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { + let value: u64 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + Ok(Value::U64(value)) + }, + (Signedness::Signed, IntegerBitSize::One) => panic!("i1 is not supported by the interpreter"), + (Signedness::Signed, IntegerBitSize::Eight) => { + let value: i8 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { -value } else { value }; + Ok(Value::I8(value)) + }, + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { + let value: i32 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { -value } else { value }; + Ok(Value::I32(value)) + }, + (Signedness::Signed, IntegerBitSize::SixtyFour) => { + let value: i64 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { + InterpreterError::IntegerOutOfRangeForType(value, typ) + })?; + let value = if is_negative { -value } else { value }; + Ok(Value::I64(value)) + }, + } + } else { + unreachable!("Non-integer integer literal of type {typ}") + } + } + + fn evaluate_block(&self, block: HirBlockExpression) -> IResult { + todo!() + } + + fn evaluate_array(&self, array: HirArrayLiteral) -> IResult { + match array { + HirArrayLiteral::Standard(elements) => { + let elements = elements.into_iter().map(|id| self.evaluate(id)).collect::>>()?; + Ok(Value::Array(elements)) + }, + HirArrayLiteral::Repeated { repeated_element, length } => { + let element = self.evaluate(repeated_element)?; + + if let Some(length) = length.evaluate_to_u64() { + let elements = (0..length).map(|_| element.clone()).collect(); + Ok(Value::Array(elements)) + } else { + Err(InterpreterError::UnableToEvaluateTypeToInteger(length)) + } + }, + } + } + + fn evaluate_slice(&self, array: HirArrayLiteral) -> IResult { + self.evaluate_array(array).map(|value| match value { + Value::Array(array) => Value::Slice(array), + other => unreachable!("Non-array value returned from evaluate array: {other:?}"), + }) + } + + fn evaluate_prefix(&mut self, prefix: HirPrefixExpression) -> IResult { + let rhs = self.evaluate(prefix.rhs)?; + match prefix.operator { + crate::UnaryOp::Minus => { + match rhs { + Value::Field(value) => Ok(Value::Field(FieldElement::zero() - value)), + Value::I8(value) => Ok(Value::I8(-value)), + Value::I32(value) => Ok(Value::I32(-value)), + Value::I64(value) => Ok(Value::I64(-value)), + Value::U8(value) => Ok(Value::U8(0-value)), + Value::U32(value) => Ok(Value::U32(0-value)), + Value::U64(value) => Ok(Value::U64(0-value)), + other => panic!("Invalid value for unary minus operation: {other:?}"), + } + }, + crate::UnaryOp::Not => { + match rhs { + Value::Bool(value) => Ok(Value::Bool(!value)), + Value::I8(value) => Ok(Value::I8(!value)), + Value::I32(value) => Ok(Value::I32(!value)), + Value::I64(value) => Ok(Value::I64(!value)), + Value::U8(value) => Ok(Value::U8(!value)), + Value::U32(value) => Ok(Value::U32(!value)), + Value::U64(value) => Ok(Value::U64(!value)), + other => panic!("Invalid value for unary not operation: {other:?}"), + } + }, + crate::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), + crate::UnaryOp::Dereference { implicitly_added: _ } => { + match rhs { + Value::Pointer(element) => Ok(element.borrow().clone()), + other => panic!("Cannot dereference {other:?}"), + } + }, + } + } + + fn evaluate_infix(&mut self, infix: HirInfixExpression) -> IResult { + let lhs = self.evaluate(infix.lhs)?; + let rhs = self.evaluate(infix.rhs)?; + + // TODO: Need to account for operator overloading + match infix.operator.kind { + BinaryOpKind::Add => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs + rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs + rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), + (lhs, rhs) => panic!("Operator (+) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Subtract => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs - rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs - rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), + (lhs, rhs) => panic!("Operator (-) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Multiply => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs * rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs * rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), + (lhs, rhs) => panic!("Operator (*) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Divide => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs / rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs / rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), + (lhs, rhs) => panic!("Operator (/) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Equal => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (lhs, rhs) => panic!("Operator (==) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::NotEqual => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (lhs, rhs) => panic!("Operator (!=) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Less => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (lhs, rhs) => panic!("Operator (<) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::LessEqual => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (lhs, rhs) => panic!("Operator (<=) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Greater => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (lhs, rhs) => panic!("Operator (>) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::GreaterEqual => { + match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (lhs, rhs) => panic!("Operator (>=) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::And => { + match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs & rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs & rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), + (lhs, rhs) => panic!("Operator (&) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Or => { + match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs | rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs | rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), + (lhs, rhs) => panic!("Operator (|) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Xor => { + match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs ^ rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs ^ rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), + (lhs, rhs) => panic!("Operator (^) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::ShiftRight => { + match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs >> rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs >> rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), + (lhs, rhs) => panic!("Operator (>>) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::ShiftLeft => { + match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs << rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs << rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), + (lhs, rhs) => panic!("Operator (<<) invalid for values {lhs:?} and {rhs:?}"), + } + }, + BinaryOpKind::Modulo => { + match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs % rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs % rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), + (lhs, rhs) => panic!("Operator (%) invalid for values {lhs:?} and {rhs:?}"), + } + }, + } + } + + fn evaluate_index(&self, index: HirIndexExpression) -> IResult { + let collection = match self.evaluate(index.collection)? { + Value::Array(array) => array, + Value::Slice(array) => array, + other => panic!("Cannot index into {other:?}"), + }; + + let index = match self.evaluate(index.index)? { + Value::Field(value) => value.try_to_u64().expect("index could not fit into u64") as usize, + Value::I8(value) => value as usize, + Value::I32(value) => value as usize, + Value::I64(value) => value as usize, + Value::U8(value) => value as usize, + Value::U32(value) => value as usize, + Value::U64(value) => value as usize, + other => panic!("Cannot use {other:?} as an index"), + }; + + Ok(collection[index].clone()) + } + + fn evaluate_constructor(&mut self, constructor: HirConstructorExpression) -> IResult { + todo!() + } + + fn evaluate_access(&mut self, access: HirMemberAccess) -> IResult { + let fields = match self.evaluate(access.lhs)? { + Value::Struct(fields) => fields, + other => panic!("Cannot access fields of a non-struct value: {other:?}"), + }; + + Ok(fields.get(&access.rhs.0.contents).unwrap_or_else(|| { + panic!("Expected struct to have field {}", access.rhs) + }).clone()) + } + + fn evaluate_call(&mut self, call: HirCallExpression) -> IResult { + todo!() + } + + fn evaluate_method_call(&mut self, call: HirMethodCallExpression) -> IResult { + todo!() + } + + fn evaluate_cast(&mut self, cast: HirCastExpression) -> IResult { + todo!() + } + + fn evaluate_if(&mut self, r#if: HirIfExpression) -> IResult { + todo!() + } + + fn evaluate_tuple(&mut self, tuple: Vec) -> IResult { + let fields = try_vecmap(tuple, |field| self.evaluate(field))?; + Ok(Value::Tuple(fields)) + } + + fn evaluate_lambda(&mut self, lambda: HirLambda) -> IResult { + todo!() + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 91621c857cf..a8ced7d539e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1 +1,2 @@ mod hir_to_ast; +mod interpreter; diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index cdfc19b3a33..bf20c89f5e1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -51,8 +51,7 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec &ExprId { - &self.0 + pub const fn as_expr(&self) -> ExprId { + self.0 } pub fn block(&self, interner: &NodeInterner) -> HirBlockExpression { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 84eb2d77315..d310c8ddec4 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -242,6 +242,8 @@ pub struct FunctionModifiers { pub attributes: Attributes, pub is_unconstrained: bool, + + pub is_comptime: bool, } impl FunctionModifiers { @@ -254,6 +256,7 @@ impl FunctionModifiers { visibility: ItemVisibility::Public, attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, } } } @@ -759,6 +762,7 @@ impl NodeInterner { visibility: function.visibility, attributes: function.attributes.clone(), is_unconstrained: function.is_unconstrained, + is_comptime: function.is_comptime, }; self.push_function_definition(id, modifiers, module, location) } From 22327937b5ba0df3d4df6f9ec4401185d63d0b6e Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 12 Apr 2024 15:52:18 -0400 Subject: [PATCH 06/21] Evaluate if exprs --- .../src/hir/comptime/interpreter.rs | 525 +++++++++--------- 1 file changed, 275 insertions(+), 250 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 7d09f6cf493..b0c621c8e45 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -6,8 +6,19 @@ use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{Shared, node_interner::{DefinitionId, FuncId, ExprId}, macros_api::{NodeInterner, HirExpression, HirLiteral}, BlockExpression, Type, hir_def::{stmt::HirPattern, expr::{HirArrayLiteral, HirBlockExpression, HirPrefixExpression, HirInfixExpression, HirIndexExpression, HirConstructorExpression, HirMemberAccess, HirCallExpression, HirMethodCallExpression, HirCastExpression, HirIfExpression, HirLambda}}, FunctionKind, IntegerBitSize, Signedness, BinaryOpKind}; - +use crate::{ + hir_def::{ + expr::{ + HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, + HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, + HirLambda, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, + }, + stmt::HirPattern, + }, + macros_api::{HirExpression, HirLiteral, NodeInterner}, + node_interner::{DefinitionId, ExprId, FuncId}, + BinaryOpKind, BlockExpression, FunctionKind, IntegerBitSize, Shared, Signedness, Type, +}; struct Interpreter<'interner> { /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner @@ -60,7 +71,12 @@ enum InterpreterError { type IResult = std::result::Result; impl<'a> Interpreter<'a> { - fn call_function(&mut self, function: FuncId, arguments: Vec, call_location: Location) -> IResult { + fn call_function( + &mut self, + function: FuncId, + arguments: Vec, + call_location: Location, + ) -> IResult { let modifiers = self.interner.function_modifiers(&function); assert!(modifiers.is_comptime, "Tried to evaluate non-comptime function!"); @@ -69,7 +85,11 @@ impl<'a> Interpreter<'a> { let meta = self.interner.function_meta(&function); if meta.parameters.len() != arguments.len() { - return Err(InterpreterError::ArgumentCountMismatch { expected: meta.parameters.len(), actual: arguments.len(), call_location }); + return Err(InterpreterError::ArgumentCountMismatch { + expected: meta.parameters.len(), + actual: arguments.len(), + call_location, + }); } for ((parameter, typ, _), argument) in meta.parameters.0.iter().zip(arguments) { @@ -110,7 +130,9 @@ impl<'a> Interpreter<'a> { if let (Value::Tuple(fields), Type::Tuple(type_fields)) = (argument, typ) { // The type check already ensures fields.len() == type_fields.len() if fields.len() == pattern_fields.len() { - for ((pattern, typ), argument) in pattern_fields.iter().zip(type_fields).zip(fields) { + for ((pattern, typ), argument) in + pattern_fields.iter().zip(type_fields).zip(fields) + { self.define_pattern(pattern, typ, argument)?; } return Ok(()); @@ -118,12 +140,13 @@ impl<'a> Interpreter<'a> { } Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) - }, + } HirPattern::Struct(struct_type, pattern_fields, _) => { self.type_check(typ, &argument)?; self.type_check(struct_type, &argument)?; - if let (Value::Struct(fields), Type::Struct(struct_def, generics)) = (argument, typ) { + if let (Value::Struct(fields), Type::Struct(struct_def, generics)) = (argument, typ) + { let struct_def = struct_def.borrow(); // The type check already ensures fields.len() == type_fields.len() @@ -144,7 +167,7 @@ impl<'a> Interpreter<'a> { } Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) - }, + } } } @@ -155,9 +178,7 @@ impl<'a> Interpreter<'a> { } fn lookup(&self, id: DefinitionId) -> IResult { - self.current_scope().get(&id).cloned().ok_or_else(|| { - InterpreterError::NoValueForId(id) - }) + self.current_scope().get(&id).cloned().ok_or_else(|| InterpreterError::NoValueForId(id)) } /// Do a quick, shallow type check to catch some obviously wrong cases. @@ -167,8 +188,8 @@ impl<'a> Interpreter<'a> { /// pointers and unsized data types like strings and (unbounded) vectors. fn type_check(&self, typ: &Type, value: &Value) -> IResult<()> { let typ = typ.follow_bindings(); - use crate::Signedness::*; use crate::IntegerBitSize::*; + use crate::Signedness::*; match (value, &typ) { (Value::Unit, Type::Unit) => (), @@ -188,7 +209,9 @@ impl<'a> Interpreter<'a> { (Value::Slice(_), Type::Slice(_)) => (), (Value::Pointer(_), _) => (), (Value::Code(_), Type::Code) => (), - _ => return Err(InterpreterError::TypeMismatch { expected: typ, value: value.clone() }), + _ => { + return Err(InterpreterError::TypeMismatch { expected: typ, value: value.clone() }) + } } Ok(()) @@ -223,7 +246,9 @@ impl<'a> Interpreter<'a> { match literal { HirLiteral::Unit => Ok(Value::Unit), HirLiteral::Bool(value) => Ok(Value::Bool(value)), - HirLiteral::Integer(value, is_negative) => self.evaluate_integer(value, is_negative, id), + HirLiteral::Integer(value, is_negative) => { + self.evaluate_integer(value, is_negative, id) + } HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), HirLiteral::FmtStr(_, _) => todo!("Evaluate format strings"), HirLiteral::Array(array) => self.evaluate_array(array), @@ -231,54 +256,69 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_integer(&self, value: FieldElement, is_negative: bool, id: ExprId) -> IResult { + fn evaluate_integer( + &self, + value: FieldElement, + is_negative: bool, + id: ExprId, + ) -> IResult { let typ = self.interner.id_type(id).follow_bindings(); if let Type::Integer(sign, bit_size) = &typ { match (sign, bit_size) { - (Signedness::Unsigned, IntegerBitSize::One) => panic!("u1 is not supported by the interpreter"), + (Signedness::Unsigned, IntegerBitSize::One) => { + panic!("u1 is not supported by the interpreter") + } (Signedness::Unsigned, IntegerBitSize::Eight) => { - let value: u8 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: u8 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; Ok(Value::U8(value)) - }, + } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - let value: u32 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: u32 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; Ok(Value::U32(value)) - }, + } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - let value: u64 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: u64 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) - }, - (Signedness::Signed, IntegerBitSize::One) => panic!("i1 is not supported by the interpreter"), + } + (Signedness::Signed, IntegerBitSize::One) => { + panic!("i1 is not supported by the interpreter") + } (Signedness::Signed, IntegerBitSize::Eight) => { - let value: i8 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: i8 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { -value } else { value }; Ok(Value::I8(value)) - }, + } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { - let value: i32 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: i32 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { -value } else { value }; Ok(Value::I32(value)) - }, + } (Signedness::Signed, IntegerBitSize::SixtyFour) => { - let value: i64 = value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { - InterpreterError::IntegerOutOfRangeForType(value, typ) - })?; + let value: i64 = value + .try_to_u64() + .and_then(|value| value.try_into().ok()) + .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; let value = if is_negative { -value } else { value }; Ok(Value::I64(value)) - }, + } } } else { unreachable!("Non-integer integer literal of type {typ}") @@ -292,9 +332,12 @@ impl<'a> Interpreter<'a> { fn evaluate_array(&self, array: HirArrayLiteral) -> IResult { match array { HirArrayLiteral::Standard(elements) => { - let elements = elements.into_iter().map(|id| self.evaluate(id)).collect::>>()?; + let elements = elements + .into_iter() + .map(|id| self.evaluate(id)) + .collect::>>()?; Ok(Value::Array(elements)) - }, + } HirArrayLiteral::Repeated { repeated_element, length } => { let element = self.evaluate(repeated_element)?; @@ -304,7 +347,7 @@ impl<'a> Interpreter<'a> { } else { Err(InterpreterError::UnableToEvaluateTypeToInteger(length)) } - }, + } } } @@ -318,36 +361,30 @@ impl<'a> Interpreter<'a> { fn evaluate_prefix(&mut self, prefix: HirPrefixExpression) -> IResult { let rhs = self.evaluate(prefix.rhs)?; match prefix.operator { - crate::UnaryOp::Minus => { - match rhs { - Value::Field(value) => Ok(Value::Field(FieldElement::zero() - value)), - Value::I8(value) => Ok(Value::I8(-value)), - Value::I32(value) => Ok(Value::I32(-value)), - Value::I64(value) => Ok(Value::I64(-value)), - Value::U8(value) => Ok(Value::U8(0-value)), - Value::U32(value) => Ok(Value::U32(0-value)), - Value::U64(value) => Ok(Value::U64(0-value)), - other => panic!("Invalid value for unary minus operation: {other:?}"), - } + crate::UnaryOp::Minus => match rhs { + Value::Field(value) => Ok(Value::Field(FieldElement::zero() - value)), + Value::I8(value) => Ok(Value::I8(-value)), + Value::I32(value) => Ok(Value::I32(-value)), + Value::I64(value) => Ok(Value::I64(-value)), + Value::U8(value) => Ok(Value::U8(0 - value)), + Value::U32(value) => Ok(Value::U32(0 - value)), + Value::U64(value) => Ok(Value::U64(0 - value)), + other => panic!("Invalid value for unary minus operation: {other:?}"), }, - crate::UnaryOp::Not => { - match rhs { - Value::Bool(value) => Ok(Value::Bool(!value)), - Value::I8(value) => Ok(Value::I8(!value)), - Value::I32(value) => Ok(Value::I32(!value)), - Value::I64(value) => Ok(Value::I64(!value)), - Value::U8(value) => Ok(Value::U8(!value)), - Value::U32(value) => Ok(Value::U32(!value)), - Value::U64(value) => Ok(Value::U64(!value)), - other => panic!("Invalid value for unary not operation: {other:?}"), - } + crate::UnaryOp::Not => match rhs { + Value::Bool(value) => Ok(Value::Bool(!value)), + Value::I8(value) => Ok(Value::I8(!value)), + Value::I32(value) => Ok(Value::I32(!value)), + Value::I64(value) => Ok(Value::I64(!value)), + Value::U8(value) => Ok(Value::U8(!value)), + Value::U32(value) => Ok(Value::U32(!value)), + Value::U64(value) => Ok(Value::U64(!value)), + other => panic!("Invalid value for unary not operation: {other:?}"), }, crate::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), - crate::UnaryOp::Dereference { implicitly_added: _ } => { - match rhs { - Value::Pointer(element) => Ok(element.borrow().clone()), - other => panic!("Cannot dereference {other:?}"), - } + crate::UnaryOp::Dereference { implicitly_added: _ } => match rhs { + Value::Pointer(element) => Ok(element.borrow().clone()), + other => panic!("Cannot dereference {other:?}"), }, } } @@ -358,194 +395,162 @@ impl<'a> Interpreter<'a> { // TODO: Need to account for operator overloading match infix.operator.kind { - BinaryOpKind::Add => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs + rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs + rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), - (lhs, rhs) => panic!("Operator (+) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Add => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs + rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs + rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), + (lhs, rhs) => panic!("Operator (+) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Subtract => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs - rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs - rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), - (lhs, rhs) => panic!("Operator (-) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Subtract => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs - rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs - rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), + (lhs, rhs) => panic!("Operator (-) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Multiply => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs * rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs * rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), - (lhs, rhs) => panic!("Operator (*) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Multiply => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs * rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs * rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), + (lhs, rhs) => panic!("Operator (*) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Divide => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs / rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs / rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), - (lhs, rhs) => panic!("Operator (/) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Divide => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs / rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs / rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), + (lhs, rhs) => panic!("Operator (/) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Equal => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => panic!("Operator (==) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Equal => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (lhs, rhs) => panic!("Operator (==) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::NotEqual => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => panic!("Operator (!=) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::NotEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (lhs, rhs) => panic!("Operator (!=) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Less => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (lhs, rhs) => panic!("Operator (<) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Less => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (lhs, rhs) => panic!("Operator (<) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::LessEqual => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (lhs, rhs) => panic!("Operator (<=) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::LessEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (lhs, rhs) => panic!("Operator (<=) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Greater => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (lhs, rhs) => panic!("Operator (>) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Greater => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (lhs, rhs) => panic!("Operator (>) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::GreaterEqual => { - match (lhs, rhs) { - (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (lhs, rhs) => panic!("Operator (>=) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::GreaterEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (lhs, rhs) => panic!("Operator (>=) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::And => { - match (lhs, rhs) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs & rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs & rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), - (lhs, rhs) => panic!("Operator (&) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::And => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs & rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs & rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), + (lhs, rhs) => panic!("Operator (&) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Or => { - match (lhs, rhs) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs | rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs | rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), - (lhs, rhs) => panic!("Operator (|) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Or => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs | rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs | rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), + (lhs, rhs) => panic!("Operator (|) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Xor => { - match (lhs, rhs) { - (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs ^ rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs ^ rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), - (lhs, rhs) => panic!("Operator (^) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Xor => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs ^ rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs ^ rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), + (lhs, rhs) => panic!("Operator (^) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::ShiftRight => { - match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs >> rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs >> rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), - (lhs, rhs) => panic!("Operator (>>) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::ShiftRight => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs >> rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs >> rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), + (lhs, rhs) => panic!("Operator (>>) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::ShiftLeft => { - match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs << rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs << rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), - (lhs, rhs) => panic!("Operator (<<) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::ShiftLeft => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs << rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs << rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), + (lhs, rhs) => panic!("Operator (<<) invalid for values {lhs:?} and {rhs:?}"), }, - BinaryOpKind::Modulo => { - match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs % rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs % rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), - (lhs, rhs) => panic!("Operator (%) invalid for values {lhs:?} and {rhs:?}"), - } + BinaryOpKind::Modulo => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs % rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs % rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), + (lhs, rhs) => panic!("Operator (%) invalid for values {lhs:?} and {rhs:?}"), }, } } @@ -558,7 +563,9 @@ impl<'a> Interpreter<'a> { }; let index = match self.evaluate(index.index)? { - Value::Field(value) => value.try_to_u64().expect("index could not fit into u64") as usize, + Value::Field(value) => { + value.try_to_u64().expect("index could not fit into u64") as usize + } Value::I8(value) => value as usize, Value::I32(value) => value as usize, Value::I64(value) => value as usize, @@ -581,9 +588,10 @@ impl<'a> Interpreter<'a> { other => panic!("Cannot access fields of a non-struct value: {other:?}"), }; - Ok(fields.get(&access.rhs.0.contents).unwrap_or_else(|| { - panic!("Expected struct to have field {}", access.rhs) - }).clone()) + Ok(fields + .get(&access.rhs.0.contents) + .unwrap_or_else(|| panic!("Expected struct to have field {}", access.rhs)) + .clone()) } fn evaluate_call(&mut self, call: HirCallExpression) -> IResult { @@ -598,8 +606,25 @@ impl<'a> Interpreter<'a> { todo!() } - fn evaluate_if(&mut self, r#if: HirIfExpression) -> IResult { - todo!() + fn evaluate_if(&mut self, if_: HirIfExpression) -> IResult { + let condition = match self.evaluate(if_.condition)? { + Value::Bool(value) => value, + other => panic!("Non-boolean value for if condition: {other:?}"), + }; + + if condition { + if if_.alternative.is_some() { + self.evaluate(if_.consequence) + } else { + self.evaluate(if_.consequence)?; + Ok(Value::Unit) + } + } else { + match if_.alternative { + Some(alternative) => self.evaluate(alternative), + None => Ok(Value::Unit), + } + } } fn evaluate_tuple(&mut self, tuple: Vec) -> IResult { From 1c649e5e4081dc8a88f8dee540eca5efeff5029f Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 15 Apr 2024 13:20:35 -0400 Subject: [PATCH 07/21] Update interpreter --- .../src/hir/comptime/interpreter.rs | 296 +++++++++++++++--- 1 file changed, 254 insertions(+), 42 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index b0c621c8e45..53f6767f446 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1,8 +1,8 @@ -use std::rc::Rc; +use std::{borrow::Cow, rc::Rc}; use acvm::FieldElement; use im::Vector; -use iter_extended::try_vecmap; +use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use rustc_hash::{FxHashMap, FxHashSet}; @@ -15,8 +15,8 @@ use crate::{ }, stmt::HirPattern, }, - macros_api::{HirExpression, HirLiteral, NodeInterner}, - node_interner::{DefinitionId, ExprId, FuncId}, + macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, + node_interner::{DefinitionId, ExprId, FuncId, StmtId}, BinaryOpKind, BlockExpression, FunctionKind, IntegerBitSize, Shared, Signedness, Type, }; @@ -50,15 +50,19 @@ enum Value { U32(u32), U64(u64), String(Rc), - Function(FuncId), + Function(FuncId, Type), + Closure(HirLambda, Vec, Type), Tuple(Vec), - Struct(FxHashMap, Value>), + Struct(FxHashMap, Value>, Type), Pointer(Shared), - Array(Vector), - Slice(Vector), + Array(Vector, Type), + Slice(Vector, Type), Code(Rc), } +/// The possible errors that can halt the interpreter. +/// +/// TODO: All error variants should have Locations enum InterpreterError { ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, TypeMismatch { expected: Type, value: Value }, @@ -66,6 +70,9 @@ enum InterpreterError { IntegerOutOfRangeForType(FieldElement, Type), UnableToEvaluateTypeToInteger(Type), ErrorNodeEncountered { location: Location }, + NonFunctionCalled { value: Value, location: Location }, + NonBoolUsedInIf { value: Value, location: Location }, + NoMethodFound { object: Value, typ: Type, location: Location }, } type IResult = std::result::Result; @@ -108,6 +115,33 @@ impl<'a> Interpreter<'a> { Ok(result) } + fn call_closure( + &mut self, + closure: HirLambda, + environment: Vec, + arguments: Vec, + call_location: Location, + ) -> IResult { + self.push_scope(); + + if closure.parameters.len() != arguments.len() { + return Err(InterpreterError::ArgumentCountMismatch { + expected: closure.parameters.len(), + actual: arguments.len(), + call_location, + }); + } + + for ((parameter, typ), argument) in closure.parameters.iter().zip(arguments) { + self.define_pattern(parameter, typ, argument); + } + + let result = self.evaluate(closure.body)?; + + self.pop_scope(); + Ok(result) + } + fn push_scope(&mut self) { self.scopes.push(FxHashMap::default()); } @@ -145,7 +179,8 @@ impl<'a> Interpreter<'a> { self.type_check(typ, &argument)?; self.type_check(struct_type, &argument)?; - if let (Value::Struct(fields), Type::Struct(struct_def, generics)) = (argument, typ) + if let (Value::Struct(fields, _), Type::Struct(struct_def, generics)) = + (argument, typ) { let struct_def = struct_def.borrow(); @@ -202,11 +237,11 @@ impl<'a> Interpreter<'a> { (Value::U32(_), Type::Integer(Unsigned, ThirtyTwo)) => (), (Value::U64(_), Type::Integer(Unsigned, SixtyFour)) => (), (Value::String(_), Type::String(_)) => (), - (Value::Function(_), Type::Function(..)) => (), + (Value::Function(..), Type::Function(..)) => (), (Value::Tuple(fields1), Type::Tuple(fields2)) if fields1.len() == fields2.len() => (), - (Value::Struct(_), _) => (), - (Value::Array(_), Type::Array(..)) => (), - (Value::Slice(_), Type::Slice(_)) => (), + (Value::Struct(..), _) => (), + (Value::Array(..), Type::Array(..)) => (), + (Value::Slice(..), Type::Slice(_)) => (), (Value::Pointer(_), _) => (), (Value::Code(_), Type::Code) => (), _ => { @@ -226,14 +261,14 @@ impl<'a> Interpreter<'a> { HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix), HirExpression::Infix(infix) => self.evaluate_infix(infix), HirExpression::Index(index) => self.evaluate_index(index), - HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor), + HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor, id), HirExpression::MemberAccess(access) => self.evaluate_access(access), - HirExpression::Call(call) => self.evaluate_call(call), - HirExpression::MethodCall(call) => self.evaluate_method_call(call), + HirExpression::Call(call) => self.evaluate_call(call, id), + HirExpression::MethodCall(call) => self.evaluate_method_call(call, id), HirExpression::Cast(cast) => self.evaluate_cast(cast), - HirExpression::If(if_) => self.evaluate_if(if_), + HirExpression::If(if_) => self.evaluate_if(if_, id), HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), - HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda), + HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), HirExpression::Error => { let location = self.interner.expr_location(&id); @@ -251,8 +286,8 @@ impl<'a> Interpreter<'a> { } HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), HirLiteral::FmtStr(_, _) => todo!("Evaluate format strings"), - HirLiteral::Array(array) => self.evaluate_array(array), - HirLiteral::Slice(array) => self.evaluate_slice(array), + HirLiteral::Array(array) => self.evaluate_array(array, id), + HirLiteral::Slice(array) => self.evaluate_slice(array, id), } } @@ -325,25 +360,38 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_block(&self, block: HirBlockExpression) -> IResult { - todo!() + fn evaluate_block(&mut self, mut block: HirBlockExpression) -> IResult { + let last_statement = block.statements.pop(); + + for statement in block.statements { + self.evaluate_statement(statement); + } + + if let Some(statement) = last_statement { + self.evaluate_statement(statement) + } else { + Ok(Value::Unit) + } } - fn evaluate_array(&self, array: HirArrayLiteral) -> IResult { + fn evaluate_array(&self, array: HirArrayLiteral, id: ExprId) -> IResult { + let typ = self.interner.id_type(id); + match array { HirArrayLiteral::Standard(elements) => { let elements = elements .into_iter() .map(|id| self.evaluate(id)) .collect::>>()?; - Ok(Value::Array(elements)) + + Ok(Value::Array(elements, typ)) } HirArrayLiteral::Repeated { repeated_element, length } => { let element = self.evaluate(repeated_element)?; if let Some(length) = length.evaluate_to_u64() { let elements = (0..length).map(|_| element.clone()).collect(); - Ok(Value::Array(elements)) + Ok(Value::Array(elements, typ)) } else { Err(InterpreterError::UnableToEvaluateTypeToInteger(length)) } @@ -351,9 +399,9 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_slice(&self, array: HirArrayLiteral) -> IResult { - self.evaluate_array(array).map(|value| match value { - Value::Array(array) => Value::Slice(array), + fn evaluate_slice(&self, array: HirArrayLiteral, id: ExprId) -> IResult { + self.evaluate_array(array, id).map(|value| match value { + Value::Array(array, typ) => Value::Slice(array, typ), other => unreachable!("Non-array value returned from evaluate array: {other:?}"), }) } @@ -557,8 +605,8 @@ impl<'a> Interpreter<'a> { fn evaluate_index(&self, index: HirIndexExpression) -> IResult { let collection = match self.evaluate(index.collection)? { - Value::Array(array) => array, - Value::Slice(array) => array, + Value::Array(array, _) => array, + Value::Slice(array, _) => array, other => panic!("Cannot index into {other:?}"), }; @@ -578,13 +626,27 @@ impl<'a> Interpreter<'a> { Ok(collection[index].clone()) } - fn evaluate_constructor(&mut self, constructor: HirConstructorExpression) -> IResult { - todo!() + fn evaluate_constructor( + &mut self, + constructor: HirConstructorExpression, + id: ExprId, + ) -> IResult { + let fields = constructor + .fields + .into_iter() + .map(|(name, expr)| { + let field_value = self.evaluate(expr)?; + Ok((Rc::new(name.0.contents), field_value)) + }) + .collect::>()?; + + let typ = self.interner.id_type(id); + Ok(Value::Struct(fields, typ)) } fn evaluate_access(&mut self, access: HirMemberAccess) -> IResult { let fields = match self.evaluate(access.lhs)? { - Value::Struct(fields) => fields, + Value::Struct(fields, _) => fields, other => panic!("Cannot access fields of a non-struct value: {other:?}"), }; @@ -594,22 +656,125 @@ impl<'a> Interpreter<'a> { .clone()) } - fn evaluate_call(&mut self, call: HirCallExpression) -> IResult { - todo!() + fn evaluate_call(&mut self, call: HirCallExpression, id: ExprId) -> IResult { + let function = self.evaluate(call.func)?; + let arguments = try_vecmap(call.arguments, |arg| self.evaluate(arg))?; + let location = self.interner.expr_location(&id); + + match function { + Value::Function(function_id, _) => self.call_function(function_id, arguments, location), + Value::Closure(closure, env, _) => self.call_closure(closure, env, arguments, location), + value => Err(InterpreterError::NonFunctionCalled { value, location }), + } } - fn evaluate_method_call(&mut self, call: HirMethodCallExpression) -> IResult { - todo!() + fn evaluate_method_call( + &mut self, + call: HirMethodCallExpression, + id: ExprId, + ) -> IResult { + let object = self.evaluate(call.object)?; + let arguments = try_vecmap(call.arguments, |arg| self.evaluate(arg))?; + let location = self.interner.expr_location(&id); + + let typ = object.get_type().follow_bindings(); + let method_name = &call.method.0.contents; + + // TODO: Traits + let method = match &typ { + Type::Struct(struct_def, _) => { + self.interner.lookup_method(&typ, struct_def.borrow().id, method_name, false) + } + _ => self.interner.lookup_primitive_method(&typ, method_name), + }; + + if let Some(method) = method { + self.call_function(method, arguments, location) + } else { + Err(InterpreterError::NoMethodFound { object, typ, location }) + } } fn evaluate_cast(&mut self, cast: HirCastExpression) -> IResult { - todo!() + macro_rules! signed_int_to_field { + ($x:expr) => {{ + // Need to convert the signed integer to an i128 before + // we negate it to preserve the MIN value. + let mut value = $x as i128; + let is_negative = value < 0; + if is_negative { + value = -value; + } + ((value as u128).into(), is_negative) + }}; + } + + let (lhs, lhs_is_negative) = match self.evaluate(cast.lhs)? { + Value::Field(value) => (value, false), + Value::U8(value) => ((value as u128).into(), false), + Value::U32(value) => ((value as u128).into(), false), + Value::U64(value) => ((value as u128).into(), false), + Value::I8(value) => signed_int_to_field!(value), + Value::I32(value) => signed_int_to_field!(value), + Value::I64(value) => signed_int_to_field!(value), + Value::Bool(value) => { + (if value { FieldElement::one() } else { FieldElement::zero() }, false) + } + other => unreachable!("Cannot cast from non-numeric value '{other:?}'"), + }; + + macro_rules! cast_to_int { + ($x:expr, $method:ident, $typ:ty, $f:ident) => {{ + let mut value = $x.$method() as $typ; + if lhs_is_negative { + value = 0 - value; + } + Ok(Value::$f(value)) + }}; + } + + // Now actually cast the lhs, bit casting and wrapping as necessary + match cast.r#type.follow_bindings() { + Type::FieldElement => { + if lhs_is_negative { + lhs = FieldElement::zero() - lhs; + } + Ok(Value::Field(lhs)) + } + Type::Integer(sign, bit_size) => match (sign, bit_size) { + (Signedness::Unsigned, IntegerBitSize::One) => { + panic!("u1 is not supported by the interpreter") + } + (Signedness::Unsigned, IntegerBitSize::Eight) => cast_to_int!(lhs, to_u128, u8, U8), + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { + cast_to_int!(lhs, to_u128, u32, U32) + } + (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { + cast_to_int!(lhs, to_u128, u64, U64) + } + (Signedness::Signed, IntegerBitSize::One) => { + panic!("i1 is not supported by the interpreter") + } + (Signedness::Signed, IntegerBitSize::Eight) => cast_to_int!(lhs, to_i128, i8, I8), + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { + cast_to_int!(lhs, to_i128, i32, I32) + } + (Signedness::Signed, IntegerBitSize::SixtyFour) => { + cast_to_int!(lhs, to_i128, i64, I64) + } + }, + Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), + other => unreachable!("Cannot cast to non-numeric type '{other}'"), + } } - fn evaluate_if(&mut self, if_: HirIfExpression) -> IResult { + fn evaluate_if(&mut self, if_: HirIfExpression, id: ExprId) -> IResult { let condition = match self.evaluate(if_.condition)? { Value::Bool(value) => value, - other => panic!("Non-boolean value for if condition: {other:?}"), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonBoolUsedInIf { value, location }); + } }; if condition { @@ -632,7 +797,54 @@ impl<'a> Interpreter<'a> { Ok(Value::Tuple(fields)) } - fn evaluate_lambda(&mut self, lambda: HirLambda) -> IResult { - todo!() + fn evaluate_lambda(&mut self, lambda: HirLambda, id: ExprId) -> IResult { + let environment = try_vecmap(&lambda.captures, |capture| self.lookup(capture.ident.id))?; + + let typ = self.interner.id_type(id); + Ok(Value::Closure(lambda, environment, typ)) + } + + fn evaluate_statement(&mut self, statement: StmtId) -> IResult { + match self.interner.statement(&statement) { + HirStatement::Let(_) => todo!(), + HirStatement::Constrain(_) => todo!(), + HirStatement::Assign(_) => todo!(), + HirStatement::For(_) => todo!(), + HirStatement::Break => todo!(), + HirStatement::Continue => todo!(), + HirStatement::Expression(_) => todo!(), + HirStatement::Semi(_) => todo!(), + HirStatement::Error => todo!(), + } + } +} + +impl Value { + fn get_type(&self) -> Cow { + Cow::Owned(match self { + Value::Unit => Type::Unit, + Value::Bool(_) => Type::Bool, + Value::Field(_) => Type::FieldElement, + Value::I8(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Eight), + Value::I32(_) => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo), + Value::I64(_) => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour), + Value::U8(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Value::String(value) => { + let length = Type::Constant(value.len() as u64); + Type::String(Box::new(length)) + } + Value::Function(_, typ) => return Cow::Borrowed(typ), + Value::Closure(_, _, typ) => return Cow::Borrowed(typ), + Value::Tuple(fields) => { + Type::Tuple(vecmap(fields, |field| field.get_type().into_owned())) + } + Value::Struct(_, typ) => return Cow::Borrowed(typ), + Value::Array(array, typ) => return Cow::Borrowed(typ), + Value::Slice(slice, typ) => return Cow::Borrowed(typ), + Value::Code(_) => Type::Code, + Value::Pointer(_) => panic!("No type equivalent for pointers yet!"), + }) } } From b5bdb9cfc363043c3ed86a8023a709dfe4865232 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 15 Apr 2024 13:34:15 -0400 Subject: [PATCH 08/21] Start evaluating statements --- .../src/hir/comptime/interpreter.rs | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 53f6767f446..55507bed312 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -13,7 +13,9 @@ use crate::{ HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }, - stmt::HirPattern, + stmt::{ + HirAssignStatement, HirConstrainStatement, HirForStatement, HirLetStatement, HirPattern, + }, }, macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, ExprId, FuncId, StmtId}, @@ -72,6 +74,8 @@ enum InterpreterError { ErrorNodeEncountered { location: Location }, NonFunctionCalled { value: Value, location: Location }, NonBoolUsedInIf { value: Value, location: Location }, + NonBoolUsedInConstrain { value: Value, location: Location }, + FailingConstraint { message: Option, location: Location }, NoMethodFound { object: Value, typ: Type, location: Location }, } @@ -806,17 +810,60 @@ impl<'a> Interpreter<'a> { fn evaluate_statement(&mut self, statement: StmtId) -> IResult { match self.interner.statement(&statement) { - HirStatement::Let(_) => todo!(), - HirStatement::Constrain(_) => todo!(), - HirStatement::Assign(_) => todo!(), - HirStatement::For(_) => todo!(), - HirStatement::Break => todo!(), - HirStatement::Continue => todo!(), - HirStatement::Expression(_) => todo!(), - HirStatement::Semi(_) => todo!(), - HirStatement::Error => todo!(), + HirStatement::Let(let_) => self.evaluate_let(let_), + HirStatement::Constrain(constrain) => self.evaluate_constrain(constrain), + HirStatement::Assign(assign) => self.evaluate_assign(assign), + HirStatement::For(for_) => self.evaluate_for(for_), + HirStatement::Break => self.evaluate_break(), + HirStatement::Continue => self.evaluate_continue(), + HirStatement::Expression(expression) => self.evaluate(expression), + HirStatement::Semi(expression) => { + self.evaluate(expression)?; + Ok(Value::Unit) + } + HirStatement::Error => { + let location = self.interner.id_location(statement); + Err(InterpreterError::ErrorNodeEncountered { location }) + } } } + + fn evaluate_let(&mut self, let_: HirLetStatement) -> IResult { + let rhs = self.evaluate(let_.expression)?; + self.define_pattern(&let_.pattern, &let_.r#type, rhs)?; + Ok(Value::Unit) + } + + fn evaluate_constrain(&mut self, constrain: HirConstrainStatement) -> IResult { + match self.evaluate(constrain.0)? { + Value::Bool(true) => Ok(Value::Unit), + Value::Bool(false) => { + let location = self.interner.expr_location(&constrain.0); + let message = constrain.2.and_then(|expr| self.evaluate(expr).ok()); + Err(InterpreterError::FailingConstraint { location, message }) + } + value => { + let location = self.interner.expr_location(&constrain.0); + Err(InterpreterError::NonBoolUsedInConstrain { value, location }) + } + } + } + + fn evaluate_assign(&mut self, assign: HirAssignStatement) -> IResult { + todo!() + } + + fn evaluate_for(&mut self, r#for: HirForStatement) -> IResult { + todo!() + } + + fn evaluate_break(&mut self) -> IResult { + todo!() + } + + fn evaluate_continue(&mut self) -> IResult { + todo!() + } } impl Value { From 65a0da9b702a68ec37a0a24c2fef1a73f5375edc Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 15 Apr 2024 13:49:53 -0400 Subject: [PATCH 09/21] Fix compiler errors --- .../src/hir/comptime/interpreter.rs | 65 ++++++++++--------- .../src/hir/def_collector/dc_mod.rs | 1 + .../src/hir/resolution/resolver.rs | 1 + .../src/monomorphization/mod.rs | 2 +- .../src/parser/parser/function.rs | 1 + 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 55507bed312..7b8063f21f7 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -103,13 +103,14 @@ impl<'a> Interpreter<'a> { }); } - for ((parameter, typ, _), argument) in meta.parameters.0.iter().zip(arguments) { - self.define_pattern(parameter, typ, argument); + for ((parameter, typ, _), argument) in meta.parameters.0.clone().iter().zip(arguments) { + self.define_pattern(parameter, typ, argument)?; } + let meta = self.interner.function_meta(&function); match meta.kind { FunctionKind::Normal => (), - other => todo!("Evaluation for {:?} is unimplemented", meta.kind), + other => todo!("Evaluation for {other:?} is unimplemented"), } let function_body = self.interner.function(&function).as_expr(); @@ -122,7 +123,8 @@ impl<'a> Interpreter<'a> { fn call_closure( &mut self, closure: HirLambda, - environment: Vec, + // TODO: How to define environment here? + _environment: Vec, arguments: Vec, call_location: Location, ) -> IResult { @@ -137,7 +139,7 @@ impl<'a> Interpreter<'a> { } for ((parameter, typ), argument) in closure.parameters.iter().zip(arguments) { - self.define_pattern(parameter, typ, argument); + self.define_pattern(parameter, typ, argument)?; } let result = self.evaluate(closure.body)?; @@ -154,20 +156,25 @@ impl<'a> Interpreter<'a> { self.scopes.pop(); } - fn current_scope(&mut self) -> &mut FxHashMap { + fn current_scope(&self) -> &FxHashMap { + self.scopes.last().unwrap() + } + + fn current_scope_mut(&mut self) -> &mut FxHashMap { self.scopes.last_mut().unwrap() } - fn define_pattern(&self, pattern: &HirPattern, typ: &Type, argument: Value) -> IResult<()> { + fn define_pattern(&mut self, pattern: &HirPattern, typ: &Type, argument: Value) -> IResult<()> { match pattern { HirPattern::Identifier(identifier) => self.define(identifier.id, typ, argument), HirPattern::Mutable(pattern, _) => self.define_pattern(pattern, typ, argument), HirPattern::Tuple(pattern_fields, _) => { self.type_check(typ, &argument)?; - if let (Value::Tuple(fields), Type::Tuple(type_fields)) = (argument, typ) { - // The type check already ensures fields.len() == type_fields.len() - if fields.len() == pattern_fields.len() { + match (argument, typ) { + (Value::Tuple(fields), Type::Tuple(type_fields)) + if fields.len() == pattern_fields.len() => + { for ((pattern, typ), argument) in pattern_fields.iter().zip(type_fields).zip(fields) { @@ -175,24 +182,23 @@ impl<'a> Interpreter<'a> { } return Ok(()); } + (value, _) => { + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value }) + } } - - Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) } HirPattern::Struct(struct_type, pattern_fields, _) => { self.type_check(typ, &argument)?; self.type_check(struct_type, &argument)?; - if let (Value::Struct(fields, _), Type::Struct(struct_def, generics)) = - (argument, typ) - { - let struct_def = struct_def.borrow(); - - // The type check already ensures fields.len() == type_fields.len() - if fields.len() == pattern_fields.len() { + match (argument, typ) { + (Value::Struct(fields, _), Type::Struct(struct_def, generics)) + if fields.len() == pattern_fields.len() => + { + let struct_def = struct_def.borrow(); for (field_name, field_pattern) in pattern_fields { let field = fields.get(&field_name.0.contents).unwrap_or_else(|| { - panic!("Expected Struct value {argument:?} to have a field named '{field_name}'") + panic!("Expected Struct value with fields {fields:?} to have a field named '{field_name}'") }); let field_type = struct_def.get_field(&field_name.0.contents, generics).unwrap_or_else(|| { @@ -203,16 +209,17 @@ impl<'a> Interpreter<'a> { } return Ok(()); } + (value, _) => { + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value }) + } } - - Err(InterpreterError::TypeMismatch { expected: typ.clone(), value: argument }) } } } - fn define(&self, id: DefinitionId, typ: &Type, argument: Value) -> IResult<()> { + fn define(&mut self, id: DefinitionId, typ: &Type, argument: Value) -> IResult<()> { self.type_check(typ, &argument)?; - self.current_scope().insert(id, argument); + self.current_scope_mut().insert(id, argument); Ok(()) } @@ -368,7 +375,7 @@ impl<'a> Interpreter<'a> { let last_statement = block.statements.pop(); for statement in block.statements { - self.evaluate_statement(statement); + self.evaluate_statement(statement)?; } if let Some(statement) = last_statement { @@ -378,7 +385,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_array(&self, array: HirArrayLiteral, id: ExprId) -> IResult { + fn evaluate_array(&mut self, array: HirArrayLiteral, id: ExprId) -> IResult { let typ = self.interner.id_type(id); match array { @@ -403,7 +410,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_slice(&self, array: HirArrayLiteral, id: ExprId) -> IResult { + fn evaluate_slice(&mut self, array: HirArrayLiteral, id: ExprId) -> IResult { self.evaluate_array(array, id).map(|value| match value { Value::Array(array, typ) => Value::Slice(array, typ), other => unreachable!("Non-array value returned from evaluate array: {other:?}"), @@ -607,7 +614,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_index(&self, index: HirIndexExpression) -> IResult { + fn evaluate_index(&mut self, index: HirIndexExpression) -> IResult { let collection = match self.evaluate(index.collection)? { Value::Array(array, _) => array, Value::Slice(array, _) => array, @@ -713,7 +720,7 @@ impl<'a> Interpreter<'a> { }}; } - let (lhs, lhs_is_negative) = match self.evaluate(cast.lhs)? { + let (mut lhs, lhs_is_negative) = match self.evaluate(cast.lhs)? { Value::Field(value) => (value, false), Value::U8(value) => ((value as u128).into(), false), Value::U32(value) => ((value as u128).into(), false), diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 6fbb3b67546..e3c79e39d31 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -416,6 +416,7 @@ impl<'a> ModCollector<'a> { // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), is_unconstrained: false, + is_comptime: false, }; let location = Location::new(name.span(), self.file_id); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 9180201fe17..479f357126a 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -246,6 +246,7 @@ impl<'a> Resolver<'a> { name: name.clone(), attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, visibility: ItemVisibility::Public, // Trait functions are always public generics: generics.clone(), parameters: vecmap(parameters, |(name, typ)| Param { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 6aa0abce152..dd737017764 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -289,7 +289,7 @@ impl<'interner> Monomorphizer<'interner> { let modifiers = self.interner.function_modifiers(&f); let name = self.interner.function_name(&f).to_owned(); - let body_expr_id = *self.interner.function(&f).as_expr(); + let body_expr_id = self.interner.function(&f).as_expr(); let body_return_type = self.interner.id_type(body_expr_id); let return_type = match meta.return_type() { Type::TraitAsType(..) => &body_return_type, diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 06e1a958eb1..18f17065038 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -36,6 +36,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser Date: Mon, 15 Apr 2024 15:14:42 -0400 Subject: [PATCH 10/21] Implement loops --- .../src/hir/comptime/interpreter.rs | 110 +++++++++++++++--- 1 file changed, 92 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 7b8063f21f7..7564c95a3ad 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -14,7 +14,8 @@ use crate::{ HirLambda, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }, stmt::{ - HirAssignStatement, HirConstrainStatement, HirForStatement, HirLetStatement, HirPattern, + HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, + HirPattern, }, }, macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, @@ -38,6 +39,8 @@ struct Interpreter<'interner> { /// True if we've expanded any macros into global scope and will need /// to redo name resolution & type checking for everything. changed_globally: bool, + + in_loop: bool, } #[derive(Debug, Clone)] @@ -77,6 +80,14 @@ enum InterpreterError { NonBoolUsedInConstrain { value: Value, location: Location }, FailingConstraint { message: Option, location: Location }, NoMethodFound { object: Value, typ: Type, location: Location }, + BreakNotInLoop, + ContinueNotInLoop, + NonIntegerUsedInLoop { value: Value, location: Location }, + + // These cases are not errors but prevent us from running more code + // until the loop can be resumed properly. + Break, + Continue, } type IResult = std::result::Result; @@ -91,9 +102,12 @@ impl<'a> Interpreter<'a> { let modifiers = self.interner.function_modifiers(&function); assert!(modifiers.is_comptime, "Tried to evaluate non-comptime function!"); - self.push_scope(); + let previous_state = self.enter_function(); let meta = self.interner.function_meta(&function); + if meta.kind != FunctionKind::Normal { + todo!("Evaluation for {:?} is unimplemented", meta.kind); + } if meta.parameters.len() != arguments.len() { return Err(InterpreterError::ArgumentCountMismatch { @@ -107,16 +121,10 @@ impl<'a> Interpreter<'a> { self.define_pattern(parameter, typ, argument)?; } - let meta = self.interner.function_meta(&function); - match meta.kind { - FunctionKind::Normal => (), - other => todo!("Evaluation for {other:?} is unimplemented"), - } - let function_body = self.interner.function(&function).as_expr(); let result = self.evaluate(function_body)?; - self.pop_scope(); + self.exit_function(previous_state); Ok(result) } @@ -128,7 +136,7 @@ impl<'a> Interpreter<'a> { arguments: Vec, call_location: Location, ) -> IResult { - self.push_scope(); + let previous_state = self.enter_function(); if closure.parameters.len() != arguments.len() { return Err(InterpreterError::ArgumentCountMismatch { @@ -144,10 +152,23 @@ impl<'a> Interpreter<'a> { let result = self.evaluate(closure.body)?; - self.pop_scope(); + self.exit_function(previous_state); Ok(result) } + /// Enters a function, pushing a new scope and resetting any required state. + /// Returns the previous values of the internal state, to be reset when + /// `exit_function` is called. + fn enter_function(&mut self) -> bool { + self.push_scope(); + std::mem::take(&mut self.in_loop) + } + + fn exit_function(&mut self, was_in_loop: bool) { + self.pop_scope(); + self.in_loop = was_in_loop; + } + fn push_scope(&mut self) { self.scopes.push(FxHashMap::default()); } @@ -857,19 +878,72 @@ impl<'a> Interpreter<'a> { } fn evaluate_assign(&mut self, assign: HirAssignStatement) -> IResult { - todo!() + let rhs = self.evaluate(assign.expression)?; + self.store_lvalue(assign.lvalue, rhs)?; + Ok(Value::Unit) } - fn evaluate_for(&mut self, r#for: HirForStatement) -> IResult { - todo!() + fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { + match lvalue { + HirLValue::Ident(ident, typ) => { + todo!() + } + HirLValue::MemberAccess { object, field_name, field_index, typ, location } => { + todo!() + } + HirLValue::Index { array, index, typ, location } => todo!(), + HirLValue::Dereference { lvalue, element_type, location } => todo!(), + } + } + + fn evaluate_for(&mut self, for_: HirForStatement) -> IResult { + // i128 can store all values from i8 - u64 + let get_index = |this: &mut Self, expr| -> IResult<(i128, fn(i128) -> Value)> { + match this.evaluate(expr)? { + Value::I8(value) => Ok((value as i128, |i| Value::I8(i as i8))), + Value::I32(value) => Ok((value as i128, |i| Value::I32(i as i32))), + Value::I64(value) => Ok((value as i128, |i| Value::I64(i as i64))), + Value::U8(value) => Ok((value as i128, |i| Value::U8(i as u8))), + Value::U32(value) => Ok((value as i128, |i| Value::U32(i as u32))), + Value::U64(value) => Ok((value as i128, |i| Value::U64(i as u64))), + value => { + let location = this.interner.expr_location(&expr); + Err(InterpreterError::NonIntegerUsedInLoop { value, location }) + } + } + }; + + let (start, make_value) = get_index(self, for_.start_range)?; + let (end, _) = get_index(self, for_.end_range)?; + + for i in start..end { + self.current_scope_mut().insert(for_.identifier.id, make_value(i)); + + match self.evaluate(for_.block) { + Ok(_) => (), + Err(InterpreterError::Break) => break, + Err(InterpreterError::Continue) => continue, + Err(other) => return Err(other), + } + } + + Ok(Value::Unit) } fn evaluate_break(&mut self) -> IResult { - todo!() + if self.in_loop { + Err(InterpreterError::Break) + } else { + Err(InterpreterError::BreakNotInLoop) + } } fn evaluate_continue(&mut self) -> IResult { - todo!() + if self.in_loop { + Err(InterpreterError::Continue) + } else { + Err(InterpreterError::ContinueNotInLoop) + } } } @@ -895,8 +969,8 @@ impl Value { Type::Tuple(vecmap(fields, |field| field.get_type().into_owned())) } Value::Struct(_, typ) => return Cow::Borrowed(typ), - Value::Array(array, typ) => return Cow::Borrowed(typ), - Value::Slice(slice, typ) => return Cow::Borrowed(typ), + Value::Array(_, typ) => return Cow::Borrowed(typ), + Value::Slice(_, typ) => return Cow::Borrowed(typ), Value::Code(_) => Type::Code, Value::Pointer(_) => panic!("No type equivalent for pointers yet!"), }) From 9a31f6837210c03fa6be1fb61a6fdcf5233f868c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 15 Apr 2024 15:56:01 -0400 Subject: [PATCH 11/21] Finish each node; still needs cleanup & testing --- .../src/hir/comptime/interpreter.rs | 125 ++++++++++++++++-- 1 file changed, 112 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 7564c95a3ad..40365fda038 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -83,6 +83,11 @@ enum InterpreterError { BreakNotInLoop, ContinueNotInLoop, NonIntegerUsedInLoop { value: Value, location: Location }, + NonPointerDereferenced { value: Value, location: Location }, + NonTupleOrStructInMemberAccess { value: Value, location: Location }, + NonArrayIndexed { value: Value, location: Location }, + NonIntegerUsedAsIndex { value: Value, location: Location }, + IndexOutOfBounds { index: usize, length: usize, location: Location }, // These cases are not errors but prevent us from running more code // until the loop can be resumed properly. @@ -292,7 +297,7 @@ impl<'a> Interpreter<'a> { HirExpression::Block(block) => self.evaluate_block(block), HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix), HirExpression::Infix(infix) => self.evaluate_infix(infix), - HirExpression::Index(index) => self.evaluate_index(index), + HirExpression::Index(index) => self.evaluate_index(index, id), HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor, id), HirExpression::MemberAccess(access) => self.evaluate_access(access), HirExpression::Call(call) => self.evaluate_call(call, id), @@ -635,14 +640,33 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_index(&mut self, index: HirIndexExpression) -> IResult { - let collection = match self.evaluate(index.collection)? { + fn evaluate_index(&mut self, index: HirIndexExpression, id: ExprId) -> IResult { + let array = self.evaluate(index.collection)?; + let index = self.evaluate(index.index)?; + + let location = self.interner.expr_location(&id); + let (array, index) = self.bounds_check(array, index, location)?; + + Ok(array[index].clone()) + } + + /// Bounds check the given array and index pair. + /// This will also ensure the given arguments are in fact an array and integer. + fn bounds_check( + &self, + array: Value, + index: Value, + location: Location, + ) -> IResult<(Vector, usize)> { + let collection = match array { Value::Array(array, _) => array, Value::Slice(array, _) => array, - other => panic!("Cannot index into {other:?}"), + value => { + return Err(InterpreterError::NonArrayIndexed { value, location }); + } }; - let index = match self.evaluate(index.index)? { + let index = match index { Value::Field(value) => { value.try_to_u64().expect("index could not fit into u64") as usize } @@ -652,10 +676,20 @@ impl<'a> Interpreter<'a> { Value::U8(value) => value as usize, Value::U32(value) => value as usize, Value::U64(value) => value as usize, - other => panic!("Cannot use {other:?} as an index"), + value => { + return Err(InterpreterError::NonIntegerUsedAsIndex { value, location }); + } }; - Ok(collection[index].clone()) + if index >= collection.len() { + return Err(InterpreterError::IndexOutOfBounds { + index, + length: collection.len(), + location, + }); + } + + Ok((collection, index)) } fn evaluate_constructor( @@ -885,14 +919,79 @@ impl<'a> Interpreter<'a> { fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { match lvalue { - HirLValue::Ident(ident, typ) => { - todo!() + HirLValue::Ident(ident, typ) => self.define(ident.id, &typ, rhs), + HirLValue::Dereference { lvalue, element_type: _, location } => { + match self.evaluate_lvalue(&lvalue)? { + Value::Pointer(value) => { + *value.borrow_mut() = rhs; + Ok(()) + } + value => Err(InterpreterError::NonPointerDereferenced { value, location }), + } + } + HirLValue::MemberAccess { object, field_name, field_index, typ: _, location } => { + let index = field_index.expect("The field index should be set after type checking"); + match self.evaluate_lvalue(&object)? { + Value::Tuple(mut fields) => { + fields[index] = rhs; + self.store_lvalue(*object, Value::Tuple(fields)) + } + Value::Struct(mut fields, typ) => { + fields.insert(Rc::new(field_name.0.contents), rhs); + self.store_lvalue(*object, Value::Struct(fields, typ)) + } + value => { + Err(InterpreterError::NonTupleOrStructInMemberAccess { value, location }) + } + } + } + HirLValue::Index { array, index, typ: _, location } => { + let array_value = self.evaluate_lvalue(&array)?; + let index = self.evaluate(index)?; + + let constructor = match &array_value { + Value::Array(..) => Value::Array, + _ => Value::Slice, + }; + + let typ = array_value.get_type().into_owned(); + let (elements, index) = self.bounds_check(array_value, index, location)?; + + let new_array = constructor(elements.update(index, rhs), typ); + self.store_lvalue(*array, new_array) + } + } + } + + fn evaluate_lvalue(&mut self, lvalue: &HirLValue) -> IResult { + match lvalue { + HirLValue::Ident(ident, _) => self.lookup(ident.id), + HirLValue::Dereference { lvalue, element_type: _, location } => { + match self.evaluate_lvalue(lvalue)? { + Value::Pointer(value) => Ok(value.borrow().clone()), + value => { + Err(InterpreterError::NonPointerDereferenced { value, location: *location }) + } + } + } + HirLValue::MemberAccess { object, field_name, field_index, typ: _, location } => { + let index = field_index.expect("The field index should be set after type checking"); + + match self.evaluate_lvalue(object)? { + Value::Tuple(mut values) => Ok(values.swap_remove(index)), + Value::Struct(fields, _) => Ok(fields[&field_name.0.contents].clone()), + value => Err(InterpreterError::NonTupleOrStructInMemberAccess { + value, + location: *location, + }), + } } - HirLValue::MemberAccess { object, field_name, field_index, typ, location } => { - todo!() + HirLValue::Index { array, index, typ: _, location } => { + let array = self.evaluate_lvalue(array)?; + let index = self.evaluate(*index)?; + let (elements, index) = self.bounds_check(array, index, *location)?; + Ok(elements[index].clone()) } - HirLValue::Index { array, index, typ, location } => todo!(), - HirLValue::Dereference { lvalue, element_type, location } => todo!(), } } From 4247e893a0e95b7beb234d7024a9eb9e5cc288e7 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 16 Apr 2024 15:55:22 -0400 Subject: [PATCH 12/21] Fix mutation after scopes were added --- .../src/hir/comptime/interpreter.rs | 595 ++++++++++++------ .../noirc_frontend/src/hir/comptime/mod.rs | 1 + .../noirc_frontend/src/hir/comptime/tests.rs | 166 +++++ .../noirc_frontend/src/hir/type_check/mod.rs | 50 +- compiler/noirc_frontend/src/hir_def/expr.rs | 4 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- 6 files changed, 600 insertions(+), 218 deletions(-) create mode 100644 compiler/noirc_frontend/src/hir/comptime/tests.rs diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 40365fda038..5f288236b43 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, rc::Rc}; +use std::{borrow::Cow, collections::hash_map::Entry, rc::Rc}; use acvm::FieldElement; use im::Vector; @@ -10,8 +10,9 @@ use crate::{ hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, - HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, - HirLambda, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, + HirConstructorExpression, HirIdent, HirIfExpression, HirIndexExpression, + HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, + HirPrefixExpression, }, stmt::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, @@ -19,11 +20,13 @@ use crate::{ }, }, macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, - node_interner::{DefinitionId, ExprId, FuncId, StmtId}, + node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, BinaryOpKind, BlockExpression, FunctionKind, IntegerBitSize, Shared, Signedness, Type, + TypeBinding, TypeBindings, TypeVariableKind, }; -struct Interpreter<'interner> { +#[allow(unused)] +pub(crate) struct Interpreter<'interner> { /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner interner: &'interner mut NodeInterner, @@ -43,8 +46,9 @@ struct Interpreter<'interner> { in_loop: bool, } -#[derive(Debug, Clone)] -enum Value { +#[allow(unused)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Value { Unit, Bool(bool), Field(FieldElement), @@ -66,28 +70,38 @@ enum Value { } /// The possible errors that can halt the interpreter. -/// -/// TODO: All error variants should have Locations -enum InterpreterError { +#[allow(unused)] +#[derive(Debug)] +pub(crate) enum InterpreterError { ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, - TypeMismatch { expected: Type, value: Value }, - NoValueForId(DefinitionId), - IntegerOutOfRangeForType(FieldElement, Type), - UnableToEvaluateTypeToInteger(Type), + TypeMismatch { expected: Type, value: Value, location: Location }, + NoValueForId { id: DefinitionId, location: Location }, + IntegerOutOfRangeForType { value: FieldElement, typ: Type, location: Location }, ErrorNodeEncountered { location: Location }, NonFunctionCalled { value: Value, location: Location }, NonBoolUsedInIf { value: Value, location: Location }, NonBoolUsedInConstrain { value: Value, location: Location }, FailingConstraint { message: Option, location: Location }, NoMethodFound { object: Value, typ: Type, location: Location }, - BreakNotInLoop, - ContinueNotInLoop, NonIntegerUsedInLoop { value: Value, location: Location }, NonPointerDereferenced { value: Value, location: Location }, NonTupleOrStructInMemberAccess { value: Value, location: Location }, NonArrayIndexed { value: Value, location: Location }, NonIntegerUsedAsIndex { value: Value, location: Location }, + NonIntegerIntegerLiteral { typ: Type, location: Location }, + NonIntegerArrayLength { typ: Type, location: Location }, + NonNumericCasted { value: Value, location: Location }, IndexOutOfBounds { index: usize, length: usize, location: Location }, + ExpectedStructToHaveField { value: Value, field_name: String, location: Location }, + TypeUnsupported { typ: Type, location: Location }, + InvalidValueForUnary { value: Value, operator: &'static str, location: Location }, + InvalidValuesForBinary { lhs: Value, rhs: Value, operator: &'static str, location: Location }, + CastToNonNumericType { typ: Type, location: Location }, + + // Perhaps this should be unreachable! due to type checking also preventing this error? + // Currently it and the Continue variant are the only interpreter errors without a Location field + BreakNotInLoop, + ContinueNotInLoop, // These cases are not errors but prevent us from running more code // until the loop can be resumed properly. @@ -95,18 +109,27 @@ enum InterpreterError { Continue, } +#[allow(unused)] type IResult = std::result::Result; +#[allow(unused)] impl<'a> Interpreter<'a> { - fn call_function( + pub(crate) fn new(interner: &'a mut NodeInterner) -> Self { + Self { + interner, + scopes: vec![FxHashMap::default()], + changed_functions: FxHashSet::default(), + changed_globally: false, + in_loop: false, + } + } + + pub(crate) fn call_function( &mut self, function: FuncId, - arguments: Vec, + arguments: Vec<(Value, Location)>, call_location: Location, ) -> IResult { - let modifiers = self.interner.function_modifiers(&function); - assert!(modifiers.is_comptime, "Tried to evaluate non-comptime function!"); - let previous_state = self.enter_function(); let meta = self.interner.function_meta(&function); @@ -122,8 +145,9 @@ impl<'a> Interpreter<'a> { }); } - for ((parameter, typ, _), argument) in meta.parameters.0.clone().iter().zip(arguments) { - self.define_pattern(parameter, typ, argument)?; + let parameters = meta.parameters.0.clone(); + for ((parameter, typ, _), (argument, arg_location)) in parameters.iter().zip(arguments) { + self.define_pattern(parameter, typ, argument, arg_location)?; } let function_body = self.interner.function(&function).as_expr(); @@ -138,7 +162,7 @@ impl<'a> Interpreter<'a> { closure: HirLambda, // TODO: How to define environment here? _environment: Vec, - arguments: Vec, + arguments: Vec<(Value, Location)>, call_location: Location, ) -> IResult { let previous_state = self.enter_function(); @@ -151,8 +175,9 @@ impl<'a> Interpreter<'a> { }); } - for ((parameter, typ), argument) in closure.parameters.iter().zip(arguments) { - self.define_pattern(parameter, typ, argument)?; + let parameters = closure.parameters.iter().zip(arguments); + for ((parameter, typ), (argument, arg_location)) in parameters { + self.define_pattern(parameter, typ, argument, arg_location)?; } let result = self.evaluate(closure.body)?; @@ -164,14 +189,19 @@ impl<'a> Interpreter<'a> { /// Enters a function, pushing a new scope and resetting any required state. /// Returns the previous values of the internal state, to be reset when /// `exit_function` is called. - fn enter_function(&mut self) -> bool { - self.push_scope(); - std::mem::take(&mut self.in_loop) + fn enter_function(&mut self) -> (bool, Vec>) { + // Drain every scope except the global scope + let scope = self.scopes.drain(1..).collect(); + let was_in_loop = std::mem::take(&mut self.in_loop); + (was_in_loop, scope) } - fn exit_function(&mut self, was_in_loop: bool) { - self.pop_scope(); - self.in_loop = was_in_loop; + fn exit_function(&mut self, mut state: (bool, Vec>)) { + self.in_loop = state.0; + + // Keep only the global scope + self.scopes.truncate(1); + self.scopes.append(&mut state.1); } fn push_scope(&mut self) { @@ -182,127 +212,161 @@ impl<'a> Interpreter<'a> { self.scopes.pop(); } - fn current_scope(&self) -> &FxHashMap { - self.scopes.last().unwrap() - } - fn current_scope_mut(&mut self) -> &mut FxHashMap { + // the global scope is always at index zero, so this is always Some self.scopes.last_mut().unwrap() } - fn define_pattern(&mut self, pattern: &HirPattern, typ: &Type, argument: Value) -> IResult<()> { + fn define_pattern( + &mut self, + pattern: &HirPattern, + typ: &Type, + argument: Value, + location: Location, + ) -> IResult<()> { match pattern { - HirPattern::Identifier(identifier) => self.define(identifier.id, typ, argument), - HirPattern::Mutable(pattern, _) => self.define_pattern(pattern, typ, argument), - HirPattern::Tuple(pattern_fields, _) => { - self.type_check(typ, &argument)?; - - match (argument, typ) { - (Value::Tuple(fields), Type::Tuple(type_fields)) - if fields.len() == pattern_fields.len() => + HirPattern::Identifier(identifier) => { + self.define(identifier.id, typ, argument, location) + } + HirPattern::Mutable(pattern, _) => { + self.define_pattern(pattern, typ, argument, location) + } + HirPattern::Tuple(pattern_fields, _) => match (argument, typ) { + (Value::Tuple(fields), Type::Tuple(type_fields)) + if fields.len() == pattern_fields.len() => + { + for ((pattern, typ), argument) in + pattern_fields.iter().zip(type_fields).zip(fields) { - for ((pattern, typ), argument) in - pattern_fields.iter().zip(type_fields).zip(fields) - { - self.define_pattern(pattern, typ, argument)?; - } - return Ok(()); - } - (value, _) => { - Err(InterpreterError::TypeMismatch { expected: typ.clone(), value }) + self.define_pattern(pattern, typ, argument, location)?; } + return Ok(()); } - } + (value, _) => { + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value, location }) + } + }, HirPattern::Struct(struct_type, pattern_fields, _) => { - self.type_check(typ, &argument)?; - self.type_check(struct_type, &argument)?; + self.type_check(typ, &argument, location)?; + self.type_check(struct_type, &argument, location)?; - match (argument, typ) { - (Value::Struct(fields, _), Type::Struct(struct_def, generics)) - if fields.len() == pattern_fields.len() => - { - let struct_def = struct_def.borrow(); + match argument { + Value::Struct(fields, struct_type) if fields.len() == pattern_fields.len() => { for (field_name, field_pattern) in pattern_fields { - let field = fields.get(&field_name.0.contents).unwrap_or_else(|| { - panic!("Expected Struct value with fields {fields:?} to have a field named '{field_name}'") - }); - - let field_type = struct_def.get_field(&field_name.0.contents, generics).unwrap_or_else(|| { - panic!("Expected struct type {typ} to have a field named '{field_name}'") - }).0; - - self.define_pattern(field_pattern, &field_type, field.clone())?; + let field = fields.get(&field_name.0.contents).ok_or_else(|| { + InterpreterError::ExpectedStructToHaveField { + value: Value::Struct(fields.clone(), struct_type.clone()), + field_name: field_name.0.contents.clone(), + location, + } + })?; + + let field_type = field.get_type().into_owned(); + self.define_pattern( + field_pattern, + &field_type, + field.clone(), + location, + )?; } return Ok(()); } - (value, _) => { - Err(InterpreterError::TypeMismatch { expected: typ.clone(), value }) - } + value => Err(InterpreterError::TypeMismatch { + expected: typ.clone(), + value, + location, + }), } } } } - fn define(&mut self, id: DefinitionId, typ: &Type, argument: Value) -> IResult<()> { - self.type_check(typ, &argument)?; + /// Define a new variable in the current scope + fn define( + &mut self, + id: DefinitionId, + typ: &Type, + argument: Value, + location: Location, + ) -> IResult<()> { + self.type_check(typ, &argument, location)?; self.current_scope_mut().insert(id, argument); Ok(()) } - fn lookup(&self, id: DefinitionId) -> IResult { - self.current_scope().get(&id).cloned().ok_or_else(|| InterpreterError::NoValueForId(id)) + /// Mutate an existing variable, potentially from a prior scope. + /// Also type checks the value being assigned + fn checked_mutate( + &mut self, + id: DefinitionId, + typ: &Type, + argument: Value, + 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 }) } - /// Do a quick, shallow type check to catch some obviously wrong cases. - /// The interpreter generally relies on expressions to already be well-typed - /// but this helps catch bugs. It is also worth noting expression types may not - /// correlate 1-1 with non-comptime code. For example, comptime code also allows - /// pointers and unsized data types like strings and (unbounded) vectors. - fn type_check(&self, typ: &Type, value: &Value) -> IResult<()> { - let typ = typ.follow_bindings(); - use crate::IntegerBitSize::*; - use crate::Signedness::*; - - match (value, &typ) { - (Value::Unit, Type::Unit) => (), - (Value::Bool(_), Type::Bool) => (), - (Value::Field(_), Type::FieldElement) => (), - (Value::I8(_), Type::Integer(Signed, Eight)) => (), - (Value::I32(_), Type::Integer(Signed, ThirtyTwo)) => (), - (Value::I64(_), Type::Integer(Signed, SixtyFour)) => (), - (Value::U8(_), Type::Integer(Unsigned, Eight)) => (), - (Value::U32(_), Type::Integer(Unsigned, ThirtyTwo)) => (), - (Value::U64(_), Type::Integer(Unsigned, SixtyFour)) => (), - (Value::String(_), Type::String(_)) => (), - (Value::Function(..), Type::Function(..)) => (), - (Value::Tuple(fields1), Type::Tuple(fields2)) if fields1.len() == fields2.len() => (), - (Value::Struct(..), _) => (), - (Value::Array(..), Type::Array(..)) => (), - (Value::Slice(..), Type::Slice(_)) => (), - (Value::Pointer(_), _) => (), - (Value::Code(_), Type::Code) => (), - _ => { - return Err(InterpreterError::TypeMismatch { expected: typ, value: value.clone() }) + /// Mutate an existing variable, potentially from a prior scope + fn mutate(&mut self, id: DefinitionId, argument: Value, location: Location) -> IResult<()> { + 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 }) + } - Ok(()) + fn lookup(&self, ident: &HirIdent) -> IResult { + 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 }) + } + + fn lookup_id(&self, id: DefinitionId, location: Location) -> IResult { + for scope in self.scopes.iter().rev() { + if let Some(value) = scope.get(&id) { + return Ok(value.clone()); + } + } + + Err(InterpreterError::NoValueForId { id, location }) + } + + fn type_check(&self, typ: &Type, value: &Value, location: Location) -> IResult<()> { + let typ = typ.follow_bindings(); + let value_type = value.get_type(); + + typ.try_unify(&value_type, &mut TypeBindings::new()).map_err(|_| { + InterpreterError::TypeMismatch { expected: typ, value: value.clone(), location } + }) } /// Evaluate an expression and return the result fn evaluate(&mut self, id: ExprId) -> IResult { match self.interner.expression(&id) { - HirExpression::Ident(ident) => self.lookup(ident.id), + HirExpression::Ident(ident) => self.evaluate_ident(ident, id), HirExpression::Literal(literal) => self.evaluate_literal(literal, id), HirExpression::Block(block) => self.evaluate_block(block), - HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix), - HirExpression::Infix(infix) => self.evaluate_infix(infix), + HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix, id), + HirExpression::Infix(infix) => self.evaluate_infix(infix, id), HirExpression::Index(index) => self.evaluate_index(index, id), HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor, id), - HirExpression::MemberAccess(access) => self.evaluate_access(access), + HirExpression::MemberAccess(access) => self.evaluate_access(access, id), HirExpression::Call(call) => self.evaluate_call(call, id), HirExpression::MethodCall(call) => self.evaluate_method_call(call, id), - HirExpression::Cast(cast) => self.evaluate_cast(cast), + HirExpression::Cast(cast) => self.evaluate_cast(cast, id), HirExpression::If(if_) => self.evaluate_if(if_, id), HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), @@ -314,6 +378,38 @@ impl<'a> Interpreter<'a> { } } + fn evaluate_ident(&mut self, ident: HirIdent, id: ExprId) -> IResult { + let definition = self.interner.definition(ident.id); + + match &definition.kind { + DefinitionKind::Function(function_id) => { + let typ = self.interner.id_type(id); + Ok(Value::Function(*function_id, typ)) + } + DefinitionKind::Local(_) => self.lookup(&ident), + DefinitionKind::Global(global_id) => { + let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); + self.evaluate_let(let_)?; + self.lookup(&ident) + } + DefinitionKind::GenericType(type_variable) => { + let value = match &*type_variable.borrow() { + TypeBinding::Unbound(_) => None, + TypeBinding::Bound(binding) => binding.evaluate_to_u64(), + }; + + if let Some(value) = value { + let typ = self.interner.id_type(id); + self.evaluate_integer((value as u128).into(), false, id) + } else { + let location = self.interner.expr_location(&id); + let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); + Err(InterpreterError::NonIntegerArrayLength { typ, location }) + } + } + } + } + fn evaluate_literal(&mut self, literal: HirLiteral, id: ExprId) -> IResult { match literal { HirLiteral::Unit => Ok(Value::Unit), @@ -335,80 +431,88 @@ impl<'a> Interpreter<'a> { id: ExprId, ) -> IResult { let typ = self.interner.id_type(id).follow_bindings(); - if let Type::Integer(sign, bit_size) = &typ { + let location = self.interner.expr_location(&id); + + if let Type::FieldElement = &typ { + Ok(Value::Field(value)) + } else if let Type::Integer(sign, bit_size) = &typ { match (sign, bit_size) { (Signedness::Unsigned, IntegerBitSize::One) => { - panic!("u1 is not supported by the interpreter") + return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Unsigned, IntegerBitSize::Eight) => { - let value: u8 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: u8 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - let value: u32 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: u32 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - let value: u64 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: u64 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } (Signedness::Signed, IntegerBitSize::One) => { - panic!("i1 is not supported by the interpreter") + return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Signed, IntegerBitSize::Eight) => { - let value: i8 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: i8 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { -value } else { value }; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { - let value: i32 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: i32 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { -value } else { value }; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { - let value: i64 = value - .try_to_u64() - .and_then(|value| value.try_into().ok()) - .ok_or_else(|| InterpreterError::IntegerOutOfRangeForType(value, typ))?; + let value: i64 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( + || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; let value = if is_negative { -value } else { value }; Ok(Value::I64(value)) } } } else { - unreachable!("Non-integer integer literal of type {typ}") + Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) } } fn evaluate_block(&mut self, mut block: HirBlockExpression) -> IResult { let last_statement = block.statements.pop(); + self.push_scope(); for statement in block.statements { self.evaluate_statement(statement)?; } - if let Some(statement) = last_statement { + let result = if let Some(statement) = last_statement { self.evaluate_statement(statement) } else { Ok(Value::Unit) - } + }; + + self.pop_scope(); + result } fn evaluate_array(&mut self, array: HirArrayLiteral, id: ExprId) -> IResult { @@ -430,7 +534,8 @@ impl<'a> Interpreter<'a> { let elements = (0..length).map(|_| element.clone()).collect(); Ok(Value::Array(elements, typ)) } else { - Err(InterpreterError::UnableToEvaluateTypeToInteger(length)) + let location = self.interner.expr_location(&id); + Err(InterpreterError::NonIntegerArrayLength { typ: length, location }) } } } @@ -443,7 +548,7 @@ impl<'a> Interpreter<'a> { }) } - fn evaluate_prefix(&mut self, prefix: HirPrefixExpression) -> IResult { + fn evaluate_prefix(&mut self, prefix: HirPrefixExpression, id: ExprId) -> IResult { let rhs = self.evaluate(prefix.rhs)?; match prefix.operator { crate::UnaryOp::Minus => match rhs { @@ -454,7 +559,14 @@ impl<'a> Interpreter<'a> { Value::U8(value) => Ok(Value::U8(0 - value)), Value::U32(value) => Ok(Value::U32(0 - value)), Value::U64(value) => Ok(Value::U64(0 - value)), - other => panic!("Invalid value for unary minus operation: {other:?}"), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::InvalidValueForUnary { + value, + location, + operator: "minus", + }); + } }, crate::UnaryOp::Not => match rhs { Value::Bool(value) => Ok(Value::Bool(!value)), @@ -464,21 +576,37 @@ impl<'a> Interpreter<'a> { Value::U8(value) => Ok(Value::U8(!value)), Value::U32(value) => Ok(Value::U32(!value)), Value::U64(value) => Ok(Value::U64(!value)), - other => panic!("Invalid value for unary not operation: {other:?}"), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::InvalidValueForUnary { + value, + location, + operator: "not", + }); + } }, crate::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), crate::UnaryOp::Dereference { implicitly_added: _ } => match rhs { Value::Pointer(element) => Ok(element.borrow().clone()), - other => panic!("Cannot dereference {other:?}"), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonPointerDereferenced { value, location }); + } }, } } - fn evaluate_infix(&mut self, infix: HirInfixExpression) -> IResult { + fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { let lhs = self.evaluate(infix.lhs)?; let rhs = self.evaluate(infix.rhs)?; // TODO: Need to account for operator overloading + assert!( + self.interner.get_selected_impl_for_expression(id).is_none(), + "Operator overloading is unimplemented in the interpreter" + ); + + use InterpreterError::InvalidValuesForBinary; match infix.operator.kind { BinaryOpKind::Add => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), @@ -488,7 +616,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), - (lhs, rhs) => panic!("Operator (+) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }); + } }, BinaryOpKind::Subtract => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), @@ -498,7 +629,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), - (lhs, rhs) => panic!("Operator (-) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }); + } }, BinaryOpKind::Multiply => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), @@ -508,7 +642,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), - (lhs, rhs) => panic!("Operator (*) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }); + } }, BinaryOpKind::Divide => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), @@ -518,7 +655,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), - (lhs, rhs) => panic!("Operator (/) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }); + } }, BinaryOpKind::Equal => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), @@ -528,7 +668,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => panic!("Operator (==) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }); + } }, BinaryOpKind::NotEqual => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), @@ -538,7 +681,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => panic!("Operator (!=) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }); + } }, BinaryOpKind::Less => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), @@ -548,7 +694,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (lhs, rhs) => panic!("Operator (<) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }); + } }, BinaryOpKind::LessEqual => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), @@ -558,7 +707,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (lhs, rhs) => panic!("Operator (<=) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }); + } }, BinaryOpKind::Greater => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), @@ -568,7 +720,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (lhs, rhs) => panic!("Operator (>) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }); + } }, BinaryOpKind::GreaterEqual => match (lhs, rhs) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), @@ -578,7 +733,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (lhs, rhs) => panic!("Operator (>=) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }); + } }, BinaryOpKind::And => match (lhs, rhs) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), @@ -588,7 +746,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), - (lhs, rhs) => panic!("Operator (&) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }); + } }, BinaryOpKind::Or => match (lhs, rhs) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), @@ -598,7 +759,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), - (lhs, rhs) => panic!("Operator (|) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }); + } }, BinaryOpKind::Xor => match (lhs, rhs) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), @@ -608,7 +772,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), - (lhs, rhs) => panic!("Operator (^) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }); + } }, BinaryOpKind::ShiftRight => match (lhs, rhs) { (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), @@ -617,7 +784,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), - (lhs, rhs) => panic!("Operator (>>) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }); + } }, BinaryOpKind::ShiftLeft => match (lhs, rhs) { (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), @@ -626,7 +796,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), - (lhs, rhs) => panic!("Operator (<<) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }); + } }, BinaryOpKind::Modulo => match (lhs, rhs) { (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), @@ -635,7 +808,10 @@ impl<'a> Interpreter<'a> { (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), - (lhs, rhs) => panic!("Operator (%) invalid for values {lhs:?} and {rhs:?}"), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }); + } }, } } @@ -682,11 +858,8 @@ impl<'a> Interpreter<'a> { }; if index >= collection.len() { - return Err(InterpreterError::IndexOutOfBounds { - index, - length: collection.len(), - location, - }); + use InterpreterError::IndexOutOfBounds; + return Err(IndexOutOfBounds { index, location, length: collection.len() }); } Ok((collection, index)) @@ -710,21 +883,28 @@ impl<'a> Interpreter<'a> { Ok(Value::Struct(fields, typ)) } - fn evaluate_access(&mut self, access: HirMemberAccess) -> IResult { - let fields = match self.evaluate(access.lhs)? { - Value::Struct(fields, _) => fields, - other => panic!("Cannot access fields of a non-struct value: {other:?}"), + fn evaluate_access(&mut self, access: HirMemberAccess, id: ExprId) -> IResult { + let (fields, struct_type) = match self.evaluate(access.lhs)? { + Value::Struct(fields, typ) => (fields, typ), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonTupleOrStructInMemberAccess { value, location }); + } }; - Ok(fields - .get(&access.rhs.0.contents) - .unwrap_or_else(|| panic!("Expected struct to have field {}", access.rhs)) - .clone()) + fields.get(&access.rhs.0.contents).cloned().ok_or_else(|| { + let location = self.interner.expr_location(&id); + let value = Value::Struct(fields, struct_type); + let field_name = access.rhs.0.contents; + InterpreterError::ExpectedStructToHaveField { value, field_name, location } + }) } fn evaluate_call(&mut self, call: HirCallExpression, id: ExprId) -> IResult { let function = self.evaluate(call.func)?; - let arguments = try_vecmap(call.arguments, |arg| self.evaluate(arg))?; + let arguments = try_vecmap(call.arguments, |arg| { + Ok((self.evaluate(arg)?, self.interner.expr_location(&arg))) + })?; let location = self.interner.expr_location(&id); match function { @@ -740,7 +920,9 @@ impl<'a> Interpreter<'a> { id: ExprId, ) -> IResult { let object = self.evaluate(call.object)?; - let arguments = try_vecmap(call.arguments, |arg| self.evaluate(arg))?; + let arguments = try_vecmap(call.arguments, |arg| { + Ok((self.evaluate(arg)?, self.interner.expr_location(&arg))) + })?; let location = self.interner.expr_location(&id); let typ = object.get_type().follow_bindings(); @@ -761,7 +943,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_cast(&mut self, cast: HirCastExpression) -> IResult { + fn evaluate_cast(&mut self, cast: HirCastExpression, id: ExprId) -> IResult { macro_rules! signed_int_to_field { ($x:expr) => {{ // Need to convert the signed integer to an i128 before @@ -786,7 +968,10 @@ impl<'a> Interpreter<'a> { Value::Bool(value) => { (if value { FieldElement::one() } else { FieldElement::zero() }, false) } - other => unreachable!("Cannot cast from non-numeric value '{other:?}'"), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonNumericCasted { value, location }); + } }; macro_rules! cast_to_int { @@ -809,7 +994,8 @@ impl<'a> Interpreter<'a> { } Type::Integer(sign, bit_size) => match (sign, bit_size) { (Signedness::Unsigned, IntegerBitSize::One) => { - panic!("u1 is not supported by the interpreter") + let location = self.interner.expr_location(&id); + Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location }) } (Signedness::Unsigned, IntegerBitSize::Eight) => cast_to_int!(lhs, to_u128, u8, U8), (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { @@ -819,7 +1005,8 @@ impl<'a> Interpreter<'a> { cast_to_int!(lhs, to_u128, u64, U64) } (Signedness::Signed, IntegerBitSize::One) => { - panic!("i1 is not supported by the interpreter") + let location = self.interner.expr_location(&id); + Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location }) } (Signedness::Signed, IntegerBitSize::Eight) => cast_to_int!(lhs, to_i128, i8, I8), (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { @@ -830,7 +1017,10 @@ impl<'a> Interpreter<'a> { } }, Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), - other => unreachable!("Cannot cast to non-numeric type '{other}'"), + typ => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::CastToNonNumericType { typ, location }) + } } } @@ -843,7 +1033,9 @@ impl<'a> Interpreter<'a> { } }; - if condition { + self.push_scope(); + + let result = if condition { if if_.alternative.is_some() { self.evaluate(if_.consequence) } else { @@ -855,7 +1047,10 @@ impl<'a> Interpreter<'a> { Some(alternative) => self.evaluate(alternative), None => Ok(Value::Unit), } - } + }; + + self.pop_scope(); + result } fn evaluate_tuple(&mut self, tuple: Vec) -> IResult { @@ -864,7 +1059,9 @@ impl<'a> Interpreter<'a> { } fn evaluate_lambda(&mut self, lambda: HirLambda, id: ExprId) -> IResult { - let environment = try_vecmap(&lambda.captures, |capture| self.lookup(capture.ident.id))?; + let location = self.interner.expr_location(&id); + let environment = + try_vecmap(&lambda.captures, |capture| self.lookup_id(capture.ident.id, location))?; let typ = self.interner.id_type(id); Ok(Value::Closure(lambda, environment, typ)) @@ -892,7 +1089,8 @@ impl<'a> Interpreter<'a> { fn evaluate_let(&mut self, let_: HirLetStatement) -> IResult { let rhs = self.evaluate(let_.expression)?; - self.define_pattern(&let_.pattern, &let_.r#type, rhs)?; + let location = self.interner.expr_location(&let_.expression); + self.define_pattern(&let_.pattern, &let_.r#type, rhs, location)?; Ok(Value::Unit) } @@ -919,7 +1117,9 @@ impl<'a> Interpreter<'a> { fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { match lvalue { - HirLValue::Ident(ident, typ) => self.define(ident.id, &typ, rhs), + HirLValue::Ident(ident, typ) => { + self.checked_mutate(ident.id, &typ, rhs, ident.location) + } HirLValue::Dereference { lvalue, element_type: _, location } => { match self.evaluate_lvalue(&lvalue)? { Value::Pointer(value) => { @@ -965,7 +1165,7 @@ impl<'a> Interpreter<'a> { fn evaluate_lvalue(&mut self, lvalue: &HirLValue) -> IResult { match lvalue { - HirLValue::Ident(ident, _) => self.lookup(ident.id), + HirLValue::Ident(ident, _) => self.lookup(ident), HirLValue::Dereference { lvalue, element_type: _, location } => { match self.evaluate_lvalue(lvalue)? { Value::Pointer(value) => Ok(value.borrow().clone()), @@ -1014,9 +1214,11 @@ impl<'a> Interpreter<'a> { let (start, make_value) = get_index(self, for_.start_range)?; let (end, _) = get_index(self, for_.end_range)?; + let was_in_loop = std::mem::replace(&mut self.in_loop, true); + self.push_scope(); for i in start..end { - self.current_scope_mut().insert(for_.identifier.id, make_value(i)); + self.mutate(for_.identifier.id, make_value(i), for_.identifier.location)?; match self.evaluate(for_.block) { Ok(_) => (), @@ -1026,6 +1228,8 @@ impl<'a> Interpreter<'a> { } } + self.pop_scope(); + self.in_loop = was_in_loop; Ok(Value::Unit) } @@ -1071,7 +1275,10 @@ impl Value { Value::Array(_, typ) => return Cow::Borrowed(typ), Value::Slice(_, typ) => return Cow::Borrowed(typ), Value::Code(_) => Type::Code, - Value::Pointer(_) => panic!("No type equivalent for pointers yet!"), + Value::Pointer(element) => { + let element = element.borrow().get_type().into_owned(); + Type::MutableReference(Box::new(element)) + } }) } } diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index a8ced7d539e..83aaddaa405 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,2 +1,3 @@ mod hir_to_ast; mod interpreter; +mod tests; diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs new file mode 100644 index 00000000000..016e7079886 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -0,0 +1,166 @@ +#![cfg(test)] + +use noirc_errors::Location; + +use super::interpreter::{Interpreter, InterpreterError, Value}; +use crate::hir::type_check::test::type_check_src_code; + +fn interpret_helper(src: &str, func_namespace: Vec) -> Result { + let (mut interner, main_id) = type_check_src_code(src, func_namespace); + let mut interpreter = Interpreter::new(&mut interner); + + let no_location = Location::dummy(); + interpreter.call_function(main_id, Vec::new(), no_location) +} + +fn interpret(src: &str, func_namespace: Vec) -> Value { + interpret_helper(src, func_namespace).unwrap_or_else(|error| { + panic!("Expected interpreter to exit successfully, but found {error:?}") + }) +} + +fn interpret_expect_error(src: &str, func_namespace: Vec) -> InterpreterError { + interpret_helper(src, func_namespace).expect_err("Expected interpreter to error") +} + +#[test] +fn interpreter_works() { + let program = "fn main() -> pub Field { 3 }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::Field(3u128.into())); +} + +#[test] +fn mutation_works() { + let program = "fn main() -> pub i8 { + let mut x = 3; + x = 4; + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I8(4)); +} + +#[test] +fn mutating_references() { + let program = "fn main() -> pub i32 { + let x = &mut 3; + *x = 4; + *x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I32(4)); +} + +#[test] +fn mutating_mutable_references() { + let program = "fn main() -> pub i64 { + let mut x = &mut 3; + *x = 4; + *x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I64(4)); +} + +#[test] +fn mutating_arrays() { + let program = "fn main() -> pub u8 { + let mut a1 = [1, 2, 3, 4]; + a1[1] = 22; + a1[1] + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U8(22)); +} + +#[test] +fn for_loop() { + let program = "fn main() -> pub u8 { + let mut x = 0; + for i in 0 .. 6 { + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U8(15)); +} + +#[test] +fn for_loop_with_break() { + let program = "unconstrained fn main() -> pub u32 { + let mut x = 0; + for i in 0 .. 6 { + if i == 4 { + break; + } + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U32(6)); +} + +#[test] +fn for_loop_with_continue() { + let program = "unconstrained fn main() -> pub u64 { + let mut x = 0; + for i in 0 .. 6 { + if i == 4 { + continue; + } + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U64(11)); +} + +#[test] +fn assert() { + let program = "fn main() { + assert(1 == 1); + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::Unit); +} + +#[test] +fn assert_fail() { + let program = "fn main() { + assert(1 == 2); + }"; + let result = interpret_expect_error(program, vec!["main".into()]); + assert!(matches!(result, InterpreterError::FailingConstraint { .. })); +} + +#[test] +fn lambda() { + let program = "fn main() -> pub u8 { + let f = |x: u8| x + 1; + f(1) + }"; + let result = interpret(program, vec!["main".into()]); + assert!(matches!(result, Value::U8(2))); +} + +#[test] +fn non_deterministic_recursion() { + let program = " + fn main() -> pub u64 { + fib(10) + } + + fn fib(x: u64) -> u64 { + if x <= 1 { + x + } else { + fib(x - 1) + fib(x - 2) + } + }"; + let result = interpret(program, vec!["main".into(), "fib".into()]); + assert_eq!(result, Value::U64(55)); +} diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index bf20c89f5e1..f5323cd07de 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -423,12 +423,12 @@ impl<'interner> TypeChecker<'interner> { // XXX: These tests are all manual currently. /// We can either build a test apparatus or pass raw code through the resolver #[cfg(test)] -mod test { +pub mod test { use std::collections::{BTreeMap, HashMap}; use std::vec; use fm::FileId; - use iter_extended::vecmap; + use iter_extended::btree_map; use noirc_errors::{Location, Span}; use crate::graph::CrateId; @@ -600,7 +600,7 @@ mod test { "#; - type_check_src_code(src, vec![String::from("main"), String::from("foo")]); + type_check_src_code(src, vec![String::from("main")]); } #[test] fn basic_closure() { @@ -611,7 +611,7 @@ mod test { } "#; - type_check_src_code(src, vec![String::from("main"), String::from("foo")]); + type_check_src_code(src, vec![String::from("main")]); } #[test] @@ -671,8 +671,8 @@ mod test { } } - fn type_check_src_code(src: &str, func_namespace: Vec) { - type_check_src_code_errors_expected(src, func_namespace, 0); + pub fn type_check_src_code(src: &str, func_namespace: Vec) -> (NodeInterner, FuncId) { + type_check_src_code_errors_expected(src, func_namespace, 0) } // This function assumes that there is only one function and this is the @@ -681,7 +681,7 @@ mod test { src: &str, func_namespace: Vec, expected_num_type_check_errs: usize, - ) { + ) -> (NodeInterner, FuncId) { let (program, errors) = parse_program(src); let mut interner = NodeInterner::default(); interner.populate_dummy_operator_traits(); @@ -694,14 +694,16 @@ mod test { errors ); - let main_id = interner.push_test_function_definition("main".into()); + let func_ids = btree_map(&func_namespace, |name| { + (name.to_string(), interner.push_test_function_definition(name.into())) + }); - let func_ids = - vecmap(&func_namespace, |name| interner.push_test_function_definition(name.into())); + let main_id = + *func_ids.get("main").unwrap_or_else(|| func_ids.first_key_value().unwrap().1); let mut path_resolver = TestPathResolver(HashMap::new()); - for (name, id) in func_namespace.into_iter().zip(func_ids.clone()) { - path_resolver.insert_func(name.to_owned(), id); + for (name, id) in func_ids.iter() { + path_resolver.insert_func(name.to_owned(), *id); } let mut def_maps = BTreeMap::new(); @@ -721,20 +723,24 @@ mod test { }, ); - let func_meta = vecmap(program.into_sorted().functions, |nf| { + for nf in program.into_sorted().functions { let resolver = Resolver::new(&mut interner, &path_resolver, &def_maps, file); - let (hir_func, func_meta, resolver_errors) = resolver.resolve_function(nf, main_id); - assert_eq!(resolver_errors, vec![]); - (hir_func, func_meta) - }); - for ((hir_func, meta), func_id) in func_meta.into_iter().zip(func_ids.clone()) { - interner.update_fn(func_id, hir_func); - interner.push_fn_meta(meta, func_id); + let function_id = *func_ids.get(nf.name()).unwrap(); + let (hir_func, func_meta, resolver_errors) = resolver.resolve_function(nf, function_id); + + interner.push_fn_meta(func_meta, function_id); + interner.update_fn(function_id, hir_func); + assert_eq!(resolver_errors, vec![]); } // Type check section - let errors = super::type_check_func(&mut interner, func_ids.first().cloned().unwrap()); + let mut errors = Vec::new(); + + for function in func_ids.values() { + errors.extend(super::type_check_func(&mut interner, *function)); + } + assert_eq!( errors.len(), expected_num_type_check_errs, @@ -743,5 +749,7 @@ mod test { errors.len(), errors ); + + (interner, main_id) } } diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index c2f6031bf6d..eb4ebf3f913 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -260,7 +260,7 @@ impl HirBlockExpression { } /// A variable captured inside a closure -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HirCapturedVar { pub ident: HirIdent, @@ -274,7 +274,7 @@ pub struct HirCapturedVar { pub transitive_capture_index: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HirLambda { pub parameters: Vec<(HirPattern, Type)>, pub return_type: Type, diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 4c9a33d3dc0..37e3651a9b2 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -61,7 +61,7 @@ pub struct HirAssignStatement { #[derive(Debug, Clone)] pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum HirPattern { Identifier(HirIdent), Mutable(Box, Location), From 7121ec7f5c2230a69cb491d3cdeb201a19846537 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 16 Apr 2024 16:21:57 -0400 Subject: [PATCH 13/21] Implement function scopes --- .../noirc_frontend/src/hir/comptime/interpreter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 5f288236b43..6c87e32e5ff 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -192,8 +192,8 @@ impl<'a> Interpreter<'a> { fn enter_function(&mut self) -> (bool, Vec>) { // Drain every scope except the global scope let scope = self.scopes.drain(1..).collect(); - let was_in_loop = std::mem::take(&mut self.in_loop); - (was_in_loop, scope) + self.push_scope(); + (std::mem::take(&mut self.in_loop), scope) } fn exit_function(&mut self, mut state: (bool, Vec>)) { @@ -386,7 +386,7 @@ impl<'a> Interpreter<'a> { let typ = self.interner.id_type(id); Ok(Value::Function(*function_id, typ)) } - DefinitionKind::Local(_) => self.lookup(&ident), + DefinitionKind::Local(_) => dbg!(self.lookup(&ident)), DefinitionKind::Global(global_id) => { let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); self.evaluate_let(let_)?; @@ -1215,10 +1215,10 @@ impl<'a> Interpreter<'a> { let (start, make_value) = get_index(self, for_.start_range)?; let (end, _) = get_index(self, for_.end_range)?; let was_in_loop = std::mem::replace(&mut self.in_loop, true); - self.push_scope(); for i in start..end { - self.mutate(for_.identifier.id, make_value(i), for_.identifier.location)?; + self.push_scope(); + self.current_scope_mut().insert(for_.identifier.id, make_value(i)); match self.evaluate(for_.block) { Ok(_) => (), @@ -1226,9 +1226,9 @@ impl<'a> Interpreter<'a> { Err(InterpreterError::Continue) => continue, Err(other) => return Err(other), } + self.pop_scope(); } - self.pop_scope(); self.in_loop = was_in_loop; Ok(Value::Unit) } From 65fc2e16ff409f8269e23d574959e812377d2945 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 16 Apr 2024 16:27:08 -0400 Subject: [PATCH 14/21] clippy --- .../src/hir/comptime/interpreter.rs | 78 +++++++++---------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 6c87e32e5ff..4f1c9a33bf4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -240,7 +240,7 @@ impl<'a> Interpreter<'a> { { self.define_pattern(pattern, typ, argument, location)?; } - return Ok(()); + Ok(()) } (value, _) => { Err(InterpreterError::TypeMismatch { expected: typ.clone(), value, location }) @@ -269,7 +269,7 @@ impl<'a> Interpreter<'a> { location, )?; } - return Ok(()); + Ok(()) } value => Err(InterpreterError::TypeMismatch { expected: typ.clone(), @@ -442,25 +442,27 @@ impl<'a> Interpreter<'a> { } (Signedness::Unsigned, IntegerBitSize::Eight) => { let value: u8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { let value: u32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { let value: u64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; + value.try_to_u64().ok_or(InterpreterError::IntegerOutOfRangeForType { + value, + typ, + location, + })?; let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } @@ -469,24 +471,24 @@ impl<'a> Interpreter<'a> { } (Signedness::Signed, IntegerBitSize::Eight) => { let value: i8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { let value: i32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { let value: i64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I64(value)) @@ -561,11 +563,11 @@ impl<'a> Interpreter<'a> { Value::U64(value) => Ok(Value::U64(0 - value)), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::InvalidValueForUnary { + Err(InterpreterError::InvalidValueForUnary { value, location, operator: "minus", - }); + }) } }, crate::UnaryOp::Not => match rhs { @@ -578,11 +580,7 @@ impl<'a> Interpreter<'a> { Value::U64(value) => Ok(Value::U64(!value)), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::InvalidValueForUnary { - value, - location, - operator: "not", - }); + Err(InterpreterError::InvalidValueForUnary { value, location, operator: "not" }) } }, crate::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), @@ -590,7 +588,7 @@ impl<'a> Interpreter<'a> { Value::Pointer(element) => Ok(element.borrow().clone()), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::NonPointerDereferenced { value, location }); + Err(InterpreterError::NonPointerDereferenced { value, location }) } }, } @@ -618,7 +616,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }) } }, BinaryOpKind::Subtract => match (lhs, rhs) { @@ -631,7 +629,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }) } }, BinaryOpKind::Multiply => match (lhs, rhs) { @@ -644,7 +642,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }) } }, BinaryOpKind::Divide => match (lhs, rhs) { @@ -657,7 +655,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }) } }, BinaryOpKind::Equal => match (lhs, rhs) { @@ -670,7 +668,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }) } }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -683,7 +681,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }) } }, BinaryOpKind::Less => match (lhs, rhs) { @@ -696,7 +694,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }) } }, BinaryOpKind::LessEqual => match (lhs, rhs) { @@ -709,7 +707,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }) } }, BinaryOpKind::Greater => match (lhs, rhs) { @@ -722,7 +720,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }) } }, BinaryOpKind::GreaterEqual => match (lhs, rhs) { @@ -735,7 +733,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }) } }, BinaryOpKind::And => match (lhs, rhs) { @@ -748,7 +746,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }) } }, BinaryOpKind::Or => match (lhs, rhs) { @@ -761,7 +759,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }) } }, BinaryOpKind::Xor => match (lhs, rhs) { @@ -774,7 +772,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }) } }, BinaryOpKind::ShiftRight => match (lhs, rhs) { @@ -786,7 +784,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }) } }, BinaryOpKind::ShiftLeft => match (lhs, rhs) { @@ -798,7 +796,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }) } }, BinaryOpKind::Modulo => match (lhs, rhs) { @@ -810,7 +808,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }) } }, } @@ -1197,7 +1195,7 @@ impl<'a> Interpreter<'a> { fn evaluate_for(&mut self, for_: HirForStatement) -> IResult { // i128 can store all values from i8 - u64 - let get_index = |this: &mut Self, expr| -> IResult<(i128, fn(i128) -> Value)> { + let get_index = |this: &mut Self, expr| -> IResult<(_, fn(_) -> _)> { match this.evaluate(expr)? { Value::I8(value) => Ok((value as i128, |i| Value::I8(i as i8))), Value::I32(value) => Ok((value as i128, |i| Value::I32(i as i32))), From 2bf6ea7a98910561b6ada294be803fa4c2b8c6ea Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 17 Apr 2024 11:40:10 -0400 Subject: [PATCH 15/21] Add comptime expression & statement --- aztec_macros/src/utils/ast_utils.rs | 2 + compiler/noirc_frontend/src/ast/statement.rs | 53 ++++++++---- compiler/noirc_frontend/src/debug/mod.rs | 3 + .../src/hir/comptime/hir_to_ast.rs | 1 + compiler/noirc_frontend/src/parser/parser.rs | 83 +++++++++++++------ .../src/parser/parser/function.rs | 15 ++-- 6 files changed, 109 insertions(+), 48 deletions(-) diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 1731dfab49c..d0d7077a946 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -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![], })) } @@ -90,6 +91,7 @@ pub fn assignment_with_type( pattern: pattern(name), r#type: make_type(typ), expression: assigned_to, + comptime: false, attributes: vec![], })) } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 753b5a31d32..c46ab39df1e 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -38,6 +38,8 @@ pub enum StatementKind { For(ForLoopStatement), Break, Continue, + /// This statement should be executed at compile-time + Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -47,6 +49,19 @@ pub enum StatementKind { } impl Statement { + pub fn add_semicolon( + mut self, + semi: Option, + 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, @@ -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(_) @@ -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) { @@ -92,9 +112,7 @@ impl Statement { (_, None, true) => StatementKind::Expression(expr), } } - }; - - Statement { kind, span: self.span } + } } } @@ -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. @@ -407,17 +431,9 @@ pub struct LetStatement { pub r#type: UnresolvedType, pub expression: Expression, pub attributes: Vec, -} -impl LetStatement { - pub fn new_let( - (((pattern, r#type), expression), attributes): ( - ((Pattern, UnresolvedType), Expression), - Vec, - ), - ) -> 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)] @@ -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, @@ -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, @@ -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"), } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 67b52071d7b..3e7d123398b 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -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, @@ -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, @@ -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; diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 8ffcbce7d62..4180738799c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -36,6 +36,7 @@ impl StmtId { pattern, r#type, expression, + comptime: false, attributes: Vec::new(), }) } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5706c3ef12f..ba5b9bf1ab0 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -234,14 +234,16 @@ fn implementation() -> impl NoirParser { /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { let p = attributes::attributes() + .then(maybe_comp_time()) .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) .then(ident().map(Pattern::Identifier)); + let p = then_commit(p, optional_type_annotation()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, expression()); - p.validate(|(((attributes, pattern), r#type), expression), span, emit| { + p.validate(|((((attributes, comptime), pattern), r#type), expression), span, emit| { let global_attributes = attributes::validate_secondary_attributes(attributes, span, emit); - LetStatement { pattern, r#type, expression, attributes: global_attributes } + LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } }) .map(TopLevelStatement::Global) } @@ -498,10 +500,11 @@ where assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), - for_loop(expr_no_constructors, statement), + for_loop(expr_no_constructors.clone(), statement.clone()), break_statement(), continue_statement(), return_statement(expr_parser.clone()), + comptime_statement(expr_parser.clone(), expr_no_constructors, statement), expr_parser.map(StatementKind::Expression), )) }) @@ -519,6 +522,35 @@ fn continue_statement() -> impl NoirParser { keyword(Keyword::Continue).to(StatementKind::Continue) } +fn comptime_statement<'a, P1, P2, S>( + expr: P1, + expr_no_constructors: P2, + statement: S, +) -> impl NoirParser + 'a +where + P1: ExprParser + 'a, + P2: ExprParser + 'a, + S: NoirParser + 'a, +{ + 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))) +} + +/// Comptime in an expression position only accepts entire blocks +fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a +where + S: NoirParser + 'a, +{ + keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::Block) +} + fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -700,24 +732,25 @@ fn optional_distinctness() -> impl NoirParser { }) } -fn maybe_comp_time() -> impl NoirParser<()> { +fn maybe_comp_time() -> impl NoirParser { keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("comptime"), + span, + )); } + opt.is_some() }) } fn field_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Field)) + keyword(Keyword::Field) .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) } fn bool_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Bool)) - .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) + keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) } fn string_type() -> impl NoirParser { @@ -744,21 +777,20 @@ fn format_string_type( } fn int_type() -> impl NoirParser { - maybe_comp_time() - .then(filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - })) - .validate(|(_, token), span, emit| { - UnresolvedTypeData::from_int_token(token) - .map(|data| data.with_span(span)) - .unwrap_or_else(|err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }) - }) + filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + }) + .validate(|token, span, emit| { + UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( + |err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }, + ) + }) } fn named_type(type_parser: impl NoirParser) -> impl NoirParser { @@ -1236,6 +1268,7 @@ where }, lambdas::lambda(expr_parser.clone()), block(statement.clone()).map(ExpressionKind::Block), + comptime_expr(statement.clone()), quote(statement), variable(), literal(), diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 18f17065038..e272eac04be 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -1,8 +1,8 @@ use super::{ attributes::{attributes, validate_attributes}, - block, fresh_statement, ident, keyword, nothing, optional_distinctness, optional_visibility, - parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, - self_parameter, where_clause, NoirParser, + block, fresh_statement, ident, keyword, maybe_comp_time, nothing, optional_distinctness, + optional_visibility, parameter_name_recovery, parameter_recovery, parenthesized, parse_type, + pattern, self_parameter, where_clause, NoirParser, }; use crate::parser::labels::ParsingRuleLabel; use crate::parser::spanned; @@ -36,8 +36,8 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { /// function_modifiers: 'unconstrained'? (visibility)? /// /// returns (is_unconstrained, visibility) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility)> { +fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) - .map(|(unconstrained, visibility)| (unconstrained.is_some(), visibility)) + .then(maybe_comp_time()) + .map(|((unconstrained, visibility), comptime)| { + (unconstrained.is_some(), visibility, comptime) + }) } /// non_empty_ident_list: ident ',' non_empty_ident_list From a901d8f72daabf5edcf1e91b402a66615668b0bc Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 17 Apr 2024 12:01:45 -0400 Subject: [PATCH 16/21] Add comptime hir node --- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 4 ++++ compiler/noirc_frontend/src/hir/type_check/stmt.rs | 1 + compiler/noirc_frontend/src/hir_def/stmt.rs | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 479f357126a..4661eb9b3df 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1276,6 +1276,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)) + }, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index fb57aa75f89..4dfc896fb29 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -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 diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 37e3651a9b2..605f25ebfbf 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -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}; @@ -19,6 +19,7 @@ pub enum HirStatement { Continue, Expression(ExprId), Semi(ExprId), + Comptime(StmtId), Error, } From 3443545f27de54b068c934c4f2c04397663ee930 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 10:28:41 -0400 Subject: [PATCH 17/21] Handle comptime node in comptime module --- .../noirc_frontend/src/hir/comptime/hir_to_ast.rs | 3 +++ .../noirc_frontend/src/hir/comptime/interpreter.rs | 13 +++++++++++++ .../noirc_frontend/src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/monomorphization/mod.rs | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 4180738799c..9f9004b5ad1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -65,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 } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 4f1c9a33bf4..3bcd0495ec3 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -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)] @@ -121,6 +125,7 @@ impl<'a> Interpreter<'a> { changed_functions: FxHashSet::default(), changed_globally: false, in_loop: false, + in_comptime_context: false, } } @@ -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) @@ -1246,6 +1252,13 @@ impl<'a> Interpreter<'a> { Err(InterpreterError::ContinueNotInLoop) } } + + fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { + 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 { diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 4661eb9b3df..76544542767 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1279,7 +1279,7 @@ impl<'a> Resolver<'a> { StatementKind::Comptime(statement) => { let statement = self.resolve_stmt(*statement, span); HirStatement::Comptime(self.interner.push_stmt(statement)) - }, + } } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2cccc18fb09..78b6508465d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -618,6 +618,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"), } } From 55c28b4597e7261e5b8cca6a5c7c0fa1eb9a8a59 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:17:55 -0400 Subject: [PATCH 18/21] Add case to tooling --- tooling/nargo_fmt/src/visitor/stmt.rs | 167 +++++++++++++------------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index ee8cc990e0e..612330ad3a3 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,7 +1,8 @@ use std::iter::zip; use noirc_frontend::{ - ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, StatementKind, + macros_api::Span, ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, + StatementKind, }; use crate::{rewrite, visitor::expr::wrap_exprs}; @@ -14,92 +15,94 @@ impl super::FmtVisitor<'_> { for (Statement { kind, span }, index) in zip(stmts, 1..) { let is_last = index == len; + self.visit_stmt(kind, span, is_last); + self.last_position = span.end(); + } + } - match kind { - StatementKind::Expression(expr) => self.visit_expr( - expr, - if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, - ), - StatementKind::Semi(expr) => { - self.visit_expr(expr, ExpressionType::Statement); - self.push_str(";"); - } - StatementKind::Let(let_stmt) => { - let let_str = - self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - - let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - - self.push_rewrite(format!("{let_str} {expr_str};"), span); - } - StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { - let mut nested_shape = self.shape(); - let shape = nested_shape; - - nested_shape.indent.block_indent(self.config); - - let message = message.map_or(String::new(), |message| { - let message = rewrite::sub_expr(self, nested_shape, message); - format!(", {message}") - }); - - let (callee, args) = match kind { - ConstrainKind::Assert | ConstrainKind::Constrain => { - let assertion = rewrite::sub_expr(self, nested_shape, expr); - let args = format!("{assertion}{message}"); - - ("assert", args) - } - ConstrainKind::AssertEq => { - if let ExpressionKind::Infix(infix) = expr.kind { - let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); - let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + fn visit_stmt(&mut self, kind: StatementKind, span: Span, is_last: bool) { + match kind { + StatementKind::Expression(expr) => self.visit_expr( + expr, + if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, + ), + StatementKind::Semi(expr) => { + self.visit_expr(expr, ExpressionType::Statement); + self.push_str(";"); + } + StatementKind::Let(let_stmt) => { + let let_str = self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - let args = format!("{lhs}, {rhs}{message}"); + let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - ("assert_eq", args) - } else { - unreachable!() - } + self.push_rewrite(format!("{let_str} {expr_str};"), span); + } + StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { + let mut nested_shape = self.shape(); + let shape = nested_shape; + + nested_shape.indent.block_indent(self.config); + + let message = message.map_or(String::new(), |message| { + let message = rewrite::sub_expr(self, nested_shape, message); + format!(", {message}") + }); + + let (callee, args) = match kind { + ConstrainKind::Assert | ConstrainKind::Constrain => { + let assertion = rewrite::sub_expr(self, nested_shape, expr); + let args = format!("{assertion}{message}"); + + ("assert", args) + } + ConstrainKind::AssertEq => { + if let ExpressionKind::Infix(infix) = expr.kind { + let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); + let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + + let args = format!("{lhs}, {rhs}{message}"); + + ("assert_eq", args) + } else { + unreachable!() } - }; - - let args = wrap_exprs( - "(", - ")", - args, - nested_shape, - shape, - NewlineMode::IfContainsNewLineAndWidth, - ); - let constrain = format!("{callee}{args};"); - - self.push_rewrite(constrain, span); - } - StatementKind::For(for_stmt) => { - let identifier = self.slice(for_stmt.identifier.span()); - let range = match for_stmt.range { - ForRange::Range(start, end) => format!( - "{}..{}", - rewrite::sub_expr(self, self.shape(), start), - rewrite::sub_expr(self, self.shape(), end) - ), - ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), - }; - let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); - - let result = format!("for {identifier} in {range} {block}"); - self.push_rewrite(result, span); - } - StatementKind::Assign(_) => { - self.push_rewrite(self.slice(span).to_string(), span); - } - StatementKind::Error => unreachable!(), - StatementKind::Break => self.push_rewrite("break;".into(), span), - StatementKind::Continue => self.push_rewrite("continue;".into(), span), + } + }; + + let args = wrap_exprs( + "(", + ")", + args, + nested_shape, + shape, + NewlineMode::IfContainsNewLineAndWidth, + ); + let constrain = format!("{callee}{args};"); + + self.push_rewrite(constrain, span); } - - self.last_position = span.end(); + StatementKind::For(for_stmt) => { + let identifier = self.slice(for_stmt.identifier.span()); + let range = match for_stmt.range { + ForRange::Range(start, end) => format!( + "{}..{}", + rewrite::sub_expr(self, self.shape(), start), + rewrite::sub_expr(self, self.shape(), end) + ), + ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), + }; + let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); + + let result = format!("for {identifier} in {range} {block}"); + self.push_rewrite(result, span); + } + StatementKind::Assign(_) => { + self.push_rewrite(self.slice(span).to_string(), span); + } + 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), } } } From 9ebf902fe67b684216298e82b6f5b4ae12f0c53d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:26:00 -0400 Subject: [PATCH 19/21] Fix merge --- compiler/noirc_frontend/src/parser/parser/function.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index bf33aa7484c..e272eac04be 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -36,7 +36,6 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser Date: Thu, 18 Apr 2024 12:48:23 -0400 Subject: [PATCH 20/21] Add missed case --- compiler/noirc_frontend/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c4f0a8d67ba..31bf2245b1f 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -780,6 +780,7 @@ mod test { HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), }; let expr = interner.expression(&expr_id); From 824c4b2bc425041b2b5c4ab28ec7ad6169ce0fe5 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:50:36 -0400 Subject: [PATCH 21/21] Remove comptime from parse tests --- compiler/noirc_frontend/src/parser/parser/function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index e272eac04be..074c902ff7b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -175,8 +175,8 @@ mod test { "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(f: Field, y : T) where T: SomeTrait {}", "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}",