diff --git a/.noir-sync-commit b/.noir-sync-commit index 8780207b075..3e5ac973b0c 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -2a0d211b92d002fa75855d4ba27267f8892dd52c +1b26440889379f491315cd9d088537b1898d57c5 diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs index 2d835482082..23832277226 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -7,8 +7,9 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use super::{ - BlockExpression, ConstructorExpression, Expression, ExpressionKind, GenericTypeArgs, - IndexExpression, ItemVisibility, MemberAccessExpression, MethodCallExpression, UnresolvedType, + BinaryOpKind, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + GenericTypeArgs, IndexExpression, InfixExpression, ItemVisibility, MemberAccessExpression, + MethodCallExpression, UnresolvedType, }; use crate::ast::UnresolvedTypeData; use crate::elaborator::types::SELF_TYPE_NAME; @@ -770,13 +771,57 @@ impl LValue { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ForBounds { + pub start: Expression, + pub end: Expression, + pub inclusive: bool, +} + +impl ForBounds { + /// Create a half-open range bounded inclusively below and exclusively above (`start..end`), + /// desugaring `start..=end` into `start..end+1` if necessary. + /// + /// Returns the `start` and `end` expressions. + pub(crate) fn into_half_open(self) -> (Expression, Expression) { + let end = if self.inclusive { + let end_span = self.end.span; + let end = ExpressionKind::Infix(Box::new(InfixExpression { + lhs: self.end, + operator: Spanned::from(end_span, BinaryOpKind::Add), + rhs: Expression::new(ExpressionKind::integer(FieldElement::from(1u32)), end_span), + })); + Expression::new(end, end_span) + } else { + self.end + }; + + (self.start, end) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum ForRange { - Range(/*start:*/ Expression, /*end:*/ Expression), + Range(ForBounds), Array(Expression), } impl ForRange { + /// Create a half-open range, bounded inclusively below and exclusively above. + pub fn range(start: Expression, end: Expression) -> Self { + Self::Range(ForBounds { start, end, inclusive: false }) + } + + /// Create a range bounded inclusively below and above. + pub fn range_inclusive(start: Expression, end: Expression) -> Self { + Self::Range(ForBounds { start, end, inclusive: true }) + } + + /// Create a range over some array. + pub fn array(value: Expression) -> Self { + Self::Array(value) + } + /// Create a 'for' expression taking care of desugaring a 'for e in array' loop /// into the following if needed: /// @@ -879,7 +924,7 @@ impl ForRange { let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { identifier: fresh_identifier, - range: ForRange::Range(start_range, end_range), + range: ForRange::range(start_range, end_range), block: new_block, span: for_loop_span, }), @@ -1009,7 +1054,14 @@ impl Display for Pattern { impl Display for ForLoopStatement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let range = match &self.range { - ForRange::Range(start, end) => format!("{start}..{end}"), + ForRange::Range(bounds) => { + format!( + "{}{}{}", + bounds.start, + if bounds.inclusive { "..=" } else { ".." }, + bounds.end + ) + } ForRange::Array(expr) => expr.to_string(), }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs index ed4d17cadd8..6e3e830e3f9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -21,9 +21,9 @@ use crate::{ }; use super::{ - FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, Pattern, Signedness, - TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, - UnresolvedTypeData, UnresolvedTypeExpression, + ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, Pattern, + Signedness, TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -1192,7 +1192,7 @@ impl ForRange { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { - ForRange::Range(start, end) => { + ForRange::Range(ForBounds { start, end, inclusive: _ }) => { start.accept(visitor); end.accept(visitor); } 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 bc1976febd4..b4ba80a307c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1125,7 +1125,7 @@ impl<'context> Elaborator<'context> { // object types in each method overlap or not. If they do, we issue an error. // If not, that is specialization which is allowed. let name = method.name_ident().clone(); - if module.declare_function(name, ItemVisibility::Public, *method_id).is_err() { + if module.declare_function(name, method.def.visibility, *method_id).is_err() { let existing = module.find_func_with_name(method.name_ident()).expect( "declare_function should only error if there is an existing function", ); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs index 0fb5a58035a..b83a1494ab9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -90,7 +90,13 @@ impl<'context> Elaborator<'context> { } fn resolve_path_in_module(&mut self, path: Path, module_id: ModuleId) -> PathResolutionResult { - let resolver = StandardPathResolver::new(module_id); + let self_type_module_id = if let Some(Type::Struct(struct_type, _)) = &self.self_type { + Some(struct_type.borrow().id.module_id()) + } else { + None + }; + + let resolver = StandardPathResolver::new(module_id, self_type_module_id); if !self.interner.lsp_mode { return resolver.resolve( 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 204a7f9cd75..6d5d0c9b467 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -196,7 +196,7 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_for(&mut self, for_loop: ForLoopStatement) -> (HirStatement, Type) { let (start, end) = match for_loop.range { - ForRange::Range(start, end) => (start, end), + ForRange::Range(bounds) => bounds.into_half_open(), ForRange::Array(_) => { let for_stmt = for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.span); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs index 3f2ecb395d0..fa45c41f8ec 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -7,7 +7,7 @@ use crate::{ ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainStatement, ConstructorExpression, Expression, ExpressionKind, - ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, + ForBounds, ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, MemberAccessExpression, MethodCallExpression, Pattern, PrefixExpression, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, @@ -267,6 +267,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { | Token::Dot | Token::DoubleColon | Token::DoubleDot + | Token::DoubleDotEqual | Token::Caret | Token::Pound | Token::Pipe @@ -713,10 +714,13 @@ fn remove_interned_in_statement_kind( }), StatementKind::For(for_loop) => StatementKind::For(ForLoopStatement { range: match for_loop.range { - ForRange::Range(from, to) => ForRange::Range( - remove_interned_in_expression(interner, from), - remove_interned_in_expression(interner, to), - ), + ForRange::Range(ForBounds { start, end, inclusive }) => { + ForRange::Range(ForBounds { + start: remove_interned_in_expression(interner, start), + end: remove_interned_in_expression(interner, end), + inclusive, + }) + } ForRange::Array(expr) => { ForRange::Array(remove_interned_in_expression(interner, expr)) } 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 63c32bc7e5f..3542c724b30 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 @@ -52,7 +52,7 @@ impl HirStatement { }), HirStatement::For(for_stmt) => StatementKind::For(ForLoopStatement { identifier: for_stmt.identifier.to_display_ast(interner), - range: ForRange::Range( + range: ForRange::range( for_stmt.start_range.to_display_ast(interner), for_stmt.end_range.to_display_ast(interner), ), 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 48827c087f9..0b028217c46 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 @@ -1514,7 +1514,8 @@ fn expr_as_for_range( ) -> IResult { expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { - if let ForRange::Range(from, to) = for_statement.range { + if let ForRange::Range(bounds) = for_statement.range { + let (from, to) = bounds.into_half_open(); let identifier = Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); let from = Value::expression(from.kind); 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 5b03b27e0b2..310c4a78414 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 @@ -28,7 +28,8 @@ fn interpret_helper(src: &str) -> Result { location, Vec::new(), Vec::new(), - false, + false, // is contract + false, // is struct ))); assert_eq!(root, module_id); @@ -160,6 +161,19 @@ fn for_loop() { assert_eq!(result, Value::U8(15)); } +#[test] +fn for_loop_inclusive() { + let program = "comptime fn main() -> pub u8 { + let mut x = 0; + for i in 0 ..= 6 { + x += i; + } + x + }"; + let result = interpret(program); + assert_eq!(result, Value::U8(21)); +} + #[test] fn for_loop_u16() { let program = "comptime fn main() -> pub u16 { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 4d348d998b7..9805e04a0b1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -554,6 +554,7 @@ fn inject_prelude( if let Ok(PathResolution { module_def_id, error }) = path_resolver::resolve_path( &context.def_maps, ModuleId { krate: crate_id, local_id: crate_root }, + None, path, &mut context.def_interner.usage_tracker, &mut None, @@ -571,6 +572,7 @@ fn inject_prelude( ImportDirective { visibility: ItemVisibility::Private, module_id: crate_root, + self_type_module_id: None, path: Path { segments, kind: PathKind::Plain, span: Span::default() }, alias: None, is_prelude: true, 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 0e20ec3a304..df2ada94ce0 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 @@ -81,6 +81,7 @@ pub fn collect_defs( collector.def_collector.imports.push(ImportDirective { visibility: import.visibility, module_id: collector.module_id, + self_type_module_id: None, path: import.path, alias: import.alias, is_prelude: false, @@ -385,7 +386,8 @@ impl<'a> ModCollector<'a> { Vec::new(), Vec::new(), false, - false, + false, // is contract + false, // is struct ) { Ok(module_id) => TraitId(ModuleId { krate, local_id: module_id.local_id }), Err(error) => { @@ -619,6 +621,7 @@ impl<'a> ModCollector<'a> { submodule.contents.inner_attributes.clone(), true, submodule.is_contract, + false, // is struct ) { Ok(child) => { self.collect_attributes( @@ -718,7 +721,8 @@ impl<'a> ModCollector<'a> { mod_decl.outer_attributes.clone(), ast.inner_attributes.clone(), true, - false, + false, // is contract + false, // is struct ) { Ok(child_mod_id) => { self.collect_attributes( @@ -770,6 +774,7 @@ impl<'a> ModCollector<'a> { inner_attributes: Vec, add_to_parent_scope: bool, is_contract: bool, + is_struct: bool, ) -> Result { push_child_module( &mut context.def_interner, @@ -782,6 +787,7 @@ impl<'a> ModCollector<'a> { inner_attributes, add_to_parent_scope, is_contract, + is_struct, ) } @@ -817,6 +823,7 @@ fn push_child_module( inner_attributes: Vec, add_to_parent_scope: bool, is_contract: bool, + is_struct: bool, ) -> Result { // Note: the difference between `location` and `mod_location` is: // - `mod_location` will point to either the token "foo" in `mod foo { ... }` @@ -826,8 +833,14 @@ fn push_child_module( // Eventually the location put in `ModuleData` is used for codelenses about `contract`s, // so we keep using `location` so that it continues to work as usual. let location = Location::new(mod_name.span(), mod_location.file); - let new_module = - ModuleData::new(Some(parent), location, outer_attributes, inner_attributes, is_contract); + let new_module = ModuleData::new( + Some(parent), + location, + outer_attributes, + inner_attributes, + is_contract, + is_struct, + ); let module_id = def_map.modules.insert(new_module); let modules = &mut def_map.modules; @@ -962,8 +975,9 @@ pub fn collect_struct( location, Vec::new(), Vec::new(), - false, - false, + false, // add to parent scope + false, // is contract + true, // is struct ) { Ok(module_id) => { interner.new_struct(&unresolved, resolved_generics, krate, module_id.local_id, file_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index 9afe897a167..e9dfc5ac067 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -102,7 +102,8 @@ impl CrateDefMap { location, Vec::new(), ast.inner_attributes.clone(), - false, + false, // is contract + false, // is struct )); let def_map = CrateDefMap { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs index ff36bcf27d6..fe6fe8285d3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs @@ -26,6 +26,9 @@ pub struct ModuleData { /// True if this module is a `contract Foo { ... }` module containing contract functions pub is_contract: bool, + /// True if this module is actually a struct + pub is_struct: bool, + pub attributes: Vec, } @@ -36,6 +39,7 @@ impl ModuleData { outer_attributes: Vec, inner_attributes: Vec, is_contract: bool, + is_struct: bool, ) -> ModuleData { let mut attributes = outer_attributes; attributes.extend(inner_attributes); @@ -47,6 +51,7 @@ impl ModuleData { definitions: ItemScope::default(), location, is_contract, + is_struct, attributes, } } 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 73a1b1ccb7c..f0b7f941ef8 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 @@ -5,6 +5,7 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::CompilationError; use crate::node_interner::ReferenceId; use crate::usage_tracker::UsageTracker; + use std::collections::BTreeMap; use crate::ast::{Ident, ItemVisibility, Path, PathKind, PathSegment}; @@ -16,6 +17,7 @@ use super::errors::ResolverError; pub struct ImportDirective { pub visibility: ItemVisibility, pub module_id: LocalModuleId, + pub self_type_module_id: Option, pub path: Path, pub alias: Option, pub is_prelude: bool, @@ -92,18 +94,15 @@ pub fn resolve_import( path_references: &mut Option<&mut Vec>, ) -> Result { let module_scope = import_directive.module_id; - let NamespaceResolution { - module_id: resolved_module, - namespace: resolved_namespace, - mut error, - } = resolve_path_to_ns( - import_directive, - crate_id, - crate_id, - def_maps, - usage_tracker, - path_references, - )?; + let NamespaceResolution { module_id: resolved_module, namespace: resolved_namespace, error } = + resolve_path_to_ns( + import_directive, + crate_id, + crate_id, + def_maps, + usage_tracker, + path_references, + )?; let name = resolve_path_name(import_directive); @@ -113,14 +112,16 @@ pub fn resolve_import( .map(|(_, visibility, _)| visibility) .expect("Found empty namespace"); - error = error.or_else(|| { - if can_reference_module_id( - def_maps, - crate_id, - import_directive.module_id, - resolved_module, - visibility, - ) { + let error = error.or_else(|| { + if import_directive.self_type_module_id == Some(resolved_module) + || can_reference_module_id( + def_maps, + crate_id, + import_directive.module_id, + resolved_module, + visibility, + ) + { None } else { Some(PathResolutionError::Private(name.clone())) @@ -408,6 +409,7 @@ fn resolve_external_dep( let dep_directive = ImportDirective { visibility: ItemVisibility::Private, module_id: dep_module.local_id, + self_type_module_id: directive.self_type_module_id, path, alias: directive.alias.clone(), is_prelude: false, @@ -442,11 +444,15 @@ pub fn can_reference_module_id( ItemVisibility::PublicCrate => same_crate, ItemVisibility::Private => { same_crate - && module_descendent_of_target( + && (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, + )) } } } @@ -466,3 +472,13 @@ fn module_descendent_of_target( .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/path_resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs index 50089d849ae..562366fae77 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs @@ -2,6 +2,7 @@ use super::import::{resolve_import, ImportDirective, PathResolution, PathResolut use crate::ast::{ItemVisibility, Path}; use crate::node_interner::ReferenceId; use crate::usage_tracker::UsageTracker; + use std::collections::BTreeMap; use crate::graph::CrateId; @@ -28,11 +29,13 @@ pub trait PathResolver { pub struct StandardPathResolver { // Module that we are resolving the path in module_id: ModuleId, + // The module of the self type, if any (for example, the ModuleId of a struct) + self_type_module_id: Option, } impl StandardPathResolver { - pub fn new(module_id: ModuleId) -> StandardPathResolver { - Self { module_id } + pub fn new(module_id: ModuleId, self_type_module_id: Option) -> StandardPathResolver { + Self { module_id, self_type_module_id } } } @@ -44,7 +47,14 @@ impl PathResolver for StandardPathResolver { usage_tracker: &mut UsageTracker, path_references: &mut Option<&mut Vec>, ) -> PathResolutionResult { - resolve_path(def_maps, self.module_id, path, usage_tracker, path_references) + resolve_path( + def_maps, + self.module_id, + self.self_type_module_id, + path, + usage_tracker, + path_references, + ) } fn local_module_id(&self) -> LocalModuleId { @@ -61,6 +71,7 @@ impl PathResolver for StandardPathResolver { pub fn resolve_path( def_maps: &BTreeMap, module_id: ModuleId, + self_type_module_id: Option, path: Path, usage_tracker: &mut UsageTracker, path_references: &mut Option<&mut Vec>, @@ -69,6 +80,7 @@ pub fn resolve_path( let import = ImportDirective { visibility: ItemVisibility::Private, module_id: module_id.local_id, + self_type_module_id, path, alias: None, is_prelude: false, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index 95eb41fd6d0..069487acb46 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -86,6 +86,11 @@ impl<'a> Lexer<'a> { self.peek_char() == Some(ch) } + /// Peeks at the character two positions ahead and returns true if it is equal to the char argument + fn peek2_char_is(&mut self, ch: char) -> bool { + self.peek2_char() == Some(ch) + } + fn ampersand(&mut self) -> SpannedTokenResult { if self.peek_char_is('&') { // When we issue this error the first '&' will already be consumed @@ -152,6 +157,7 @@ impl<'a> Lexer<'a> { Ok(token.into_single_span(self.position)) } + /// If `single` is followed by `character` then extend it as `double`. fn single_double_peek_token( &mut self, character: char, @@ -169,13 +175,23 @@ impl<'a> Lexer<'a> { } } - /// Given that some tokens can contain two characters, such as <= , !=, >= - /// Glue will take the first character of the token and check if it can be glued onto the next character - /// forming a double token + /// Given that some tokens can contain two characters, such as <= , !=, >=, or even three like ..= + /// Glue will take the first character of the token and check if it can be glued onto the next character(s) + /// forming a double or triple token + /// + /// Returns an error if called with a token which cannot be extended with anything. fn glue(&mut self, prev_token: Token) -> SpannedTokenResult { - let spanned_prev_token = prev_token.clone().into_single_span(self.position); match prev_token { - Token::Dot => self.single_double_peek_token('.', prev_token, Token::DoubleDot), + Token::Dot => { + if self.peek_char_is('.') && self.peek2_char_is('=') { + let start = self.position; + self.next_char(); + self.next_char(); + Ok(Token::DoubleDotEqual.into_span(start, start + 2)) + } else { + self.single_double_peek_token('.', prev_token, Token::DoubleDot) + } + } Token::Less => { let start = self.position; if self.peek_char_is('=') { @@ -214,7 +230,7 @@ impl<'a> Lexer<'a> { return self.parse_block_comment(start); } - Ok(spanned_prev_token) + Ok(prev_token.into_single_span(start)) } _ => Err(LexerErrorKind::NotADoubleChar { span: Span::single_char(self.position), @@ -703,8 +719,8 @@ mod tests { use crate::token::{CustomAttribute, FunctionAttribute, SecondaryAttribute, TestScope}; #[test] - fn test_single_double_char() { - let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>"; + fn test_single_multi_char() { + let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. ..= % / * = == << >>"; let expected = vec![ Token::Bang, @@ -730,6 +746,7 @@ mod tests { Token::Arrow, Token::Dot, Token::DoubleDot, + Token::DoubleDotEqual, Token::Percent, Token::Slash, Token::Star, 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 97faea4b445..626c46d392f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -73,6 +73,8 @@ pub enum BorrowedToken<'input> { Dot, /// .. DoubleDot, + /// ..= + DoubleDotEqual, /// ( LeftParen, /// ) @@ -190,6 +192,8 @@ pub enum Token { Dot, /// .. DoubleDot, + /// ..= + DoubleDotEqual, /// ( LeftParen, /// ) @@ -279,6 +283,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::ShiftRight => BorrowedToken::ShiftRight, Token::Dot => BorrowedToken::Dot, Token::DoubleDot => BorrowedToken::DoubleDot, + Token::DoubleDotEqual => BorrowedToken::DoubleDotEqual, Token::LeftParen => BorrowedToken::LeftParen, Token::RightParen => BorrowedToken::RightParen, Token::LeftBrace => BorrowedToken::LeftBrace, @@ -409,6 +414,7 @@ impl fmt::Display for Token { Token::ShiftRight => write!(f, ">>"), Token::Dot => write!(f, "."), Token::DoubleDot => write!(f, ".."), + Token::DoubleDotEqual => write!(f, "..="), Token::LeftParen => write!(f, "("), Token::RightParen => write!(f, ")"), Token::LeftBrace => write!(f, "{{"), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index df656dc5a7d..60128c6cafe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -1076,9 +1076,15 @@ where { expr_no_constructors .clone() - .then_ignore(just(Token::DoubleDot)) + .then(just(Token::DoubleDot).or(just(Token::DoubleDotEqual))) .then(expr_no_constructors.clone()) - .map(|(start, end)| ForRange::Range(start, end)) + .map(|((start, dots), end)| { + if dots == Token::DoubleDotEqual { + ForRange::range_inclusive(start, end) + } else { + ForRange::range(start, end) + } + }) .or(expr_no_constructors.map(ForRange::Array)) } @@ -1465,15 +1471,21 @@ mod test { fn parse_for_loop() { parse_all( for_loop(expression_no_constructors(expression()), fresh_statement()), - vec!["for i in x+y..z {}", "for i in 0..100 { foo; bar }"], + vec![ + "for i in x+y..z {}", + "for i in 0..100 { foo; bar }", + "for i in 90..=100 { foo; bar }", + ], ); parse_all_failing( for_loop(expression_no_constructors(expression()), fresh_statement()), vec![ "for 1 in x+y..z {}", // Cannot have a literal as the loop identifier - "for i in 0...100 {}", // Only '..' is supported, there are no inclusive ranges yet - "for i in 0..=100 {}", // Only '..' is supported, there are no inclusive ranges yet + "for i in 0...100 {}", // Only '..' is supported + "for i in .. {}", // Range needs needs bounds + "for i in ..=10 {}", // Range needs lower bound + "for i in 0.. {}", // Range needs upper bound ], ); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index f7ef9955550..a8d8c807797 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -90,7 +90,8 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation location, Vec::new(), inner_attributes.clone(), - false, + false, // is contract + false, // is struct )); let def_map = CrateDefMap { @@ -1071,6 +1072,18 @@ fn resolve_for_expr() { assert_no_errors(src); } +#[test] +fn resolve_for_expr_incl() { + let src = r#" + fn main(x : u64) { + for i in 1..=20 { + let _z = x + i; + }; + } + "#; + assert_no_errors(src); +} + #[test] fn resolve_call_expr() { let src = r#" diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs index b1156b20eb0..71e63e878e8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs @@ -29,11 +29,11 @@ fn turbofish_numeric_generic_nested_call() { } impl Foo { - fn static_method() -> [u8; N] { + pub fn static_method() -> [u8; N] { [0; N] } - fn impl_method(self) -> [T; N] { + pub fn impl_method(self) -> [T; N] { [self.a; N] } } @@ -108,7 +108,7 @@ fn turbofish_in_middle_of_variable_unsupported_yet() { } impl Foo { - fn new(x: T) -> Self { + pub fn new(x: T) -> Self { Foo { x } } } 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 bca1c9af829..6054ddad47d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -3,7 +3,7 @@ use crate::{ def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, resolution::{errors::ResolverError, import::PathResolutionError}, }, - tests::get_program_errors, + tests::{assert_no_errors, get_program_errors}, }; #[test] @@ -105,3 +105,44 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { assert_eq!(ident.to_string(), "bar"); } + +#[test] +fn does_not_error_if_calling_private_struct_function_from_same_struct() { + let src = r#" + struct Foo { + + } + + impl Foo { + fn foo() { + Foo::bar() + } + + fn bar() {} + } + + fn main() { + let _ = Foo {}; + } + "#; + assert_no_errors(src); +} + +#[test] +fn does_not_error_if_calling_private_struct_function_from_same_module() { + let src = r#" + struct Foo; + + impl Foo { + fn bar() -> Field { + 0 + } + } + + fn main() { + let _ = Foo {}; + assert_eq(Foo::bar(), 0); + } + "#; + assert_no_errors(src); +} diff --git a/noir/noir-repo/docs/docs/noir/concepts/control_flow.md b/noir/noir-repo/docs/docs/noir/concepts/control_flow.md index 045d3c3a5f5..b365bb22728 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/control_flow.md +++ b/noir/noir-repo/docs/docs/noir/concepts/control_flow.md @@ -42,6 +42,8 @@ for i in 0..10 { } ``` +Alternatively, `start..=end` can be used for a range that is inclusive on both ends. + The index for loops is of type `u64`. ### Break and Continue 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 445b6d96554..f5e7c7e8528 100644 --- a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr @@ -13,12 +13,12 @@ pub struct EmbeddedCurvePoint { impl EmbeddedCurvePoint { /// Elliptic curve point doubling operation /// returns the doubled point of a point P, i.e P+P - fn double(self) -> EmbeddedCurvePoint { + pub fn double(self) -> EmbeddedCurvePoint { embedded_curve_add(self, self) } /// Returns the null element of the curve; 'the point at infinity' - fn point_at_infinity() -> EmbeddedCurvePoint { + pub fn point_at_infinity() -> EmbeddedCurvePoint { EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true } } } @@ -67,14 +67,14 @@ impl EmbeddedCurveScalar { } #[field(bn254)] - fn from_field(scalar: Field) -> EmbeddedCurveScalar { + pub fn from_field(scalar: Field) -> EmbeddedCurveScalar { let (a,b) = crate::field::bn254::decompose(scalar); EmbeddedCurveScalar { lo: a, hi: b } } //Bytes to scalar: take the first (after the specified offset) 16 bytes of the input as the lo value, and the next 16 bytes as the hi value #[field(bn254)] - fn from_bytes(bytes: [u8; 64], offset: u32) -> EmbeddedCurveScalar { + pub(crate) fn from_bytes(bytes: [u8; 64], offset: u32) -> EmbeddedCurveScalar { let mut v = 1; let mut lo = 0 as Field; let mut hi = 0 as Field; diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr index 6b61ca32302..fb813120fef 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr @@ -20,7 +20,7 @@ impl Poseidon2 { } } - fn new(iv: Field) -> Poseidon2 { + pub(crate) fn new(iv: Field) -> Poseidon2 { let mut result = Poseidon2 { cache: [0; 3], state: [0; 4], cache_size: 0, squeeze_mode: false }; result.state[RATE] = iv; result diff --git a/noir/noir-repo/noir_stdlib/src/meta/ctstring.nr b/noir/noir-repo/noir_stdlib/src/meta/ctstring.nr index ec205fa6f88..4b4854682db 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/ctstring.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/ctstring.nr @@ -2,20 +2,20 @@ use crate::append::Append; impl CtString { // docs:start:new - comptime fn new() -> Self { + pub comptime fn new() -> Self { // docs:end:new "".as_ctstring() } // Bug: using &mut self as the object results in this method not being found // docs:start:append_str - comptime fn append_str(self, s: str) -> Self { + pub comptime fn append_str(self, s: str) -> Self { // docs:end:append_str f"{self}{s}".as_ctstring() } // docs:start:append_fmtstr - comptime fn append_fmtstr(self, s: fmtstr) -> Self { + pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { // docs:end:append_fmtstr f"{self}{s}".as_ctstring() } @@ -24,7 +24,7 @@ impl CtString { /// To get around this, we return a quoted str and the underlying str can /// be accessed using macro insertion `foo.as_quoted_str!()`. // docs:start:as_quoted_str - comptime fn as_quoted_str(self) -> Quoted { + pub comptime fn as_quoted_str(self) -> Quoted { // docs:end:as_quoted_str quote { $self } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index c96f7d27442..83a165fc533 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -8,89 +8,89 @@ impl Expr { /// If this expression is an array literal `[elem1, ..., elemN]`, this returns a slice of each element in the array. #[builtin(expr_as_array)] // docs:start:as_array - comptime fn as_array(self) -> Option<[Expr]> {} + pub comptime fn as_array(self) -> Option<[Expr]> {} // docs:end:as_array /// If this expression is an assert, this returns the assert expression and the optional message. #[builtin(expr_as_assert)] // docs:start:as_assert - comptime fn as_assert(self) -> Option<(Expr, Option)> {} + pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} // docs:end:as_assert /// If this expression is an assert_eq, this returns the left-hand-side and right-hand-side /// expressions, together with the optional message. #[builtin(expr_as_assert_eq)] // docs:start:as_assert_eq - comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} + pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} // docs:end:as_assert_eq /// If this expression is an assignment, this returns a tuple with the left hand side /// and right hand side in order. #[builtin(expr_as_assign)] // docs:start:as_assign - comptime fn as_assign(self) -> Option<(Expr, Expr)> {} + pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} // docs:end:as_assign /// If this expression is a binary operator operation ` `, /// return the left-hand side, operator, and the right-hand side of the operation. #[builtin(expr_as_binary_op)] // docs:start:as_binary_op - comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} // docs:end:as_binary_op /// If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return /// a slice containing each statement. #[builtin(expr_as_block)] // docs:start:as_block - comptime fn as_block(self) -> Option<[Expr]> {} + pub comptime fn as_block(self) -> Option<[Expr]> {} // docs:end:as_block /// If this expression is a boolean literal, return that literal. #[builtin(expr_as_bool)] // docs:start:as_bool - comptime fn as_bool(self) -> Option {} + pub comptime fn as_bool(self) -> Option {} // docs:end:as_bool /// If this expression is a cast expression `expr as type`, returns the casted /// expression and the type to cast to. // docs:start:as_cast #[builtin(expr_as_cast)] - comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} + pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} // docs:end:as_cast /// If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, /// return each statement in the block. #[builtin(expr_as_comptime)] // docs:start:as_comptime - comptime fn as_comptime(self) -> Option<[Expr]> {} + pub comptime fn as_comptime(self) -> Option<[Expr]> {} // docs:end:as_comptime /// If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, /// return the type and the fields. #[builtin(expr_as_constructor)] // docs:start:as_constructor - comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} + pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} // docs:end:as_constructor /// If this expression is a for statement over a single expression, return the identifier, /// the expression and the for loop body. #[builtin(expr_as_for)] // docs:start:as_for - comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} + pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} // docs:end:as_for /// If this expression is a for statement over a range, return the identifier, /// the range start, the range end and the for loop body. #[builtin(expr_as_for_range)] // docs:start:as_for_range - comptime fn as_for_range(self) -> Option<(Quoted, Expr, Expr, Expr)> {} + pub comptime fn as_for_range(self) -> Option<(Quoted, Expr, Expr, Expr)> {} // docs:end:as_for_range /// If this expression is a function call `foo(arg1, ..., argN)`, return /// the function and a slice of each argument. #[builtin(expr_as_function_call)] // docs:start:as_function_call - comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} + pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} // docs:end:as_function_call /// If this expression is an `if condition { then_branch } else { else_branch }`, @@ -98,90 +98,90 @@ impl Expr { /// `None` is returned for that branch instead. #[builtin(expr_as_if)] // docs:start:as_if - comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} + pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} // docs:end:as_if /// If this expression is an index into an array `array[index]`, return the /// array and the index. #[builtin(expr_as_index)] // docs:start:as_index - comptime fn as_index(self) -> Option<(Expr, Expr)> {} + pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} // docs:end:as_index /// If this expression is an integer literal, return the integer as a field /// as well as whether the integer is negative (true) or not (false). #[builtin(expr_as_integer)] // docs:start:as_integer - comptime fn as_integer(self) -> Option<(Field, bool)> {} + pub comptime fn as_integer(self) -> Option<(Field, bool)> {} // docs:end:as_integer /// If this expression is a lambda, returns the parameters, return type and body. #[builtin(expr_as_lambda)] // docs:start:as_lambda - comptime fn as_lambda(self) -> Option<([(Expr, Option)], Option, Expr)> {} + pub comptime fn as_lambda(self) -> Option<([(Expr, Option)], Option, Expr)> {} // docs:end:as_lambda /// If this expression is a let statement, returns the let pattern as an `Expr`, /// the optional type annotation, and the assigned expression. #[builtin(expr_as_let)] // docs:start:as_let - comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} + pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} // docs:end:as_let /// If this expression is a member access `foo.bar`, return the struct/tuple /// expression and the field. The field will be represented as a quoted value. #[builtin(expr_as_member_access)] // docs:start:as_member_access - comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} + pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} // docs:end:as_member_access /// If this expression is a method call `foo.bar::(arg1, ..., argN)`, return /// the receiver, method name, a slice of each generic argument, and a slice of each argument. #[builtin(expr_as_method_call)] // docs:start:as_method_call - comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} + pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} // docs:end:as_method_call /// If this expression is a repeated element array `[elem; length]`, return /// the repeated element and the length expressions. #[builtin(expr_as_repeated_element_array)] // docs:start:as_repeated_element_array - comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} + pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} // docs:end:as_repeated_element_array /// If this expression is a repeated element slice `[elem; length]`, return /// the repeated element and the length expressions. #[builtin(expr_as_repeated_element_slice)] // docs:start:as_repeated_element_slice - comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} + pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} // docs:end:as_repeated_element_slice /// If this expression is a slice literal `&[elem1, ..., elemN]`, /// return each element of the slice. #[builtin(expr_as_slice)] // docs:start:as_slice - comptime fn as_slice(self) -> Option<[Expr]> {} + pub comptime fn as_slice(self) -> Option<[Expr]> {} // docs:end:as_slice /// If this expression is a tuple `(field1, ..., fieldN)`, /// return each element of the tuple. #[builtin(expr_as_tuple)] // docs:start:as_tuple - comptime fn as_tuple(self) -> Option<[Expr]> {} + pub comptime fn as_tuple(self) -> Option<[Expr]> {} // docs:end:as_tuple /// If this expression is a unary operation ` `, /// return the unary operator as well as the right-hand side expression. #[builtin(expr_as_unary_op)] // docs:start:as_unary_op - comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} + pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} // docs:end:as_unary_op /// If this expression is an `unsafe { stmt1; ...; stmtN }` block, /// return each statement inside in a slice. #[builtin(expr_as_unsafe)] // docs:start:as_unsafe - comptime fn as_unsafe(self) -> Option<[Expr]> {} + pub comptime fn as_unsafe(self) -> Option<[Expr]> {} // docs:end:as_unsafe /// Returns `true` if this expression is trailed by a semicolon. @@ -202,19 +202,19 @@ impl Expr { /// ``` #[builtin(expr_has_semicolon)] // docs:start:has_semicolon - comptime fn has_semicolon(self) -> bool {} + pub comptime fn has_semicolon(self) -> bool {} // docs:end:has_semicolon /// Returns `true` if this expression is `break`. #[builtin(expr_is_break)] // docs:start:is_break - comptime fn is_break(self) -> bool {} + pub comptime fn is_break(self) -> bool {} // docs:end:is_break /// Returns `true` if this expression is `continue`. #[builtin(expr_is_continue)] // docs:start:is_continue - comptime fn is_continue(self) -> bool {} + pub comptime fn is_continue(self) -> bool {} // docs:end:is_continue /// Applies a mapping function to this expression and to all of its sub-expressions. @@ -225,7 +225,7 @@ impl Expr { /// For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` /// for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. // docs:start:modify - comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { + pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { // docs:end:modify let result = modify_array(self, f); let result = result.or_else(|| modify_assert(self, f)); @@ -262,7 +262,7 @@ impl Expr { /// Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. // docs:start:quoted - comptime fn quoted(self) -> Quoted { + pub comptime fn quoted(self) -> Quoted { // docs:end:quoted quote { $self } } @@ -278,7 +278,7 @@ impl Expr { /// the current `comptime` function. #[builtin(expr_resolve)] // docs:start:resolve - comptime fn resolve(self, in_function: Option) -> TypedExpr {} + pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} // docs:end:resolve } diff --git a/noir/noir-repo/noir_stdlib/src/meta/format_string.nr b/noir/noir-repo/noir_stdlib/src/meta/format_string.nr index 075a69fdafb..f3c18212599 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/format_string.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/format_string.nr @@ -1,6 +1,6 @@ impl fmtstr { #[builtin(fmtstr_quoted_contents)] // docs:start:quoted_contents - comptime fn quoted_contents(self) -> Quoted {} + pub comptime fn quoted_contents(self) -> Quoted {} // docs:end:quoted_contents } diff --git a/noir/noir-repo/noir_stdlib/src/meta/function_def.nr b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr index cd5f9cc79fb..3c29d57e20c 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/function_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr @@ -1,67 +1,67 @@ impl FunctionDefinition { #[builtin(function_def_add_attribute)] // docs:start:add_attribute - comptime fn add_attribute(self, attribute: str) {} + pub comptime fn add_attribute(self, attribute: str) {} // docs:end:add_attribute #[builtin(function_def_body)] // docs:start:body - comptime fn body(self) -> Expr {} + pub comptime fn body(self) -> Expr {} // docs:end:body #[builtin(function_def_has_named_attribute)] // docs:start:has_named_attribute - comptime fn has_named_attribute(self, name: str) -> bool {} + pub comptime fn has_named_attribute(self, name: str) -> bool {} // docs:end:has_named_attribute #[builtin(function_def_is_unconstrained)] // docs:start:is_unconstrained - comptime fn is_unconstrained(self) -> bool {} + pub comptime fn is_unconstrained(self) -> bool {} // docs:end:is_unconstrained #[builtin(function_def_module)] // docs:start:module - comptime fn module(self) -> Module {} + pub comptime fn module(self) -> Module {} // docs:end:module #[builtin(function_def_name)] // docs:start:name - comptime fn name(self) -> Quoted {} + pub comptime fn name(self) -> Quoted {} // docs:end:name #[builtin(function_def_parameters)] // docs:start:parameters - comptime fn parameters(self) -> [(Quoted, Type)] {} + pub comptime fn parameters(self) -> [(Quoted, Type)] {} // docs:end:parameters #[builtin(function_def_return_type)] // docs:start:return_type - comptime fn return_type(self) -> Type {} + pub comptime fn return_type(self) -> Type {} // docs:end:return_type #[builtin(function_def_set_body)] // docs:start:set_body - comptime fn set_body(self, body: Expr) {} + pub comptime fn set_body(self, body: Expr) {} // docs:end:set_body #[builtin(function_def_set_parameters)] // docs:start:set_parameters - comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} + pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} // docs:end:set_parameters #[builtin(function_def_set_return_type)] // docs:start:set_return_type - comptime fn set_return_type(self, return_type: Type) {} + pub comptime fn set_return_type(self, return_type: Type) {} // docs:end:set_return_type #[builtin(function_def_set_return_public)] // docs:start:set_return_public - comptime fn set_return_public(self, public: bool) {} + pub comptime fn set_return_public(self, public: bool) {} // docs:end:set_return_public #[builtin(function_def_set_unconstrained)] // docs:start:set_unconstrained - comptime fn set_unconstrained(self, value: bool) {} + pub comptime fn set_unconstrained(self, value: bool) {} // docs:end:set_unconstrained } diff --git a/noir/noir-repo/noir_stdlib/src/meta/module.nr b/noir/noir-repo/noir_stdlib/src/meta/module.nr index 1b29301c4ef..e9fac5ded0a 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/module.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/module.nr @@ -1,32 +1,32 @@ impl Module { #[builtin(module_add_item)] // docs:start:add_item - comptime fn add_item(self, item: Quoted) {} + pub comptime fn add_item(self, item: Quoted) {} // docs:end:add_item #[builtin(module_has_named_attribute)] // docs:start:has_named_attribute - comptime fn has_named_attribute(self, name: str) -> bool {} + pub comptime fn has_named_attribute(self, name: str) -> bool {} // docs:end:has_named_attribute #[builtin(module_is_contract)] // docs:start:is_contract - comptime fn is_contract(self) -> bool {} + pub comptime fn is_contract(self) -> bool {} // docs:end:is_contract #[builtin(module_functions)] // docs:start:functions - comptime fn functions(self) -> [FunctionDefinition] {} + pub comptime fn functions(self) -> [FunctionDefinition] {} // docs:end:functions #[builtin(module_structs)] // docs:start:structs - comptime fn structs(self) -> [StructDefinition] {} + pub comptime fn structs(self) -> [StructDefinition] {} // docs:end:structs #[builtin(module_name)] // docs:start:name - comptime fn name(self) -> Quoted {} + pub comptime fn name(self) -> Quoted {} // docs:end:name } diff --git a/noir/noir-repo/noir_stdlib/src/meta/quoted.nr b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr index 6e8d001c57c..cf97107ed68 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/quoted.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr @@ -4,27 +4,27 @@ use crate::option::Option; impl Quoted { #[builtin(quoted_as_expr)] // docs:start:as_expr - comptime fn as_expr(self) -> Option {} + pub comptime fn as_expr(self) -> Option {} // docs:end:as_expr #[builtin(quoted_as_module)] // docs:start:as_module - comptime fn as_module(self) -> Option {} + pub comptime fn as_module(self) -> Option {} // docs:end:as_module #[builtin(quoted_as_trait_constraint)] // docs:start:as_trait_constraint - comptime fn as_trait_constraint(self) -> TraitConstraint {} + pub comptime fn as_trait_constraint(self) -> TraitConstraint {} // docs:end:as_trait_constraint #[builtin(quoted_as_type)] // docs:start:as_type - comptime fn as_type(self) -> Type {} + pub comptime fn as_type(self) -> Type {} // docs:end:as_type #[builtin(quoted_tokens)] // docs:start:tokens - comptime fn tokens(self) -> [Quoted] {} + pub comptime fn tokens(self) -> [Quoted] {} // docs:end:tokens } diff --git a/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr b/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr index b17c0d37613..fe7eabc7007 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr @@ -1,47 +1,47 @@ impl StructDefinition { #[builtin(struct_def_add_attribute)] // docs:start:add_attribute - comptime fn add_attribute(self, attribute: str) {} + pub comptime fn add_attribute(self, attribute: str) {} // docs:end:add_attribute #[builtin(struct_def_add_generic)] // docs:start:add_generic - comptime fn add_generic(self, generic_name: str) -> Type {} + pub comptime fn add_generic(self, generic_name: str) -> Type {} // docs:end:add_generic /// Return a syntactic version of this struct definition as a type. /// For example, `as_type(quote { type Foo { ... } })` would return `Foo` #[builtin(struct_def_as_type)] // docs:start:as_type - comptime fn as_type(self) -> Type {} + pub comptime fn as_type(self) -> Type {} // docs:end:as_type #[builtin(struct_def_has_named_attribute)] // docs:start:has_named_attribute - comptime fn has_named_attribute(self, name: str) -> bool {} + pub comptime fn has_named_attribute(self, name: str) -> bool {} // docs:end:has_named_attribute /// Return each generic on this struct. #[builtin(struct_def_generics)] // docs:start:generics - comptime fn generics(self) -> [Type] {} + pub comptime fn generics(self) -> [Type] {} // docs:end:generics /// Returns (name, type) pairs of each field in this struct. Each type is as-is /// with any generic arguments unchanged. #[builtin(struct_def_fields)] // docs:start:fields - comptime fn fields(self) -> [(Quoted, Type)] {} + pub comptime fn fields(self) -> [(Quoted, Type)] {} // docs:end:fields #[builtin(struct_def_module)] // docs:start:module - comptime fn module(self) -> Module {} + pub comptime fn module(self) -> Module {} // docs:end:module #[builtin(struct_def_name)] // docs:start:name - comptime fn name(self) -> Quoted {} + pub comptime fn name(self) -> Quoted {} // docs:end:name /// Sets the fields of this struct to the given fields list. @@ -50,7 +50,7 @@ impl StructDefinition { /// Each name is expected to be a single identifier. #[builtin(struct_def_set_fields)] // docs:start:set_fields - comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} + pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} // docs:end:set_fields } diff --git a/noir/noir-repo/noir_stdlib/src/meta/trait_def.nr b/noir/noir-repo/noir_stdlib/src/meta/trait_def.nr index 51676efbc34..0c2e7cfa5c1 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/trait_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/trait_def.nr @@ -4,7 +4,7 @@ use crate::cmp::Eq; impl TraitDefinition { #[builtin(trait_def_as_trait_constraint)] // docs:start:as_trait_constraint - comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} + pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} // docs:end:as_trait_constraint } diff --git a/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr b/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr index 6755a5c2031..9db859bf8a0 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr @@ -1,11 +1,11 @@ impl TraitImpl { #[builtin(trait_impl_trait_generic_args)] -// docs:start:trait_generic_args - comptime fn trait_generic_args(self) -> [Type] {} + // docs:start:trait_generic_args + pub comptime fn trait_generic_args(self) -> [Type] {} // docs:end:trait_generic_args #[builtin(trait_impl_methods)] -// docs:start:methods - comptime fn methods(self) -> [FunctionDefinition] {} + // docs:start:methods + pub comptime fn methods(self) -> [FunctionDefinition] {} // docs:end:methods } diff --git a/noir/noir-repo/noir_stdlib/src/meta/typ.nr b/noir/noir-repo/noir_stdlib/src/meta/typ.nr index 5a748c2c823..50f8fa60fb9 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/typ.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/typ.nr @@ -8,58 +8,58 @@ pub comptime fn fresh_type_variable() -> Type {} impl Type { #[builtin(type_as_array)] -// docs:start:as_array - comptime fn as_array(self) -> Option<(Type, Type)> {} + // docs:start:as_array + pub comptime fn as_array(self) -> Option<(Type, Type)> {} // docs:end:as_array #[builtin(type_as_constant)] -// docs:start:as_constant - comptime fn as_constant(self) -> Option {} + // docs:start:as_constant + pub comptime fn as_constant(self) -> Option {} // docs:end:as_constant #[builtin(type_as_integer)] -// docs:start:as_integer - comptime fn as_integer(self) -> Option<(bool, u8)> {} + // docs:start:as_integer + pub comptime fn as_integer(self) -> Option<(bool, u8)> {} // docs:end:as_integer #[builtin(type_as_slice)] -// docs:start:as_slice - comptime fn as_slice(self) -> Option {} + // docs:start:as_slice + pub comptime fn as_slice(self) -> Option {} // docs:end:as_slice #[builtin(type_as_str)] -// docs:start:as_str - comptime fn as_str(self) -> Option {} + // docs:start:as_str + pub comptime fn as_str(self) -> Option {} // docs:end:as_str #[builtin(type_as_struct)] -// docs:start:as_struct - comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} + // docs:start:as_struct + pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} // docs:end:as_struct #[builtin(type_as_tuple)] -// docs:start:as_tuple - comptime fn as_tuple(self) -> Option<[Type]> {} + // docs:start:as_tuple + pub comptime fn as_tuple(self) -> Option<[Type]> {} // docs:end:as_tuple #[builtin(type_get_trait_impl)] -// docs:start:get_trait_impl - comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} + // docs:start:get_trait_impl + pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} // docs:end:get_trait_impl #[builtin(type_implements)] -// docs:start:implements - comptime fn implements(self, constraint: TraitConstraint) -> bool {} + // docs:start:implements + pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} // docs:end:implements #[builtin(type_is_bool)] -// docs:start:is_bool - comptime fn is_bool(self) -> bool {} + // docs:start:is_bool + pub comptime fn is_bool(self) -> bool {} // docs:end:is_bool #[builtin(type_is_field)] // docs:start:is_field - comptime fn is_field(self) -> bool {} + pub comptime fn is_field(self) -> bool {} // docs:end:is_field } diff --git a/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr b/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr index 1d3b073b6da..c69139ea233 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr @@ -5,12 +5,12 @@ impl TypedExpr { /// If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. #[builtin(typed_expr_as_function_definition)] // docs:start:as_function_definition - comptime fn as_function_definition(self) -> Option {} + pub comptime fn as_function_definition(self) -> Option {} // docs:end:as_function_definition /// Returns the type of the expression, if the expression could be resolved without errors. #[builtin(typed_expr_get_type)] // docs:start:get_type - comptime fn get_type(self) -> Option {} + pub comptime fn get_type(self) -> Option {} // docs:end:get_type } diff --git a/noir/noir-repo/noir_stdlib/src/meta/unresolved_type.nr b/noir/noir-repo/noir_stdlib/src/meta/unresolved_type.nr index f53635414cc..c6e6d396546 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/unresolved_type.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/unresolved_type.nr @@ -1,6 +1,6 @@ impl UnresolvedType { #[builtin(unresolved_type_is_field)] // docs:start:is_field - comptime fn is_field(self) -> bool {} + pub comptime fn is_field(self) -> bool {} // docs:end:is_field } diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr index a3d7491eaff..9cb94567d94 100644 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ b/noir/noir-repo/noir_stdlib/src/uint128.nr @@ -98,7 +98,7 @@ impl U128 { ((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z' } - fn decode_ascii(ascii: u8) -> Field { + pub(crate) fn decode_ascii(ascii: u8) -> Field { (if ascii < 58 { ascii - 48 } else { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr index 770660bb2a1..9de8c66b051 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr @@ -4,6 +4,7 @@ fn main(sum: u32) { unsafe { assert(loop(4) == sum); + assert(loop_incl(3) == sum); assert(plain_loop() == sum); } } @@ -16,6 +17,14 @@ unconstrained fn loop(x: u32) -> u32 { sum } +unconstrained fn loop_incl(x: u32) -> u32 { + let mut sum = 0; + for i in 0..=x { + sum = sum + i; + } + sum +} + unconstrained fn plain_loop() -> u32 { let mut sum = 0; for i in 0..4 { diff --git a/noir/noir-repo/test_programs/execution_success/loop/Prover.toml b/noir/noir-repo/test_programs/execution_success/loop/Prover.toml new file mode 100644 index 00000000000..0f44bf96f44 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/loop/Prover.toml @@ -0,0 +1 @@ +six_as_u32 = "6" diff --git a/noir/noir-repo/test_programs/execution_success/loop/src/main.nr b/noir/noir-repo/test_programs/execution_success/loop/src/main.nr new file mode 100644 index 00000000000..8365cf6f801 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/loop/src/main.nr @@ -0,0 +1,23 @@ +// Tests a very simple program. +// +// The features being tested is basic looping. +fn main(six_as_u32: u32) { + assert_eq(loop(4), six_as_u32); + assert_eq(loop_incl(3), six_as_u32); +} + +fn loop(x: u32) -> u32 { + let mut sum = 0; + for i in 0..x { + sum = sum + i; + } + sum +} + +fn loop_incl(x: u32) -> u32 { + let mut sum = 0; + for i in 0..=x { + sum = sum + i; + } + sum +} diff --git a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs index f08c8073a79..0387d35d41f 100644 --- a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs +++ b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs @@ -103,7 +103,7 @@ impl<'a> Visitor for AttributeReferenceFinder<'a> { return; }; - let resolver = StandardPathResolver::new(self.module_id); + let resolver = StandardPathResolver::new(self.module_id, None); let mut usage_tracker = UsageTracker::default(); let Ok(result) = resolver.resolve(self.def_maps, path, &mut usage_tracker, &mut None) else { diff --git a/noir/noir-repo/tooling/nargo_fmt/build.rs b/noir/noir-repo/tooling/nargo_fmt/build.rs index 7d5f07c43bf..4051597c088 100644 --- a/noir/noir-repo/tooling/nargo_fmt/build.rs +++ b/noir/noir-repo/tooling/nargo_fmt/build.rs @@ -12,7 +12,7 @@ fn main() { // is the root of the repository and append the crate path let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") { Ok(dir) => PathBuf::from(dir), - Err(_) => std::env::current_dir().unwrap().join("crates").join("nargo_cli"), + Err(_) => std::env::current_dir().unwrap().join("tooling").join("nargo_fmt"), }; let test_dir = manifest_dir.join("tests"); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs index 7298be641d9..7696c4c5fd4 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs @@ -2,7 +2,9 @@ use std::iter::zip; use noirc_errors::Span; -use noirc_frontend::ast::{ConstrainKind, ConstrainStatement, ForRange, Statement, StatementKind}; +use noirc_frontend::ast::{ + ConstrainKind, ConstrainStatement, ForBounds, ForRange, Statement, StatementKind, +}; use crate::{rewrite, visitor::expr::wrap_exprs}; @@ -67,11 +69,13 @@ impl super::FmtVisitor<'_> { StatementKind::For(for_stmt) => { let identifier = self.slice(for_stmt.identifier.span()); let range = match for_stmt.range { - ForRange::Range(start, end) => format!( - "{}..{}", + ForRange::Range(ForBounds { start, end, inclusive }) => format!( + "{}{}{}", rewrite::sub_expr(self, self.shape(), start), + if inclusive { "..=" } else { ".." }, rewrite::sub_expr(self, self.shape(), end) ), + ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), }; let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/for.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/for.nr index 98dff672bef..748c1fe008d 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/for.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/for.nr @@ -10,6 +10,10 @@ fn for_stmt() { b *= b; } + for k in 0..=C1 { + d *= d; + } + z *= if b == 1 { 1 } else { c }; c *= c; diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/for.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/for.nr index 99b796df820..bf0a497fe65 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/input/for.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/for.nr @@ -12,6 +12,10 @@ fn for_stmt() { b *= b; } + + for k in 0 ..= C1 + { + d *= d; } z *= if b == 1 { 1 } else { c };