diff --git a/.noir-sync-commit b/.noir-sync-commit index b893f23d601..1c28890d644 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -f6a7306436ea1a37ec7f3b884721b50467e9a063 +70cbeb4322a0b11c1c167ab27bf0408d04fe7b7d diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index f11b310494b..6914bf87c5d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -115,6 +115,11 @@ impl Context { terminator.mutate_blocks(|old_block| self.new_ids.blocks[&old_block]); new_function.dfg.set_block_terminator(new_block_id, terminator); } + + // Also map the values in the databus + let old_databus = &old_function.dfg.data_bus; + new_function.dfg.data_bus = old_databus + .map_values(|old_value| self.new_ids.map_value(new_function, old_function, old_value)); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 94e81b19582..07f15f37c6e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -19,6 +19,7 @@ pub use visitor::Visitor; pub use expression::*; pub use function::*; +use acvm::FieldElement; pub use docs::*; use noirc_errors::Span; use serde::{Deserialize, Serialize}; @@ -219,7 +220,7 @@ pub struct UnaryRhsMethodCall { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { Variable(Path), - Constant(u32, Span), + Constant(FieldElement, Span), BinaryOperation( Box, BinaryTypeOperator, @@ -421,12 +422,13 @@ impl UnresolvedTypeExpression { fn from_expr_helper(expr: Expression) -> Result { match expr.kind { ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { - Some(int) => Ok(UnresolvedTypeExpression::Constant(int, expr.span)), + Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.span)), None => Err(expr), }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span)); + let lhs = + Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 1675629ff14..8c8fe6e6387 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -19,8 +19,15 @@ pub struct NoirStruct { pub span: Span, } +impl NoirStruct { + pub fn is_abi(&self) -> bool { + self.attributes.iter().any(|attr| attr.is_abi()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct StructField { + pub visibility: ItemVisibility, pub name: Ident, pub typ: UnresolvedType, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index cbd72788c85..06bcafc55c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1,3 +1,4 @@ +use acvm::{AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Location, Span}; use regex::Regex; @@ -6,13 +7,15 @@ use rustc_hash::FxHashSet as HashSet; use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, - Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, Lambda, - Literal, MemberAccessExpression, MethodCallExpression, PrefixExpression, StatementKind, - UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, + Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, + ItemVisibility, Lambda, Literal, MemberAccessExpression, MethodCallExpression, + PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, }, hir::{ comptime::{self, InterpreterError}, - resolution::errors::ResolverError, + resolution::{ + errors::ResolverError, import::PathResolutionError, visibility::method_call_is_visible, + }, type_check::{generics::TraitGenerics, TypeCheckError}, }, hir_def::{ @@ -161,7 +164,7 @@ impl<'context> Elaborator<'context> { (Lit(int), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { - let len = Type::Constant(str.len() as u32, Kind::u32()); + let len = Type::Constant(str.len().into(), Kind::u32()); (Lit(HirLiteral::Str(str)), Type::String(Box::new(len))) } Literal::FmtStr(str) => self.elaborate_fmt_string(str, span), @@ -203,7 +206,7 @@ impl<'context> Elaborator<'context> { elem_id }); - let length = Type::Constant(elements.len() as u32, Kind::u32()); + let length = Type::Constant(elements.len().into(), Kind::u32()); (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { @@ -211,7 +214,7 @@ impl<'context> Elaborator<'context> { let length = UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { self.push_err(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(0, span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), span) }); let length = self.convert_expression_type(length, &Kind::u32(), span); @@ -267,7 +270,7 @@ impl<'context> Elaborator<'context> { } } - let len = Type::Constant(str.len() as u32, Kind::u32()); + let len = Type::Constant(str.len().into(), Kind::u32()); let typ = Type::FmtString(Box::new(len), Box::new(Type::Tuple(capture_types))); (HirExpression::Literal(HirLiteral::FmtStr(str, fmt_str_idents)), typ) } @@ -448,6 +451,8 @@ impl<'context> Elaborator<'context> { let method_call = HirMethodCallExpression { method, object, arguments, location, generics }; + self.check_method_call_visibility(func_id, &object_type, &method_call.method); + // Desugar the method call into a normal, resolved function call // so that the backend doesn't need to worry about methods // TODO: update object_type here? @@ -486,6 +491,20 @@ impl<'context> Elaborator<'context> { } } + fn check_method_call_visibility(&mut self, func_id: FuncId, object_type: &Type, name: &Ident) { + if !method_call_is_visible( + object_type, + func_id, + self.module_id(), + self.interner, + self.def_maps, + ) { + self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( + name.clone(), + ))); + } + } + fn elaborate_constructor( &mut self, constructor: ConstructorExpression, @@ -548,7 +567,7 @@ impl<'context> Elaborator<'context> { let generics = struct_generics.clone(); let fields = constructor.fields; - let field_types = r#type.borrow().get_fields(&struct_generics); + let field_types = r#type.borrow().get_fields_with_visibility(&struct_generics); let fields = self.resolve_constructor_expr_fields(struct_type.clone(), field_types, fields, span); let expr = HirExpression::Constructor(HirConstructorExpression { @@ -576,7 +595,7 @@ impl<'context> Elaborator<'context> { fn resolve_constructor_expr_fields( &mut self, struct_type: Shared, - field_types: Vec<(String, Type)>, + field_types: Vec<(String, ItemVisibility, Type)>, fields: Vec<(Ident, Expression)>, span: Span, ) -> Vec<(Ident, ExprId)> { @@ -588,10 +607,11 @@ impl<'context> Elaborator<'context> { let expected_field_with_index = field_types .iter() .enumerate() - .find(|(_, (name, _))| name == &field_name.0.contents); - let expected_index = expected_field_with_index.map(|(index, _)| index); + .find(|(_, (name, _, _))| name == &field_name.0.contents); + let expected_index_and_visibility = + expected_field_with_index.map(|(index, (_, visibility, _))| (index, visibility)); let expected_type = - expected_field_with_index.map(|(_, (_, typ))| typ).unwrap_or(&Type::Error); + expected_field_with_index.map(|(_, (_, _, typ))| typ).unwrap_or(&Type::Error); let field_span = field.span; let (resolved, field_type) = self.elaborate_expression(field); @@ -618,11 +638,21 @@ impl<'context> Elaborator<'context> { }); } - if let Some(expected_index) = expected_index { + if let Some((index, visibility)) = expected_index_and_visibility { + let struct_type = struct_type.borrow(); + let field_span = field_name.span(); + let field_name = &field_name.0.contents; + self.check_struct_field_visibility( + &struct_type, + field_name, + *visibility, + field_span, + ); + self.interner.add_struct_member_reference( - struct_type.borrow().id, - expected_index, - Location::new(field_name.span(), self.file), + struct_type.id, + index, + Location::new(field_span, self.file), ); } @@ -665,7 +695,7 @@ impl<'context> Elaborator<'context> { fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { let (lhs, lhs_type) = self.elaborate_expression(cast.lhs); let r#type = self.resolve_type(cast.r#type); - let result = self.check_cast(&lhs_type, &r#type, span); + let result = self.check_cast(&lhs, &lhs_type, &r#type, span); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index ddbb80e2bfc..3fee36830b8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -3,7 +3,7 @@ use std::{ rc::Rc, }; -use crate::ast::ItemVisibility; +use crate::{ast::ItemVisibility, StructField}; use crate::{ ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, @@ -751,7 +751,7 @@ impl<'context> Elaborator<'context> { ); if is_entry_point { - self.mark_parameter_type_as_used(&typ); + self.mark_type_as_used(&typ); } let pattern = self.elaborate_pattern_and_store_ids( @@ -832,33 +832,36 @@ impl<'context> Elaborator<'context> { self.current_item = None; } - fn mark_parameter_type_as_used(&mut self, typ: &Type) { + fn mark_type_as_used(&mut self, typ: &Type) { match typ { - Type::Array(_n, typ) => self.mark_parameter_type_as_used(typ), - Type::Slice(typ) => self.mark_parameter_type_as_used(typ), + Type::Array(_n, typ) => self.mark_type_as_used(typ), + Type::Slice(typ) => self.mark_type_as_used(typ), Type::Tuple(types) => { for typ in types { - self.mark_parameter_type_as_used(typ); + self.mark_type_as_used(typ); } } Type::Struct(struct_type, generics) => { self.mark_struct_as_constructed(struct_type.clone()); for generic in generics { - self.mark_parameter_type_as_used(generic); + self.mark_type_as_used(generic); + } + for (_, typ) in struct_type.borrow().get_fields(generics) { + self.mark_type_as_used(&typ); } for (_, typ) in struct_type.borrow().get_fields(generics) { self.mark_parameter_type_as_used(&typ); } } Type::Alias(alias_type, generics) => { - self.mark_parameter_type_as_used(&alias_type.borrow().get_type(generics)); + self.mark_type_as_used(&alias_type.borrow().get_type(generics)); } Type::MutableReference(typ) => { - self.mark_parameter_type_as_used(typ); + self.mark_type_as_used(typ); } Type::InfixExpr(left, _op, right) => { - self.mark_parameter_type_as_used(left); - self.mark_parameter_type_as_used(right); + self.mark_type_as_used(left); + self.mark_type_as_used(right); } Type::FieldElement | Type::Integer(..) @@ -1277,6 +1280,13 @@ impl<'context> Elaborator<'context> { self.local_module = typ.module_id; let fields = self.resolve_struct_fields(&typ.struct_def, *type_id); + + if typ.struct_def.is_abi() { + for field in &fields { + self.mark_type_as_used(&field.typ); + } + } + let fields_len = fields.len(); self.interner.update_struct(*type_id, |struct_def| { struct_def.set_fields(fields); @@ -1315,7 +1325,7 @@ impl<'context> Elaborator<'context> { &mut self, unresolved: &NoirStruct, struct_id: StructId, - ) -> Vec<(Ident, Type)> { + ) -> Vec { self.recover_generics(|this| { this.current_item = Some(DependencyId::Struct(struct_id)); @@ -1327,7 +1337,8 @@ impl<'context> Elaborator<'context> { let fields = vecmap(&unresolved.fields, |field| { let ident = &field.item.name; let typ = &field.item.typ; - (ident.clone(), this.resolve_type(typ.clone())) + let visibility = field.item.visibility; + StructField { visibility, name: ident.clone(), typ: this.resolve_type(typ.clone()) } }); this.resolving_ids.remove(&struct_id); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs index bb8d041eab4..0f6cb78fae7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -4,7 +4,8 @@ use rustc_hash::FxHashSet as HashSet; use crate::{ ast::{ - Expression, ExpressionKind, Ident, Path, Pattern, TypePath, UnresolvedType, ERROR_IDENT, + Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, UnresolvedType, + ERROR_IDENT, }, hir::{ def_collector::dc_crate::CompilationError, @@ -272,7 +273,9 @@ impl<'context> Elaborator<'context> { let mut unseen_fields = struct_type.borrow().field_names(); for (field, pattern) in fields { - let field_type = expected_type.get_field_type(&field.0.contents).unwrap_or(Type::Error); + let (field_type, visibility) = expected_type + .get_field_type_and_visibility(&field.0.contents) + .unwrap_or((Type::Error, ItemVisibility::Public)); let resolved = self.elaborate_pattern_mut( pattern, field_type, @@ -286,6 +289,13 @@ impl<'context> Elaborator<'context> { if unseen_fields.contains(&field) { unseen_fields.remove(&field); seen_fields.insert(field.clone()); + + self.check_struct_field_visibility( + &struct_type.borrow(), + &field.0.contents, + visibility, + field.span(), + ); } else if seen_fields.contains(&field) { // duplicate field self.push_err(ResolverError::DuplicateField { field: field.clone() }); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 6d5d0c9b467..0be71e39587 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -3,11 +3,14 @@ use noirc_errors::{Location, Span, Spanned}; use crate::{ ast::{ AssignStatement, BinaryOpKind, ConstrainKind, ConstrainStatement, Expression, - ExpressionKind, ForLoopStatement, ForRange, InfixExpression, LValue, LetStatement, Path, - Statement, StatementKind, + ExpressionKind, ForLoopStatement, ForRange, Ident, InfixExpression, ItemVisibility, LValue, + LetStatement, Path, Statement, StatementKind, }, hir::{ - resolution::errors::ResolverError, + resolution::{ + errors::ResolverError, import::PathResolutionError, + visibility::struct_member_is_visible, + }, type_check::{Source, TypeCheckError}, }, hir_def::{ @@ -18,7 +21,7 @@ use crate::{ }, }, node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, - Type, + StructType, Type, }; use super::{lints, Elaborator}; @@ -439,10 +442,12 @@ impl<'context> Elaborator<'context> { match &lhs_type { Type::Struct(s, args) => { let s = s.borrow(); - if let Some((field, index)) = s.get_field(field_name, args) { + if let Some((field, visibility, index)) = s.get_field(field_name, args) { let reference_location = Location::new(span, self.file); self.interner.add_struct_member_reference(s.id, index, reference_location); + self.check_struct_field_visibility(&s, field_name, visibility, span); + return Some((field, index)); } } @@ -497,6 +502,20 @@ impl<'context> Elaborator<'context> { None } + pub(super) fn check_struct_field_visibility( + &mut self, + struct_type: &StructType, + field_name: &str, + visibility: ItemVisibility, + span: Span, + ) { + if !struct_member_is_visible(struct_type.id, visibility, self.module_id(), self.def_maps) { + self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( + Ident::new(field_name.to_string(), span), + ))); + } + } + fn elaborate_comptime_statement(&mut self, statement: Statement) -> (HirStatement, Type) { let span = statement.span; let (hir_statement, _typ) = diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index ef06cfdaad8..82d14743428 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -417,7 +417,17 @@ impl<'context> Elaborator<'context> { .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) .unwrap_or(Kind::u32()); - Some(Type::Constant(self.eval_global_as_array_length(id, path), kind)) + // TODO(https://github.com/noir-lang/noir/issues/6238): + // support non-u32 generics here + if !kind.unifies(&Kind::u32()) { + let error = TypeCheckError::EvaluatedGlobalIsntU32 { + expected_kind: Kind::u32().to_string(), + expr_kind: kind.to_string(), + expr_span: path.span(), + }; + self.push_err(error); + } + Some(Type::Constant(self.eval_global_as_array_length(id, path).into(), kind)) } _ => None, } @@ -833,12 +843,27 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { - match from.follow_bindings() { - Type::Integer(..) | Type::FieldElement | Type::Bool => (), + pub(super) fn check_cast( + &mut self, + from_expr_id: &ExprId, + from: &Type, + to: &Type, + span: Span, + ) -> Type { + let from_follow_bindings = from.follow_bindings(); - Type::TypeVariable(var) if var.is_integer() || var.is_integer_or_field() => (), + let from_value_opt = match self.interner.expression(from_expr_id) { + HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), + // TODO(https://github.com/noir-lang/noir/issues/6247): + // handle negative literals + _ => None, + }; + + let from_is_polymorphic = match from_follow_bindings { + Type::Integer(..) | Type::FieldElement | Type::Bool => false, + + Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => true, Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. @@ -846,13 +871,32 @@ impl<'context> Elaborator<'context> { self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), span, + reason: "casting from a non-integral type is unsupported".into(), }); + true } Type::Error => return Type::Error, from => { - self.push_err(TypeCheckError::InvalidCast { from, span }); + let reason = "casting from this type is unsupported".into(); + self.push_err(TypeCheckError::InvalidCast { from, span, reason }); return Type::Error; } + }; + + // TODO(https://github.com/noir-lang/noir/issues/6247): + // handle negative literals + // when casting a polymorphic value to a specifically sized type, + // check that it fits or throw a warning + if let (Some(from_value), Some(to_maximum_size)) = + (from_value_opt, to.integral_maximum_size()) + { + if from_is_polymorphic && from_value > to_maximum_size { + let from = from.clone(); + let to = to.clone(); + let reason = format!("casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it"); + // we warn that the 'to' type is too small for the value + self.push_err(TypeCheckError::DownsizingCast { from, to, span, reason }); + } } match to { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 3542c724b30..97d90b905d4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -417,7 +417,7 @@ impl HirArrayLiteral { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { Type::Constant(length, _kind) => { - let literal = Literal::Integer((*length as u128).into(), false); + let literal = Literal::Integer(*length, false); let expr_kind = ExpressionKind::Literal(literal); Box::new(Expression::new(expr_kind, span)) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 1690295ffda..ffb759e74a2 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -586,12 +586,14 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_, _) => None, - TypeBinding::Bound(binding) => binding.evaluate_to_u32(), + TypeBinding::Bound(binding) => { + binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())) + } }; if let Some(value) = value { let typ = self.elaborator.interner.id_type(id); - self.evaluate_integer((value as u128).into(), false, id) + self.evaluate_integer(value, false, id) } else { let location = self.elaborator.interner.expr_location(&id); let typ = Type::TypeVariable(type_variable.clone()); @@ -895,71 +897,138 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { - let lhs = self.evaluate(infix.lhs)?; - let rhs = self.evaluate(infix.rhs)?; + let lhs_value = self.evaluate(infix.lhs)?; + let rhs_value = self.evaluate(infix.rhs)?; if self.elaborator.interner.get_selected_impl_for_expression(id).is_some() { - return self.evaluate_overloaded_infix(infix, lhs, rhs, id); + return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } - let make_error = |this: &mut Self, lhs: Value, rhs: Value, operator| { - let location = this.elaborator.interner.expr_location(&id); - let lhs = lhs.get_type().into_owned(); - let rhs = rhs.get_type().into_owned(); - Err(InvalidValuesForBinary { lhs, rhs, location, operator }) + let lhs_type = lhs_value.get_type().into_owned(); + let rhs_type = rhs_value.get_type().into_owned(); + let location = self.elaborator.interner.expr_location(&id); + + let error = |operator| { + let lhs = lhs_type.clone(); + let rhs = rhs_type.clone(); + InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } }; use InterpreterError::InvalidValuesForBinary; match infix.operator.kind { - BinaryOpKind::Add => match (lhs, rhs) { + BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "+"), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(lhs.checked_add(rhs).ok_or(error("+"))?)) + } + (lhs, rhs) => Err(error("+")), }, - BinaryOpKind::Subtract => match (lhs, rhs) { + BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "-"), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(lhs.checked_sub(rhs).ok_or(error("-"))?)) + } + (lhs, rhs) => Err(error("-")), }, - BinaryOpKind::Multiply => match (lhs, rhs) { + BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "*"), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(lhs.checked_mul(rhs).ok_or(error("*"))?)) + } + (lhs, rhs) => Err(error("*")), }, - BinaryOpKind::Divide => match (lhs, rhs) { + BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "/"), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(lhs.checked_div(rhs).ok_or(error("/"))?)) + } + (lhs, rhs) => Err(error("/")), }, - BinaryOpKind::Equal => match (lhs, rhs) { + BinaryOpKind::Equal => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs == rhs)), @@ -970,9 +1039,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "=="), + (lhs, rhs) => Err(error("==")), }, - BinaryOpKind::NotEqual => match (lhs, rhs) { + BinaryOpKind::NotEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs != rhs)), @@ -983,9 +1052,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "!="), + (lhs, rhs) => Err(error("!=")), }, - BinaryOpKind::Less => match (lhs, rhs) { + BinaryOpKind::Less => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs < rhs)), @@ -995,9 +1064,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(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) => make_error(self, lhs, rhs, "<"), + (lhs, rhs) => Err(error("<")), }, - BinaryOpKind::LessEqual => match (lhs, rhs) { + BinaryOpKind::LessEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs <= rhs)), @@ -1007,9 +1076,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(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) => make_error(self, lhs, rhs, "<="), + (lhs, rhs) => Err(error("<=")), }, - BinaryOpKind::Greater => match (lhs, rhs) { + BinaryOpKind::Greater => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs > rhs)), @@ -1019,9 +1088,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(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) => make_error(self, lhs, rhs, ">"), + (lhs, rhs) => Err(error(">")), }, - BinaryOpKind::GreaterEqual => match (lhs, rhs) { + BinaryOpKind::GreaterEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs >= rhs)), @@ -1031,9 +1100,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(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) => make_error(self, lhs, rhs, ">="), + (lhs, rhs) => Err(error(">=")), }, - BinaryOpKind::And => match (lhs, rhs) { + BinaryOpKind::And => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs & rhs)), @@ -1043,9 +1112,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "&"), + (lhs, rhs) => Err(error("&")), }, - BinaryOpKind::Or => match (lhs, rhs) { + BinaryOpKind::Or => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs | rhs)), @@ -1055,9 +1124,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "|"), + (lhs, rhs) => Err(error("|")), }, - BinaryOpKind::Xor => match (lhs, rhs) { + BinaryOpKind::Xor => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs ^ rhs)), @@ -1067,40 +1136,88 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "^"), + (lhs, rhs) => Err(error("^")), }, - BinaryOpKind::ShiftRight => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, ">>"), + BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_shr(rhs).ok_or(error(">>"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (lhs, rhs) => Err(error(">>")), }, - BinaryOpKind::ShiftLeft => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "<<"), + BinaryOpKind::ShiftLeft => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_shl(rhs).ok_or(error("<<"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (lhs, rhs) => Err(error("<<")), }, - BinaryOpKind::Modulo => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(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::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(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) => make_error(self, lhs, rhs, "%"), + BinaryOpKind::Modulo => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(lhs.checked_rem(rhs).ok_or(error("%"))?)) + } + (lhs, rhs) => Err(error("%")), }, } } @@ -1638,7 +1755,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Pointer(elem, true) => Ok(elem.borrow().clone()), other => Ok(other), }, - HirLValue::Dereference { lvalue, element_type: _, location } => { + HirLValue::Dereference { lvalue, element_type, location } => { match self.evaluate_lvalue(lvalue)? { Value::Pointer(value, _) => Ok(value.borrow().clone()), value => { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 5bb85cd79b6..170cd67e146 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -19,8 +19,8 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, - FunctionKind, FunctionReturnType, Ident, IntegerBitSize, LValue, Literal, Pattern, - Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, + FunctionKind, FunctionReturnType, Ident, IntegerBitSize, ItemVisibility, LValue, Literal, + Pattern, Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, hir::{ @@ -32,10 +32,9 @@ use crate::{ def_collector::dc_crate::CollectedItems, def_map::ModuleDefId, }, - hir_def::{ - expr::{HirExpression, HirLiteral}, - function::FunctionBody, - }, + hir_def::expr::{HirExpression, HirLiteral}, + hir_def::function::FunctionBody, + hir_def::{self}, node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser::{Parser, StatementOrExpressionOrLValue}, token::{Attribute, SecondaryAttribute, Token}, @@ -316,7 +315,7 @@ fn str_as_bytes( let bytes: im::Vector = string.bytes().map(Value::U8).collect(); let byte_array_type = Type::Array( - Box::new(Type::Constant(bytes.len() as u32, Kind::u32())), + Box::new(Type::Constant(bytes.len().into(), Kind::u32())), Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), ); Ok(Value::Array(bytes, byte_array_type)) @@ -498,9 +497,9 @@ fn struct_def_fields( let mut fields = im::Vector::new(); - for (name, typ) in struct_def.get_fields_as_written() { - let name = Value::Quoted(Rc::new(vec![Token::Ident(name)])); - let typ = Value::Type(typ); + for field in struct_def.get_fields_as_written() { + let name = Value::Quoted(Rc::new(vec![Token::Ident(field.name.to_string())])); + let typ = Value::Type(field.typ); fields.push_back(Value::Tuple(vec![name, typ])); } @@ -567,7 +566,11 @@ fn struct_def_set_fields( match name_tokens.first() { Some(Token::Ident(name)) if name_tokens.len() == 1 => { - Ok((Ident::new(name.clone(), field_location.span), typ)) + Ok(hir_def::types::StructField { + visibility: ItemVisibility::Public, + name: Ident::new(name.clone(), field_location.span), + typ, + }) } _ => { let value = name_value.display(interner).to_string(); @@ -802,8 +805,12 @@ fn to_le_radix( let value = get_field(value)?; let radix = get_u32(radix)?; let limb_count = if let Type::Array(length, _) = return_type { - if let Type::Constant(limb_count, _kind) = *length { - limb_count + if let Type::Constant(limb_count, kind) = *length { + if kind.unifies(&Kind::u32()) { + limb_count + } else { + return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); + } } else { return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); } @@ -813,10 +820,11 @@ fn to_le_radix( // Decompose the integer into its radix digits in little endian form. let decomposed_integer = compute_to_radix_le(value, radix); - let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) { - Some(digit) => Value::U8(*digit), - None => Value::U8(0), - }); + let decomposed_integer = + vecmap(0..limb_count.to_u128() as usize, |i| match decomposed_integer.get(i) { + Some(digit) => Value::U8(*digit), + None => Value::U8(0), + }); Ok(Value::Array( decomposed_integer.into(), Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 458a186a3f8..e033ec6ddb9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -78,6 +78,23 @@ fn interpreter_works() { assert_eq!(result, Value::Field(3u128.into())); } +#[test] +fn interpreter_type_checking_works() { + let program = "comptime fn main() -> pub u8 { 3 }"; + let result = interpret(program); + assert_eq!(result, Value::U8(3u8)); +} + +#[test] +fn let_statement_works() { + let program = "comptime fn main() -> pub i8 { + let x = 4; + x + }"; + let result = interpret(program); + assert_eq!(result, Value::I8(4)); +} + #[test] fn mutation_works() { let program = "comptime fn main() -> pub i8 { @@ -277,5 +294,5 @@ fn generic_functions() { } "; let result = interpret(program); - assert!(matches!(result, Value::U8(2))); + assert_eq!(result, Value::U8(2)); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index 4b55735fcb1..945fb45026d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -120,7 +120,7 @@ impl Value { 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 u32, Kind::u32()); + let length = Type::Constant(value.len().into(), Kind::u32()); Type::String(Box::new(length)) } Value::FormatString(_, typ) => return Cow::Borrowed(typ), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index ba1075dfb30..b9ce8f361f7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1013,12 +1013,14 @@ pub fn collect_struct( let parent_module_id = ModuleId { krate, local_id: module_id }; - interner.usage_tracker.add_unused_item( - parent_module_id, - name.clone(), - UnusedItem::Struct(id), - visibility, - ); + if !unresolved.struct_def.is_abi() { + interner.usage_tracker.add_unused_item( + parent_module_id, + name.clone(), + UnusedItem::Struct(id), + visibility, + ); + } if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -1201,6 +1203,7 @@ pub(crate) fn collect_global( let global = global.item; let name = global.pattern.name_ident().clone(); + let is_abi = global.attributes.iter().any(|attribute| attribute.is_abi()); let global_id = interner.push_empty_global( name.clone(), @@ -1215,13 +1218,15 @@ pub(crate) fn collect_global( // Add the statement to the scope so its path can be looked up later let result = def_map.modules[module_id.0].declare_global(name.clone(), visibility, global_id); - let parent_module_id = ModuleId { krate: crate_id, local_id: module_id }; - interner.usage_tracker.add_unused_item( - parent_module_id, - name, - UnusedItem::Global(global_id), - visibility, - ); + if !is_abi { + let parent_module_id = ModuleId { krate: crate_id, local_id: module_id }; + interner.usage_tracker.add_unused_item( + parent_module_id, + name, + UnusedItem::Global(global_id), + visibility, + ); + } let error = result.err().map(|(first_def, second_def)| { let err = diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index e3f3d488c43..a6eb1864d13 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; @@ -125,7 +126,12 @@ pub enum ResolverError { #[error("Associated constants may only be a field or integer type")] AssociatedConstantsMustBeNumeric { span: Span }, #[error("Overflow in `{lhs} {op} {rhs}`")] - OverflowInType { lhs: u32, op: crate::BinaryTypeOperator, rhs: u32, span: Span }, + OverflowInType { + lhs: FieldElement, + op: crate::BinaryTypeOperator, + rhs: FieldElement, + span: Span, + }, #[error("`quote` cannot be used in runtime code")] QuoteInRuntimeCode { span: Span }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs index f0b7f941ef8..93039b1ea7f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -12,6 +12,7 @@ use crate::ast::{Ident, ItemVisibility, Path, PathKind, PathSegment}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId, PerNs}; use super::errors::ResolverError; +use super::visibility::can_reference_module_id; #[derive(Debug, Clone)] pub struct ImportDirective { @@ -169,7 +170,7 @@ fn resolve_path_to_ns( import_path, import_directive.module_id, def_maps, - true, + true, // plain or crate usage_tracker, path_references, ); @@ -199,7 +200,7 @@ fn resolve_path_to_ns( import_path, import_directive.module_id, def_maps, - true, + true, // plain or crate usage_tracker, path_references, ) @@ -224,7 +225,7 @@ fn resolve_path_to_ns( import_path, parent_module_id, def_maps, - false, + false, // plain or crate usage_tracker, path_references, ) @@ -253,7 +254,7 @@ fn resolve_path_from_crate_root( import_path, starting_mod, def_maps, - false, + true, // plain or crate usage_tracker, path_references, ) @@ -266,7 +267,7 @@ fn resolve_name_in_module( import_path: &[PathSegment], starting_mod: LocalModuleId, def_maps: &BTreeMap, - plain: bool, + plain_or_crate: bool, usage_tracker: &mut UsageTracker, path_references: &mut Option<&mut Vec>, ) -> NamespaceResolutionResult { @@ -331,9 +332,9 @@ fn resolve_name_in_module( }; warning = warning.or_else(|| { - // If the path is plain, the first segment will always refer to + // If the path is plain or crate, the first segment will always refer to // something that's visible from the current module. - if (plain && index == 0) + if (plain_or_crate && index == 0) || can_reference_module_id( def_maps, importing_crate, @@ -424,61 +425,3 @@ fn resolve_external_dep( path_references, ) } - -// Returns false if the given private function is being called from a non-child module, or -// if the given pub(crate) function is being called from another crate. Otherwise returns true. -pub fn can_reference_module_id( - def_maps: &BTreeMap, - importing_crate: CrateId, - current_module: LocalModuleId, - target_module: ModuleId, - visibility: ItemVisibility, -) -> bool { - // Note that if the target module is in a different crate from the current module then we will either - // return true as the target module is public or return false as it is private without looking at the `CrateDefMap` in either case. - let same_crate = target_module.krate == importing_crate; - let target_crate_def_map = &def_maps[&target_module.krate]; - - match visibility { - ItemVisibility::Public => true, - ItemVisibility::PublicCrate => same_crate, - ItemVisibility::Private => { - same_crate - && (module_descendent_of_target( - target_crate_def_map, - target_module.local_id, - current_module, - ) || module_is_parent_of_struct_module( - target_crate_def_map, - current_module, - target_module.local_id, - )) - } - } -} - -// Returns true if `current` is a (potentially nested) child module of `target`. -// This is also true if `current == target`. -fn module_descendent_of_target( - def_map: &CrateDefMap, - target: LocalModuleId, - current: LocalModuleId, -) -> bool { - if current == target { - return true; - } - - def_map.modules[current.0] - .parent - .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) -} - -/// Returns true if `target` is a struct and its parent is `current`. -fn module_is_parent_of_struct_module( - def_map: &CrateDefMap, - current: LocalModuleId, - target: LocalModuleId, -) -> bool { - let module_data = &def_map.modules[target.0]; - module_data.is_struct && module_data.parent == Some(current) -} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/mod.rs index 01a3fe856e5..223b88b5c5d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/mod.rs @@ -8,3 +8,4 @@ pub mod errors; pub mod import; pub mod path_resolver; +pub mod visibility; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs new file mode 100644 index 00000000000..492f303d2c4 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs @@ -0,0 +1,141 @@ +use crate::graph::CrateId; +use crate::node_interner::{FuncId, NodeInterner, StructId}; +use crate::Type; + +use std::collections::BTreeMap; + +use crate::ast::ItemVisibility; +use crate::hir::def_map::{CrateDefMap, DefMaps, LocalModuleId, ModuleId}; + +// Returns false if the given private function is being called from a non-child module, or +// if the given pub(crate) function is being called from another crate. Otherwise returns true. +pub fn can_reference_module_id( + def_maps: &BTreeMap, + importing_crate: CrateId, + current_module: LocalModuleId, + target_module: ModuleId, + visibility: ItemVisibility, +) -> bool { + // Note that if the target module is in a different crate from the current module then we will either + // return true as the target module is public or return false as it is private without looking at the `CrateDefMap` in either case. + let same_crate = target_module.krate == importing_crate; + + match visibility { + ItemVisibility::Public => true, + ItemVisibility::PublicCrate => same_crate, + ItemVisibility::Private => { + let target_crate_def_map = &def_maps[&target_module.krate]; + same_crate + && (module_descendent_of_target( + target_crate_def_map, + target_module.local_id, + current_module, + ) || module_is_parent_of_struct_module( + target_crate_def_map, + current_module, + target_module.local_id, + )) + } + } +} + +// Returns true if `current` is a (potentially nested) child module of `target`. +// This is also true if `current == target`. +pub(crate) fn module_descendent_of_target( + def_map: &CrateDefMap, + target: LocalModuleId, + current: LocalModuleId, +) -> bool { + if current == target { + return true; + } + + def_map.modules[current.0] + .parent + .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) +} + +/// Returns true if `target` is a struct and its parent is `current`. +fn module_is_parent_of_struct_module( + def_map: &CrateDefMap, + current: LocalModuleId, + target: LocalModuleId, +) -> bool { + let module_data = &def_map.modules[target.0]; + module_data.is_struct && module_data.parent == Some(current) +} + +pub fn struct_member_is_visible( + struct_id: StructId, + visibility: ItemVisibility, + current_module_id: ModuleId, + def_maps: &BTreeMap, +) -> bool { + match visibility { + ItemVisibility::Public => true, + ItemVisibility::PublicCrate => { + struct_id.parent_module_id(def_maps).krate == current_module_id.krate + } + ItemVisibility::Private => { + let struct_parent_module_id = struct_id.parent_module_id(def_maps); + if struct_parent_module_id.krate != current_module_id.krate { + return false; + } + + if struct_parent_module_id.local_id == current_module_id.local_id { + return true; + } + + let def_map = &def_maps[¤t_module_id.krate]; + module_descendent_of_target( + def_map, + struct_parent_module_id.local_id, + current_module_id.local_id, + ) + } + } +} + +pub fn method_call_is_visible( + object_type: &Type, + func_id: FuncId, + current_module: ModuleId, + interner: &NodeInterner, + def_maps: &DefMaps, +) -> bool { + let modifiers = interner.function_modifiers(&func_id); + match modifiers.visibility { + ItemVisibility::Public => true, + ItemVisibility::PublicCrate => { + if object_type.is_primitive() { + current_module.krate.is_stdlib() + } else { + interner.function_module(func_id).krate == current_module.krate + } + } + ItemVisibility::Private => { + if object_type.is_primitive() { + let func_module = interner.function_module(func_id); + can_reference_module_id( + def_maps, + current_module.krate, + current_module.local_id, + func_module, + modifiers.visibility, + ) + } else { + let func_meta = interner.function_meta(&func_id); + if let Some(struct_id) = func_meta.struct_id { + struct_member_is_visible( + struct_id, + modifiers.visibility, + current_module, + def_maps, + ) + } else { + true + } + } + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs index 54699792901..d8dae1f6549 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -48,12 +48,17 @@ pub enum TypeCheckError { TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, + // TODO(https://github.com/noir-lang/noir/issues/6238): implement handling for larger types + #[error("Expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)")] + EvaluatedGlobalIsntU32 { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span }, #[error("Return type in a function cannot be public")] PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] - InvalidCast { from: Type, span: Span }, + InvalidCast { from: Type, span: Span, reason: String }, + #[error("Casting value of type {from} to a smaller type ({to})")] + DownsizingCast { from: Type, to: Type, span: Span, reason: String }, #[error("Expected a function, but found a(n) {found}")] ExpectedFunction { found: Type, span: Span }, #[error("Type {lhs_type} has no member named {field_name}")] @@ -227,6 +232,15 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { *expr_span, ) } + // TODO(https://github.com/noir-lang/noir/issues/6238): implement + // handling for larger types + TypeCheckError::EvaluatedGlobalIsntU32 { expected_kind, expr_kind, expr_span } => { + Diagnostic::simple_warning( + format!("Expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)"), + String::new(), + *expr_span, + ) + } TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), @@ -284,8 +298,14 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { }; Diagnostic::simple_error(msg, String::new(), *span) } - TypeCheckError::InvalidCast { span, .. } - | TypeCheckError::ExpectedFunction { span, .. } + TypeCheckError::InvalidCast { span, reason, .. } => { + Diagnostic::simple_error(error.to_string(), reason.clone(), *span) + } + TypeCheckError::DownsizingCast { span, reason, .. } => { + Diagnostic::simple_warning(error.to_string(), reason.clone(), *span) + } + + TypeCheckError::ExpectedFunction { span, .. } | TypeCheckError::AccessUnknownMember { span, .. } | TypeCheckError::UnsupportedCast { span } | TypeCheckError::TupleIndexOutOfBounds { span, .. } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 5c2322acfda..69e5066c596 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -5,8 +5,10 @@ use std::{ rc::Rc, }; +use acvm::{AcirField, FieldElement}; + use crate::{ - ast::IntegerBitSize, + ast::{IntegerBitSize, ItemVisibility}, hir::type_check::{generics::TraitGenerics, TypeCheckError}, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, }; @@ -110,9 +112,11 @@ pub enum Type { /// will be and thus needs the full TypeVariable link. Forall(GenericTypeVars, Box), - /// A type-level integer. Included to let an Array's size type variable - /// bind to an integer without special checks to bind it to a non-type. - Constant(u32, Kind), + /// A type-level integer. Included to let + /// 1. an Array's size type variable + /// bind to an integer without special checks to bind it to a non-type. + /// 2. values to be used at the type level + Constant(FieldElement, Kind), /// The type of quoted code in macros. This is always a comptime-only type Quoted(QuotedType), @@ -158,14 +162,29 @@ pub enum Kind { impl Kind { pub(crate) fn is_error(&self) -> bool { - match self { - Self::Numeric(typ) => **typ == Type::Error, + match self.follow_bindings() { + Self::Numeric(typ) => *typ == Type::Error, _ => false, } } pub(crate) fn is_numeric(&self) -> bool { - matches!(self, Self::Numeric { .. }) + matches!(self.follow_bindings(), Self::Numeric { .. }) + } + + pub(crate) fn is_type_level_field_element(&self) -> bool { + let type_level = false; + self.is_field_element(type_level) + } + + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { + match self.follow_bindings() { + Kind::Numeric(typ) => typ.is_field_element(value_level), + Kind::IntegerOrField => value_level, + _ => false, + } } pub(crate) fn u32() -> Self { @@ -229,6 +248,21 @@ impl Kind { Kind::Numeric(typ) => Some(*typ.clone()), } } + + fn integral_maximum_size(&self) -> Option { + match self.follow_bindings() { + Kind::Any | Kind::IntegerOrField | Kind::Integer | Kind::Normal => None, + Self::Numeric(typ) => typ.integral_maximum_size(), + } + } + + /// Ensure the given value fits in self.integral_maximum_size() + fn ensure_value_fits(&self, value: FieldElement) -> Option { + match self.integral_maximum_size() { + None => Some(value), + Some(maximum_size) => (value <= maximum_size).then_some(value), + } + } } impl std::fmt::Display for Kind { @@ -269,7 +303,6 @@ pub type TypeBindings = HashMap; /// Represents a struct type in the type system. Each instance of this /// rust struct will be shared across all Type::Struct variants that represent /// the same struct type. -#[derive(Eq)] pub struct StructType { /// A unique id representing this struct type. Used to check if two /// struct types are equal. @@ -280,12 +313,18 @@ pub struct StructType { /// Fields are ordered and private, they should only /// be accessed through get_field(), get_fields(), or instantiate() /// since these will handle applying generic arguments to fields as well. - fields: Vec<(Ident, Type)>, + fields: Vec, pub generics: Generics, pub location: Location, } +pub struct StructField { + pub visibility: ItemVisibility, + pub name: Ident, + pub typ: Type, +} + /// Corresponds to generic lists such as `` in the source program. /// Used mainly for resolved types which no longer need information such /// as names or kinds @@ -329,6 +368,8 @@ impl std::hash::Hash for StructType { } } +impl Eq for StructType {} + impl PartialEq for StructType { fn eq(&self, other: &Self) -> bool { self.id == other.id @@ -353,7 +394,7 @@ impl StructType { name: Ident, location: Location, - fields: Vec<(Ident, Type)>, + fields: Vec, generics: Generics, ) -> StructType { StructType { id, fields, name, location, generics } @@ -363,7 +404,7 @@ impl StructType { /// fields are resolved strictly after the struct itself is initially /// created. Therefore, this method is used to set the fields once they /// become known. - pub fn set_fields(&mut self, fields: Vec<(Ident, Type)>) { + pub fn set_fields(&mut self, fields: Vec) { self.fields = fields; } @@ -371,12 +412,16 @@ impl StructType { self.fields.len() } - /// Returns the field matching the given field name, as well as its field index. - pub fn get_field(&self, field_name: &str, generic_args: &[Type]) -> Option<(Type, usize)> { + /// Returns the field matching the given field name, as well as its visibility and field index. + pub fn get_field( + &self, + field_name: &str, + generic_args: &[Type], + ) -> Option<(Type, ItemVisibility, usize)> { assert_eq!(self.generics.len(), generic_args.len()); - self.fields.iter().enumerate().find(|(_, (name, _))| name.0.contents == field_name).map( - |(i, (_, typ))| { + self.fields.iter().enumerate().find(|(_, field)| field.name.0.contents == field_name).map( + |(i, field)| { let substitutions = self .generics .iter() @@ -389,28 +434,46 @@ impl StructType { }) .collect(); - (typ.substitute(&substitutions), i) + (field.typ.substitute(&substitutions), field.visibility, i) }, ) } /// Returns all the fields of this type, after being applied to the given generic arguments. + pub fn get_fields_with_visibility( + &self, + generic_args: &[Type], + ) -> Vec<(String, ItemVisibility, Type)> { + let substitutions = self.get_fields_substitutions(generic_args); + + vecmap(&self.fields, |field| { + let name = field.name.0.contents.clone(); + (name, field.visibility, field.typ.substitute(&substitutions)) + }) + } + pub fn get_fields(&self, generic_args: &[Type]) -> Vec<(String, Type)> { + let substitutions = self.get_fields_substitutions(generic_args); + + vecmap(&self.fields, |field| { + let name = field.name.0.contents.clone(); + (name, field.typ.substitute(&substitutions)) + }) + } + + fn get_fields_substitutions( + &self, + generic_args: &[Type], + ) -> HashMap { assert_eq!(self.generics.len(), generic_args.len()); - let substitutions = self - .generics + self.generics .iter() .zip(generic_args) .map(|(old, new)| { (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone())) }) - .collect(); - - vecmap(&self.fields, |(name, typ)| { - let name = name.0.contents.clone(); - (name, typ.substitute(&substitutions)) - }) + .collect() } /// Returns the name and raw types of each field of this type. @@ -419,23 +482,27 @@ impl StructType { /// /// This method is almost never what is wanted for type checking or monomorphization, /// prefer to use `get_fields` whenever possible. - pub fn get_fields_as_written(&self) -> Vec<(String, Type)> { - vecmap(&self.fields, |(name, typ)| (name.0.contents.clone(), typ.clone())) + pub fn get_fields_as_written(&self) -> Vec { + vecmap(&self.fields, |field| StructField { + visibility: field.visibility, + name: field.name.clone(), + typ: field.typ.clone(), + }) } /// Returns the field at the given index. Panics if no field exists at the given index. - pub fn field_at(&self, index: usize) -> &(Ident, Type) { + pub fn field_at(&self, index: usize) -> &StructField { &self.fields[index] } pub fn field_names(&self) -> BTreeSet { - self.fields.iter().map(|(name, _)| name.clone()).collect() + self.fields.iter().map(|field| field.name.clone()).collect() } /// Search the fields of a struct for any types with a `TypeKind::Numeric` pub fn find_numeric_generics_in_fields(&self, found_names: &mut Vec) { - for (_, field) in self.fields.iter() { - field.find_numeric_type_vars(found_names); + for field in self.fields.iter() { + field.typ.find_numeric_type_vars(found_names); } } @@ -680,8 +747,10 @@ impl TypeVariable { /// and if unbound, that it's a Kind::Integer pub fn is_integer(&self) -> bool { match &*self.borrow() { - TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::Integer), + TypeBinding::Bound(binding) => matches!(binding.follow_bindings(), Type::Integer(..)), + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind.follow_bindings(), Kind::Integer) + } } } @@ -690,9 +759,20 @@ impl TypeVariable { pub fn is_integer_or_field(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => { - matches!(binding, Type::Integer(..) | Type::FieldElement) + matches!(binding.follow_bindings(), Type::Integer(..) | Type::FieldElement) + } + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind.follow_bindings(), Kind::IntegerOrField) } - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), + } + } + + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => binding.is_field_element(value_level), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.is_field_element(value_level), } } } @@ -923,6 +1003,17 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(_, _)) } + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { + match self.follow_bindings() { + Type::FieldElement => value_level, + Type::TypeVariable(var) => var.is_field_element(value_level), + Type::Constant(_, kind) => !value_level && kind.is_field_element(true), + _ => false, + } + } + pub fn is_signed(&self) -> bool { matches!(self.follow_bindings(), Type::Integer(Signedness::Signed, _)) } @@ -950,6 +1041,34 @@ impl Type { } } + pub fn is_primitive(&self) -> bool { + match self.follow_bindings() { + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(..) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | Type::Function(..) + | Type::Tuple(..) => true, + Type::Alias(alias_type, generics) => { + alias_type.borrow().get_type(&generics).is_primitive() + } + Type::MutableReference(typ) => typ.is_primitive(), + Type::Struct(..) + | Type::TypeVariable(..) + | Type::TraitAsType(..) + | Type::NamedGeneric(..) + | Type::Forall(..) + | Type::Constant(..) + | Type::Quoted(..) + | Type::InfixExpr(..) + | Type::Error => false, + } + } + pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { // Return whether the named generic has a Kind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { @@ -1635,8 +1754,7 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - // TODO(https://github.com/noir-lang/noir/pull/6137): replace evaluate_to_u32 - if let Some(other_value) = other.evaluate_to_u32() { + if let Some(other_value) = other.evaluate_to_field_element(kind) { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -1805,19 +1923,38 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some((binding, _kind)) = self.get_inner_type_variable() { + self.evaluate_to_field_element(&Kind::u32()) + .and_then(|field_element| field_element.try_to_u32()) + } + + // TODO(https://github.com/noir-lang/noir/issues/6260): remove + // the unifies checks once all kinds checks are implemented? + pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { + if let Some((binding, binding_kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return binding.evaluate_to_u32(); + if kind.unifies(&binding_kind) { + return binding.evaluate_to_field_element(&binding_kind); + } } } match self.canonicalize() { - Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x, _kind) => Some(x), + Type::Constant(x, constant_kind) => { + if kind.unifies(&constant_kind) { + kind.ensure_value_fits(x) + } else { + None + } + } Type::InfixExpr(lhs, op, rhs) => { - let lhs_u32 = lhs.evaluate_to_u32()?; - let rhs_u32 = rhs.evaluate_to_u32()?; - op.function(lhs_u32, rhs_u32, &lhs.infix_kind(&rhs)) + let infix_kind = lhs.infix_kind(&rhs); + if kind.unifies(&infix_kind) { + let lhs_value = lhs.evaluate_to_field_element(&infix_kind)?; + let rhs_value = rhs.evaluate_to_field_element(&infix_kind)?; + op.function(lhs_value, rhs_value, &infix_kind) + } else { + None + } } _ => None, } @@ -1831,8 +1968,8 @@ impl Type { // only to have to call .into_iter again afterward. Trying to elide // collecting to a Vec leads to us dropping the temporary Ref before // the iterator is returned - Type::Struct(def, args) => vecmap(&def.borrow().fields, |(name, _)| { - let name = &name.0.contents; + Type::Struct(def, args) => vecmap(&def.borrow().fields, |field| { + let name = &field.name.0.contents; let typ = def.borrow().get_field(name, args).unwrap().0; (name.clone(), typ) }), @@ -1847,14 +1984,20 @@ impl Type { /// Retrieves the type of the given field name /// Panics if the type is not a struct or tuple. - pub fn get_field_type(&self, field_name: &str) -> Option { + pub fn get_field_type_and_visibility( + &self, + field_name: &str, + ) -> Option<(Type, ItemVisibility)> { match self.follow_bindings() { - Type::Struct(def, args) => { - def.borrow().get_field(field_name, &args).map(|(typ, _)| typ) - } + Type::Struct(def, args) => def + .borrow() + .get_field(field_name, &args) + .map(|(typ, visibility, _)| (typ, visibility)), Type::Tuple(fields) => { let mut fields = fields.into_iter().enumerate(); - fields.find(|(i, _)| i.to_string() == *field_name).map(|(_, typ)| typ) + fields + .find(|(i, _)| i.to_string() == *field_name) + .map(|(_, typ)| (typ, ItemVisibility::Public)) } _ => None, } @@ -2188,7 +2331,6 @@ impl Type { } self.clone() } - Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| arg.follow_bindings()); let ret = Box::new(ret.follow_bindings()); @@ -2313,6 +2455,51 @@ impl Type { _ => None, } } + + pub(crate) fn integral_maximum_size(&self) -> Option { + match self { + Type::FieldElement => None, + Type::Integer(sign, num_bits) => { + let mut max_bit_size = num_bits.bit_size(); + if sign == &Signedness::Signed { + max_bit_size -= 1; + } + Some(((1u128 << max_bit_size) - 1).into()) + } + Type::Bool => Some(FieldElement::one()), + Type::TypeVariable(var) => { + let binding = &var.1; + match &*binding.borrow() { + TypeBinding::Unbound(_, type_var_kind) => match type_var_kind { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => None, + Kind::Numeric(typ) => typ.integral_maximum_size(), + }, + TypeBinding::Bound(typ) => typ.integral_maximum_size(), + } + } + Type::Alias(alias, args) => alias.borrow().get_type(args).integral_maximum_size(), + Type::NamedGeneric(binding, _name) => match &*binding.borrow() { + TypeBinding::Bound(typ) => typ.integral_maximum_size(), + TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), + }, + Type::MutableReference(typ) => typ.integral_maximum_size(), + Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs).integral_maximum_size(), + Type::Constant(_, kind) => kind.integral_maximum_size(), + + Type::Array(..) + | Type::Slice(..) + | Type::String(..) + | Type::FmtString(..) + | Type::Unit + | Type::Tuple(..) + | Type::Struct(..) + | Type::TraitAsType(..) + | Type::Function(..) + | Type::Forall(..) + | Type::Quoted(..) + | Type::Error => None, + } + } } /// Wraps a given `expression` in `expression.as_slice()` @@ -2350,15 +2537,29 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - // TODO(https://github.com/noir-lang/noir/pull/6137): the Kind is included - // since it'll be needed for size checks - pub fn function(self, a: u32, b: u32, _kind: &Kind) -> Option { - match self { - BinaryTypeOperator::Addition => a.checked_add(b), - BinaryTypeOperator::Subtraction => a.checked_sub(b), - BinaryTypeOperator::Multiplication => a.checked_mul(b), - BinaryTypeOperator::Division => a.checked_div(b), - BinaryTypeOperator::Modulo => a.checked_rem(b), + pub fn function(self, a: FieldElement, b: FieldElement, kind: &Kind) -> Option { + match kind.follow_bindings().integral_maximum_size() { + None => match self { + BinaryTypeOperator::Addition => Some(a + b), + BinaryTypeOperator::Subtraction => Some(a - b), + BinaryTypeOperator::Multiplication => Some(a * b), + BinaryTypeOperator::Division => (b != FieldElement::zero()).then(|| a / b), + BinaryTypeOperator::Modulo => None, + }, + Some(_maximum_size) => { + let a = a.to_i128(); + let b = b.to_i128(); + + let result = match self { + BinaryTypeOperator::Addition => a.checked_add(b)?, + BinaryTypeOperator::Subtraction => a.checked_sub(b)?, + BinaryTypeOperator::Multiplication => a.checked_mul(b)?, + BinaryTypeOperator::Division => a.checked_div(b)?, + BinaryTypeOperator::Modulo => a.checked_rem(b)?, + }; + + Some(result.into()) + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index af94ef27535..0eee7dbf824 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -1,5 +1,7 @@ use std::collections::BTreeMap; +use acvm::{AcirField, FieldElement}; + use crate::{BinaryTypeOperator, Type, TypeBindings, UnificationError}; impl Type { @@ -15,10 +17,11 @@ impl Type { pub fn canonicalize(&self) -> Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs) => { - // evaluate_to_u32 also calls canonicalize so if we just called - // `self.evaluate_to_u32()` we'd get infinite recursion. + let kind = lhs.infix_kind(&rhs); + // evaluate_to_field_element also calls canonicalize so if we just called + // `self.evaluate_to_field_element(..)` we'd get infinite recursion. if let (Some(lhs_u32), Some(rhs_u32)) = - (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) + (lhs.evaluate_to_field_element(&kind), rhs.evaluate_to_field_element(&kind)) { let kind = lhs.infix_kind(&rhs); if let Some(result) = op.function(lhs_u32, rhs_u32, &kind) { @@ -58,7 +61,11 @@ impl Type { // Maps each term to the number of times that term was used. let mut sorted = BTreeMap::new(); - let zero_value = if op == BinaryTypeOperator::Addition { 0 } else { 1 }; + let zero_value = if op == BinaryTypeOperator::Addition { + FieldElement::zero() + } else { + FieldElement::one() + }; let mut constant = zero_value; // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. @@ -175,14 +182,15 @@ impl Type { fn parse_partial_constant_expr( lhs: &Type, rhs: &Type, - ) -> Option<(Box, BinaryTypeOperator, u32, u32)> { - let rhs = rhs.evaluate_to_u32()?; + ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { + let kind = lhs.infix_kind(rhs); + let rhs = rhs.evaluate_to_field_element(&kind)?; let Type::InfixExpr(l_type, l_op, l_rhs) = lhs.follow_bindings() else { return None; }; - let l_rhs = l_rhs.evaluate_to_u32()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind)?; Some((l_type, l_op, l_rhs, rhs)) } @@ -215,8 +223,12 @@ impl Type { if l_op == Division { op = op.inverse()?; } + + let divides_evenly = !lhs.infix_kind(rhs).is_type_level_field_element() + && l_const.to_i128().checked_rem(r_const.to_i128()) == Some(0); + // If op is a division we need to ensure it divides evenly - if op == Division && (r_const == 0 || l_const % r_const != 0) { + if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { None } else { let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; @@ -237,8 +249,9 @@ impl Type { ) -> Result<(), UnificationError> { if let Type::InfixExpr(lhs_a, op_a, rhs_a) = self { if let Some(inverse) = op_a.inverse() { - if let Some(rhs_a_u32) = rhs_a.evaluate_to_u32() { - let rhs_a = Box::new(Type::Constant(rhs_a_u32, lhs_a.infix_kind(rhs_a))); + let kind = lhs_a.infix_kind(rhs_a); + if let Some(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind) { + let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); let mut tmp_bindings = bindings.clone(); @@ -252,8 +265,9 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b) = other { if let Some(inverse) = op_b.inverse() { - if let Some(rhs_b_u32) = rhs_b.evaluate_to_u32() { - let rhs_b = Box::new(Type::Constant(rhs_b_u32, lhs_b.infix_kind(rhs_b))); + let kind = lhs_b.infix_kind(rhs_b); + if let Some(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind) { + let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b); let mut tmp_bindings = bindings.clone(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index c4c31a2dd8a..4037135a889 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -995,6 +995,10 @@ impl SecondaryAttribute { _ => false, } } + + pub(crate) fn is_abi(&self) -> bool { + matches!(self, SecondaryAttribute::Abi(_)) + } } impl fmt::Display for SecondaryAttribute { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index f3439316538..48660142d0a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -47,7 +47,10 @@ impl NodeInterner { ReferenceId::StructMember(id, field_index) => { let struct_type = self.get_struct(id); let struct_type = struct_type.borrow(); - Location::new(struct_type.field_at(field_index).0.span(), struct_type.location.file) + Location::new( + struct_type.field_at(field_index).name.span(), + struct_type.location.file, + ) } ReferenceId::Trait(id) => { let trait_type = self.get_trait(id); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index 4b1951a1aac..e2ff94f521b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -11,6 +11,7 @@ pub enum MonomorphizationError { InterpreterError(InterpreterError), ComptimeFnInRuntimeCode { name: String, location: Location }, ComptimeTypeInRuntimeCode { typ: String, location: Location }, + CheckedTransmuteFailed { actual: Type, expected: Type, location: Location }, } impl MonomorphizationError { @@ -21,6 +22,7 @@ impl MonomorphizationError { | MonomorphizationError::InternalError { location, .. } | MonomorphizationError::ComptimeFnInRuntimeCode { location, .. } | MonomorphizationError::ComptimeTypeInRuntimeCode { location, .. } + | MonomorphizationError::CheckedTransmuteFailed { location, .. } | MonomorphizationError::NoDefaultType { location, .. } => *location, MonomorphizationError::InterpreterError(error) => error.get_location(), } @@ -45,6 +47,9 @@ impl MonomorphizationError { MonomorphizationError::UnknownConstant { .. } => { "Could not resolve constant".to_string() } + MonomorphizationError::CheckedTransmuteFailed { actual, expected, .. } => { + format!("checked_transmute failed: `{actual}` != `{expected}`") + } MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); let secondary = "Could not determine type of generic argument".into(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index 295aa9056b6..b61eb6b5c17 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -928,9 +928,11 @@ impl<'interner> Monomorphizer<'interner> { TypeBinding::Unbound(_, _) => { unreachable!("Unbound type variable used in expression") } - TypeBinding::Bound(binding) => binding.evaluate_to_u32().unwrap_or_else(|| { - panic!("Non-numeric type variable used in expression expecting a value") - }), + TypeBinding::Bound(binding) => binding + .evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())) + .unwrap_or_else(|| { + panic!("Non-numeric type variable used in expression expecting a value") + }), }; let location = self.interner.id_location(expr_id); @@ -1289,7 +1291,7 @@ impl<'interner> Monomorphizer<'interner> { }; let call = self - .try_evaluate_call(&func, &id, &return_type) + .try_evaluate_call(&func, &id, &call.arguments, &arguments, &return_type)? .unwrap_or(ast::Expression::Call(ast::Call { func, arguments, return_type, location })); if !block_expressions.is_empty() { @@ -1371,13 +1373,15 @@ impl<'interner> Monomorphizer<'interner> { &mut self, func: &ast::Expression, expr_id: &node_interner::ExprId, + arguments: &[node_interner::ExprId], + argument_values: &[ast::Expression], result_type: &ast::Type, - ) -> Option { + ) -> Result, MonomorphizationError> { if let ast::Expression::Ident(ident) = func { if let Definition::Builtin(opcode) = &ident.definition { // TODO(#1736): Move this builtin to the SSA pass let location = self.interner.expr_location(expr_id); - return match opcode.as_str() { + return Ok(match opcode.as_str() { "modulus_num_bits" => { let bits = (FieldElement::max_num_bits() as u128).into(); let typ = @@ -1406,11 +1410,35 @@ impl<'interner> Monomorphizer<'interner> { let bytes = FieldElement::modulus().to_bytes_le(); Some(self.modulus_slice_literal(bytes, IntegerBitSize::Eight, location)) } + "checked_transmute" => { + Some(self.checked_transmute(*expr_id, arguments, argument_values)?) + } _ => None, - }; + }); } } - None + Ok(None) + } + + fn checked_transmute( + &mut self, + expr_id: node_interner::ExprId, + arguments: &[node_interner::ExprId], + argument_values: &[ast::Expression], + ) -> Result { + let location = self.interner.expr_location(&expr_id); + let actual = self.interner.id_type(arguments[0]).follow_bindings(); + let expected = self.interner.id_type(expr_id).follow_bindings(); + + if actual.unify(&expected).is_err() { + Err(MonomorphizationError::CheckedTransmuteFailed { actual, expected, location }) + } else { + // Evaluate `checked_transmute(arg)` to `{ arg }` + // in case the user did `&mut checked_transmute(arg)`. Wrapping the + // arg in a block prevents mutating the original argument. + let argument = argument_values[0].clone(); + Ok(ast::Expression::Block(vec![argument])) + } } fn modulus_slice_literal( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs index 6775cf35a78..5514a33ef0d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -61,17 +61,24 @@ impl<'a> Parser<'a> { fn parse_struct_field(&mut self) -> Option> { let mut doc_comments; let name; + let mut visibility; // Loop until we find an identifier, skipping anything that's not one loop { let doc_comments_start_span = self.current_token_span; doc_comments = self.parse_outer_doc_comments(); + visibility = self.parse_item_visibility(); + if let Some(ident) = self.eat_ident() { name = ident; break; } + if visibility != ItemVisibility::Private { + self.expected_identifier(); + } + if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, @@ -97,7 +104,7 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Colon); let typ = self.parse_type_or_error(); - Some(Documented::new(StructField { name, typ }, doc_comments)) + Some(Documented::new(StructField { visibility, name, typ }, doc_comments)) } fn empty_struct( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs index c3f27d9d49a..7dd59aedb45 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,14 +1,11 @@ use crate::{ - ast::{ - Expression, ExpressionKind, GenericTypeArgs, Literal, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, - }, - parser::{labels::ParsingRuleLabel, ParserError, ParserErrorReason}, + ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, + parser::{labels::ParsingRuleLabel, ParserError}, token::Token, BinaryTypeOperator, }; -use acvm::acir::AcirField; +use acvm::acir::{AcirField, FieldElement}; use noirc_errors::Span; use super::{parse_many::separated_by_comma_until_right_paren, Parser}; @@ -119,7 +116,7 @@ impl<'a> Parser<'a> { if self.eat(Token::Minus) { return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); let op = BinaryTypeOperator::Subtraction; let span = self.span_since(start_span); Some(UnresolvedTypeExpression::BinaryOperation( @@ -165,20 +162,6 @@ impl<'a> Parser<'a> { return None; }; - let int = if let Some(int) = int.try_to_u32() { - int - } else { - let err_expr = Expression { - kind: ExpressionKind::Literal(Literal::Integer(int, false)), - span: self.previous_token_span, - }; - self.push_error( - ParserErrorReason::InvalidTypeExpression(err_expr), - self.previous_token_span, - ); - 0 - }; - Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) } @@ -267,7 +250,7 @@ impl<'a> Parser<'a> { // If we ate '-' what follows must be a type expression, never a type return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); let op = BinaryTypeOperator::Subtraction; let span = self.span_since(start_span); let type_expr = UnresolvedTypeExpression::BinaryOperation( @@ -444,7 +427,7 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - assert_eq!(n, 42); + assert_eq!(n, 42_u32.into()); } #[test] @@ -496,7 +479,7 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - assert_eq!(n, 42); + assert_eq!(n, 42_u32.into()); } #[test] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index 6702704d32c..42fae40f669 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,3 +1,5 @@ +use acvm::{AcirField, FieldElement}; + use crate::{ ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{labels::ParsingRuleLabel, ParserErrorReason}, @@ -137,7 +139,8 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + let expr = + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); return Some(UnresolvedTypeData::String(expr)); } @@ -145,7 +148,7 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(0, self.current_token_span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) } }; @@ -161,7 +164,8 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + let expr = + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } @@ -170,7 +174,7 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(0, self.current_token_span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) } }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 3d099fe09c1..f190ef38bab 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -1946,6 +1946,125 @@ fn numeric_generic_used_in_turbofish() { assert_no_errors(src); } +// TODO(https://github.com/noir-lang/noir/issues/6245): +// allow u16 to be used as an array size +#[test] +fn numeric_generic_u16_array_size() { + let src = r#" + fn len(_arr: [Field; N]) -> u32 { + N + } + + pub fn foo() -> u32 { + let fields: [Field; N] = [0; N]; + len(fields) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +// TODO(https://github.com/noir-lang/noir/issues/6238): +// The EvaluatedGlobalIsntU32 warning is a stopgap +// (originally from https://github.com/noir-lang/noir/issues/6125) +#[test] +fn numeric_generic_field_larger_than_u32() { + let src = r#" + global A: Field = 4294967297; + + fn foo() { } + + fn main() { + let _ = foo::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::IntegerTooLarge { .. }) + )); +} + +// TODO(https://github.com/noir-lang/noir/issues/6238): +// The EvaluatedGlobalIsntU32 warning is a stopgap +// (originally from https://github.com/noir-lang/noir/issues/6126) +#[test] +fn numeric_generic_field_arithmetic_larger_than_u32() { + let src = r#" + struct Foo {} + + impl Foo { + fn size(self) -> Field { + F + } + } + + // 2^32 - 1 + global A: Field = 4294967295; + + fn foo() -> Foo { + Foo {} + } + + fn main() { + let _ = foo::().size(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), + )); + + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) + )); +} + +#[test] +fn cast_256_to_u8_size_checks() { + let src = r#" + fn main() { + assert(256 as u8 == 0); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), + )); +} + +// TODO(https://github.com/noir-lang/noir/issues/6247): +// add negative integer literal checks +#[test] +fn cast_negative_one_to_u8_size_checks() { + let src = r#" + fn main() { + assert((-1) as u8 != 0); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} + #[test] fn constant_used_with_numeric_generic() { let src = r#" @@ -2042,11 +2161,25 @@ fn numeric_generics_type_kind_mismatch() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 3); + + // TODO(https://github.com/noir-lang/noir/issues/6238): + // The EvaluatedGlobalIsntU32 warning is a stopgap assert!(matches!( errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), + )); + + assert!(matches!( + errors[1].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); + + // TODO(https://github.com/noir-lang/noir/issues/6238): see above + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), + )); } #[test] @@ -3116,8 +3249,10 @@ fn struct_array_len() { )); } +// TODO(https://github.com/noir-lang/noir/issues/6245): +// support u16 as an array size #[test] -fn non_u32_in_array_length() { +fn non_u32_as_array_length() { let src = r#" global ARRAY_LEN: u8 = 3; @@ -3127,10 +3262,13 @@ fn non_u32_in_array_length() { "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - + assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }) + )); + assert!(matches!( + errors[1].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) )); } @@ -3185,17 +3323,17 @@ fn trait_unconstrained_methods_typechecked_correctly() { self } - unconstrained fn foo(self) -> u64; + unconstrained fn foo(self) -> Field; } - impl Foo for Field { - unconstrained fn foo(self) -> u64 { - self as u64 + impl Foo for u64 { + unconstrained fn foo(self) -> Field { + self as Field } } unconstrained fn main() { - assert_eq(2.foo() as Field, 2.identity()); + assert_eq(2.foo(), 2.identity() as Field); } "#; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs index 80b6c7db09a..51bdf785688 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs @@ -211,6 +211,19 @@ fn warns_on_unused_global() { assert_eq!(item.item_type(), "global"); } +#[test] +fn does_not_warn_on_unused_global_if_it_has_an_abi_attribute() { + let src = r#" + contract foo { + #[abi(notes)] + global bar = 1; + } + + fn main() {} + "#; + assert_no_errors(src); +} + #[test] fn no_warning_on_inner_struct_when_parent_is_used() { let src = r#" @@ -231,3 +244,33 @@ fn no_warning_on_inner_struct_when_parent_is_used() { let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } + +#[test] +fn no_warning_on_struct_if_it_has_an_abi_attribute() { + let src = r#" + #[abi(functions)] + struct Foo { + a: Field, + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn no_warning_on_indirect_struct_if_it_has_an_abi_attribute() { + let src = r#" + struct Bar { + field: Field, + } + + #[abi(functions)] + struct Foo { + bar: Bar, + } + + fn main() {} + "#; + assert_no_errors(src); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs index 30a01818879..f02771b3760 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -106,6 +106,61 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { assert_eq!(ident.to_string(), "bar"); } +#[test] +fn warns_if_calling_private_struct_method() { + let src = r#" + mod moo { + pub struct Foo {} + + impl Foo { + fn bar(self) { + let _ = self; + } + } + } + + pub fn method(foo: moo::Foo) { + foo.bar() + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "bar"); +} + +#[test] +fn does_not_warn_if_calling_pub_crate_struct_method_from_same_crate() { + let src = r#" + mod moo { + pub struct Foo {} + + impl Foo { + pub(crate) fn bar(self) { + let _ = self; + } + } + } + + pub fn method(foo: moo::Foo) { + foo.bar() + } + + fn main() {} + "#; + assert_no_errors(src); +} + #[test] fn does_not_error_if_calling_private_struct_function_from_same_struct() { let src = r#" @@ -146,3 +201,146 @@ fn does_not_error_if_calling_private_struct_function_from_same_module() { "#; assert_no_errors(src); } + +#[test] +fn error_when_accessing_private_struct_field() { + let src = r#" + mod moo { + pub struct Foo { + x: Field + } + } + + fn foo(foo: moo::Foo) -> Field { + foo.x + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "x"); +} + +#[test] +fn does_not_error_when_accessing_private_struct_field_from_nested_module() { + let src = r#" + struct Foo { + x: Field + } + + mod nested { + fn foo(foo: super::Foo) -> Field { + foo.x + } + } + + fn main() { + let _ = Foo { x: 1 }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn does_not_error_when_accessing_pub_crate_struct_field_from_nested_module() { + let src = r#" + mod moo { + pub(crate) struct Foo { + pub(crate) x: Field + } + } + + fn foo(foo: moo::Foo) -> Field { + foo.x + } + + fn main() { + let _ = moo::Foo { x: 1 }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn error_when_using_private_struct_field_in_constructor() { + let src = r#" + mod moo { + pub struct Foo { + x: Field + } + } + + fn main() { + let _ = moo::Foo { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "x"); +} + +#[test] +fn error_when_using_private_struct_field_in_struct_pattern() { + let src = r#" + mod moo { + pub struct Foo { + x: Field + } + } + + fn foo(foo: moo::Foo) -> Field { + let moo::Foo { x } = foo; + x + } + + fn main() { + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(ident), + )) = &errors[0].0 + else { + panic!("Expected a private error"); + }; + + assert_eq!(ident.to_string(), "x"); +} + +#[test] +fn does_not_error_if_referring_to_top_level_private_module_via_crate() { + let src = r#" + mod foo { + pub fn bar() {} + } + + use crate::foo::bar; + + fn main() { + bar() + } + "#; + assert_no_errors(src); +} diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/index.md b/noir/noir-repo/docs/docs/noir/concepts/data_types/index.md index 11f51e2b65a..0f2db2b2d75 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/index.md +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/index.md @@ -105,7 +105,7 @@ type Bad2 = Bad1; // ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 ``` -By default, like functions, type aliases are private to the module the exist in. You can use `pub` +By default, like functions, type aliases are private to the module they exist in. You can use `pub` to make the type alias public or `pub(crate)` to make it public to just its crate: ```rust diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/structs.md b/noir/noir-repo/docs/docs/noir/concepts/data_types/structs.md index e529347f27d..29951ae843a 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/structs.md +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/structs.md @@ -69,7 +69,9 @@ fn get_octopus() -> Animal { The new variables can be bound with names different from the original struct field names, as showcased in the `legs --> feet` binding in the example above. -By default, like functions, structs are private to the module the exist in. You can use `pub` +### Visibility + +By default, like functions, structs are private to the module they exist in. You can use `pub` to make the struct public or `pub(crate)` to make it public to just its crate: ```rust @@ -79,4 +81,16 @@ pub struct Animal { legs: Field, eyes: u8, } +``` + +The same applies to struct fields: by default they are private to the module they exist in, +but they can be made `pub` or `pub(crate)`: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, // private to its module + pub(crate) legs: Field, // accessible from the entire crate + pub eyes: u8, // accessible from anywhere +} ``` \ No newline at end of file diff --git a/noir/noir-repo/docs/docs/noir/concepts/globals.md b/noir/noir-repo/docs/docs/noir/concepts/globals.md index 1145c55dfc7..6b8314399a2 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/globals.md +++ b/noir/noir-repo/docs/docs/noir/concepts/globals.md @@ -73,7 +73,7 @@ function performs side-effects like `println`, as these will still occur on each ### Visibility -By default, like functions, globals are private to the module the exist in. You can use `pub` +By default, like functions, globals are private to the module they exist in. You can use `pub` to make the global public or `pub(crate)` to make it public to just its crate: ```rust diff --git a/noir/noir-repo/docs/docs/noir/concepts/traits.md b/noir/noir-repo/docs/docs/noir/concepts/traits.md index 5d07e0c68f0..b3235a1a29b 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/traits.md +++ b/noir/noir-repo/docs/docs/noir/concepts/traits.md @@ -466,7 +466,7 @@ unwrapping of values when converting to and from the `Wrapper` and `Foo` types. ### Visibility -By default, like functions, traits are private to the module the exist in. You can use `pub` +By default, like functions, traits are private to the module they exist in. You can use `pub` to make the trait public or `pub(crate)` to make it public to just its crate: ```rust diff --git a/noir/noir-repo/docs/docs/noir/modules_packages_crates/modules.md b/noir/noir-repo/docs/docs/noir/modules_packages_crates/modules.md index 05399c38b4c..14aa1f0579a 100644 --- a/noir/noir-repo/docs/docs/noir/modules_packages_crates/modules.md +++ b/noir/noir-repo/docs/docs/noir/modules_packages_crates/modules.md @@ -212,7 +212,7 @@ In this example, the module `some_module` re-exports two public names defined in ### Visibility -By default, like functions, modules are private to the module (or crate) the exist in. You can use `pub` +By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` to make the module public or `pub(crate)` to make it public to just its crate: ```rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/mem.md b/noir/noir-repo/docs/docs/noir/standard_library/mem.md new file mode 100644 index 00000000000..95d36ac2a72 --- /dev/null +++ b/noir/noir-repo/docs/docs/noir/standard_library/mem.md @@ -0,0 +1,52 @@ +--- +title: Memory Module +description: + This module contains functions which manipulate memory in a low-level way +keywords: + [ + mem, memory, zeroed, transmute, checked_transmute + ] +--- + +# `std::mem::zeroed` + +```rust +fn zeroed() -> T +``` + +Returns a zeroed value of any type. +This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. +It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. +The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. +Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Functions + +Using it on other types could result in unexpected behavior. + +# `std::mem::checked_transmute` + +```rust +fn checked_transmute(value: T) -> U +``` + +Transmutes a value of one type into the same value but with a new type `U`. + +This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. +This function is useful for cases where the compiler may fails a type check that is expected to pass where +a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler +does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. +In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety +by checking this equality once `N`, `A`, `B` are fully resolved. + +Note that since this safety check is performed after type checking rather than during, no error is issued if the function +containing `checked_transmute` is never called. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/zeroed.md b/noir/noir-repo/docs/docs/noir/standard_library/zeroed.md deleted file mode 100644 index f450fecdd36..00000000000 --- a/noir/noir-repo/docs/docs/noir/standard_library/zeroed.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Zeroed Function -description: - The zeroed function returns a zeroed value of any type. -keywords: - [ - zeroed - ] ---- - -Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. - -You can access the function at `std::unsafe::zeroed`. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- Slice -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/noir/noir-repo/noir_stdlib/src/collections/vec.nr b/noir/noir-repo/noir_stdlib/src/collections/vec.nr index f24ed4ac783..1e641c384f0 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/vec.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/vec.nr @@ -1,5 +1,5 @@ pub struct Vec { - slice: [T] + pub(crate) slice: [T] } // A mutable vector type implemented as a wrapper around immutable slices. // A separate type is technically not needed but helps differentiate which operations are mutable. diff --git a/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr b/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr index 8c49226071f..f2425f6a786 100644 --- a/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr +++ b/noir/noir-repo/noir_stdlib/src/ec/consts/te.nr @@ -2,9 +2,9 @@ use crate::ec::tecurve::affine::Point as TEPoint; use crate::ec::tecurve::affine::Curve as TECurve; pub struct BabyJubjub { - curve: TECurve, - base8: TEPoint, - suborder: Field, + pub curve: TECurve, + pub base8: TEPoint, + pub suborder: Field, } #[field(bn254)] diff --git a/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr b/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr index b8077b6b639..11b6a964c2d 100644 --- a/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr +++ b/noir/noir-repo/noir_stdlib/src/ec/montcurve.nr @@ -16,16 +16,16 @@ pub mod affine { // Curve specification pub struct Curve { // Montgomery Curve configuration (ky^2 = x^3 + j*x^2 + x) - j: Field, - k: Field, + pub j: Field, + pub k: Field, // Generator as point in Cartesian coordinates - gen: Point + pub gen: Point } // Point in Cartesian coordinates pub struct Point { - x: Field, - y: Field, - infty: bool // Indicator for point at infinity + pub x: Field, + pub y: Field, + pub infty: bool // Indicator for point at infinity } impl Point { @@ -40,7 +40,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Point { + pub fn into_group(self) -> curvegroup::Point { if self.is_zero() { curvegroup::Point::zero() } else { @@ -55,14 +55,14 @@ pub mod affine { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y, infty} = self; Self { x, y: 0 - y, infty } } // Map into equivalent Twisted Edwards curve - fn into_tecurve(self) -> TEPoint { + pub fn into_tecurve(self) -> TEPoint { let Self {x, y, infty} = self; if infty | (y * (x + 1) == 0) { @@ -95,7 +95,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Curve { + pub fn into_group(self) -> curvegroup::Curve { curvegroup::Curve::new(self.j, self.k, self.gen.into_group()) } @@ -114,17 +114,17 @@ pub mod affine { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() } // Scalar multiplication (p + ... + p n times) - fn mul(self, n: Field, p: Point) -> Point { + pub fn mul(self, n: Field, p: Point) -> Point { self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() } // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -135,12 +135,12 @@ pub mod affine { } // Point subtraction - fn subtract(self, p1: Point, p2: Point) -> Point { + pub fn subtract(self, p1: Point, p2: Point) -> Point { self.add(p1, p2.negate()) } // Conversion to equivalent Twisted Edwards curve - fn into_tecurve(self) -> TECurve { + pub fn into_tecurve(self) -> TECurve { let Self {j, k, gen} = self; TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) } @@ -165,7 +165,7 @@ pub mod affine { } // Point mapping from equivalent Short Weierstrass curve - fn map_from_swcurve(self, p: SWPoint) -> Point { + pub fn map_from_swcurve(self, p: SWPoint) -> Point { let SWPoint {x, y, infty} = p; let j = self.j; let k = self.k; @@ -174,7 +174,7 @@ pub mod affine { } // Elligator 2 map-to-curve method; see . - fn elligator2_map(self, u: Field) -> Point { + pub fn elligator2_map(self, u: Field) -> Point { let j = self.j; let k = self.k; let z = ZETA; // Non-square Field element required for map @@ -205,7 +205,7 @@ pub mod affine { } // SWU map-to-curve method (via rational map) - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { self.map_from_swcurve(self.into_swcurve().swu_map(z, u)) } } @@ -223,16 +223,16 @@ pub mod curvegroup { use crate::cmp::Eq; pub struct Curve { // Montgomery Curve configuration (ky^2 z = x*(x^2 + j*x*z + z*z)) - j: Field, - k: Field, + pub j: Field, + pub k: Field, // Generator as point in projective coordinates - gen: Point + pub gen: Point } // Point in projective coordinates pub struct Point { - x: Field, - y: Field, - z: Field + pub x: Field, + pub y: Field, + pub z: Field } impl Point { @@ -247,7 +247,7 @@ pub mod curvegroup { } // Conversion to affine coordinates - fn into_affine(self) -> affine::Point { + pub fn into_affine(self) -> affine::Point { if self.is_zero() { affine::Point::zero() } else { @@ -262,14 +262,14 @@ pub mod curvegroup { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y, z} = self; Point::new(x, 0 - y, z) } // Map into equivalent Twisted Edwards curve - fn into_tecurve(self) -> TEPoint { + pub fn into_tecurve(self) -> TEPoint { self.into_affine().into_tecurve().into_group() } } @@ -297,7 +297,7 @@ pub mod curvegroup { } // Conversion to affine coordinates - fn into_affine(self) -> affine::Curve { + pub fn into_affine(self) -> affine::Curve { affine::Curve::new(self.j, self.k, self.gen.into_affine()) } @@ -316,7 +316,7 @@ pub mod curvegroup { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() } @@ -326,7 +326,7 @@ pub mod curvegroup { } // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -342,13 +342,13 @@ pub mod curvegroup { } // Conversion to equivalent Twisted Edwards curve - fn into_tecurve(self) -> TECurve { + pub fn into_tecurve(self) -> TECurve { let Self {j, k, gen} = self; TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) } // Conversion to equivalent Short Weierstrass curve - fn into_swcurve(self) -> SWCurve { + pub fn into_swcurve(self) -> SWCurve { let j = self.j; let k = self.k; let a0 = (3 - j * j) / (3 * k * k); @@ -363,17 +363,17 @@ pub mod curvegroup { } // Point mapping from equivalent Short Weierstrass curve - fn map_from_swcurve(self, p: SWPoint) -> Point { + pub fn map_from_swcurve(self, p: SWPoint) -> Point { self.into_affine().map_from_swcurve(p.into_affine()).into_group() } // Elligator 2 map-to-curve method - fn elligator2_map(self, u: Field) -> Point { + pub fn elligator2_map(self, u: Field) -> Point { self.into_affine().elligator2_map(u).into_group() } // SWU map-to-curve method (via rational map) - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { self.into_affine().swu_map(z, u).into_group() } } diff --git a/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr b/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr index 9c40ddd1adc..6e1054ad84e 100644 --- a/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr +++ b/noir/noir-repo/noir_stdlib/src/ec/swcurve.nr @@ -12,16 +12,16 @@ pub mod affine { // Curve specification pub struct Curve { // Short Weierstrass curve // Coefficients in defining equation y^2 = x^3 + ax + b - a: Field, - b: Field, + pub a: Field, + pub b: Field, // Generator as point in Cartesian coordinates - gen: Point + pub gen: Point } // Point in Cartesian coordinates pub struct Point { - x: Field, - y: Field, - infty: bool // Indicator for point at infinity + pub x: Field, + pub y: Field, + pub infty: bool // Indicator for point at infinity } impl Point { @@ -36,7 +36,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Point { + pub fn into_group(self) -> curvegroup::Point { let Self {x, y, infty} = self; if infty { @@ -52,7 +52,7 @@ pub mod affine { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y, infty} = self; Self { x, y: 0 - y, infty } } @@ -82,7 +82,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Curve { + pub fn into_group(self) -> curvegroup::Curve { let Curve{a, b, gen} = self; curvegroup::Curve { a, b, gen: gen.into_group() } @@ -100,7 +100,7 @@ pub mod affine { } // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. - fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { + pub fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { if p1.is_zero() { p2 } else if p2.is_zero() { @@ -133,7 +133,7 @@ pub mod affine { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_group().bit_mul(bits, p.into_group()).into_affine() } @@ -161,7 +161,7 @@ pub mod affine { // Simplified Shallue-van de Woestijne-Ulas map-to-curve method; see . // First determine non-square z != -1 in Field s.t. g(x) - z irreducible over Field and g(b/(z*a)) is square, // where g(x) = x^3 + a*x + b. swu_map(c,z,.) then maps a Field element to a point on curve c. - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { // Check whether curve is admissible assert(self.a * self.b != 0); @@ -196,16 +196,16 @@ pub mod curvegroup { // Curve specification pub struct Curve { // Short Weierstrass curve // Coefficients in defining equation y^2 = x^3 + axz^4 + bz^6 - a: Field, - b: Field, + pub a: Field, + pub b: Field, // Generator as point in Cartesian coordinates - gen: Point + pub gen: Point } // Point in three-dimensional Jacobian coordinates pub struct Point { - x: Field, - y: Field, - z: Field // z = 0 corresponds to point at infinity. + pub x: Field, + pub y: Field, + pub z: Field // z = 0 corresponds to point at infinity. } impl Point { @@ -236,7 +236,7 @@ pub mod curvegroup { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y, z} = self; Self { x, y: 0 - y, z } } @@ -338,7 +338,7 @@ pub mod curvegroup { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -363,7 +363,7 @@ pub mod curvegroup { } // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -379,7 +379,7 @@ pub mod curvegroup { } // Simplified SWU map-to-curve method - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { self.into_affine().swu_map(z, u).into_group() } } diff --git a/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr b/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr index c37b7c94a54..0eb1521b19d 100644 --- a/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr +++ b/noir/noir-repo/noir_stdlib/src/ec/tecurve.nr @@ -14,15 +14,15 @@ pub mod affine { // Curve specification pub struct Curve { // Twisted Edwards curve // Coefficients in defining equation ax^2 + y^2 = 1 + dx^2y^2 - a: Field, - d: Field, + pub a: Field, + pub d: Field, // Generator as point in Cartesian coordinates - gen: Point + pub gen: Point } // Point in Cartesian coordinates pub struct Point { - x: Field, - y: Field + pub x: Field, + pub y: Field } impl Point { @@ -38,7 +38,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Point { + pub fn into_group(self) -> curvegroup::Point { let Self {x, y} = self; curvegroup::Point::new(x, y, x * y, 1) @@ -50,13 +50,13 @@ pub mod affine { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y} = self; Point::new(0 - x, y) } // Map into prime-order subgroup of equivalent Montgomery curve - fn into_montcurve(self) -> MPoint { + pub fn into_montcurve(self) -> MPoint { if self.is_zero() { MPoint::zero() } else { @@ -93,7 +93,7 @@ pub mod affine { } // Conversion to CurveGroup coordinates - fn into_group(self) -> curvegroup::Curve { + pub fn into_group(self) -> curvegroup::Curve { let Curve{a, d, gen} = self; curvegroup::Curve { a, d, gen: gen.into_group() } @@ -111,7 +111,7 @@ pub mod affine { } // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. - fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { + pub fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { let Point{x: x1, y: y1} = p1; let curvegroup::Point{x: x2, y: y2, t: t2, z: z2} = p2; @@ -133,17 +133,17 @@ pub mod affine { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_group().bit_mul(bits, p.into_group()).into_affine() } // Scalar multiplication (p + ... + p n times) - fn mul(self, n: Field, p: Point) -> Point { + pub fn mul(self, n: Field, p: Point) -> Point { self.into_group().mul(n, p.into_group()).into_affine() } // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -154,7 +154,7 @@ pub mod affine { } // Point subtraction - fn subtract(self, p1: Point, p2: Point) -> Point { + pub fn subtract(self, p1: Point, p2: Point) -> Point { self.add(p1, p2.negate()) } @@ -178,17 +178,17 @@ pub mod affine { } // Point mapping from equivalent Short Weierstrass curve - fn map_from_swcurve(self, p: SWPoint) -> Point { + pub fn map_from_swcurve(self, p: SWPoint) -> Point { self.into_montcurve().map_from_swcurve(p).into_tecurve() } // Elligator 2 map-to-curve method (via rational map) - fn elligator2_map(self, u: Field) -> Point { + pub fn elligator2_map(self, u: Field) -> Point { self.into_montcurve().elligator2_map(u).into_tecurve() } // Simplified SWU map-to-curve method (via rational map) - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { self.into_montcurve().swu_map(z, u).into_tecurve() } } @@ -207,17 +207,17 @@ pub mod curvegroup { // Curve specification pub struct Curve { // Twisted Edwards curve // Coefficients in defining equation a(x^2 + y^2)z^2 = z^4 + dx^2y^2 - a: Field, - d: Field, + pub a: Field, + pub d: Field, // Generator as point in projective coordinates - gen: Point + pub gen: Point } // Point in extended twisted Edwards coordinates pub struct Point { - x: Field, - y: Field, - t: Field, - z: Field + pub x: Field, + pub y: Field, + pub t: Field, + pub z: Field } impl Point { @@ -245,14 +245,14 @@ pub mod curvegroup { } // Negation - fn negate(self) -> Self { + pub fn negate(self) -> Self { let Self {x, y, t, z} = self; Point::new(0 - x, y, 0 - t, z) } // Map into prime-order subgroup of equivalent Montgomery curve - fn into_montcurve(self) -> MPoint { + pub fn into_montcurve(self) -> MPoint { self.into_affine().into_montcurve().into_group() } } @@ -341,7 +341,7 @@ pub mod curvegroup { // Scalar multiplication with scalar represented by a bit array (little-endian convention). // If k is the natural number represented by `bits`, then this computes p + ... + p k times. - fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + pub fn bit_mul(self, bits: [u1; N], p: Point) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -366,7 +366,7 @@ pub mod curvegroup { } // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) - fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + pub fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); for i in 0..N { @@ -377,17 +377,17 @@ pub mod curvegroup { } // Point subtraction - fn subtract(self, p1: Point, p2: Point) -> Point { + pub fn subtract(self, p1: Point, p2: Point) -> Point { self.add(p1, p2.negate()) } // Conversion to equivalent Montgomery curve - fn into_montcurve(self) -> MCurve { + pub fn into_montcurve(self) -> MCurve { self.into_affine().into_montcurve().into_group() } // Conversion to equivalent Short Weierstrass curve - fn into_swcurve(self) -> SWCurve { + pub fn into_swcurve(self) -> SWCurve { self.into_montcurve().into_swcurve() } @@ -397,17 +397,17 @@ pub mod curvegroup { } // Point mapping from equivalent short Weierstrass curve - fn map_from_swcurve(self, p: SWPoint) -> Point { + pub fn map_from_swcurve(self, p: SWPoint) -> Point { self.into_montcurve().map_from_swcurve(p).into_tecurve() } // Elligator 2 map-to-curve method (via rational maps) - fn elligator2_map(self, u: Field) -> Point { + pub fn elligator2_map(self, u: Field) -> Point { self.into_montcurve().elligator2_map(u).into_tecurve() } // Simplified SWU map-to-curve method (via rational map) - fn swu_map(self, z: Field, u: Field) -> Point { + pub fn swu_map(self, z: Field, u: Field) -> Point { self.into_montcurve().swu_map(z, u).into_tecurve() } } diff --git a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr index f5e7c7e8528..ad7196b4494 100644 --- a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr @@ -5,9 +5,9 @@ use crate::cmp::Eq; /// By definition, the base field of the embedded curve is the scalar field of the proof system curve, i.e the Noir Field. /// x and y denotes the Weierstrass coordinates of the point, if is_infinite is false. pub struct EmbeddedCurvePoint { - x: Field, - y: Field, - is_infinite: bool + pub x: Field, + pub y: Field, + pub is_infinite: bool } impl EmbeddedCurvePoint { @@ -57,8 +57,8 @@ impl Eq for EmbeddedCurvePoint { /// By definition, the scalar field of the embedded curve is base field of the proving system curve. /// It may not fit into a Field element, so it is represented with two Field elements; its low and high limbs. pub struct EmbeddedCurveScalar { - lo: Field, - hi: Field, + pub lo: Field, + pub hi: Field, } impl EmbeddedCurveScalar { diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr index 081f7deb0fa..6c56a722fa7 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr @@ -79,7 +79,6 @@ fn verify_msg_block( } global BLOCK_SIZE = 64; -global ZERO = 0; // Variable size SHA-256 hash pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { diff --git a/noir/noir-repo/noir_stdlib/src/mem.nr b/noir/noir-repo/noir_stdlib/src/mem.nr index 88d17e20ee3..0d47a21b50d 100644 --- a/noir/noir-repo/noir_stdlib/src/mem.nr +++ b/noir/noir-repo/noir_stdlib/src/mem.nr @@ -4,3 +4,14 @@ #[builtin(zeroed)] pub fn zeroed() -> T {} +/// Transmutes a value of type T to a value of type U. +/// +/// Both types are asserted to be equal during compilation but after type checking. +/// If not, a compilation error is issued. +/// +/// This function is useful for types using arithmetic generics in cases +/// which the compiler otherwise cannot prove equal during type checking. +/// You can use this to obtain a value of the correct type while still asserting +/// that it is equal to the previous. +#[builtin(checked_transmute)] +pub fn checked_transmute(value: T) -> U {} diff --git a/noir/noir-repo/noir_stdlib/src/option.nr b/noir/noir-repo/noir_stdlib/src/option.nr index 6b3a2bf2f3c..0c120a71568 100644 --- a/noir/noir-repo/noir_stdlib/src/option.nr +++ b/noir/noir-repo/noir_stdlib/src/option.nr @@ -57,7 +57,7 @@ impl Option { } /// Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value - fn expect(self, message: fmtstr) -> T { + pub fn expect(self, message: fmtstr) -> T { assert(self.is_some(), message); self._value } diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr index 9cb94567d94..4a035ef91ef 100644 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ b/noir/noir-repo/noir_stdlib/src/uint128.nr @@ -4,8 +4,8 @@ use crate::cmp::{Eq, Ord, Ordering}; global pow64 : Field = 18446744073709551616; //2^64; global pow63 : Field = 9223372036854775808; // 2^63; pub struct U128 { - lo: Field, - hi: Field, + pub(crate) lo: Field, + pub(crate) hi: Field, } impl U128 { diff --git a/noir/noir-repo/test_programs/compile_failure/checked_transmute/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/checked_transmute/Nargo.toml new file mode 100644 index 00000000000..9d01c873b03 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/checked_transmute/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "checked_transmute" +type = "bin" +authors = [""] +compiler_version = ">=0.35.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/checked_transmute/src/main.nr b/noir/noir-repo/test_programs/compile_failure/checked_transmute/src/main.nr new file mode 100644 index 00000000000..058fa0ec911 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/checked_transmute/src/main.nr @@ -0,0 +1,9 @@ +use std::mem::checked_transmute; + +fn main() { + let _: [Field; 2] = transmute_fail([1]); +} + +pub fn transmute_fail(x: [Field; N]) -> [Field; N + 1] { + checked_transmute(x) +} diff --git a/noir/noir-repo/test_programs/compile_success_contract/abi_attribute/src/main.nr b/noir/noir-repo/test_programs/compile_success_contract/abi_attribute/src/main.nr index 164512b03db..c4d4a24e3c2 100644 --- a/noir/noir-repo/test_programs/compile_success_contract/abi_attribute/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_contract/abi_attribute/src/main.nr @@ -1,9 +1,9 @@ contract Foo { #[abi(foo)] - global foo: Field = 42; + pub global foo: Field = 42; #[abi(bar)] - struct Bar { + pub struct Bar { inner: Field } } diff --git a/noir/noir-repo/test_programs/compile_success_contract/contract_with_impl/src/main.nr b/noir/noir-repo/test_programs/compile_success_contract/contract_with_impl/src/main.nr index 1c6b6c217c4..9d45b88fbc9 100644 --- a/noir/noir-repo/test_programs/compile_success_contract/contract_with_impl/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_contract/contract_with_impl/src/main.nr @@ -1,7 +1,9 @@ contract Foo { - struct T { x: [Field] } + pub struct T { x: [Field] } impl T { - fn t(self) {} + fn t(self) { + let _ = self; + } } } diff --git a/noir/noir-repo/test_programs/compile_success_contract/non_entry_point_method/src/main.nr b/noir/noir-repo/test_programs/compile_success_contract/non_entry_point_method/src/main.nr index b768653262a..f49c2f14f9d 100644 --- a/noir/noir-repo/test_programs/compile_success_contract/non_entry_point_method/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_contract/non_entry_point_method/src/main.nr @@ -1,6 +1,6 @@ contract Foo { - struct PlaceholderStruct{x : u32 } + pub struct PlaceholderStruct{x : u32 } #[contract_library_method] - fn has_mut(_context: &mut PlaceholderStruct) {} + pub fn has_mut(_context: &mut PlaceholderStruct) {} } diff --git a/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr b/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr index 7412e1386bf..7742ed6139b 100644 --- a/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_contract/simple_contract/src/main.nr @@ -10,7 +10,7 @@ contract Foo { } // Regression for issue #3344 #[contract_library_method] - fn foo(x: u8) -> u8 { + pub fn foo(x: u8) -> u8 { x } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index 0a7d319485c..9a002356144 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -97,7 +97,7 @@ fn mul_add() -> Equiv, (), W< } // (N + 1) * N == N * N + N -fn demo_proof() -> Equiv, (Equiv, (), W, ()>, Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>), W, (Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>, Equiv, (), W, ()>)> { +pub fn demo_proof() -> Equiv, (Equiv, (), W, ()>, Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>), W, (Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>, Equiv, (), W, ()>)> { let p1: Equiv, (), W, ()> = mul_comm(); let p2: Equiv, (), W, ()> = mul_add::(); let p3_sub: Equiv, (), W, ()> = mul_one_r(); diff --git a/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr index 6178df5e749..492afd9e2f1 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr @@ -1,7 +1,7 @@ #[attr_with_args(1, 2)] #[varargs(1, 2)] #[varargs(1, 2, 3, 4)] -struct Foo {} +pub struct Foo {} comptime fn attr_with_args(s: StructDefinition, a: Field, b: Field) { // Ensure all variables are in scope. diff --git a/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr index 5c82145b431..f02e7973878 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr @@ -1,6 +1,6 @@ #['some_attribute] #['another_attribute] -struct SomeStruct { +pub struct SomeStruct { a: Field, b: Field } @@ -11,7 +11,7 @@ fn main() {} #[abi(something)] #[add_attribute] -struct Foo { +pub struct Foo { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/Nargo.toml new file mode 100644 index 00000000000..f3392ec79bb --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "checked_transmute" +type = "bin" +authors = [""] +compiler_version = ">=0.35.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/src/main.nr new file mode 100644 index 00000000000..fa6240fb43a --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/checked_transmute/src/main.nr @@ -0,0 +1,15 @@ +use std::mem::checked_transmute; + +fn main() { + // 1*(2 + 3) = 1*2 + 1*3 = 5 + let _: [Field; 5] = distribute::<1, 2, 3>([1, 2, 3, 4, 5]); +} + +pub fn distribute(x: [Field; N * (A + B)]) -> [Field; N * A + N * B] { + // asserts: [Field; N * (A + B)] = [Field; N * A + N * B] + // -> N * A + B = N * A + N * B + // + // This assert occurs during monomorphization when the actual values for N, A, and B + // become known. This also means if this function is not called, the assert will not trigger. + checked_transmute(x) +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr index a1a2c4b125a..86bde1c5eba 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr @@ -1,3 +1,3 @@ -comptime global FOO: i32 = Default::default(); +pub comptime global FOO: i32 = Default::default(); fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr index da2871a253d..97d99d0de6b 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr @@ -1,11 +1,11 @@ #[my_comptime_fn] -struct MyType { +pub struct MyType { field1: [A; 10], field2: (B, C), } #[mutate_struct_fields] -struct I32AndField { +pub struct I32AndField { z: i8, } @@ -26,14 +26,14 @@ comptime fn mutate_struct_fields(s: StructDefinition) { mod foo { #[attr] - struct Foo {} + pub struct Foo {} comptime fn attr(s: StructDefinition) { assert_eq(s.module().name(), quote { foo }); } #[add_generic] - struct Bar {} + pub struct Bar {} // docs:start:add-generic-example comptime fn add_generic(s: StructDefinition) { diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr index 448da96a460..43075058480 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr @@ -1,4 +1,4 @@ -use std::hash::{Hash, Hasher}; +use std::hash::Hasher; trait TraitWithGenerics { fn foo(self) -> (A, B); diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr index 87b48e7a357..8498e75d7f4 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr @@ -1,9 +1,7 @@ -use std::meta::type_of; - trait SomeTrait { fn foo(); } -struct SomeStruct { +pub struct SomeStruct { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr index 7d1e116dd0c..60fe264c57c 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr @@ -26,7 +26,7 @@ impl Neg for MyType { } } -fn neg_at_comptime() { +pub fn neg_at_comptime() { comptime { let value = MyType { value: 1 }; diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr index 2b1bd215960..68c3477b027 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -7,13 +7,13 @@ struct Foo { trait SomeTrait { } -struct StructImplementsSomeTrait { +pub struct StructImplementsSomeTrait { } impl SomeTrait for StructImplementsSomeTrait {} -struct StructDoesNotImplementSomeTrait { +pub struct StructDoesNotImplementSomeTrait { } @@ -160,7 +160,7 @@ fn main() { } // docs:start:implements_example -fn function_with_where(_x: T) where T: SomeTrait { +pub fn function_with_where(_x: T) where T: SomeTrait { comptime { let t = quote { T }.as_type(); diff --git a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr index 616ac7ef6ee..207869e5291 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr @@ -1,7 +1,6 @@ // Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc use std::ec::tecurve::affine::Curve as AffineCurve; use std::ec::tecurve::affine::Point as Gaffine; -use std::ec::tecurve::curvegroup::Curve; use std::ec::tecurve::curvegroup::Point as G; use std::ec::swcurve::affine::Point as SWGaffine; diff --git a/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr index 39992a6454b..5a619906775 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr @@ -1,4 +1,4 @@ -use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; +use std::embedded_curve_ops::EmbeddedCurvePoint; fn main() { let zero = EmbeddedCurvePoint::point_at_infinity(); diff --git a/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr index ec22b730d3f..1bc524d4cb5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr @@ -1,5 +1,5 @@ #[function_attr] -fn foo() {} +pub fn foo() {} struct Foo {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr index de58271cae6..26493c4836b 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr @@ -9,7 +9,9 @@ struct Struct { a: A, b: B } // Before the fix, this candidate is searched first, binding ? to `u8` permanently. impl Struct { - fn foo(self) {} + fn foo(self) { + let _ = self; + } } // Then this candidate would be searched next but would not be a valid @@ -19,5 +21,7 @@ impl Struct { // method is actually selected. So this candidate is now valid since // `Struct` unifies with `Struct` with `? = u32`. impl Struct { - fn foo(self) {} + fn foo(self) { + let _ = self; + } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/mod_nr_entrypoint/src/foo/mod.nr b/noir/noir-repo/test_programs/compile_success_empty/mod_nr_entrypoint/src/foo/mod.nr index 4eac6cb8514..216294fbf08 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/mod_nr_entrypoint/src/foo/mod.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/mod_nr_entrypoint/src/foo/mod.nr @@ -1,4 +1,4 @@ -mod bar; +pub mod bar; pub fn in_foo_mod() -> Field { 1 diff --git a/noir/noir-repo/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr index 3ca9c841a8c..8b809715529 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/no_duplicate_methods/src/main.nr @@ -7,7 +7,7 @@ trait ToField2 { fn to_field(self) -> Field; } -struct Foo { x: Field } +pub struct Foo { x: Field } impl ToField for Foo { fn to_field(self) -> Field { diff --git a/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index 5c618e9db36..c940e28dac2 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -85,7 +85,7 @@ trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -struct PublicStorage {} +pub struct PublicStorage {} impl PublicStorage { fn read() -> T where T: Deserialize { @@ -102,10 +102,10 @@ impl PublicStorage { // Check that we can thread numeric generics into nested structs // and also that we can handle nested structs with numeric generics // which are declared after the parent struct -struct NestedNumeric { +pub struct NestedNumeric { a: Field, b: InnerNumeric } -struct InnerNumeric { +pub struct InnerNumeric { inner: [u32; N], } diff --git a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr index 660f72f56e5..b390daec8c8 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_2099/src/main.nr @@ -1,13 +1,5 @@ use std::ec::tecurve::affine::Curve as AffineCurve; use std::ec::tecurve::affine::Point as Gaffine; -use std::ec::tecurve::curvegroup::Curve; -use std::ec::tecurve::curvegroup::Point as G; - -use std::ec::swcurve::affine::Point as SWGaffine; -use std::ec::swcurve::curvegroup::Point as SWG; - -use std::ec::montcurve::affine::Point as MGaffine; -use std::ec::montcurve::curvegroup::Point as MG; fn main() { // Define Baby Jubjub (ERC-2494) parameters in affine representation diff --git a/noir/noir-repo/test_programs/compile_success_empty/regression_4436/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/regression_4436/src/main.nr index 336d0f1f4ed..30e4a942bdf 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/regression_4436/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_4436/src/main.nr @@ -3,15 +3,15 @@ trait LibTrait { fn get_constant() -> Field; } -global STRUCT_A_LEN: u32 = 3; -global STRUCT_B_LEN: u32 = 5; +pub global STRUCT_A_LEN: u32 = 3; +pub global STRUCT_B_LEN: u32 = 5; -struct StructA; -struct StructB; +pub struct StructA; +pub struct StructB; impl LibTrait for StructA { fn broadcast() { - Self::get_constant(); + let _ = Self::get_constant(); } fn get_constant() -> Field { @@ -20,7 +20,7 @@ impl LibTrait for StructA { } impl LibTrait for StructB { fn broadcast() { - Self::get_constant(); + let _ = Self::get_constant(); } fn get_constant() -> Field { diff --git a/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr index e1095cd7fe2..1a9023e2d7a 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr @@ -1,5 +1,3 @@ -use std::embedded_curve_ops; - // Note: If main has any unsized types, then the verifier will never be able // to figure out the circuit instance fn main() { diff --git a/noir/noir-repo/test_programs/compile_success_empty/slice_init_with_complex_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/slice_init_with_complex_type/src/main.nr index 01ccf2fdeff..27339f812e7 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/slice_init_with_complex_type/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/slice_init_with_complex_type/src/main.nr @@ -4,14 +4,14 @@ struct strct1 { fn main() { let var1: [[i32; 1]] = [[0]]; - let var2: [[i32; 1]] = var1; + let _var2: [[i32; 1]] = var1; let var1: [(i32, u8)] = [(1, 2)]; - let var2: [(i32, u8)] = var1; + let _var2: [(i32, u8)] = var1; let var3: [strct1] = [strct1 { elem1: 1321351 }]; - let var4: [strct1] = var3; + let _var4: [strct1] = var3; let var1: [i32; 1] = [0]; - let var2: [[i32; 1]] = [var1]; + let _var2: [[i32; 1]] = [var1]; } diff --git a/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr index e61d9388ceb..11d30e4e069 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/static_assert/src/main.nr @@ -9,7 +9,7 @@ global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; -struct Foo { +pub struct Foo { field: Field, array: [Field; 3], slice: [Field], diff --git a/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/Nargo.toml new file mode 100644 index 00000000000..37307b94af5 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "struct_public_field" +type = "bin" +authors = [""] + +[dependencies] +dependency = {path = "dependency"} \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/Nargo.toml new file mode 100644 index 00000000000..2e471678a44 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dependency" +type = "lib" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/src/lib.nr b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/src/lib.nr new file mode 100644 index 00000000000..0e9bd4bd9f8 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/dependency/src/lib.nr @@ -0,0 +1,4 @@ +pub struct Point { + pub x: Field, + pub y: Field, +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/src/main.nr new file mode 100644 index 00000000000..c269c474de7 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/struct_public_field/src/main.nr @@ -0,0 +1,8 @@ +use dependency::Point; + +fn main() { + let point = Point { x: 1, y: 2 }; + let _ = point.x; + let Point { x, y } = point; + let _ = (x, y); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr index 44cad58c2a6..4104ca71037 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_allowed_item_name_matches/src/main.nr @@ -1,22 +1,22 @@ -trait Trait1 { +pub trait Trait1 { // types and consts with the same name are allowed type Tralala; let Tralala: u32; } -trait Trait2 { +pub trait Trait2 { // consts and types with the same name are allowed let Tralala: u32; type Tralala; } -trait Trait3 { +pub trait Trait3 { // types and functions with the same name are allowed type Tralala; fn Tralala(); } -trait Trait4 { +pub trait Trait4 { // functions and types with the same name are allowed fn Tralala(); type Tralala; diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_as_constraint/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_as_constraint/src/main.nr index 1911f045c27..b516376aae5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_as_constraint/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_as_constraint/src/main.nr @@ -1,5 +1,5 @@ #[test_as_constraint] -trait Foo {} +pub trait Foo {} comptime fn test_as_constraint(t: TraitDefinition) { let constraint = t.as_trait_constraint(); diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr index 412a75010f6..06bf311aa3c 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_associated_member_names_clashes/src/main.nr @@ -6,7 +6,7 @@ trait Trait2 { fn tralala() -> Field; } -struct Struct1 { +pub struct Struct1 { } impl Struct1 { diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_call_full_path/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_call_full_path/src/main.nr index 2d4b003f2ad..aea0f436dce 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_call_full_path/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_call_full_path/src/main.nr @@ -1,5 +1,5 @@ mod foo { - trait Trait { + pub trait Trait { fn me(self) -> Self; } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_function_calls/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_function_calls/src/main.nr index 62af0e756cd..352f18a74c0 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_function_calls/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_function_calls/src/main.nr @@ -26,6 +26,7 @@ trait Trait1a { self.trait_method2() * 7892 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 43278 } } @@ -43,6 +44,7 @@ trait Trait1b { struct Struct1b { vl: Field } impl Trait1b for Struct1b { fn trait_method2(self) -> Field { + let _ = self; 2394 } } @@ -56,6 +58,7 @@ trait Trait1c { struct Struct1c { vl: Field } impl Trait1c for Struct1c { fn trait_method2(self) -> Field { + let _ = self; 5485 } } @@ -65,6 +68,7 @@ trait Trait1d { self.trait_method2() * 2825 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 29341 } } @@ -89,6 +93,7 @@ impl Trait1e for Struct1e { self.trait_method2() * 47324 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 58945 } } @@ -105,6 +110,7 @@ impl Trait1f for Struct1f { self.trait_method2() * 34875 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 5748 } } @@ -112,6 +118,7 @@ impl Trait1f for Struct1f { trait Trait1g { fn trait_method1(self) -> Field; fn trait_method2(self) -> Field { + let _ = self; 37845 } } @@ -134,6 +141,7 @@ impl Trait1h for Struct1h { self.trait_method2() * 3482 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 8542 } } @@ -148,6 +156,7 @@ impl Trait1i for Struct1i { self.trait_method2() * 23478 - self.vl } fn trait_method2(self) -> Field { + let _ = self; 98543 } } @@ -290,6 +299,7 @@ trait Trait3a { b.trait_method2() * 8344 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 19212 } } @@ -307,6 +317,7 @@ trait Trait3b { struct Struct3b { vl: Field } impl Trait3b for Struct3b { fn trait_method2(self) -> Field { + let _ = self; 2392 } } @@ -320,6 +331,7 @@ trait Trait3c { struct Struct3c { vl: Field } impl Trait3c for Struct3c { fn trait_method2(self) -> Field { + let _ = self; 7743 } } @@ -329,6 +341,7 @@ trait Trait3d { b.trait_method2() * 291 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 3328 } } @@ -353,6 +366,7 @@ impl Trait3e for Struct3e { b.trait_method2() * 81232 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 80002 } } @@ -369,6 +383,7 @@ impl Trait3f for Struct3f { b.trait_method2() * 29223 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 63532 } } @@ -376,6 +391,7 @@ impl Trait3f for Struct3f { trait Trait3g { fn trait_function1(a: Field, b: Self) -> Field; fn trait_method2(self) -> Field { + let _ = self; 8887 } } @@ -398,6 +414,7 @@ impl Trait3h for Struct3h { b.trait_method2() * 74747 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 6283 } } @@ -412,6 +429,7 @@ impl Trait3i for Struct3i { b.trait_method2() * 1237 - b.vl + a } fn trait_method2(self) -> Field { + let _ = self; 84352 } } @@ -425,7 +443,7 @@ trait Trait4a { 2932 } } -struct Struct4a { vl: Field } +pub struct Struct4a { vl: Field } impl Trait4a for Struct4a {} // 4b) trait default function -> trait overriden function trait Trait4b { @@ -436,7 +454,7 @@ trait Trait4b { 2932 } } -struct Struct4b { vl: Field } +pub struct Struct4b { vl: Field } impl Trait4b for Struct4b { fn trait_function2() -> Field { 9353 @@ -449,7 +467,7 @@ trait Trait4c { } fn trait_function2() -> Field; } -struct Struct4c { vl: Field } +pub struct Struct4c { vl: Field } impl Trait4c for Struct4c { fn trait_function2() -> Field { 2928 @@ -464,7 +482,7 @@ trait Trait4d { 9332 } } -struct Struct4d { vl: Field } +pub struct Struct4d { vl: Field } impl Trait4d for Struct4d { fn trait_function1() -> Field { Self::trait_function2() * 8374 @@ -479,7 +497,7 @@ trait Trait4e { 28328 } } -struct Struct4e { vl: Field } +pub struct Struct4e { vl: Field } impl Trait4e for Struct4e { fn trait_function1() -> Field { Self::trait_function2() * 12323 @@ -495,7 +513,7 @@ trait Trait4f { } fn trait_function2() -> Field; } -struct Struct4f { vl: Field } +pub struct Struct4f { vl: Field } impl Trait4f for Struct4f { fn trait_function1() -> Field { Self::trait_function2() * 21392 @@ -511,7 +529,7 @@ trait Trait4g { 2932 } } -struct Struct4g { vl: Field } +pub struct Struct4g { vl: Field } impl Trait4g for Struct4g { fn trait_function1() -> Field { Self::trait_function2() * 3345 @@ -524,7 +542,7 @@ trait Trait4h { 5756 } } -struct Struct4h { vl: Field } +pub struct Struct4h { vl: Field } impl Trait4h for Struct4h { fn trait_function1() -> Field { Self::trait_function2() * 6478 @@ -538,7 +556,7 @@ trait Trait4i { fn trait_function1() -> Field; fn trait_function2() -> Field; } -struct Struct4i { vl: Field } +pub struct Struct4i { vl: Field } impl Trait4i for Struct4i { fn trait_function1() -> Field { Self::trait_function2() * 8239 diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr index 4d41ff2909a..b5c05d69378 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module1.nr @@ -1,2 +1,2 @@ -trait MyTrait { +pub trait MyTrait { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr index 3cadb6d78cb..c1335fe95e4 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module2.nr @@ -1,2 +1,2 @@ -struct MyStruct { +pub struct MyStruct { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr index f9458e83c4a..f1e3c407531 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module4.nr @@ -1,2 +1,2 @@ -trait MyTrait4 { +pub trait MyTrait4 { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr index cd9b7f0bf39..8c1c41ea25e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_multi_module_test/src/module5.nr @@ -1,2 +1,2 @@ -struct MyStruct5 { +pub struct MyStruct5 { } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/main.nr index 655450d05ac..86e7f70a3a3 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/main.nr @@ -42,6 +42,7 @@ impl StaticTrait for Static100 { struct Static200 {} impl StaticTrait for Static200 { fn static_function(slf: Self) -> Field { + let _ = slf; 200 } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr index c5cac4a1186..6390856731e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_where_clause/src/the_trait.nr @@ -1,9 +1,10 @@ -trait Asd { +pub trait Asd { fn asd(self) -> Field; } -trait StaticTrait { +pub trait StaticTrait { fn static_function(slf: Self) -> Field { + let _ = slf; 100 } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/type_path/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/type_path/src/main.nr index 96f3a29d96b..968812801c6 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/type_path/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/type_path/src/main.nr @@ -8,7 +8,7 @@ fn main() { } } -struct Foo {} +pub struct Foo {} impl Foo { fn static() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/unary_operators/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unary_operators/src/main.nr index ef622fd3eb9..8793086c1c1 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unary_operators/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unary_operators/src/main.nr @@ -3,5 +3,5 @@ fn main() { assert(x == 1 - 2); let y: i32 = -1; - assert(x == 1 - 2); + assert(y == 1 - 2); } diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_function/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_function/src/main.nr index 273a091b26d..7b6442abe8a 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_function/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_function/src/main.nr @@ -3,7 +3,7 @@ fn main() { } #[output_function] -fn foo() {} +pub fn foo() {} comptime fn output_function(_f: FunctionDefinition) -> Quoted { quote { diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr index 04f07f038e5..11d50fc2ab5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr @@ -1,5 +1,5 @@ #[foo] -struct Foo {} +pub struct Foo {} fn main() { assert_eq(ONE, 1); diff --git a/noir/noir-repo/test_programs/compile_success_empty/unused_variables/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unused_variables/src/main.nr index f82cace0509..2fb57e3b275 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unused_variables/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unused_variables/src/main.nr @@ -1 +1,4 @@ -fn main(x: Field, y: pub Field) {} +fn main(x: Field, y: pub Field) { + let _ = x; + let _ = y; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/use_callers_scope/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/use_callers_scope/src/main.nr index b4e8a7f7c4d..30db6c48f7c 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/use_callers_scope/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/use_callers_scope/src/main.nr @@ -1,7 +1,7 @@ #[bar::struct_attr] -struct Foo {} +pub struct Foo {} -struct Bar {} +pub struct Bar {} #[bar::fn_attr] fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr b/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr index e3a1539ea65..e56c127f562 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library/src/lib.nr @@ -1,2 +1,2 @@ // Re-export -use library2::ReExportMeFromAnotherLib; +pub use library2::ReExportMeFromAnotherLib; diff --git a/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr b/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr index 7e5a29a1424..c38c7bd1675 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr @@ -1,5 +1,5 @@ // When we re-export this type from another library and then use it in // main, we get a panic -struct ReExportMeFromAnotherLib { +pub struct ReExportMeFromAnotherLib { x : Field, } diff --git a/noir/noir-repo/test_programs/execution_success/check_large_field_bits/Nargo.toml b/noir/noir-repo/test_programs/execution_success/check_large_field_bits/Nargo.toml new file mode 100644 index 00000000000..33d5dd66484 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/check_large_field_bits/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "check_large_field_bits" +type = "bin" +authors = [""] +compiler_version = ">=0.35.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/check_large_field_bits/src/main.nr b/noir/noir-repo/test_programs/execution_success/check_large_field_bits/src/main.nr new file mode 100644 index 00000000000..1d65b342966 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/check_large_field_bits/src/main.nr @@ -0,0 +1,45 @@ +// 2^32 + 1 +global A: Field = 4294967297; +global B: Field = 4294967297; + +// 2^33 + 2 +global C: Field = A + B; + +fn main() { + // 2 * (2^32 + 1) == 2^33 + 2 + assert(C == 8589934594); + + let mut leading_zeroes = 0; + let mut stop = false; + let bits: [u1; 64] = C.to_be_bits(); + for i in 0..64 { + if (bits[i] == 0) & !stop { + leading_zeroes += 1; + } else { + stop = true; + } + } + let size = 64 - leading_zeroes; + + // 8589934594 has 34 bits + assert(size == 34); + C.assert_max_bit_size(34); + + assert( + C.to_be_bits() == [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); + + // leading big-endian bits past 34 are 0's + assert( + C.to_be_bits() == [ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); + assert( + C.to_be_bits() == [ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); +} diff --git a/noir/noir-repo/test_programs/test_libraries/exporting_lib/src/lib.nr b/noir/noir-repo/test_programs/test_libraries/exporting_lib/src/lib.nr index fdd9f139d41..7da75ce5413 100644 --- a/noir/noir-repo/test_programs/test_libraries/exporting_lib/src/lib.nr +++ b/noir/noir-repo/test_programs/test_libraries/exporting_lib/src/lib.nr @@ -1,8 +1,8 @@ -struct MyStruct { - inner: Field +pub struct MyStruct { + pub inner: Field } -type FooStruct = MyStruct; +pub type FooStruct = MyStruct; pub fn is_struct_zero(val: MyStruct) -> bool { val.inner == 0 diff --git a/noir/noir-repo/test_programs/test_libraries/reexporting_lib/src/lib.nr b/noir/noir-repo/test_programs/test_libraries/reexporting_lib/src/lib.nr index 1bced548304..f106971028d 100644 --- a/noir/noir-repo/test_programs/test_libraries/reexporting_lib/src/lib.nr +++ b/noir/noir-repo/test_programs/test_libraries/reexporting_lib/src/lib.nr @@ -1,3 +1,3 @@ -use exporting_lib::{MyStruct, FooStruct}; +pub use exporting_lib::{MyStruct, FooStruct}; -use exporting_lib as lib; +pub use exporting_lib as lib; diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index 771f67e1fa2..39e14a74007 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -267,12 +267,16 @@ fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result { if let Some(toml_path) = find_file_manifest(file_path) { - return resolve_workspace_from_toml( + match resolve_workspace_from_toml( &toml_path, PackageSelection::All, Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), - ) - .map_err(|err| LspError::WorkspaceResolutionError(err.to_string())); + ) { + Ok(workspace) => return Ok(workspace), + Err(error) => { + eprintln!("Error while processing {:?}: {}", toml_path, error); + } + } } let Some(parent_folder) = file_path @@ -285,14 +289,22 @@ pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result name, + Err(error) => { + eprintln!("{}", error); + CrateName::from_str("root").unwrap() + } + }; + let assumed_package = Package { version: None, compiler_required_version: Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), root_dir: PathBuf::from(parent_folder), package_type: PackageType::Binary, entry_path: PathBuf::from(file_path), - name: CrateName::from_str(parent_folder) - .map_err(|err| LspError::WorkspaceResolutionError(err.to_string()))?, + name: crate_name, dependencies: BTreeMap::new(), expression_width: None, }; @@ -309,7 +321,11 @@ pub(crate) fn workspace_package_for_file<'a>( workspace: &'a Workspace, file_path: &Path, ) -> Option<&'a Package> { - workspace.members.iter().find(|package| file_path.starts_with(&package.root_dir)) + if workspace.is_assumed { + workspace.members.first() + } else { + workspace.members.iter().find(|package| file_path.starts_with(&package.root_dir)) + } } pub(crate) fn prepare_package<'file_manager, 'parsed_files>( diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs index be8602d99a9..739f0bf4a21 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs @@ -31,8 +31,8 @@ impl<'a> CodeActionFinder<'a> { let mut fields = struct_type.get_fields_as_written(); // Remove the ones that already exists in the constructor - for (field, _) in &constructor.fields { - fields.retain(|(name, _)| name != &field.0.contents); + for (constructor_field, _) in &constructor.fields { + fields.retain(|field| field.name.0.contents != constructor_field.0.contents); } if fields.is_empty() { @@ -93,7 +93,7 @@ impl<'a> CodeActionFinder<'a> { new_text.push(' '); } - for (index, (name, _)) in fields.iter().enumerate() { + for (index, field) in fields.iter().enumerate() { if index > 0 { new_text.push(','); if let Some(line_indent) = &line_indent { @@ -103,7 +103,7 @@ impl<'a> CodeActionFinder<'a> { new_text.push(' '); } } - new_text.push_str(name); + new_text.push_str(&field.name.0.contents); new_text.push_str(": ()"); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 2882cb143bf..658033fc526 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -24,10 +24,12 @@ use noirc_frontend::{ UseTreeKind, Visitor, }, graph::{CrateId, Dependency}, - hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}, + hir::{ + def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}, + resolution::visibility::{method_call_is_visible, struct_member_is_visible}, + }, hir_def::traits::Trait, - node_interner::NodeInterner, - node_interner::ReferenceId, + node_interner::{NodeInterner, ReferenceId, StructId}, parser::{Item, ItemKind, ParsedSubModule}, token::{CustomAttribute, Token, Tokens}, Kind, ParsedModule, StructType, Type, TypeBinding, @@ -202,14 +204,14 @@ impl<'a> NodeFinder<'a> { // Remove the ones that already exists in the constructor for (used_name, _) in &constructor_expression.fields { - fields.retain(|(_, (name, _))| name != &used_name.0.contents); + fields.retain(|(_, field)| field.name.0.contents != used_name.0.contents); } let self_prefix = false; - for (field_index, (field, typ)) in &fields { + for (field_index, field) in &fields { self.completion_items.push(self.struct_field_completion_item( - field, - typ, + &field.name.0.contents, + &field.typ, struct_type.id, *field_index, self_prefix, @@ -629,6 +631,9 @@ impl<'a> NodeFinder<'a> { return; }; + let struct_id = get_type_struct_id(typ); + let is_primitive = typ.is_primitive(); + for (name, methods) in methods_by_name { for (func_id, method_type) in methods.iter() { if function_kind == FunctionKind::Any { @@ -639,6 +644,31 @@ impl<'a> NodeFinder<'a> { } } + if let Some(struct_id) = struct_id { + let modifiers = self.interner.function_modifiers(&func_id); + let visibility = modifiers.visibility; + if !struct_member_is_visible( + struct_id, + visibility, + self.module_id, + self.def_maps, + ) { + continue; + } + } + + if is_primitive + && !method_call_is_visible( + typ, + func_id, + self.module_id, + self.interner, + self.def_maps, + ) + { + continue; + } + if name_matches(name, prefix) { let completion_items = self.function_completion_items( name, @@ -691,16 +721,25 @@ impl<'a> NodeFinder<'a> { prefix: &str, self_prefix: bool, ) { - for (field_index, (name, typ)) in struct_type.get_fields(generics).iter().enumerate() { - if name_matches(name, prefix) { - self.completion_items.push(self.struct_field_completion_item( - name, - typ, - struct_type.id, - field_index, - self_prefix, - )); + for (field_index, (name, visibility, typ)) in + struct_type.get_fields_with_visibility(generics).iter().enumerate() + { + if !struct_member_is_visible(struct_type.id, *visibility, self.module_id, self.def_maps) + { + continue; + } + + if !name_matches(name, prefix) { + continue; } + + self.completion_items.push(self.struct_field_completion_item( + name, + typ, + struct_type.id, + field_index, + self_prefix, + )); } } @@ -1691,6 +1730,18 @@ fn get_array_element_type(typ: Type) -> Option { } } +fn get_type_struct_id(typ: &Type) -> Option { + match typ { + Type::Struct(struct_type, _) => Some(struct_type.borrow().id), + Type::Alias(type_alias, generics) => { + let type_alias = type_alias.borrow(); + let typ = type_alias.get_type(generics); + get_type_struct_id(&typ) + } + _ => None, + } +} + /// Returns true if name matches a prefix written in code. /// `prefix` must already be in snake case. /// This method splits both name and prefix by underscore, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index bc8bb75e10c..668255eb34d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -1071,6 +1071,22 @@ mod completion_tests { assert_completion(src, vec![field_completion_item("bar", "i32")]).await; } + #[test] + async fn test_does_not_suggest_private_struct_field() { + let src = r#" + mod moo { + pub struct Some { + property: i32, + } + } + + fn foo(s: moo::Some) { + s.>|< + } + "#; + assert_completion(src, vec![]).await; + } + #[test] async fn test_suggests_struct_impl_method() { let src = r#" @@ -1805,6 +1821,37 @@ mod completion_tests { .await; } + #[test] + async fn test_does_not_suggest_private_struct_methods() { + let src = r#" + mod moo { + pub struct Foo {} + + impl Foo { + fn bar(self) {} + } + } + + fn x(f: moo::Foo) { + f.>|<() + } + "#; + assert_completion(src, vec![]).await; + } + + #[test] + async fn test_does_not_suggest_private_primitive_methods() { + let src = r#" + fn foo(x: Field) { + x.>|< + } + "#; + let items = get_completions(src).await; + if items.iter().any(|item| item.label == "__assert_max_bit_size") { + panic!("Private method __assert_max_bit_size was suggested"); + } + } + #[test] async fn test_suggests_pub_use() { let src = r#" diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index 7b1fa7352a6..5087955ea77 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -137,11 +137,11 @@ fn format_struct(id: StructId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(&struct_type.name.0.contents); format_generics(&struct_type.generics, &mut string); string.push_str(" {\n"); - for (field_name, field_type) in struct_type.get_fields_as_written() { + for field in struct_type.get_fields_as_written() { string.push_str(" "); - string.push_str(&field_name); + string.push_str(&field.name.0.contents); string.push_str(": "); - string.push_str(&format!("{}", field_type)); + string.push_str(&format!("{}", field.typ)); string.push_str(",\n"); } string.push_str(" }"); @@ -158,7 +158,7 @@ fn format_struct_member( ) -> String { let struct_type = args.interner.get_struct(id); let struct_type = struct_type.borrow(); - let (field_name, field_type) = struct_type.field_at(field_index); + let field = struct_type.field_at(field_index); let mut string = String::new(); if format_parent_module(ReferenceId::Struct(id), args, &mut string) { @@ -167,10 +167,10 @@ fn format_struct_member( string.push_str(&struct_type.name.0.contents); string.push('\n'); string.push_str(" "); - string.push_str(&field_name.0.contents); + string.push_str(&field.name.0.contents); string.push_str(": "); - string.push_str(&format!("{}", field_type)); - string.push_str(&go_to_type_links(field_type, args.interner, args.files)); + string.push_str(&format!("{}", field.typ)); + string.push_str(&go_to_type_links(&field.typ, args.interner, args.files)); append_doc_comments(args.interner, ReferenceId::StructMember(id, field_index), &mut string); diff --git a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs index e119ee0d5b6..f7b3e6a748d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -97,8 +97,8 @@ impl<'a> InlayHintCollector<'a> { ReferenceId::StructMember(struct_id, field_index) => { let struct_type = self.interner.get_struct(struct_id); let struct_type = struct_type.borrow(); - let (_field_name, field_type) = struct_type.field_at(field_index); - self.push_type_hint(lsp_location, field_type, false); + let field = struct_type.field_at(field_index); + self.push_type_hint(lsp_location, &field.typ, false); } ReferenceId::Module(_) | ReferenceId::Struct(_) diff --git a/noir/noir-repo/tooling/lsp/src/visibility.rs b/noir/noir-repo/tooling/lsp/src/visibility.rs index d6e26f7bc48..207302f327e 100644 --- a/noir/noir-repo/tooling/lsp/src/visibility.rs +++ b/noir/noir-repo/tooling/lsp/src/visibility.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ graph::CrateId, hir::{ def_map::{CrateDefMap, ModuleId}, - resolution::import::can_reference_module_id, + resolution::visibility::can_reference_module_id, }, }; diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index 9f694080cf5..94f74a06149 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -59,6 +59,15 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ "is_unconstrained", ]; +/// Some tests are expected to have warnings +/// These should be fixed and removed from this list. +const TESTS_WITH_EXPECTED_WARNINGS: [&str; 2] = [ + // TODO(https://github.com/noir-lang/noir/issues/6238): remove from list once issue is closed + "brillig_cast", + // TODO(https://github.com/noir-lang/noir/issues/6238): remove from list once issue is closed + "macros_in_comptime", +]; + fn read_test_cases( test_data_dir: &Path, test_sub_dir: &str, @@ -234,13 +243,21 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa for (test_name, test_dir) in test_cases { let test_dir = test_dir.display(); - let assert_zero_opcodes = r#" + let mut assert_zero_opcodes = r#" let output = nargo.output().expect("Failed to execute command"); if !output.status.success() {{ panic!("`nargo info` failed with: {}", String::from_utf8(output.stderr).unwrap_or_default()); }} - + "#.to_string(); + + if !TESTS_WITH_EXPECTED_WARNINGS.contains(&test_name.as_str()) { + assert_zero_opcodes += r#" + nargo.assert().success().stderr(predicate::str::contains("warning:").not()); + "#; + } + + assert_zero_opcodes += r#" // `compile_success_empty` tests should be able to compile down to an empty circuit. let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ panic!("JSON was not well-formatted {:?}\n\n{:?}", e, std::str::from_utf8(&output.stdout)) @@ -284,7 +301,7 @@ fn generate_compile_success_contract_tests(test_file: &mut File, test_data_dir: &test_dir, r#" nargo.arg("compile").arg("--force"); - nargo.assert().success();"#, + nargo.assert().success().stderr(predicate::str::contains("warning:").not());"#, ); } writeln!(test_file, "}}").unwrap(); diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.proptest-regressions b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.proptest-regressions index ab88db8b6c2..d41d504e530 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.proptest-regressions +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.proptest-regressions @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 88db0227d5547742f771c14b1679f6783570b46bf7cf9e6897ee1aca4bd5034d # shrinks to io = SnippetInputOutput { description: "force_brillig = false, max_len = 200", inputs: {"input": Vec([Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(1), Field(5), Field(20), Field(133), Field(233), Field(99), Field(2⁶), Field(196), Field(232), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0)]), "message_size": Field(2⁶)}, expected_output: Vec([Field(102), Field(26), Field(94), Field(212), Field(102), Field(1), Field(215), Field(217), Field(167), Field(175), Field(158), Field(18), Field(20), Field(244), Field(158), Field(200), Field(2⁷), Field(186), Field(251), Field(243), Field(20), Field(207), Field(22), Field(3), Field(139), Field(81), Field(207), Field(2⁴), Field(50), Field(167), Field(1), Field(163)]) } +cc 0f334fe0c29748e8d0964d63f0d1f3a4eee536afa665eabc838045d8e1c67792 # shrinks to io = SnippetInputOutput { description: "force_brillig = true, max_len = 135", inputs: {"input": Vec([Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(29), Field(131), Field(217), Field(115), Field(221), Field(92), Field(23), Field(14), Field(58), Field(90), Field(232), Field(155), Field(59), Field(209), Field(2⁴×15), Field(137), Field(214), Field(129), Field(11), Field(140), Field(99), Field(131), Field(188), Field(159), Field(27), Field(206), Field(89), Field(137), Field(248), Field(30), Field(149), Field(194), Field(121), Field(127), Field(245), Field(202), Field(155), Field(203), Field(122), Field(2⁵), Field(209), Field(194), Field(214), Field(11), Field(82), Field(26), Field(244), Field(34), Field(30), Field(125), Field(83), Field(2⁴×13), Field(30), Field(2⁴×10), Field(85), Field(245), Field(220), Field(211), Field(190), Field(46), Field(159), Field(87), Field(74), Field(51), Field(42), Field(202), Field(230), Field(137), Field(127), Field(29), Field(126), Field(243), Field(106), Field(156), Field(2⁴×6), Field(154), Field(70), Field(100), Field(130)]), "message_size": Field(135)}, expected_output: Vec([Field(149), Field(114), Field(68), Field(219), Field(215), Field(147), Field(139), Field(34), Field(145), Field(204), Field(248), Field(145), Field(21), Field(119), Field(2⁵), Field(125), Field(181), Field(142), Field(106), Field(169), Field(202), Field(111), Field(110), Field(6), Field(210), Field(250), Field(2⁴), Field(110), Field(209), Field(2), Field(33), Field(104)]) } diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/struct.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/struct.nr index 8fc642f7cd5..f3651de607d 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/struct.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/struct.nr @@ -5,7 +5,7 @@ struct Foo { struct Pair { first: Foo, - second: Field, + pub second: Field, } impl Foo { diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/struct.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/struct.nr index 5e3530e8364..2e5fff47f9e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/input/struct.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/struct.nr @@ -5,7 +5,7 @@ struct Foo { struct Pair { first: Foo, - second: Field, + pub second: Field, } impl Foo {