From 237418f63b809a4d3f60dce3fa3f81a73e4eb116 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 10 Apr 2024 16:02:59 -0400 Subject: [PATCH 01/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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 9d4983f725c746cdc48751170a2950833c3aa132 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 17 Apr 2024 11:58:18 -0400 Subject: [PATCH 15/18] Update compiler/noirc_frontend/src/hir/comptime/interpreter.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/hir/comptime/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 4f1c9a33bf4..ae57d7fbc9d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -4,7 +4,7 @@ use acvm::FieldElement; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::{FxHashMap as HashMap, FxHashSet}; use crate::{ hir_def::{ From 0e594e00e8b15efd068f68a9f984b52f19b31215 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 17 Apr 2024 11:58:26 -0400 Subject: [PATCH 16/18] Update compiler/noirc_frontend/src/hir/comptime/interpreter.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/hir/comptime/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index ae57d7fbc9d..441a4ad1a33 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -33,7 +33,7 @@ pub(crate) struct Interpreter<'interner> { /// 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>, + scopes: Vec>, /// True if we've expanded any macros into any functions and will need /// to redo name resolution & type checking for that function. From 300b6ea787ed72a3f4c1118a2b91aff3addaf10c Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 17 Apr 2024 11:58:32 -0400 Subject: [PATCH 17/18] Update compiler/noirc_frontend/src/hir/comptime/interpreter.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/hir/comptime/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 441a4ad1a33..eecfe64b68f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -117,7 +117,7 @@ impl<'a> Interpreter<'a> { pub(crate) fn new(interner: &'a mut NodeInterner) -> Self { Self { interner, - scopes: vec![FxHashMap::default()], + scopes: vec![HashMap::default()], changed_functions: FxHashSet::default(), changed_globally: false, in_loop: false, From a8d59df4ef374034dbf1d1abeb7a75b93c2b9e55 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 17 Apr 2024 12:03:13 -0400 Subject: [PATCH 18/18] No Fx --- .../src/hir/comptime/interpreter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index eecfe64b68f..81050073008 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -4,7 +4,7 @@ use acvm::FieldElement; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; -use rustc_hash::{FxHashMap as HashMap, FxHashSet}; +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ hir_def::{ @@ -37,7 +37,7 @@ pub(crate) struct Interpreter<'interner> { /// 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, + changed_functions: HashSet, /// True if we've expanded any macros into global scope and will need /// to redo name resolution & type checking for everything. @@ -62,7 +62,7 @@ pub(crate) enum Value { Function(FuncId, Type), Closure(HirLambda, Vec, Type), Tuple(Vec), - Struct(FxHashMap, Value>, Type), + Struct(HashMap, Value>, Type), Pointer(Shared), Array(Vector, Type), Slice(Vector, Type), @@ -118,7 +118,7 @@ impl<'a> Interpreter<'a> { Self { interner, scopes: vec![HashMap::default()], - changed_functions: FxHashSet::default(), + changed_functions: HashSet::default(), changed_globally: false, in_loop: false, } @@ -189,14 +189,14 @@ 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, Vec>) { + fn enter_function(&mut self) -> (bool, Vec>) { // Drain every scope except the global scope let scope = self.scopes.drain(1..).collect(); self.push_scope(); (std::mem::take(&mut self.in_loop), scope) } - fn exit_function(&mut self, mut state: (bool, Vec>)) { + fn exit_function(&mut self, mut state: (bool, Vec>)) { self.in_loop = state.0; // Keep only the global scope @@ -205,14 +205,14 @@ impl<'a> Interpreter<'a> { } fn push_scope(&mut self) { - self.scopes.push(FxHashMap::default()); + self.scopes.push(HashMap::default()); } fn pop_scope(&mut self) { self.scopes.pop(); } - fn current_scope_mut(&mut self) -> &mut FxHashMap { + fn current_scope_mut(&mut self) -> &mut HashMap { // the global scope is always at index zero, so this is always Some self.scopes.last_mut().unwrap() }