From 237418f63b809a4d3f60dce3fa3f81a73e4eb116 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 10 Apr 2024 16:02:59 -0400 Subject: [PATCH 01/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] clippy --- .../src/hir/comptime/interpreter.rs | 78 +++++++++---------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 6c87e32e5ff..4f1c9a33bf4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -240,7 +240,7 @@ impl<'a> Interpreter<'a> { { self.define_pattern(pattern, typ, argument, location)?; } - return Ok(()); + Ok(()) } (value, _) => { Err(InterpreterError::TypeMismatch { expected: typ.clone(), value, location }) @@ -269,7 +269,7 @@ impl<'a> Interpreter<'a> { location, )?; } - return Ok(()); + Ok(()) } value => Err(InterpreterError::TypeMismatch { expected: typ.clone(), @@ -442,25 +442,27 @@ impl<'a> Interpreter<'a> { } (Signedness::Unsigned, IntegerBitSize::Eight) => { let value: u8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { let value: u32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { let value: u64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; + value.try_to_u64().ok_or(InterpreterError::IntegerOutOfRangeForType { + value, + typ, + location, + })?; let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } @@ -469,24 +471,24 @@ impl<'a> Interpreter<'a> { } (Signedness::Signed, IntegerBitSize::Eight) => { let value: i8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { let value: i32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { let value: i64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else( - || InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, )?; let value = if is_negative { -value } else { value }; Ok(Value::I64(value)) @@ -561,11 +563,11 @@ impl<'a> Interpreter<'a> { Value::U64(value) => Ok(Value::U64(0 - value)), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::InvalidValueForUnary { + Err(InterpreterError::InvalidValueForUnary { value, location, operator: "minus", - }); + }) } }, crate::UnaryOp::Not => match rhs { @@ -578,11 +580,7 @@ impl<'a> Interpreter<'a> { Value::U64(value) => Ok(Value::U64(!value)), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::InvalidValueForUnary { - value, - location, - operator: "not", - }); + Err(InterpreterError::InvalidValueForUnary { value, location, operator: "not" }) } }, crate::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), @@ -590,7 +588,7 @@ impl<'a> Interpreter<'a> { Value::Pointer(element) => Ok(element.borrow().clone()), value => { let location = self.interner.expr_location(&id); - return Err(InterpreterError::NonPointerDereferenced { value, location }); + Err(InterpreterError::NonPointerDereferenced { value, location }) } }, } @@ -618,7 +616,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }) } }, BinaryOpKind::Subtract => match (lhs, rhs) { @@ -631,7 +629,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }) } }, BinaryOpKind::Multiply => match (lhs, rhs) { @@ -644,7 +642,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }) } }, BinaryOpKind::Divide => match (lhs, rhs) { @@ -657,7 +655,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }) } }, BinaryOpKind::Equal => match (lhs, rhs) { @@ -670,7 +668,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }) } }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -683,7 +681,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }) } }, BinaryOpKind::Less => match (lhs, rhs) { @@ -696,7 +694,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }) } }, BinaryOpKind::LessEqual => match (lhs, rhs) { @@ -709,7 +707,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }) } }, BinaryOpKind::Greater => match (lhs, rhs) { @@ -722,7 +720,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }) } }, BinaryOpKind::GreaterEqual => match (lhs, rhs) { @@ -735,7 +733,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }) } }, BinaryOpKind::And => match (lhs, rhs) { @@ -748,7 +746,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }) } }, BinaryOpKind::Or => match (lhs, rhs) { @@ -761,7 +759,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }) } }, BinaryOpKind::Xor => match (lhs, rhs) { @@ -774,7 +772,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }) } }, BinaryOpKind::ShiftRight => match (lhs, rhs) { @@ -786,7 +784,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }) } }, BinaryOpKind::ShiftLeft => match (lhs, rhs) { @@ -798,7 +796,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }) } }, BinaryOpKind::Modulo => match (lhs, rhs) { @@ -810,7 +808,7 @@ impl<'a> Interpreter<'a> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), (lhs, rhs) => { let location = self.interner.expr_location(&id); - return Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }) } }, } @@ -1197,7 +1195,7 @@ impl<'a> Interpreter<'a> { fn evaluate_for(&mut self, for_: HirForStatement) -> IResult { // i128 can store all values from i8 - u64 - let get_index = |this: &mut Self, expr| -> IResult<(i128, fn(i128) -> Value)> { + let get_index = |this: &mut Self, expr| -> IResult<(_, fn(_) -> _)> { match this.evaluate(expr)? { Value::I8(value) => Ok((value as i128, |i| Value::I8(i as i8))), Value::I32(value) => Ok((value as i128, |i| Value::I32(i as i32))), From 2bf6ea7a98910561b6ada294be803fa4c2b8c6ea Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 17 Apr 2024 11:40:10 -0400 Subject: [PATCH 15/29] Add comptime expression & statement --- aztec_macros/src/utils/ast_utils.rs | 2 + compiler/noirc_frontend/src/ast/statement.rs | 53 ++++++++---- compiler/noirc_frontend/src/debug/mod.rs | 3 + .../src/hir/comptime/hir_to_ast.rs | 1 + compiler/noirc_frontend/src/parser/parser.rs | 83 +++++++++++++------ .../src/parser/parser/function.rs | 15 ++-- 6 files changed, 109 insertions(+), 48 deletions(-) diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 1731dfab49c..d0d7077a946 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -66,6 +66,7 @@ pub fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { pattern: mutable(name), r#type: make_type(UnresolvedTypeData::Unspecified), expression: assigned_to, + comptime: false, attributes: vec![], })) } @@ -90,6 +91,7 @@ pub fn assignment_with_type( pattern: pattern(name), r#type: make_type(typ), expression: assigned_to, + comptime: false, attributes: vec![], })) } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 753b5a31d32..c46ab39df1e 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -38,6 +38,8 @@ pub enum StatementKind { For(ForLoopStatement), Break, Continue, + /// This statement should be executed at compile-time + Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -47,6 +49,19 @@ pub enum StatementKind { } impl Statement { + pub fn add_semicolon( + mut self, + semi: Option, + span: Span, + last_statement_in_block: bool, + emit_error: &mut dyn FnMut(ParserError), + ) -> Self { + self.kind = self.kind.add_semicolon(semi, span, last_statement_in_block, emit_error); + self + } +} + +impl StatementKind { pub fn add_semicolon( self, semi: Option, @@ -57,7 +72,7 @@ impl Statement { let missing_semicolon = ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span); - let kind = match self.kind { + match self { StatementKind::Let(_) | StatementKind::Constrain(_) | StatementKind::Assign(_) @@ -69,10 +84,15 @@ impl Statement { if semi.is_none() { emit_error(missing_semicolon); } - self.kind + self + } + StatementKind::Comptime(mut statement) => { + *statement = + statement.add_semicolon(semi, span, last_statement_in_block, emit_error); + StatementKind::Comptime(statement) } // A semicolon on a for loop is optional and does nothing - StatementKind::For(_) => self.kind, + StatementKind::For(_) => self, StatementKind::Expression(expr) => { match (&expr.kind, semi, last_statement_in_block) { @@ -92,9 +112,7 @@ impl Statement { (_, None, true) => StatementKind::Expression(expr), } } - }; - - Statement { kind, span: self.span } + } } } @@ -108,7 +126,13 @@ impl StatementKind { pub fn new_let( ((pattern, r#type), expression): ((Pattern, UnresolvedType), Expression), ) -> StatementKind { - StatementKind::Let(LetStatement { pattern, r#type, expression, attributes: vec![] }) + StatementKind::Let(LetStatement { + pattern, + r#type, + expression, + comptime: false, + attributes: vec![], + }) } /// Create a Statement::Assign value, desugaring any combined operators like += if needed. @@ -407,17 +431,9 @@ pub struct LetStatement { pub r#type: UnresolvedType, pub expression: Expression, pub attributes: Vec, -} -impl LetStatement { - pub fn new_let( - (((pattern, r#type), expression), attributes): ( - ((Pattern, UnresolvedType), Expression), - Vec, - ), - ) -> LetStatement { - LetStatement { pattern, r#type, expression, attributes } - } + // True if this should only be run during compile-time + pub comptime: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -573,6 +589,7 @@ impl ForRange { pattern: Pattern::Identifier(array_ident.clone()), r#type: UnresolvedType::unspecified(), expression: array, + comptime: false, attributes: vec![], }), span: array_span, @@ -616,6 +633,7 @@ impl ForRange { pattern: Pattern::Identifier(identifier), r#type: UnresolvedType::unspecified(), expression: Expression::new(loop_element, array_span), + comptime: false, attributes: vec![], }), span: array_span, @@ -666,6 +684,7 @@ impl Display for StatementKind { StatementKind::For(for_loop) => for_loop.fmt(f), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), + StatementKind::Comptime(statement) => write!(f, "comptime {statement}"), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 67b52071d7b..3e7d123398b 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -145,6 +145,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), r#type: ast::UnresolvedType::unspecified(), expression: ret_expr.clone(), + comptime: false, attributes: vec![], }), span: ret_expr.span, @@ -243,6 +244,7 @@ impl DebugInstrumenter { kind: ast::StatementKind::Let(ast::LetStatement { pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), r#type: ast::UnresolvedType::unspecified(), + comptime: false, expression: ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: block_stmts, @@ -275,6 +277,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), r#type: ast::UnresolvedType::unspecified(), expression: assign_stmt.expression.clone(), + comptime: false, attributes: vec![], }); let expression_span = assign_stmt.expression.span; diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 8ffcbce7d62..4180738799c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -36,6 +36,7 @@ impl StmtId { pattern, r#type, expression, + comptime: false, attributes: Vec::new(), }) } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5706c3ef12f..ba5b9bf1ab0 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -234,14 +234,16 @@ fn implementation() -> impl NoirParser { /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { let p = attributes::attributes() + .then(maybe_comp_time()) .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) .then(ident().map(Pattern::Identifier)); + let p = then_commit(p, optional_type_annotation()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, expression()); - p.validate(|(((attributes, pattern), r#type), expression), span, emit| { + p.validate(|((((attributes, comptime), pattern), r#type), expression), span, emit| { let global_attributes = attributes::validate_secondary_attributes(attributes, span, emit); - LetStatement { pattern, r#type, expression, attributes: global_attributes } + LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } }) .map(TopLevelStatement::Global) } @@ -498,10 +500,11 @@ where assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), - for_loop(expr_no_constructors, statement), + for_loop(expr_no_constructors.clone(), statement.clone()), break_statement(), continue_statement(), return_statement(expr_parser.clone()), + comptime_statement(expr_parser.clone(), expr_no_constructors, statement), expr_parser.map(StatementKind::Expression), )) }) @@ -519,6 +522,35 @@ fn continue_statement() -> impl NoirParser { keyword(Keyword::Continue).to(StatementKind::Continue) } +fn comptime_statement<'a, P1, P2, S>( + expr: P1, + expr_no_constructors: P2, + statement: S, +) -> impl NoirParser + 'a +where + P1: ExprParser + 'a, + P2: ExprParser + 'a, + S: NoirParser + 'a, +{ + keyword(Keyword::CompTime) + .ignore_then(choice(( + declaration(expr), + for_loop(expr_no_constructors, statement.clone()), + block(statement).map_with_span(|block, span| { + StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) + }), + ))) + .map(|statement| StatementKind::Comptime(Box::new(statement))) +} + +/// Comptime in an expression position only accepts entire blocks +fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a +where + S: NoirParser + 'a, +{ + keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::Block) +} + fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -700,24 +732,25 @@ fn optional_distinctness() -> impl NoirParser { }) } -fn maybe_comp_time() -> impl NoirParser<()> { +fn maybe_comp_time() -> impl NoirParser { keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("comptime"), + span, + )); } + opt.is_some() }) } fn field_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Field)) + keyword(Keyword::Field) .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) } fn bool_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Bool)) - .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) + keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) } fn string_type() -> impl NoirParser { @@ -744,21 +777,20 @@ fn format_string_type( } fn int_type() -> impl NoirParser { - maybe_comp_time() - .then(filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - })) - .validate(|(_, token), span, emit| { - UnresolvedTypeData::from_int_token(token) - .map(|data| data.with_span(span)) - .unwrap_or_else(|err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }) - }) + filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + }) + .validate(|token, span, emit| { + UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( + |err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }, + ) + }) } fn named_type(type_parser: impl NoirParser) -> impl NoirParser { @@ -1236,6 +1268,7 @@ where }, lambdas::lambda(expr_parser.clone()), block(statement.clone()).map(ExpressionKind::Block), + comptime_expr(statement.clone()), quote(statement), variable(), literal(), diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 18f17065038..e272eac04be 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -1,8 +1,8 @@ use super::{ attributes::{attributes, validate_attributes}, - block, fresh_statement, ident, keyword, nothing, optional_distinctness, optional_visibility, - parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, - self_parameter, where_clause, NoirParser, + block, fresh_statement, ident, keyword, maybe_comp_time, nothing, optional_distinctness, + optional_visibility, parameter_name_recovery, parameter_recovery, parenthesized, parse_type, + pattern, self_parameter, where_clause, NoirParser, }; use crate::parser::labels::ParsingRuleLabel; use crate::parser::spanned; @@ -36,8 +36,8 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { /// function_modifiers: 'unconstrained'? (visibility)? /// /// returns (is_unconstrained, visibility) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility)> { +fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) - .map(|(unconstrained, visibility)| (unconstrained.is_some(), visibility)) + .then(maybe_comp_time()) + .map(|((unconstrained, visibility), comptime)| { + (unconstrained.is_some(), visibility, comptime) + }) } /// non_empty_ident_list: ident ',' non_empty_ident_list From a901d8f72daabf5edcf1e91b402a66615668b0bc Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 17 Apr 2024 12:01:45 -0400 Subject: [PATCH 16/29] Add comptime hir node --- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 4 ++++ compiler/noirc_frontend/src/hir/type_check/stmt.rs | 1 + compiler/noirc_frontend/src/hir_def/stmt.rs | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 479f357126a..4661eb9b3df 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1276,6 +1276,10 @@ impl<'a> Resolver<'a> { HirStatement::Continue } StatementKind::Error => HirStatement::Error, + StatementKind::Comptime(statement) => { + let statement = self.resolve_stmt(*statement, span); + HirStatement::Comptime(self.interner.push_stmt(statement)) + }, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index fb57aa75f89..4dfc896fb29 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -51,6 +51,7 @@ impl<'interner> TypeChecker<'interner> { HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), HirStatement::For(for_loop) => self.check_for_loop(for_loop), + HirStatement::Comptime(statement) => return self.check_statement(&statement), HirStatement::Break | HirStatement::Continue | HirStatement::Error => (), } Type::Unit diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 37e3651a9b2..605f25ebfbf 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,6 +1,6 @@ use super::expr::HirIdent; use crate::macros_api::SecondaryAttribute; -use crate::node_interner::ExprId; +use crate::node_interner::{ExprId, StmtId}; use crate::{Ident, Type}; use fm::FileId; use noirc_errors::{Location, Span}; @@ -19,6 +19,7 @@ pub enum HirStatement { Continue, Expression(ExprId), Semi(ExprId), + Comptime(StmtId), Error, } From 3443545f27de54b068c934c4f2c04397663ee930 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 10:28:41 -0400 Subject: [PATCH 17/29] Handle comptime node in comptime module --- .../noirc_frontend/src/hir/comptime/hir_to_ast.rs | 3 +++ .../noirc_frontend/src/hir/comptime/interpreter.rs | 13 +++++++++++++ .../noirc_frontend/src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/monomorphization/mod.rs | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 4180738799c..9f9004b5ad1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -65,6 +65,9 @@ impl StmtId { HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), HirStatement::Error => StatementKind::Error, + HirStatement::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement.to_ast(interner).kind)) + } }; Statement { kind, span } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 4f1c9a33bf4..3bcd0495ec3 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -44,6 +44,10 @@ pub(crate) struct Interpreter<'interner> { changed_globally: bool, in_loop: bool, + + /// True if we're currently in a compile-time context. + /// If this is false code is skipped over instead of executed. + in_comptime_context: bool, } #[allow(unused)] @@ -121,6 +125,7 @@ impl<'a> Interpreter<'a> { changed_functions: FxHashSet::default(), changed_globally: false, in_loop: false, + in_comptime_context: false, } } @@ -1074,6 +1079,7 @@ impl<'a> Interpreter<'a> { HirStatement::Break => self.evaluate_break(), HirStatement::Continue => self.evaluate_continue(), HirStatement::Expression(expression) => self.evaluate(expression), + HirStatement::Comptime(statement) => self.evaluate_comptime(statement), HirStatement::Semi(expression) => { self.evaluate(expression)?; Ok(Value::Unit) @@ -1246,6 +1252,13 @@ impl<'a> Interpreter<'a> { Err(InterpreterError::ContinueNotInLoop) } } + + fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { + let was_in_comptime = std::mem::replace(&mut self.in_comptime_context, true); + let result = self.evaluate_statement(statement); + self.in_comptime_context = was_in_comptime; + result + } } impl Value { diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 4661eb9b3df..76544542767 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1279,7 +1279,7 @@ impl<'a> Resolver<'a> { StatementKind::Comptime(statement) => { let statement = self.resolve_stmt(*statement, span); HirStatement::Comptime(self.interner.push_stmt(statement)) - }, + } } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2cccc18fb09..78b6508465d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -618,6 +618,9 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Break => Ok(ast::Expression::Break), HirStatement::Continue => Ok(ast::Expression::Continue), HirStatement::Error => unreachable!(), + + // All `comptime` statements & expressions should be removed before runtime. + HirStatement::Comptime(_) => unreachable!("comptime statement in runtime code"), } } From 55c28b4597e7261e5b8cca6a5c7c0fa1eb9a8a59 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:17:55 -0400 Subject: [PATCH 18/29] Add case to tooling --- tooling/nargo_fmt/src/visitor/stmt.rs | 167 +++++++++++++------------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index ee8cc990e0e..612330ad3a3 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,7 +1,8 @@ use std::iter::zip; use noirc_frontend::{ - ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, StatementKind, + macros_api::Span, ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, + StatementKind, }; use crate::{rewrite, visitor::expr::wrap_exprs}; @@ -14,92 +15,94 @@ impl super::FmtVisitor<'_> { for (Statement { kind, span }, index) in zip(stmts, 1..) { let is_last = index == len; + self.visit_stmt(kind, span, is_last); + self.last_position = span.end(); + } + } - match kind { - StatementKind::Expression(expr) => self.visit_expr( - expr, - if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, - ), - StatementKind::Semi(expr) => { - self.visit_expr(expr, ExpressionType::Statement); - self.push_str(";"); - } - StatementKind::Let(let_stmt) => { - let let_str = - self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - - let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - - self.push_rewrite(format!("{let_str} {expr_str};"), span); - } - StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { - let mut nested_shape = self.shape(); - let shape = nested_shape; - - nested_shape.indent.block_indent(self.config); - - let message = message.map_or(String::new(), |message| { - let message = rewrite::sub_expr(self, nested_shape, message); - format!(", {message}") - }); - - let (callee, args) = match kind { - ConstrainKind::Assert | ConstrainKind::Constrain => { - let assertion = rewrite::sub_expr(self, nested_shape, expr); - let args = format!("{assertion}{message}"); - - ("assert", args) - } - ConstrainKind::AssertEq => { - if let ExpressionKind::Infix(infix) = expr.kind { - let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); - let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + fn visit_stmt(&mut self, kind: StatementKind, span: Span, is_last: bool) { + match kind { + StatementKind::Expression(expr) => self.visit_expr( + expr, + if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, + ), + StatementKind::Semi(expr) => { + self.visit_expr(expr, ExpressionType::Statement); + self.push_str(";"); + } + StatementKind::Let(let_stmt) => { + let let_str = self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - let args = format!("{lhs}, {rhs}{message}"); + let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - ("assert_eq", args) - } else { - unreachable!() - } + self.push_rewrite(format!("{let_str} {expr_str};"), span); + } + StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { + let mut nested_shape = self.shape(); + let shape = nested_shape; + + nested_shape.indent.block_indent(self.config); + + let message = message.map_or(String::new(), |message| { + let message = rewrite::sub_expr(self, nested_shape, message); + format!(", {message}") + }); + + let (callee, args) = match kind { + ConstrainKind::Assert | ConstrainKind::Constrain => { + let assertion = rewrite::sub_expr(self, nested_shape, expr); + let args = format!("{assertion}{message}"); + + ("assert", args) + } + ConstrainKind::AssertEq => { + if let ExpressionKind::Infix(infix) = expr.kind { + let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); + let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + + let args = format!("{lhs}, {rhs}{message}"); + + ("assert_eq", args) + } else { + unreachable!() } - }; - - let args = wrap_exprs( - "(", - ")", - args, - nested_shape, - shape, - NewlineMode::IfContainsNewLineAndWidth, - ); - let constrain = format!("{callee}{args};"); - - self.push_rewrite(constrain, span); - } - StatementKind::For(for_stmt) => { - let identifier = self.slice(for_stmt.identifier.span()); - let range = match for_stmt.range { - ForRange::Range(start, end) => format!( - "{}..{}", - rewrite::sub_expr(self, self.shape(), start), - rewrite::sub_expr(self, self.shape(), end) - ), - ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), - }; - let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); - - let result = format!("for {identifier} in {range} {block}"); - self.push_rewrite(result, span); - } - StatementKind::Assign(_) => { - self.push_rewrite(self.slice(span).to_string(), span); - } - StatementKind::Error => unreachable!(), - StatementKind::Break => self.push_rewrite("break;".into(), span), - StatementKind::Continue => self.push_rewrite("continue;".into(), span), + } + }; + + let args = wrap_exprs( + "(", + ")", + args, + nested_shape, + shape, + NewlineMode::IfContainsNewLineAndWidth, + ); + let constrain = format!("{callee}{args};"); + + self.push_rewrite(constrain, span); } - - self.last_position = span.end(); + StatementKind::For(for_stmt) => { + let identifier = self.slice(for_stmt.identifier.span()); + let range = match for_stmt.range { + ForRange::Range(start, end) => format!( + "{}..{}", + rewrite::sub_expr(self, self.shape(), start), + rewrite::sub_expr(self, self.shape(), end) + ), + ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), + }; + let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); + + let result = format!("for {identifier} in {range} {block}"); + self.push_rewrite(result, span); + } + StatementKind::Assign(_) => { + self.push_rewrite(self.slice(span).to_string(), span); + } + StatementKind::Error => unreachable!(), + StatementKind::Break => self.push_rewrite("break;".into(), span), + StatementKind::Continue => self.push_rewrite("continue;".into(), span), + StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last), } } } From 9ebf902fe67b684216298e82b6f5b4ae12f0c53d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:26:00 -0400 Subject: [PATCH 19/29] Fix merge --- compiler/noirc_frontend/src/parser/parser/function.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index bf33aa7484c..e272eac04be 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -36,7 +36,6 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser Date: Thu, 18 Apr 2024 12:48:23 -0400 Subject: [PATCH 20/29] Add missed case --- compiler/noirc_frontend/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c4f0a8d67ba..31bf2245b1f 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -780,6 +780,7 @@ mod test { HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), }; let expr = interner.expression(&expr_id); From 824c4b2bc425041b2b5c4ab28ec7ad6169ce0fe5 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 12:50:36 -0400 Subject: [PATCH 21/29] Remove comptime from parse tests --- compiler/noirc_frontend/src/parser/parser/function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index e272eac04be..074c902ff7b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -175,8 +175,8 @@ mod test { "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(f: Field, y : T) where T: SomeTrait {}", "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", From 63fdb4aa5b077311f96c9b1896d29cf6f6f24ba2 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 18 Apr 2024 16:09:20 -0400 Subject: [PATCH 22/29] Add scanning pass --- compiler/noirc_frontend/src/ast/statement.rs | 8 +- .../noirc_frontend/src/hir/comptime/errors.rs | 49 +++++ .../src/hir/comptime/hir_to_ast.rs | 4 +- .../src/hir/comptime/interpreter.rs | 128 ++----------- .../noirc_frontend/src/hir/comptime/mod.rs | 3 + .../noirc_frontend/src/hir/comptime/scan.rs | 175 ++++++++++++++++++ .../noirc_frontend/src/hir/comptime/tests.rs | 4 +- .../noirc_frontend/src/hir/comptime/value.rs | 67 +++++++ .../src/hir/resolution/resolver.rs | 4 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 2 +- tooling/nargo_fmt/src/visitor/stmt.rs | 2 +- 15 files changed, 327 insertions(+), 127 deletions(-) create mode 100644 compiler/noirc_frontend/src/hir/comptime/errors.rs create mode 100644 compiler/noirc_frontend/src/hir/comptime/scan.rs create mode 100644 compiler/noirc_frontend/src/hir/comptime/value.rs diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index c46ab39df1e..fd98ac40dd3 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -39,7 +39,7 @@ pub enum StatementKind { Break, Continue, /// This statement should be executed at compile-time - Comptime(Box), + CompTime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -86,10 +86,10 @@ impl StatementKind { } self } - StatementKind::Comptime(mut statement) => { + StatementKind::CompTime(mut statement) => { *statement = statement.add_semicolon(semi, span, last_statement_in_block, emit_error); - StatementKind::Comptime(statement) + StatementKind::CompTime(statement) } // A semicolon on a for loop is optional and does nothing StatementKind::For(_) => self, @@ -684,7 +684,7 @@ impl Display for StatementKind { StatementKind::For(for_loop) => for_loop.fmt(f), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), - StatementKind::Comptime(statement) => write!(f, "comptime {statement}"), + StatementKind::CompTime(statement) => write!(f, "comptime {statement}"), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs new file mode 100644 index 00000000000..c0948417267 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -0,0 +1,49 @@ +use crate::{node_interner::DefinitionId, Type}; +use acvm::FieldElement; +use noirc_errors::Location; + +use super::value::Value; + +/// The possible errors that can halt the interpreter. +#[allow(unused)] +#[derive(Debug)] +pub(crate) enum InterpreterError { + ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, + 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 }, + 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 }, + QuoteInRuntimeCode { 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. + Break, + Continue, +} + +#[allow(unused)] +pub(super) type IResult = std::result::Result; 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 9f9004b5ad1..ca21bde5ebe 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -65,8 +65,8 @@ impl StmtId { HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), HirStatement::Error => StatementKind::Error, - HirStatement::Comptime(statement) => { - StatementKind::Comptime(Box::new(statement.to_ast(interner).kind)) + HirStatement::CompTime(statement) => { + StatementKind::CompTime(Box::new(statement.to_ast(interner).kind)) } }; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 54f1c321a3b..1cf97d46cb6 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::{borrow::Cow, collections::hash_map::Entry, rc::Rc}; +use std::{collections::hash_map::Entry, rc::Rc}; use acvm::FieldElement; use im::Vector; -use iter_extended::{try_vecmap, vecmap}; +use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -21,14 +21,17 @@ use crate::{ }, macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, - BinaryOpKind, BlockExpression, FunctionKind, IntegerBitSize, Shared, Signedness, Type, - TypeBinding, TypeBindings, TypeVariableKind, + BinaryOpKind, FunctionKind, IntegerBitSize, Shared, Signedness, Type, TypeBinding, + TypeBindings, TypeVariableKind, }; +use super::errors::{IResult, InterpreterError}; +use super::value::Value; + #[allow(unused)] pub(crate) struct Interpreter<'interner> { /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner - interner: &'interner mut NodeInterner, + pub(super) 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 @@ -37,11 +40,11 @@ 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: HashSet, + pub(super) changed_functions: HashSet, /// True if we've expanded any macros into global scope and will need /// to redo name resolution & type checking for everything. - changed_globally: bool, + pub(super) changed_globally: bool, in_loop: bool, @@ -50,72 +53,6 @@ pub(crate) struct Interpreter<'interner> { in_comptime_context: bool, } -#[allow(unused)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum Value { - Unit, - Bool(bool), - Field(FieldElement), - I8(i8), - I32(i32), - I64(i64), - U8(u8), - U32(u32), - U64(u64), - String(Rc), - Function(FuncId, Type), - Closure(HirLambda, Vec, Type), - Tuple(Vec), - Struct(HashMap, Value>, Type), - Pointer(Shared), - Array(Vector, Type), - Slice(Vector, Type), - Code(Rc), -} - -/// The possible errors that can halt the interpreter. -#[allow(unused)] -#[derive(Debug)] -pub(crate) enum InterpreterError { - ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, - 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 }, - 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. - Break, - Continue, -} - -#[allow(unused)] -type IResult = std::result::Result; - #[allow(unused)] impl<'a> Interpreter<'a> { pub(crate) fn new(interner: &'a mut NodeInterner) -> Self { @@ -194,14 +131,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>) { + pub(super) 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>)) { + pub(super) fn exit_function(&mut self, mut state: (bool, Vec>)) { self.in_loop = state.0; // Keep only the global scope @@ -209,11 +146,11 @@ impl<'a> Interpreter<'a> { self.scopes.append(&mut state.1); } - fn push_scope(&mut self) { + pub(super) fn push_scope(&mut self) { self.scopes.push(HashMap::default()); } - fn pop_scope(&mut self) { + pub(super) fn pop_scope(&mut self) { self.scopes.pop(); } @@ -1079,7 +1016,7 @@ impl<'a> Interpreter<'a> { HirStatement::Break => self.evaluate_break(), HirStatement::Continue => self.evaluate_continue(), HirStatement::Expression(expression) => self.evaluate(expression), - HirStatement::Comptime(statement) => self.evaluate_comptime(statement), + HirStatement::CompTime(statement) => self.evaluate_comptime(statement), HirStatement::Semi(expression) => { self.evaluate(expression)?; Ok(Value::Unit) @@ -1253,43 +1190,10 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { + pub(super) fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { let was_in_comptime = std::mem::replace(&mut self.in_comptime_context, true); let result = self.evaluate_statement(statement); self.in_comptime_context = was_in_comptime; result } } - -impl Value { - 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(_, typ) => return Cow::Borrowed(typ), - Value::Slice(_, typ) => return Cow::Borrowed(typ), - Value::Code(_) => Type::Code, - 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 83aaddaa405..17e0c943288 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,3 +1,6 @@ +mod errors; mod hir_to_ast; mod interpreter; +mod scan; mod tests; +mod value; diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs new file mode 100644 index 00000000000..f2bf47d925e --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -0,0 +1,175 @@ +//! This module is for the scanning of the Hir by the interpreter. +//! In this initial step, the Hir is scanned for `CompTime` nodes +//! without actually executing anything until such a node is found. +//! Once such a node is found, the interpreter will call the relevant +//! evaluate method on that node type, insert the result into the Ast, +//! and continue scanning the rest of the program. +//! +//! Since it mostly just needs to recur on the Hir looking for CompTime +//! nodes, this pass is fairly simple. The only thing it really needs to +//! ensure to do is to push and pop scopes on the interpreter as needed +//! so that any variables defined within e.g. an `if` statement containing +//! a `CompTime` block aren't accessible outside of the `if`. +use crate::{ + hir_def::{ + expr::{ + HirArrayLiteral, HirBlockExpression, HirCallExpression, HirConstructorExpression, + HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, + HirMethodCallExpression, + }, + stmt::HirForStatement, + }, + macros_api::{HirExpression, HirLiteral, HirStatement}, + node_interner::{ExprId, StmtId}, +}; + +use super::{ + errors::{IResult, InterpreterError}, + interpreter::Interpreter, +}; + +#[allow(dead_code)] +impl<'interner> Interpreter<'interner> { + fn scan_expression(&mut self, expr: ExprId) -> IResult<()> { + match self.interner.expression(&expr) { + HirExpression::Ident(_) => todo!(), + HirExpression::Literal(literal) => self.scan_literal(literal), + HirExpression::Block(block) => self.scan_block(block), + HirExpression::Prefix(prefix) => self.scan_expression(prefix.rhs), + HirExpression::Infix(infix) => self.scan_infix(infix), + HirExpression::Index(index) => self.scan_index(index), + HirExpression::Constructor(constructor) => self.scan_constructor(constructor), + HirExpression::MemberAccess(member_access) => self.scan_expression(member_access.lhs), + HirExpression::Call(call) => self.scan_call(call), + HirExpression::MethodCall(method_call) => self.scan_method_call(method_call), + HirExpression::Cast(cast) => self.scan_expression(cast.lhs), + HirExpression::If(if_) => self.scan_if(if_), + HirExpression::Tuple(tuple) => self.scan_tuple(tuple), + HirExpression::Lambda(lambda) => self.scan_lambda(lambda), + HirExpression::Quote(_) => { + // This error could be detected much earlier in the compiler pipeline but + // it just makes sense for the comptime code to handle comptime things. + let location = self.interner.expr_location(&expr); + Err(InterpreterError::QuoteInRuntimeCode { location }) + } + HirExpression::Error => Ok(()), + } + } + + fn scan_literal(&mut self, literal: HirLiteral) -> IResult<()> { + match literal { + HirLiteral::Array(elements) | HirLiteral::Slice(elements) => match elements { + HirArrayLiteral::Standard(elements) => { + for element in elements { + self.scan_expression(element)?; + } + Ok(()) + } + HirArrayLiteral::Repeated { repeated_element, length: _ } => { + self.scan_expression(repeated_element) + } + }, + HirLiteral::Bool(_) + | HirLiteral::Integer(_, _) + | HirLiteral::Str(_) + | HirLiteral::FmtStr(_, _) + | HirLiteral::Unit => Ok(()), + } + } + + fn scan_block(&mut self, block: HirBlockExpression) -> IResult<()> { + self.push_scope(); + for statement in &block.statements { + self.scan_statement(*statement)?; + } + self.pop_scope(); + Ok(()) + } + + fn scan_infix(&mut self, infix: HirInfixExpression) -> IResult<()> { + self.scan_expression(infix.lhs)?; + self.scan_expression(infix.rhs) + } + + fn scan_index(&mut self, index: HirIndexExpression) -> IResult<()> { + self.scan_expression(index.collection)?; + self.scan_expression(index.index) + } + + fn scan_constructor(&mut self, constructor: HirConstructorExpression) -> IResult<()> { + for (_, field) in constructor.fields { + self.scan_expression(field)?; + } + Ok(()) + } + + fn scan_call(&mut self, call: HirCallExpression) -> IResult<()> { + self.scan_expression(call.func)?; + for arg in call.arguments { + self.scan_expression(arg)?; + } + Ok(()) + } + + fn scan_method_call(&mut self, method_call: HirMethodCallExpression) -> IResult<()> { + self.scan_expression(method_call.object)?; + for arg in method_call.arguments { + self.scan_expression(arg)?; + } + Ok(()) + } + + fn scan_if(&mut self, if_: HirIfExpression) -> IResult<()> { + self.scan_expression(if_.condition)?; + + self.push_scope(); + self.scan_expression(if_.consequence)?; + self.pop_scope(); + + if let Some(alternative) = if_.alternative { + self.push_scope(); + self.scan_expression(alternative)?; + self.pop_scope(); + } + Ok(()) + } + + fn scan_tuple(&mut self, tuple: Vec) -> IResult<()> { + for field in tuple { + self.scan_expression(field)?; + } + Ok(()) + } + + fn scan_lambda(&mut self, lambda: HirLambda) -> IResult<()> { + self.scan_expression(lambda.body) + } + + fn scan_statement(&mut self, statement: StmtId) -> IResult<()> { + match self.interner.statement(&statement) { + HirStatement::Let(let_) => self.scan_expression(let_.expression), + HirStatement::Constrain(constrain) => self.scan_expression(constrain.0), + HirStatement::Assign(assign) => self.scan_expression(assign.expression), + HirStatement::For(for_) => self.scan_for(for_), + HirStatement::Break => Ok(()), + HirStatement::Continue => Ok(()), + HirStatement::Expression(expression) => self.scan_expression(expression), + HirStatement::Semi(semi) => self.scan_expression(semi), + HirStatement::Error => Ok(()), + HirStatement::CompTime(comptime) => { + let _value = self.evaluate_comptime(comptime)?; + todo!("Inline value into hir") + } + } + } + + fn scan_for(&mut self, for_: HirForStatement) -> IResult<()> { + // We don't need to set self.in_loop since we're not actually evaluating this loop. + // We just need to push a scope so that if there's a `comptime { .. }` expr inside this + // loop, any variables it defines aren't accessible outside of it. + self.push_scope(); + self.scan_expression(for_.block)?; + self.pop_scope(); + Ok(()) + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 016e7079886..1a84dae4a87 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -2,7 +2,9 @@ use noirc_errors::Location; -use super::interpreter::{Interpreter, InterpreterError, Value}; +use super::errors::InterpreterError; +use super::interpreter::Interpreter; +use super::value::Value; use crate::hir::type_check::test::type_check_src_code; fn interpret_helper(src: &str, func_namespace: Vec) -> Result { diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs new file mode 100644 index 00000000000..aff087dee5e --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -0,0 +1,67 @@ +use std::{borrow::Cow, rc::Rc}; + +use acvm::FieldElement; +use im::Vector; +use iter_extended::vecmap; + +use crate::{ + hir_def::expr::HirLambda, node_interner::FuncId, BlockExpression, IntegerBitSize, Shared, + Signedness, Type, +}; +use rustc_hash::FxHashMap as HashMap; + +#[allow(unused)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Value { + Unit, + Bool(bool), + Field(FieldElement), + I8(i8), + I32(i32), + I64(i64), + U8(u8), + U32(u32), + U64(u64), + String(Rc), + Function(FuncId, Type), + Closure(HirLambda, Vec, Type), + Tuple(Vec), + Struct(HashMap, Value>, Type), + Pointer(Shared), + Array(Vector, Type), + Slice(Vector, Type), + Code(Rc), +} + +impl Value { + pub(crate) 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(_, typ) => return Cow::Borrowed(typ), + Value::Slice(_, typ) => return Cow::Borrowed(typ), + Value::Code(_) => Type::Code, + Value::Pointer(element) => { + let element = element.borrow().get_type().into_owned(); + Type::MutableReference(Box::new(element)) + } + }) + } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 76544542767..df95ed14e43 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1276,9 +1276,9 @@ impl<'a> Resolver<'a> { HirStatement::Continue } StatementKind::Error => HirStatement::Error, - StatementKind::Comptime(statement) => { + StatementKind::CompTime(statement) => { let statement = self.resolve_stmt(*statement, span); - HirStatement::Comptime(self.interner.push_stmt(statement)) + HirStatement::CompTime(self.interner.push_stmt(statement)) } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 4dfc896fb29..f4d103dc9f1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -51,7 +51,7 @@ impl<'interner> TypeChecker<'interner> { HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), HirStatement::For(for_loop) => self.check_for_loop(for_loop), - HirStatement::Comptime(statement) => return self.check_statement(&statement), + HirStatement::CompTime(statement) => return self.check_statement(&statement), HirStatement::Break | HirStatement::Continue | HirStatement::Error => (), } Type::Unit diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 605f25ebfbf..81212614453 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -19,7 +19,7 @@ pub enum HirStatement { Continue, Expression(ExprId), Semi(ExprId), - Comptime(StmtId), + CompTime(StmtId), Error, } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 78b6508465d..07f247355f9 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -620,7 +620,7 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Error => unreachable!(), // All `comptime` statements & expressions should be removed before runtime. - HirStatement::Comptime(_) => unreachable!("comptime statement in runtime code"), + HirStatement::CompTime(_) => unreachable!("comptime statement in runtime code"), } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index ba5b9bf1ab0..864e2025810 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -540,7 +540,7 @@ where StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) }), ))) - .map(|statement| StatementKind::Comptime(Box::new(statement))) + .map(|statement| StatementKind::CompTime(Box::new(statement))) } /// Comptime in an expression position only accepts entire blocks diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 31bf2245b1f..ac3d7bbc4cc 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -780,7 +780,7 @@ mod test { HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), - HirStatement::Comptime(_) => panic!("Unexpected comptime"), + HirStatement::CompTime(_) => panic!("Unexpected comptime"), }; let expr = interner.expression(&expr_id); diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 612330ad3a3..97a8009a591 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -102,7 +102,7 @@ impl super::FmtVisitor<'_> { StatementKind::Error => unreachable!(), StatementKind::Break => self.push_rewrite("break;".into(), span), StatementKind::Continue => self.push_rewrite("continue;".into(), span), - StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last), + StatementKind::CompTime(statement) => self.visit_stmt(*statement, span, is_last), } } } From e37612123cc79e903cf91bb1db170c2138117603 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 19 Apr 2024 12:40:07 -0400 Subject: [PATCH 23/29] Refactor name resolution a bit --- compiler/noirc_frontend/src/ast/expression.rs | 2 + .../src/hir/comptime/hir_to_ast.rs | 15 +- .../src/hir/comptime/interpreter.rs | 5 +- .../noirc_frontend/src/hir/comptime/mod.rs | 12 ++ .../noirc_frontend/src/hir/comptime/scan.rs | 13 +- .../src/hir/def_collector/dc_crate.rs | 144 ++++++++++-------- .../src/hir/resolution/globals.rs | 1 + .../noirc_frontend/src/hir/resolution/mod.rs | 2 +- .../src/hir/resolution/resolver.rs | 11 +- .../noirc_frontend/src/hir/type_check/expr.rs | 59 ++++--- compiler/noirc_frontend/src/hir_def/expr.rs | 3 +- .../src/monomorphization/mod.rs | 3 + compiler/noirc_frontend/src/parser/parser.rs | 2 +- tooling/nargo_fmt/src/rewrite/expr.rs | 3 + 14 files changed, 165 insertions(+), 110 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 755739af8fe..2b1b59a445a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -28,6 +28,7 @@ pub enum ExpressionKind { Lambda(Box), Parenthesized(Box), Quote(BlockExpression), + CompTime(BlockExpression), Error, } @@ -504,6 +505,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Quote(block) => write!(f, "quote {block}"), + CompTime(block) => write!(f, "comptime {block}"), Error => write!(f, "Error"), } } 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 ca21bde5ebe..5f7274d6cfc 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -2,7 +2,7 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; -use crate::hir_def::expr::{HirArrayLiteral, HirExpression, HirIdent}; +use crate::hir_def::expr::{HirArrayLiteral, HirBlockExpression, HirExpression, HirIdent}; use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; use crate::macros_api::HirLiteral; use crate::node_interner::{ExprId, NodeInterner, StmtId}; @@ -107,10 +107,7 @@ impl ExprId { 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::Block(expr) => ExpressionKind::Block(expr.into_ast(interner)), HirExpression::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { operator: prefix.operator, rhs: prefix.rhs.to_ast(interner), @@ -173,6 +170,7 @@ impl ExprId { } HirExpression::Quote(block) => ExpressionKind::Quote(block), HirExpression::Error => ExpressionKind::Error, + HirExpression::CompTime(block) => ExpressionKind::CompTime(block.into_ast(interner)), }; Expression::new(kind, span) @@ -352,3 +350,10 @@ impl HirArrayLiteral { } } } + +impl HirBlockExpression { + fn into_ast(self, interner: &NodeInterner) -> BlockExpression { + let statements = vecmap(self.statements, |statement| statement.to_ast(interner)); + BlockExpression { statements } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 1cf97d46cb6..b38fe8110a7 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -29,7 +29,7 @@ use super::errors::{IResult, InterpreterError}; use super::value::Value; #[allow(unused)] -pub(crate) struct Interpreter<'interner> { +pub struct Interpreter<'interner> { /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner pub(super) interner: &'interner mut NodeInterner, @@ -313,6 +313,7 @@ impl<'a> Interpreter<'a> { HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), + HirExpression::CompTime(block) => self.evaluate_block(block), HirExpression::Error => { let location = self.interner.expr_location(&id); Err(InterpreterError::ErrorNodeEncountered { location }) @@ -441,7 +442,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_block(&mut self, mut block: HirBlockExpression) -> IResult { + pub(super) fn evaluate_block(&mut self, mut block: HirBlockExpression) -> IResult { let last_statement = block.statements.pop(); self.push_scope(); diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 17e0c943288..96e23664712 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,6 +1,18 @@ +use crate::{node_interner::FuncId, macros_api::NodeInterner}; + mod errors; mod hir_to_ast; mod interpreter; mod scan; mod tests; mod value; + +pub use interpreter::Interpreter; + +/// Scan through a function, evaluating any CompTime nodes found. +/// These nodes will be modified in place, replaced with the +/// result of their evaluation. +pub fn scan_function(function: FuncId, interner: &mut NodeInterner) { + let mut interpreter = Interpreter::new(interner); + interpreter.scan_function(function); +} diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index f2bf47d925e..162981315a7 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -20,7 +20,7 @@ use crate::{ stmt::HirForStatement, }, macros_api::{HirExpression, HirLiteral, HirStatement}, - node_interner::{ExprId, StmtId}, + node_interner::{ExprId, StmtId, FuncId}, }; use super::{ @@ -30,6 +30,13 @@ use super::{ #[allow(dead_code)] impl<'interner> Interpreter<'interner> { + /// Scan through a function, evaluating any CompTime nodes found. + /// These nodes will be modified in place, replaced with the + /// result of their evaluation. + pub fn scan_function(&mut self, function: FuncId) { + + } + fn scan_expression(&mut self, expr: ExprId) -> IResult<()> { match self.interner.expression(&expr) { HirExpression::Ident(_) => todo!(), @@ -46,6 +53,10 @@ impl<'interner> Interpreter<'interner> { HirExpression::If(if_) => self.scan_if(if_), HirExpression::Tuple(tuple) => self.scan_tuple(tuple), HirExpression::Lambda(lambda) => self.scan_lambda(lambda), + HirExpression::CompTime(block) => { + let _value = self.evaluate_block(block)?; + todo!("Inline block into hir") + } HirExpression::Quote(_) => { // This error could be detected much earlier in the compiler pipeline but // it just makes sense for the comptime code to handle comptime things. diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 463b8a4b329..c09ba6c2571 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -1,6 +1,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; +use crate::hir::comptime::Interpreter; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; @@ -30,6 +31,15 @@ use std::collections::{BTreeMap, HashMap}; use std::vec; +#[derive(Default)] +pub struct ResolvedModule { + pub globals: Vec<(FileId, GlobalId)>, + pub functions: Vec<(FileId, FuncId)>, + pub trait_impl_functions: Vec<(FileId, FuncId)>, + + pub errors: Vec<(CompilationError, FileId)>, +} + /// Stores all of the unresolved functions in a particular file/mod #[derive(Clone)] pub struct UnresolvedFunctions { @@ -304,6 +314,9 @@ impl DefCollector { } } + let mut resolved_module = ResolvedModule::default(); + resolved_module.errors = errors; + // We must first resolve and intern the globals before we can resolve any stmts inside each function. // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope // @@ -312,21 +325,21 @@ impl DefCollector { let (literal_globals, other_globals) = filter_literal_globals(def_collector.collected_globals); - let mut resolved_globals = resolve_globals(context, literal_globals, crate_id); + resolved_module.resolve_globals(context, literal_globals, crate_id); - errors.extend(resolve_type_aliases( + resolved_module.errors.extend(resolve_type_aliases( context, def_collector.collected_type_aliases, crate_id, )); - errors.extend(resolve_traits(context, def_collector.collected_traits, crate_id)); + resolved_module.errors.extend(resolve_traits(context, def_collector.collected_traits, crate_id)); // Must resolve structs before we resolve globals. - errors.extend(resolve_structs(context, def_collector.collected_types, crate_id)); + resolved_module.errors.extend(resolve_structs(context, def_collector.collected_types, crate_id)); // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. - errors.extend(collect_trait_impls( + resolved_module.errors.extend(collect_trait_impls( context, crate_id, &mut def_collector.collected_traits_impls, @@ -339,54 +352,51 @@ impl DefCollector { // // These are resolved after trait impls so that struct methods are chosen // over trait methods if there are name conflicts. - errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); + resolved_module.errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); // We must wait to resolve non-integer globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. - resolved_globals.extend(resolve_globals(context, other_globals, crate_id)); - errors.extend(resolved_globals.errors); + resolved_module.resolve_globals(context, other_globals, crate_id); // Resolve each function in the crate. This is now possible since imports have been resolved - let mut functions = Vec::new(); - functions.extend(resolve_free_functions( + resolved_module.functions = resolve_free_functions( &mut context.def_interner, crate_id, &context.def_maps, def_collector.collected_functions, None, - &mut errors, - )); + &mut resolved_module.errors, + ); - functions.extend(resolve_impls( + resolved_module.functions.extend(resolve_impls( &mut context.def_interner, crate_id, &context.def_maps, def_collector.collected_impls, - &mut errors, + &mut resolved_module.errors, )); - let impl_functions = resolve_trait_impls( + resolved_module.trait_impl_functions = resolve_trait_impls( context, def_collector.collected_traits_impls, crate_id, - &mut errors, + &mut resolved_module.errors, ); for macro_processor in macro_processors { macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( |(macro_err, file_id)| { - errors.push((macro_err.into(), file_id)); + resolved_module.errors.push((macro_err.into(), file_id)); }, ); } - errors.extend(context.def_interner.check_for_dependency_cycles()); + resolved_module.errors.extend(context.def_interner.check_for_dependency_cycles()); - errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); - errors.extend(type_check_functions(&mut context.def_interner, functions)); - errors.extend(type_check_trait_impl_signatures(&mut context.def_interner, &impl_functions)); - errors.extend(type_check_functions(&mut context.def_interner, impl_functions)); - errors + resolved_module.type_check(context); + resolved_module.evaluate_comptime(&mut context.def_interner); + + resolved_module.errors } } @@ -444,48 +454,52 @@ fn filter_literal_globals( }) } -fn type_check_globals( - interner: &mut NodeInterner, - global_ids: Vec<(FileId, GlobalId)>, -) -> Vec<(CompilationError, fm::FileId)> { - global_ids - .into_iter() - .flat_map(|(file_id, global_id)| { - TypeChecker::check_global(global_id, interner) - .iter() - .cloned() - .map(|e| (e.into(), file_id)) - .collect::>() - }) - .collect() -} +impl ResolvedModule { + fn type_check(&mut self, context: &mut Context) { + self.type_check_globals(&mut context.def_interner); + self.type_check_functions(&mut context.def_interner); + self.type_check_trait_impl_function(&mut context.def_interner); + } -fn type_check_functions( - interner: &mut NodeInterner, - file_func_ids: Vec<(FileId, FuncId)>, -) -> Vec<(CompilationError, fm::FileId)> { - file_func_ids - .into_iter() - .flat_map(|(file, func)| { - type_check_func(interner, func) - .into_iter() - .map(|e| (e.into(), file)) - .collect::>() - }) - .collect() -} + fn type_check_globals(&mut self, interner: &mut NodeInterner) { + for (file_id, global_id) in self.globals.iter() { + for error in TypeChecker::check_global(*global_id, interner) { + self.errors.push((error.into(), *file_id)); + } + } + } -fn type_check_trait_impl_signatures( - interner: &mut NodeInterner, - file_func_ids: &[(FileId, FuncId)], -) -> Vec<(CompilationError, fm::FileId)> { - file_func_ids - .iter() - .flat_map(|(file, func)| { - check_trait_impl_method_matches_declaration(interner, *func) - .into_iter() - .map(|e| (e.into(), *file)) - .collect::>() - }) - .collect() + fn type_check_functions(&mut self, interner: &mut NodeInterner) { + for (file, func) in self.functions.iter() { + for error in type_check_func(interner, *func) { + self.errors.push((error.into(), *file)); + } + } + } + + fn type_check_trait_impl_function(&mut self, interner: &mut NodeInterner) { + for (file, func) in self.trait_impl_functions.iter() { + for error in check_trait_impl_method_matches_declaration(interner, *func) { + self.errors.push((error.into(), *file)); + } + for error in type_check_func(interner, *func) { + self.errors.push((error.into(), *file)); + } + } + } + + /// Evaluate all `comptime` expressions in this module + fn evaluate_comptime(&self, interner: &mut NodeInterner) { + let mut interpreter = Interpreter::new(interner); + + for (_file, function) in &self.functions { + interpreter.scan_function(*function); + } + } + + fn resolve_globals(&mut self, context: &mut Context, literal_globals: Vec, crate_id: CrateId) { + let globals = resolve_globals(context, literal_globals, crate_id); + self.globals.extend(globals.globals); + self.errors.extend(globals.errors); + } } diff --git a/compiler/noirc_frontend/src/hir/resolution/globals.rs b/compiler/noirc_frontend/src/hir/resolution/globals.rs index 9fb31271727..f7717bdad1d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/globals.rs +++ b/compiler/noirc_frontend/src/hir/resolution/globals.rs @@ -11,6 +11,7 @@ use crate::{ use fm::FileId; use iter_extended::vecmap; +#[derive(Default)] pub(crate) struct ResolvedGlobals { pub(crate) globals: Vec<(FileId, GlobalId)>, pub(crate) errors: Vec<(CompilationError, FileId)>, diff --git a/compiler/noirc_frontend/src/hir/resolution/mod.rs b/compiler/noirc_frontend/src/hir/resolution/mod.rs index 8c16a9cca80..489bd8e09a5 100644 --- a/compiler/noirc_frontend/src/hir/resolution/mod.rs +++ b/compiler/noirc_frontend/src/hir/resolution/mod.rs @@ -18,7 +18,7 @@ mod traits; mod type_aliases; pub(crate) use functions::resolve_free_functions; -pub(crate) use globals::resolve_globals; +pub(crate) use globals::{resolve_globals, ResolvedGlobals}; pub(crate) use impls::{collect_impls, resolve_impls}; pub(crate) use structs::resolve_structs; pub(crate) use traits::{ diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index df95ed14e43..bb618615858 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1535,7 +1535,9 @@ impl<'a> Resolver<'a> { collection: self.resolve_expression(indexed_expr.collection), index: self.resolve_expression(indexed_expr.index), }), - ExpressionKind::Block(block_expr) => self.resolve_block(block_expr), + ExpressionKind::Block(block_expr) => { + HirExpression::Block(self.resolve_block(block_expr)) + } ExpressionKind::Constructor(constructor) => { let span = constructor.type_name.span(); @@ -1602,6 +1604,7 @@ impl<'a> Resolver<'a> { // The quoted expression isn't resolved since we don't want errors if variables aren't defined ExpressionKind::Quote(block) => HirExpression::Quote(block), + ExpressionKind::CompTime(block) => HirExpression::CompTime(self.resolve_block(block)), }; // If these lines are ever changed, make sure to change the early return @@ -1934,14 +1937,14 @@ impl<'a> Resolver<'a> { Ok(path_resolution.module_def_id) } - fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression { + fn resolve_block(&mut self, block_expr: BlockExpression) -> HirBlockExpression { let statements = self.in_new_scope(|this| vecmap(block_expr.statements, |stmt| this.intern_stmt(stmt))); - HirExpression::Block(HirBlockExpression { statements }) + HirBlockExpression { statements } } pub fn intern_block(&mut self, block: BlockExpression) -> ExprId { - let hir_block = self.resolve_block(block); + let hir_block = HirExpression::Block(self.resolve_block(block)); self.interner.push_expr(hir_block) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index b56e2dce2a9..322f9dc9121 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -5,8 +5,8 @@ use crate::{ hir::{resolution::resolver::verify_mutable_reference, type_check::errors::Source}, hir_def::{ expr::{ - self, HirArrayLiteral, HirBinaryOp, HirExpression, HirIdent, HirLiteral, - HirMethodCallExpression, HirMethodReference, HirPrefixExpression, ImplKind, + self, HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirExpression, HirIdent, + HirLiteral, HirMethodCallExpression, HirMethodReference, HirPrefixExpression, ImplKind, }, types::Type, }, @@ -270,34 +270,7 @@ impl<'interner> TypeChecker<'interner> { let span = self.interner.expr_span(expr_id); self.check_cast(lhs_type, cast_expr.r#type, span) } - HirExpression::Block(block_expr) => { - let mut block_type = Type::Unit; - - let statements = block_expr.statements(); - for (i, stmt) in statements.iter().enumerate() { - let expr_type = self.check_statement(stmt); - - if let crate::hir_def::stmt::HirStatement::Semi(expr) = - self.interner.statement(stmt) - { - let inner_expr_type = self.interner.id_type(expr); - let span = self.interner.expr_span(&expr); - - self.unify(&inner_expr_type, &Type::Unit, || { - TypeCheckError::UnusedResultError { - expr_type: inner_expr_type.clone(), - expr_span: span, - } - }); - } - - if i + 1 == statements.len() { - block_type = expr_type; - } - } - - block_type - } + HirExpression::Block(block_expr) => self.check_block(block_expr), HirExpression::Prefix(prefix_expr) => { let rhs_type = self.check_expression(&prefix_expr.rhs); let span = self.interner.expr_span(&prefix_expr.rhs); @@ -335,12 +308,38 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } HirExpression::Quote(_) => Type::Code, + HirExpression::CompTime(block) => self.check_block(block), }; self.interner.push_expr_type(*expr_id, typ.clone()); typ } + fn check_block(&mut self, block: HirBlockExpression) -> Type { + let mut block_type = Type::Unit; + + let statements = block.statements(); + for (i, stmt) in statements.iter().enumerate() { + let expr_type = self.check_statement(stmt); + + if let crate::hir_def::stmt::HirStatement::Semi(expr) = self.interner.statement(stmt) { + let inner_expr_type = self.interner.id_type(expr); + let span = self.interner.expr_span(&expr); + + self.unify(&inner_expr_type, &Type::Unit, || TypeCheckError::UnusedResultError { + expr_type: inner_expr_type.clone(), + expr_span: span, + }); + } + + if i + 1 == statements.len() { + block_type = expr_type; + } + } + + block_type + } + /// Returns the type of the given identifier fn check_ident(&mut self, ident: HirIdent, expr_id: &ExprId) -> Type { let mut bindings = TypeBindings::new(); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index eb4ebf3f913..9b11aa96f57 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -30,8 +30,9 @@ pub enum HirExpression { If(HirIfExpression), Tuple(Vec), Lambda(HirLambda), - Error, Quote(crate::BlockExpression), + CompTime(HirBlockExpression), + Error, } impl HirExpression { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 07f247355f9..0657fa6983a 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -515,6 +515,9 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), + HirExpression::CompTime(_) => { + unreachable!("comptime expression remaining in runtime code") + } }; Ok(expr) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 864e2025810..95303ae53a4 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -548,7 +548,7 @@ fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a where S: NoirParser + 'a, { - keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::Block) + keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::CompTime) } fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 6cf69a2309d..83b4cb96a7f 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -159,6 +159,9 @@ pub(crate) fn rewrite( } ExpressionKind::Lambda(_) | ExpressionKind::Variable(_) => visitor.slice(span).to_string(), ExpressionKind::Quote(block) => format!("quote {}", rewrite_block(visitor, block, span)), + ExpressionKind::CompTime(block) => { + format!("comptime {}", rewrite_block(visitor, block, span)) + } ExpressionKind::Error => unreachable!(), } } From 65f65134f02cc0f8dd07802cbbffe26a75c68b0f Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 22 Apr 2024 09:44:05 -0500 Subject: [PATCH 24/29] Add into_expression --- .../src/hir/comptime/interpreter.rs | 2 +- .../noirc_frontend/src/hir/comptime/scan.rs | 17 ++++-- .../noirc_frontend/src/hir/comptime/value.rs | 55 ++++++++++++++++++- .../src/hir/resolution/globals.rs | 7 --- .../noirc_frontend/src/hir/resolution/mod.rs | 2 +- 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index b38fe8110a7..c1040ac0c38 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -329,7 +329,7 @@ impl<'a> Interpreter<'a> { let typ = self.interner.id_type(id); Ok(Value::Function(*function_id, typ)) } - DefinitionKind::Local(_) => dbg!(self.lookup(&ident)), + DefinitionKind::Local(_) => self.lookup(&ident), DefinitionKind::Global(global_id) => { let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); self.evaluate_let(let_)?; diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index 162981315a7..036fd63e79c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -34,12 +34,16 @@ impl<'interner> Interpreter<'interner> { /// These nodes will be modified in place, replaced with the /// result of their evaluation. pub fn scan_function(&mut self, function: FuncId) { + let function = self.interner.function(&function); + let state = self.enter_function(); + self.scan_expression(function.as_expr()).unwrap(); + self.exit_function(state); } fn scan_expression(&mut self, expr: ExprId) -> IResult<()> { match self.interner.expression(&expr) { - HirExpression::Ident(_) => todo!(), + HirExpression::Ident(_) => Ok(()), HirExpression::Literal(literal) => self.scan_literal(literal), HirExpression::Block(block) => self.scan_block(block), HirExpression::Prefix(prefix) => self.scan_expression(prefix.rhs), @@ -54,8 +58,10 @@ impl<'interner> Interpreter<'interner> { HirExpression::Tuple(tuple) => self.scan_tuple(tuple), HirExpression::Lambda(lambda) => self.scan_lambda(lambda), HirExpression::CompTime(block) => { - let _value = self.evaluate_block(block)?; - todo!("Inline block into hir") + let new_expr = self.evaluate_block(block)?.into_expression(self.interner); + let new_expr = self.interner.expression(&new_expr); + self.interner.replace_expr(&expr, new_expr); + Ok(()) } HirExpression::Quote(_) => { // This error could be detected much earlier in the compiler pipeline but @@ -168,8 +174,9 @@ impl<'interner> Interpreter<'interner> { HirStatement::Semi(semi) => self.scan_expression(semi), HirStatement::Error => Ok(()), HirStatement::CompTime(comptime) => { - let _value = self.evaluate_comptime(comptime)?; - todo!("Inline value into hir") + let new_expr = self.evaluate_comptime(comptime)?.into_expression(self.interner); + self.interner.replace_statement(statement, HirStatement::Expression(new_expr)); + Ok(()) } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index aff087dee5e..42f666c458a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -5,8 +5,8 @@ use im::Vector; use iter_extended::vecmap; use crate::{ - hir_def::expr::HirLambda, node_interner::FuncId, BlockExpression, IntegerBitSize, Shared, - Signedness, Type, + hir_def::expr::HirLambda, node_interner::{FuncId, ExprId}, BlockExpression, IntegerBitSize, Shared, + Signedness, Type, macros_api::{NodeInterner, HirExpression, HirLiteral}, }; use rustc_hash::FxHashMap as HashMap; @@ -64,4 +64,55 @@ impl Value { } }) } + + pub(crate) fn into_expression(self, interner: &mut NodeInterner) -> ExprId { + let expression = match self { + Value::Unit => todo!(), + Value::Bool(value) => { + HirExpression::Literal(HirLiteral::Bool(value)) + }, + Value::Field(value) => { + HirExpression::Literal(HirLiteral::Integer(value, false)) + }, + Value::I8(value) => { + let negative = value < 0 ; + let value = value.abs(); + let value = (value as u128).into(); + HirExpression::Literal(HirLiteral::Integer(value, negative)) + }, + Value::I32(value) => { + let negative = value < 0 ; + let value = value.abs(); + let value = (value as u128).into(); + HirExpression::Literal(HirLiteral::Integer(value, negative)) + }, + Value::I64(value) => { + let negative = value < 0 ; + let value = value.abs(); + let value = (value as u128).into(); + HirExpression::Literal(HirLiteral::Integer(value, negative)) + }, + Value::U8(value) => { + HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + }, + Value::U32(value) => { + HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + }, + Value::U64(value) => { + HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + }, + Value::String(value) => { + HirExpression::Literal(HirLiteral::Str(value.as_ref().clone())) + }, + Value::Function(_, _) => todo!(), + Value::Closure(_, _, _) => todo!(), + Value::Tuple(_) => todo!(), + Value::Struct(_, _) => todo!(), + Value::Pointer(_) => todo!(), + Value::Array(_, _) => todo!(), + Value::Slice(_, _) => todo!(), + Value::Code(_) => todo!(), + }; + interner.push_expr(expression) + } } diff --git a/compiler/noirc_frontend/src/hir/resolution/globals.rs b/compiler/noirc_frontend/src/hir/resolution/globals.rs index f7717bdad1d..bcda4e75d3d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/globals.rs +++ b/compiler/noirc_frontend/src/hir/resolution/globals.rs @@ -17,13 +17,6 @@ pub(crate) struct ResolvedGlobals { pub(crate) errors: Vec<(CompilationError, FileId)>, } -impl ResolvedGlobals { - pub(crate) fn extend(&mut self, oth: Self) { - self.globals.extend(oth.globals); - self.errors.extend(oth.errors); - } -} - pub(crate) fn resolve_globals( context: &mut Context, globals: Vec, diff --git a/compiler/noirc_frontend/src/hir/resolution/mod.rs b/compiler/noirc_frontend/src/hir/resolution/mod.rs index 489bd8e09a5..8c16a9cca80 100644 --- a/compiler/noirc_frontend/src/hir/resolution/mod.rs +++ b/compiler/noirc_frontend/src/hir/resolution/mod.rs @@ -18,7 +18,7 @@ mod traits; mod type_aliases; pub(crate) use functions::resolve_free_functions; -pub(crate) use globals::{resolve_globals, ResolvedGlobals}; +pub(crate) use globals::resolve_globals; pub(crate) use impls::{collect_impls, resolve_impls}; pub(crate) use structs::resolve_structs; pub(crate) use traits::{ From 11a6acdf68877e4631bff120a734ec8cf4d6e9c5 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 22 Apr 2024 15:37:17 -0500 Subject: [PATCH 25/29] Finish fleshing out into_expression --- .../noirc_frontend/src/hir/comptime/errors.rs | 5 + .../src/hir/comptime/hir_to_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 6 + .../noirc_frontend/src/hir/comptime/mod.rs | 2 +- .../noirc_frontend/src/hir/comptime/scan.rs | 19 ++- .../noirc_frontend/src/hir/comptime/value.rs | 113 +++++++++++++----- .../src/hir/def_collector/dc_crate.rs | 25 +++- .../src/hir/resolution/resolver.rs | 4 +- .../noirc_frontend/src/hir/type_check/expr.rs | 6 + compiler/noirc_frontend/src/hir_def/expr.rs | 1 + .../src/monomorphization/mod.rs | 3 + compiler/noirc_frontend/src/node_interner.rs | 10 +- 12 files changed, 158 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index c0948417267..0a792107896 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -33,6 +33,11 @@ pub(crate) enum InterpreterError { InvalidValuesForBinary { lhs: Value, rhs: Value, operator: &'static str, location: Location }, CastToNonNumericType { typ: Type, location: Location }, QuoteInRuntimeCode { location: Location }, + NonStructInConstructor { typ: Type, location: Location }, + CannotInlineMacro { value: Value, location: Location }, + UnquoteFoundDuringEvaluation { location: Location }, + + Unimplemented { item: &'static str, 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 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 5f7274d6cfc..ec019fb4496 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -25,7 +25,7 @@ impl StmtId { #[allow(unused)] fn to_ast(self, interner: &NodeInterner) -> Statement { let statement = interner.statement(&self); - let span = interner.statement_span(&self); + let span = interner.statement_span(self); let kind = match statement { HirStatement::Let(let_stmt) => { @@ -168,9 +168,12 @@ impl ExprId { 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, HirExpression::CompTime(block) => ExpressionKind::CompTime(block.into_ast(interner)), + HirExpression::Quote(block) => ExpressionKind::Quote(block), + + // A macro was evaluated here! + HirExpression::Unquote(block) => ExpressionKind::Block(block), }; Expression::new(kind, span) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index c1040ac0c38..e7877c4724f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -314,6 +314,12 @@ impl<'a> Interpreter<'a> { HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), HirExpression::CompTime(block) => self.evaluate_block(block), + HirExpression::Unquote(block) => { + // An Unquote expression being found is indicative of a macro being + // expanded within another comptime fn which we don't currently support. + let location = self.interner.expr_location(&id); + Err(InterpreterError::UnquoteFoundDuringEvaluation { location }) + } HirExpression::Error => { let location = self.interner.expr_location(&id); Err(InterpreterError::ErrorNodeEncountered { location }) diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 96e23664712..f5e13875a25 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,4 +1,4 @@ -use crate::{node_interner::FuncId, macros_api::NodeInterner}; +use crate::{macros_api::NodeInterner, node_interner::FuncId}; mod errors; mod hir_to_ast; diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index 036fd63e79c..0d9603a0759 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -20,7 +20,7 @@ use crate::{ stmt::HirForStatement, }, macros_api::{HirExpression, HirLiteral, HirStatement}, - node_interner::{ExprId, StmtId, FuncId}, + node_interner::{ExprId, FuncId, StmtId}, }; use super::{ @@ -58,7 +58,9 @@ impl<'interner> Interpreter<'interner> { HirExpression::Tuple(tuple) => self.scan_tuple(tuple), HirExpression::Lambda(lambda) => self.scan_lambda(lambda), HirExpression::CompTime(block) => { - let new_expr = self.evaluate_block(block)?.into_expression(self.interner); + let location = self.interner.expr_location(&expr); + let new_expr = + self.evaluate_block(block)?.into_expression(self.interner, location)?; let new_expr = self.interner.expression(&new_expr); self.interner.replace_expr(&expr, new_expr); Ok(()) @@ -70,6 +72,15 @@ impl<'interner> Interpreter<'interner> { Err(InterpreterError::QuoteInRuntimeCode { location }) } HirExpression::Error => Ok(()), + + // Unquote should only be inserted by the comptime interpreter while expanding macros + // and is removed by the Hir -> Ast conversion pass which converts it into a normal block. + // If we find one now during scanning it most likely means the Hir -> Ast conversion + // missed it somehow. In the future we may allow users to manually write unquote + // expressions in their code but for now this is unreachable. + HirExpression::Unquote(block) => { + unreachable!("Found unquote block while scanning: {block}") + } } } @@ -174,7 +185,9 @@ impl<'interner> Interpreter<'interner> { HirStatement::Semi(semi) => self.scan_expression(semi), HirStatement::Error => Ok(()), HirStatement::CompTime(comptime) => { - let new_expr = self.evaluate_comptime(comptime)?.into_expression(self.interner); + let location = self.interner.statement_location(comptime); + let new_expr = + self.evaluate_comptime(comptime)?.into_expression(self.interner, location)?; self.interner.replace_statement(statement, HirStatement::Expression(new_expr)); Ok(()) } diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 42f666c458a..ee2c14f4bdc 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -2,14 +2,19 @@ use std::{borrow::Cow, rc::Rc}; use acvm::FieldElement; use im::Vector; -use iter_extended::vecmap; +use iter_extended::{try_vecmap, vecmap}; +use noirc_errors::Location; use crate::{ - hir_def::expr::HirLambda, node_interner::{FuncId, ExprId}, BlockExpression, IntegerBitSize, Shared, - Signedness, Type, macros_api::{NodeInterner, HirExpression, HirLiteral}, + hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, + macros_api::{HirExpression, HirLiteral, NodeInterner}, + node_interner::{ExprId, FuncId}, + BlockExpression, Ident, IntegerBitSize, Shared, Signedness, Type, }; use rustc_hash::FxHashMap as HashMap; +use super::errors::{IResult, InterpreterError}; + #[allow(unused)] #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum Value { @@ -65,54 +70,100 @@ impl Value { }) } - pub(crate) fn into_expression(self, interner: &mut NodeInterner) -> ExprId { + pub(crate) fn into_expression( + self, + interner: &mut NodeInterner, + location: Location, + ) -> IResult { + let typ = self.get_type().into_owned(); + let expression = match self { - Value::Unit => todo!(), - Value::Bool(value) => { - HirExpression::Literal(HirLiteral::Bool(value)) - }, - Value::Field(value) => { - HirExpression::Literal(HirLiteral::Integer(value, false)) - }, + Value::Unit => HirExpression::Literal(HirLiteral::Unit), + Value::Bool(value) => HirExpression::Literal(HirLiteral::Bool(value)), + Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value, false)), Value::I8(value) => { - let negative = value < 0 ; + let negative = value < 0; let value = value.abs(); let value = (value as u128).into(); HirExpression::Literal(HirLiteral::Integer(value, negative)) - }, + } Value::I32(value) => { - let negative = value < 0 ; + let negative = value < 0; let value = value.abs(); let value = (value as u128).into(); HirExpression::Literal(HirLiteral::Integer(value, negative)) - }, + } Value::I64(value) => { - let negative = value < 0 ; + let negative = value < 0; let value = value.abs(); let value = (value as u128).into(); HirExpression::Literal(HirLiteral::Integer(value, negative)) - }, + } Value::U8(value) => { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) - }, + } Value::U32(value) => { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) - }, + } Value::U64(value) => { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + } + Value::String(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), + Value::Function(id, _typ) => { + let id = interner.function_definition_id(id); + let impl_kind = ImplKind::NotATraitMethod; + HirExpression::Ident(HirIdent { location, id, impl_kind }) + } + Value::Closure(_lambda, _env, _typ) => { + // TODO: How should a closure's environment be inlined? + let item = "returning closures from a comptime fn"; + return Err(InterpreterError::Unimplemented { item, location }) }, - Value::String(value) => { - HirExpression::Literal(HirLiteral::Str(value.as_ref().clone())) - }, - Value::Function(_, _) => todo!(), - Value::Closure(_, _, _) => todo!(), - Value::Tuple(_) => todo!(), - Value::Struct(_, _) => todo!(), - Value::Pointer(_) => todo!(), - Value::Array(_, _) => todo!(), - Value::Slice(_, _) => todo!(), - Value::Code(_) => todo!(), + Value::Tuple(fields) => { + let fields = try_vecmap(fields, |field| field.into_expression(interner, location))?; + HirExpression::Tuple(fields) + } + Value::Struct(fields, typ) => { + let fields = try_vecmap(fields, |(name, field)| { + let field = field.into_expression(interner, location)?; + Ok((Ident::new(unwrap_rc(name), location.span), field)) + })?; + + let (r#type, struct_generics) = match typ.follow_bindings() { + Type::Struct(def, generics) => (def, generics), + _ => return Err(InterpreterError::NonStructInConstructor { typ, location }), + }; + + HirExpression::Constructor(HirConstructorExpression { + r#type, + struct_generics, + fields, + }) + } + Value::Array(elements, _) => { + let elements = + try_vecmap(elements, |elements| elements.into_expression(interner, location))?; + HirExpression::Literal(HirLiteral::Array(HirArrayLiteral::Standard(elements))) + } + Value::Slice(elements, _) => { + let elements = + try_vecmap(elements, |elements| elements.into_expression(interner, location))?; + HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) + } + Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)), + Value::Pointer(_) => { + return Err(InterpreterError::CannotInlineMacro { value: self, location }) + } }; - interner.push_expr(expression) + + let id = interner.push_expr(expression); + interner.push_expr_location(id, location.span, location.file); + interner.push_expr_type(id, typ); + Ok(id) } } + +/// Unwraps an Rc value without cloning the inner value if the reference count is 1. Clones otherwise. +fn unwrap_rc(rc: Rc) -> T { + Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()) +} diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index c09ba6c2571..a814a897ce0 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -333,9 +333,17 @@ impl DefCollector { crate_id, )); - resolved_module.errors.extend(resolve_traits(context, def_collector.collected_traits, crate_id)); + resolved_module.errors.extend(resolve_traits( + context, + def_collector.collected_traits, + crate_id, + )); // Must resolve structs before we resolve globals. - resolved_module.errors.extend(resolve_structs(context, def_collector.collected_types, crate_id)); + resolved_module.errors.extend(resolve_structs( + context, + def_collector.collected_types, + crate_id, + )); // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. @@ -352,7 +360,11 @@ impl DefCollector { // // These are resolved after trait impls so that struct methods are chosen // over trait methods if there are name conflicts. - resolved_module.errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); + resolved_module.errors.extend(collect_impls( + context, + crate_id, + &def_collector.collected_impls, + )); // We must wait to resolve non-integer globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. @@ -497,7 +509,12 @@ impl ResolvedModule { } } - fn resolve_globals(&mut self, context: &mut Context, literal_globals: Vec, crate_id: CrateId) { + fn resolve_globals( + &mut self, + context: &mut Context, + literal_globals: Vec, + crate_id: CrateId, + ) { let globals = resolve_globals(context, literal_globals, crate_id); self.globals.extend(globals.globals); self.errors.extend(globals.errors); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index bb618615858..e0a66ea6e3a 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1327,7 +1327,9 @@ impl<'a> Resolver<'a> { pub fn intern_stmt(&mut self, stmt: Statement) -> StmtId { let hir_stmt = self.resolve_stmt(stmt.kind, stmt.span); - self.interner.push_stmt(hir_stmt) + let id = self.interner.push_stmt(hir_stmt); + self.interner.push_statement_location(id, stmt.span, self.file); + id } fn resolve_lvalue(&mut self, lvalue: LValue) -> HirLValue { diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 322f9dc9121..84be7d8c29c 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -309,6 +309,12 @@ impl<'interner> TypeChecker<'interner> { } HirExpression::Quote(_) => Type::Code, HirExpression::CompTime(block) => self.check_block(block), + + // Unquote should be inserted & removed by the comptime interpreter. + // Even if we allowed it here, we wouldn't know what type to give to the result. + HirExpression::Unquote(block) => { + unreachable!("Unquote remaining during type checking {block}") + } }; self.interner.push_expr_type(*expr_id, typ.clone()); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 9b11aa96f57..247f169b654 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -31,6 +31,7 @@ pub enum HirExpression { Tuple(Vec), Lambda(HirLambda), Quote(crate::BlockExpression), + Unquote(crate::BlockExpression), CompTime(HirBlockExpression), Error, } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 0657fa6983a..eda2c6b0f70 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -515,6 +515,9 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), + HirExpression::Unquote(_) => { + unreachable!("unquote expression remaining in runtime code") + } HirExpression::CompTime(_) => { unreachable!("comptime expression remaining in runtime code") } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5b375be8d56..6f5d205d6ce 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -921,10 +921,18 @@ impl NodeInterner { self.id_location(expr_id) } - pub fn statement_span(&self, stmt_id: &StmtId) -> Span { + pub fn statement_span(&self, stmt_id: StmtId) -> Span { self.id_location(stmt_id).span } + pub fn statement_location(&self, stmt_id: StmtId) -> Location { + self.id_location(stmt_id) + } + + pub fn push_statement_location(&mut self, id: StmtId, span: Span, file: FileId) { + self.id_to_location.insert(id.into(), Location::new(span, file)); + } + pub fn get_struct(&self, id: StructId) -> Shared { self.structs[&id].clone() } From a4e596e503a0f61df401ba1bec308de2aa3777f7 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 22 Apr 2024 15:54:11 -0500 Subject: [PATCH 26/29] fmt --- compiler/noirc_frontend/src/hir/comptime/interpreter.rs | 2 +- compiler/noirc_frontend/src/hir/comptime/value.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index d50bda91043..f36399195fd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -22,7 +22,7 @@ use crate::{ }, macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, - TypeBindings, TypeVariableKind, Type, TypeBinding, Shared + Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; use super::errors::{IResult, InterpreterError}; diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 1e319a0a496..123dac29b4a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -6,11 +6,11 @@ use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use crate::{ + ast::{BlockExpression, Ident, IntegerBitSize, Signedness}, hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{HirExpression, HirLiteral, NodeInterner}, node_interner::{ExprId, FuncId}, - ast::{ BlockExpression, Ident, IntegerBitSize, Signedness }, - Type, Shared, + Shared, Type, }; use rustc_hash::FxHashMap as HashMap; From 7e75b644dca8b1ee0f97dcfd82c3934a2b6f6cf6 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 23 Apr 2024 11:15:03 -0500 Subject: [PATCH 27/29] Code review --- .../noirc_frontend/src/hir/comptime/errors.rs | 8 ++++---- .../src/hir/comptime/interpreter.rs | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 0a792107896..1d55a906303 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -41,11 +41,11 @@ pub(crate) enum InterpreterError { // 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, + BreakNotInLoop { location: Location }, + ContinueNotInLoop { location: Location }, - // These cases are not errors but prevent us from running more code - // until the loop can be resumed properly. + // These cases are not errors, they are just used to prevent us from running more code + // until the loop can be resumed properly. These cases will never be displayed to users. Break, Continue, } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index f36399195fd..f4aeaba4a2c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -40,11 +40,11 @@ pub 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. - pub(super) changed_functions: HashSet, + changed_functions: HashSet, /// True if we've expanded any macros into global scope and will need /// to redo name resolution & type checking for everything. - pub(super) changed_globally: bool, + changed_globally: bool, in_loop: bool, @@ -1020,8 +1020,8 @@ impl<'a> Interpreter<'a> { 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::Break => self.evaluate_break(statement), + HirStatement::Continue => self.evaluate_continue(statement), HirStatement::Expression(expression) => self.evaluate(expression), HirStatement::CompTime(statement) => self.evaluate_comptime(statement), HirStatement::Semi(expression) => { @@ -1181,19 +1181,21 @@ impl<'a> Interpreter<'a> { Ok(Value::Unit) } - fn evaluate_break(&mut self) -> IResult { + fn evaluate_break(&mut self, id: StmtId) -> IResult { if self.in_loop { Err(InterpreterError::Break) } else { - Err(InterpreterError::BreakNotInLoop) + let location = self.interner.statement_location(id); + Err(InterpreterError::BreakNotInLoop { location }) } } - fn evaluate_continue(&mut self) -> IResult { + fn evaluate_continue(&mut self, id: StmtId) -> IResult { if self.in_loop { Err(InterpreterError::Continue) } else { - Err(InterpreterError::ContinueNotInLoop) + let location = self.interner.statement_location(id); + Err(InterpreterError::ContinueNotInLoop { location }) } } From 522867ba915575e632e30be5ed0ca41416e45e97 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 23 Apr 2024 11:52:39 -0500 Subject: [PATCH 28/29] Improve error slightly --- .../noirc_frontend/src/hir/comptime/errors.rs | 4 +-- .../src/hir/comptime/interpreter.rs | 26 +++++++------------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 1d55a906303..31a13d878ca 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -1,4 +1,4 @@ -use crate::{node_interner::DefinitionId, Type}; +use crate::Type; use acvm::FieldElement; use noirc_errors::Location; @@ -10,7 +10,7 @@ use super::value::Value; pub(crate) enum InterpreterError { ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, TypeMismatch { expected: Type, value: Value, location: Location }, - NoValueForId { id: DefinitionId, location: Location }, + NonCompTimeVarReferenced { name: String, location: Location }, IntegerOutOfRangeForType { value: FieldElement, typ: Type, location: Location }, ErrorNodeEncountered { location: Location }, NonFunctionCalled { value: Value, location: Location }, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index f4aeaba4a2c..4f91f785a98 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -246,13 +246,7 @@ impl<'a> Interpreter<'a> { location: Location, ) -> IResult<()> { self.type_check(typ, &argument, location)?; - for scope in self.scopes.iter_mut().rev() { - if let Entry::Occupied(mut entry) = scope.entry(id) { - entry.insert(argument); - return Ok(()); - } - } - Err(InterpreterError::NoValueForId { id, location }) + self.mutate(id, argument, location) } /// Mutate an existing variable, potentially from a prior scope @@ -263,17 +257,12 @@ impl<'a> Interpreter<'a> { return Ok(()); } } - Err(InterpreterError::NoValueForId { id, location }) + let name = self.interner.definition(id).name.clone(); + Err(InterpreterError::NonCompTimeVarReferenced { name, location }) } fn lookup(&self, ident: &HirIdent) -> IResult { - for scope in self.scopes.iter().rev() { - if let Some(value) = scope.get(&ident.id) { - return Ok(value.clone()); - } - } - - Err(InterpreterError::NoValueForId { id: ident.id, location: ident.location }) + self.lookup_id(ident.id, ident.location) } fn lookup_id(&self, id: DefinitionId, location: Location) -> IResult { @@ -283,7 +272,12 @@ impl<'a> Interpreter<'a> { } } - Err(InterpreterError::NoValueForId { id, location }) + // Justification for `NonCompTimeVarReferenced`: + // If we have an id to lookup at all that means name resolution successfully + // found another variable in scope for this name. If the name is in scope + // but unknown by the interpreter it must be because it was not a comptime variable. + let name = self.interner.definition(id).name.clone(); + Err(InterpreterError::NonCompTimeVarReferenced { name, location }) } fn type_check(&self, typ: &Type, value: &Value, location: Location) -> IResult<()> { From 47ef177f8e7818960b1f4bb57fec3d1d1d7fa2cb Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 23 Apr 2024 13:29:04 -0500 Subject: [PATCH 29/29] Finish missed CompTime -> Comptime cases --- compiler/noirc_frontend/src/ast/statement.rs | 8 ++++---- compiler/noirc_frontend/src/hir/comptime/errors.rs | 2 +- compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs | 4 ++-- .../noirc_frontend/src/hir/comptime/interpreter.rs | 8 ++++---- compiler/noirc_frontend/src/hir/comptime/scan.rs | 10 +++++----- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 4 ++-- compiler/noirc_frontend/src/hir/type_check/stmt.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- compiler/noirc_frontend/src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/noir_parser.lalrpop | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 2 +- compiler/noirc_frontend/src/parser/parser/types.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 2 +- tooling/nargo_fmt/src/visitor/stmt.rs | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index f37c7adc983..1831a046f5b 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -40,7 +40,7 @@ pub enum StatementKind { Break, Continue, /// This statement should be executed at compile-time - CompTime(Box), + Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -87,10 +87,10 @@ impl StatementKind { } self } - StatementKind::CompTime(mut statement) => { + StatementKind::Comptime(mut statement) => { *statement = statement.add_semicolon(semi, span, last_statement_in_block, emit_error); - StatementKind::CompTime(statement) + StatementKind::Comptime(statement) } // A semicolon on a for loop is optional and does nothing StatementKind::For(_) => self, @@ -685,7 +685,7 @@ impl Display for StatementKind { StatementKind::For(for_loop) => for_loop.fmt(f), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), - StatementKind::CompTime(statement) => write!(f, "comptime {statement}"), + StatementKind::Comptime(statement) => write!(f, "comptime {statement}"), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index e16dc242cbc..67d9a006b22 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -9,7 +9,7 @@ use super::value::Value; pub enum InterpreterError { ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, TypeMismatch { expected: Type, value: Value, location: Location }, - NonCompTimeVarReferenced { name: String, location: Location }, + NonComptimeVarReferenced { name: String, location: Location }, IntegerOutOfRangeForType { value: FieldElement, typ: Type, location: Location }, ErrorNodeEncountered { location: Location }, NonFunctionCalled { value: Value, location: Location }, 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 dd23edf0004..42ee76d29fa 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -66,8 +66,8 @@ impl StmtId { HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), HirStatement::Error => StatementKind::Error, - HirStatement::CompTime(statement) => { - StatementKind::CompTime(Box::new(statement.to_ast(interner).kind)) + HirStatement::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement.to_ast(interner).kind)) } }; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 2aa11573b65..c01c985a40c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -258,7 +258,7 @@ impl<'a> Interpreter<'a> { } } let name = self.interner.definition(id).name.clone(); - Err(InterpreterError::NonCompTimeVarReferenced { name, location }) + Err(InterpreterError::NonComptimeVarReferenced { name, location }) } fn lookup(&self, ident: &HirIdent) -> IResult { @@ -272,12 +272,12 @@ impl<'a> Interpreter<'a> { } } - // Justification for `NonCompTimeVarReferenced`: + // Justification for `NonComptimeVarReferenced`: // If we have an id to lookup at all that means name resolution successfully // found another variable in scope for this name. If the name is in scope // but unknown by the interpreter it must be because it was not a comptime variable. let name = self.interner.definition(id).name.clone(); - Err(InterpreterError::NonCompTimeVarReferenced { name, location }) + Err(InterpreterError::NonComptimeVarReferenced { name, location }) } fn type_check(&self, typ: &Type, value: &Value, location: Location) -> IResult<()> { @@ -1017,7 +1017,7 @@ impl<'a> Interpreter<'a> { HirStatement::Break => self.evaluate_break(statement), HirStatement::Continue => self.evaluate_continue(statement), HirStatement::Expression(expression) => self.evaluate(expression), - HirStatement::CompTime(statement) => self.evaluate_comptime(statement), + HirStatement::Comptime(statement) => self.evaluate_comptime(statement), HirStatement::Semi(expression) => { self.evaluate(expression)?; Ok(Value::Unit) diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index d4fa355627f..2e13daea527 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -1,15 +1,15 @@ //! This module is for the scanning of the Hir by the interpreter. -//! In this initial step, the Hir is scanned for `CompTime` nodes +//! In this initial step, the Hir is scanned for `Comptime` nodes //! without actually executing anything until such a node is found. //! Once such a node is found, the interpreter will call the relevant //! evaluate method on that node type, insert the result into the Ast, //! and continue scanning the rest of the program. //! -//! Since it mostly just needs to recur on the Hir looking for CompTime +//! Since it mostly just needs to recur on the Hir looking for Comptime //! nodes, this pass is fairly simple. The only thing it really needs to //! ensure to do is to push and pop scopes on the interpreter as needed //! so that any variables defined within e.g. an `if` statement containing -//! a `CompTime` block aren't accessible outside of the `if`. +//! a `Comptime` block aren't accessible outside of the `if`. use crate::{ hir_def::{ expr::{ @@ -30,7 +30,7 @@ use super::{ #[allow(dead_code)] impl<'interner> Interpreter<'interner> { - /// Scan through a function, evaluating any CompTime nodes found. + /// Scan through a function, evaluating any Comptime nodes found. /// These nodes will be modified in place, replaced with the /// result of their evaluation. pub fn scan_function(&mut self, function: FuncId) -> IResult<()> { @@ -185,7 +185,7 @@ impl<'interner> Interpreter<'interner> { HirStatement::Expression(expression) => self.scan_expression(expression), HirStatement::Semi(semi) => self.scan_expression(semi), HirStatement::Error => Ok(()), - HirStatement::CompTime(comptime) => { + HirStatement::Comptime(comptime) => { let location = self.interner.statement_location(comptime); let new_expr = self.evaluate_comptime(comptime)?.into_expression(self.interner, location)?; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 8d2cb17189b..8e29f8f9fce 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1272,9 +1272,9 @@ impl<'a> Resolver<'a> { HirStatement::Continue } StatementKind::Error => HirStatement::Error, - StatementKind::CompTime(statement) => { + StatementKind::Comptime(statement) => { let statement = self.resolve_stmt(*statement, span); - HirStatement::CompTime(self.interner.push_stmt(statement)) + HirStatement::Comptime(self.interner.push_stmt(statement)) } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 064fefc8ae9..f5f6e1e8180 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -51,7 +51,7 @@ impl<'interner> TypeChecker<'interner> { HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), HirStatement::For(for_loop) => self.check_for_loop(for_loop), - HirStatement::CompTime(statement) => return self.check_statement(&statement), + HirStatement::Comptime(statement) => return self.check_statement(&statement), HirStatement::Break | HirStatement::Continue | HirStatement::Error => (), } Type::Unit diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 48e7d7344e3..7e22e5ee9c0 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -20,7 +20,7 @@ pub enum HirStatement { Continue, Expression(ExprId), Semi(ExprId), - CompTime(StmtId), + Comptime(StmtId), Error, } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index d92b6c65d7a..b130af9d125 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -632,7 +632,7 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Error => unreachable!(), // All `comptime` statements & expressions should be removed before runtime. - HirStatement::CompTime(_) => unreachable!("comptime statement in runtime code"), + HirStatement::Comptime(_) => unreachable!("comptime statement in runtime code"), } } diff --git a/compiler/noirc_frontend/src/noir_parser.lalrpop b/compiler/noirc_frontend/src/noir_parser.lalrpop index ec2b4c8ab46..9acb5ef8b58 100644 --- a/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -61,7 +61,7 @@ extern { "break" => BorrowedToken::Keyword(noir_token::Keyword::Break), "call_data" => BorrowedToken::Keyword(noir_token::Keyword::CallData), "char" => BorrowedToken::Keyword(noir_token::Keyword::Char), - "comptime" => BorrowedToken::Keyword(noir_token::Keyword::CompTime), + "comptime" => BorrowedToken::Keyword(noir_token::Keyword::Comptime), "constrain" => BorrowedToken::Keyword(noir_token::Keyword::Constrain), "continue" => BorrowedToken::Keyword(noir_token::Keyword::Continue), "contract" => BorrowedToken::Keyword(noir_token::Keyword::Contract), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 77d7b063800..6e9f3969297 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -540,7 +540,7 @@ where StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) }), ))) - .map(|statement| StatementKind::CompTime(Box::new(statement))) + .map(|statement| StatementKind::Comptime(Box::new(statement))) } /// Comptime in an expression position only accepts entire blocks diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index d8a63d161b9..82dd3dad681 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -12,7 +12,7 @@ use chumsky::prelude::*; use noirc_errors::Span; fn maybe_comp_time() -> impl NoirParser<()> { - keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { + keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { if opt.is_some() { emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index ac3d7bbc4cc..31bf2245b1f 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -780,7 +780,7 @@ mod test { HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), - HirStatement::CompTime(_) => panic!("Unexpected comptime"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), }; let expr = interner.expression(&expr_id); diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 869977d5f3c..e41827c94a1 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -103,7 +103,7 @@ impl super::FmtVisitor<'_> { StatementKind::Error => unreachable!(), StatementKind::Break => self.push_rewrite("break;".into(), span), StatementKind::Continue => self.push_rewrite("continue;".into(), span), - StatementKind::CompTime(statement) => self.visit_stmt(*statement, span, is_last), + StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last), } } }