diff --git a/aztec_macros/src/utils/hir_utils.rs b/aztec_macros/src/utils/hir_utils.rs index 3801ff1cc7..99b02acd60 100644 --- a/aztec_macros/src/utils/hir_utils.rs +++ b/aztec_macros/src/utils/hir_utils.rs @@ -228,6 +228,7 @@ pub fn inject_global( module_id, file_id, global.attributes.clone(), + false, ); // Add the statement to the scope so its path can be looked up later diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 1831a046f5..0da39edfd8 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -40,7 +40,7 @@ pub enum StatementKind { Break, Continue, /// This statement should be executed at compile-time - Comptime(Box), + Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -486,7 +486,8 @@ impl Pattern { pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, - _ => panic!("only the identifier pattern can return a name"), + Pattern::Mutable(pattern, ..) => pattern.name_ident(), + _ => panic!("Only the Identifier or Mutable patterns can return a name"), } } @@ -685,7 +686,7 @@ impl Display for StatementKind { StatementKind::For(for_loop) => for_loop.fmt(f), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), - StatementKind::Comptime(statement) => write!(f, "comptime {statement}"), + StatementKind::Comptime(statement) => write!(f, "comptime {}", statement.kind), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 42ee76d29f..1ab9c13ea2 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -67,7 +67,7 @@ impl StmtId { HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), HirStatement::Error => StatementKind::Error, HirStatement::Comptime(statement) => { - StatementKind::Comptime(Box::new(statement.to_ast(interner).kind)) + StatementKind::Comptime(Box::new(statement.to_ast(interner))) } }; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 94456f3037..26b7c212a3 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -134,8 +134,11 @@ impl<'a> Interpreter<'a> { /// `exit_function` is called. pub(super) fn enter_function(&mut self) -> (bool, Vec>) { // Drain every scope except the global scope - let scope = self.scopes.drain(1..).collect(); - self.push_scope(); + let mut scope = Vec::new(); + if self.scopes.len() > 1 { + scope = self.scopes.drain(1..).collect(); + self.push_scope(); + } (std::mem::take(&mut self.in_loop), scope) } @@ -160,7 +163,7 @@ impl<'a> Interpreter<'a> { self.scopes.last_mut().unwrap() } - fn define_pattern( + pub(super) fn define_pattern( &mut self, pattern: &HirPattern, typ: &Type, @@ -262,7 +265,7 @@ impl<'a> Interpreter<'a> { Err(InterpreterError::NonComptimeVarReferenced { name, location }) } - fn lookup(&self, ident: &HirIdent) -> IResult { + pub(super) fn lookup(&self, ident: &HirIdent) -> IResult { self.lookup_id(ident.id, ident.location) } @@ -291,7 +294,7 @@ impl<'a> Interpreter<'a> { } /// Evaluate an expression and return the result - fn evaluate(&mut self, id: ExprId) -> IResult { + pub(super) fn evaluate(&mut self, id: ExprId) -> IResult { match self.interner.expression(&id) { HirExpression::Ident(ident) => self.evaluate_ident(ident, id), HirExpression::Literal(literal) => self.evaluate_literal(literal, id), @@ -322,7 +325,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_ident(&mut self, ident: HirIdent, id: ExprId) -> IResult { + pub(super) fn evaluate_ident(&mut self, ident: HirIdent, id: ExprId) -> IResult { let definition = self.interner.definition(ident.id); match &definition.kind { @@ -332,9 +335,15 @@ impl<'a> Interpreter<'a> { } DefinitionKind::Local(_) => self.lookup(&ident), DefinitionKind::Global(global_id) => { - let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); - self.evaluate_let(let_)?; - self.lookup(&ident) + // Don't need to check let_.comptime, we can evaluate non-comptime globals too. + // Avoid resetting the value if it is already known + if let Ok(value) = self.lookup(&ident) { + Ok(value) + } else { + let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); + self.evaluate_let(let_)?; + self.lookup(&ident) + } } DefinitionKind::GenericType(type_variable) => { let value = match &*type_variable.borrow() { @@ -1027,7 +1036,7 @@ impl<'a> Interpreter<'a> { } } - fn evaluate_let(&mut self, let_: HirLetStatement) -> IResult { + pub(super) fn evaluate_let(&mut self, let_: HirLetStatement) -> IResult { let rhs = self.evaluate(let_.expression)?; let location = self.interner.expr_location(&let_.expression); self.define_pattern(&let_.pattern, &let_.r#type, rhs, location)?; diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 148fa56b4c..0c8efae63e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -7,3 +7,4 @@ mod value; pub use errors::InterpreterError; pub use interpreter::Interpreter; +pub use value::Value; diff --git a/compiler/noirc_frontend/src/hir/comptime/scan.rs b/compiler/noirc_frontend/src/hir/comptime/scan.rs index 50c8ccb455..7101a158dd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -14,18 +14,19 @@ use crate::{ hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirConstructorExpression, - HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, + HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMethodCallExpression, }, stmt::HirForStatement, }, macros_api::{HirExpression, HirLiteral, HirStatement}, - node_interner::{ExprId, FuncId, StmtId}, + node_interner::{DefinitionKind, ExprId, FuncId, GlobalId, StmtId}, }; use super::{ errors::{IResult, InterpreterError}, interpreter::Interpreter, + Value, }; #[allow(dead_code)] @@ -48,9 +49,23 @@ impl<'interner> Interpreter<'interner> { Ok(()) } + /// Evaluate this global if it is a comptime global. + /// Otherwise, scan through its expression for any comptime blocks to evaluate. + pub fn scan_global(&mut self, global: GlobalId) -> IResult<()> { + if let Some(let_) = self.interner.get_global_let_statement(global) { + if let_.comptime { + self.evaluate_let(let_)?; + } else { + self.scan_expression(let_.expression)?; + } + } + + Ok(()) + } + fn scan_expression(&mut self, expr: ExprId) -> IResult<()> { match self.interner.expression(&expr) { - HirExpression::Ident(_) => Ok(()), + HirExpression::Ident(ident) => self.scan_ident(ident, expr), HirExpression::Literal(literal) => self.scan_literal(literal), HirExpression::Block(block) => self.scan_block(block), HirExpression::Prefix(prefix) => self.scan_expression(prefix.rhs), @@ -91,6 +106,27 @@ impl<'interner> Interpreter<'interner> { } } + // Identifiers have no code to execute but we may need to inline any values + // of comptime variables into runtime code. + fn scan_ident(&mut self, ident: HirIdent, id: ExprId) -> IResult<()> { + let definition = self.interner.definition(ident.id); + + match &definition.kind { + DefinitionKind::Function(_) => Ok(()), + _ => { + // Opportunistically evaluate this identifier to see if it is compile-time known. + // If so, inline its value. + if let Ok(value) = self.evaluate_ident(ident, id) { + // TODO(#4922): Inlining closures is currently unimplemented + if !matches!(value, Value::Closure(..)) { + self.inline_expression(value, id)?; + } + } + Ok(()) + } + } + } + fn scan_literal(&mut self, literal: HirLiteral) -> IResult<()> { match literal { HirLiteral::Array(elements) | HirLiteral::Slice(elements) => match elements { @@ -210,4 +246,12 @@ impl<'interner> Interpreter<'interner> { self.pop_scope(); Ok(()) } + + fn inline_expression(&mut self, value: Value, expr: ExprId) -> IResult<()> { + let location = self.interner.expr_location(&expr); + let new_expr = value.into_expression(self.interner, location)?; + let new_expr = self.interner.expression(&new_expr); + self.interner.replace_expr(&expr, new_expr); + Ok(()) + } } diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 1a84dae4a8..5a12eb7292 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -76,6 +76,20 @@ fn mutating_arrays() { assert_eq!(result, Value::U8(22)); } +#[test] +fn mutate_in_new_scope() { + let program = "fn main() -> pub u8 { + let mut x = 0; + x += 1; + { + x += 1; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U8(2)); +} + #[test] fn for_loop() { let program = "fn main() -> pub u8 { diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index d98b8a521a..2f6b101e62 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -158,6 +158,13 @@ pub enum CompilationError { InterpreterError(InterpreterError), } +impl CompilationError { + fn is_error(&self) -> bool { + let diagnostic = CustomDiagnostic::from(self); + diagnostic.is_error() + } +} + impl<'a> From<&'a CompilationError> for CustomDiagnostic { fn from(value: &'a CompilationError) -> Self { match value { @@ -404,10 +411,15 @@ impl DefCollector { ); } - resolved_module.errors.extend(context.def_interner.check_for_dependency_cycles()); + let cycle_errors = context.def_interner.check_for_dependency_cycles(); + let cycles_present = !cycle_errors.is_empty(); + resolved_module.errors.extend(cycle_errors); resolved_module.type_check(context); - resolved_module.evaluate_comptime(&mut context.def_interner); + + if !cycles_present { + resolved_module.evaluate_comptime(&mut context.def_interner); + } resolved_module.errors } @@ -503,13 +515,21 @@ impl ResolvedModule { /// Evaluate all `comptime` expressions in this module fn evaluate_comptime(&mut self, interner: &mut NodeInterner) { - let mut interpreter = Interpreter::new(interner); + if self.count_errors() == 0 { + let mut interpreter = Interpreter::new(interner); - for (_file, function) in &self.functions { - // The file returned by the error may be different than the file the - // function is in so only use the error's file id. - if let Err(error) = interpreter.scan_function(*function) { - self.errors.push(error.into_compilation_error_pair()); + for (_file, global) in &self.globals { + if let Err(error) = interpreter.scan_global(*global) { + self.errors.push(error.into_compilation_error_pair()); + } + } + + for (_file, function) in &self.functions { + // The file returned by the error may be different than the file the + // function is in so only use the error's file id. + if let Err(error) = interpreter.scan_function(*function) { + self.errors.push(error.into_compilation_error_pair()); + } } } } @@ -524,4 +544,9 @@ impl ResolvedModule { self.globals.extend(globals.globals); self.errors.extend(globals.errors); } + + /// Counts the number of errors (minus warnings) this program currently has + fn count_errors(&self) -> usize { + self.errors.iter().filter(|(error, _)| error.is_error()).count() + } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index baedb10996..b2ec7dbc81 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -6,7 +6,8 @@ use noirc_errors::Location; use crate::ast::{ FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, + NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItem, TraitItem, + TypeImpl, }; use crate::{ graph::CrateId, @@ -109,6 +110,7 @@ impl<'a> ModCollector<'a> { self.module_id, self.file_id, global.attributes.clone(), + matches!(global.pattern, Pattern::Mutable { .. }), ); // Add the statement to the scope so its path can be looked up later @@ -463,6 +465,7 @@ impl<'a> ModCollector<'a> { trait_id.0.local_id, self.file_id, vec![], + false, ); if let Err((first_def, second_def)) = self.def_collector.def_map.modules diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index d542bd4802..1727471c34 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -86,6 +86,8 @@ pub enum ResolverError { JumpInConstrainedFn { is_break: bool, span: Span }, #[error("break/continue are only allowed within loops")] JumpOutsideLoop { is_break: bool, span: Span }, + #[error("Only `comptime` globals can be mutable")] + MutableGlobal { span: Span }, #[error("Self-referential structs are not supported")] SelfReferentialStruct { span: Span }, #[error("#[inline(tag)] attribute is only allowed on constrained functions")] @@ -346,6 +348,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::MutableGlobal { span } => { + Diagnostic::simple_error( + "Only `comptime` globals may be mutable".into(), + String::new(), + *span, + ) + }, ResolverError::SelfReferentialStruct { span } => { Diagnostic::simple_error( "Self-referential structs are not supported".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index e3b1251955..b13ffe05c2 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1222,15 +1222,18 @@ impl<'a> Resolver<'a> { ) -> HirStatement { self.current_item = Some(DependencyId::Global(global_id)); let expression = self.resolve_expression(let_stmt.expression); - let global_id = self.interner.next_global_id(); let definition = DefinitionKind::Global(global_id); if !self.in_contract && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { - self.push_err(ResolverError::AbiAttributeOusideContract { - span: let_stmt.pattern.span(), - }); + let span = let_stmt.pattern.span(); + self.push_err(ResolverError::AbiAttributeOusideContract { span }); + } + + if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { + let span = let_stmt.pattern.span(); + self.push_err(ResolverError::MutableGlobal { span }); } HirStatement::Let(HirLetStatement { @@ -1238,6 +1241,7 @@ impl<'a> Resolver<'a> { r#type: self.resolve_type(let_stmt.r#type), expression, attributes: let_stmt.attributes, + comptime: let_stmt.comptime, }) } @@ -1251,6 +1255,7 @@ impl<'a> Resolver<'a> { r#type: self.resolve_type(let_stmt.r#type), expression, attributes: let_stmt.attributes, + comptime: let_stmt.comptime, }) } StatementKind::Constrain(constrain_stmt) => { @@ -1322,8 +1327,10 @@ impl<'a> Resolver<'a> { } StatementKind::Error => HirStatement::Error, StatementKind::Comptime(statement) => { - let statement = self.resolve_stmt(*statement, span); - HirStatement::Comptime(self.interner.push_stmt(statement)) + let hir_statement = self.resolve_stmt(statement.kind, statement.span); + let statement_id = self.interner.push_stmt(hir_statement); + self.interner.push_statement_location(statement_id, statement.span, self.file); + HirStatement::Comptime(statement_id) } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 03ebb44fa1..6235fe3848 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -508,6 +508,7 @@ pub mod test { r#type: Type::FieldElement, expression: expr_id, attributes: vec![], + comptime: false, }; let stmt_id = interner.push_stmt(HirStatement::Let(let_stmt)); let expr_id = interner diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 7e22e5ee9c..0b4dbeb300 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -30,6 +30,7 @@ pub struct HirLetStatement { pub r#type: Type, pub expression: ExprId, pub attributes: Vec, + pub comptime: bool, } impl HirLetStatement { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5e4fa3df6c..7ec32613cb 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -653,11 +653,13 @@ impl NodeInterner { let_statement: StmtId, file: FileId, attributes: Vec, + mutable: bool, ) -> GlobalId { let id = GlobalId(self.globals.len()); let location = Location::new(ident.span(), file); let name = ident.to_string(); - let definition_id = self.push_definition(name, false, DefinitionKind::Global(id), location); + let definition_id = + self.push_definition(name, mutable, DefinitionKind::Global(id), location); self.globals.push(GlobalInfo { id, @@ -682,9 +684,13 @@ impl NodeInterner { local_id: LocalModuleId, file: FileId, attributes: Vec, + mutable: bool, ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); - self.push_global(name, local_id, statement, file, attributes) + let span = name.span(); + let id = self.push_global(name, local_id, statement, file, attributes, mutable); + self.push_statement_location(statement, span, file); + id } /// Intern an empty function. diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 5cbab9bde3..4a85f6cb18 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -235,16 +235,27 @@ fn implementation() -> impl NoirParser { fn global_declaration() -> impl NoirParser { let p = attributes::attributes() .then(maybe_comp_time()) + .then(spanned(keyword(Keyword::Mut)).or_not()) .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) .then(ident().map(Pattern::Identifier)); let p = then_commit(p, optional_type_annotation()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, expression()); - p.validate(|((((attributes, comptime), pattern), r#type), expression), span, emit| { - let global_attributes = attributes::validate_secondary_attributes(attributes, span, emit); - LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } - }) + p.validate( + |(((((attributes, comptime), mutable), mut pattern), r#type), expression), span, emit| { + let global_attributes = + attributes::validate_secondary_attributes(attributes, span, emit); + + // Only comptime globals are allowed to be mutable, but we always parse the `mut` + // and throw the error in name resolution. + if let Some((_, mut_span)) = mutable { + let span = mut_span.merge(pattern.span()); + pattern = Pattern::Mutable(Box::new(pattern), span, false); + } + LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } + }, + ) .map(TopLevelStatement::Global) } @@ -532,15 +543,16 @@ where P2: ExprParser + 'a, S: NoirParser + 'a, { - keyword(Keyword::Comptime) - .ignore_then(choice(( - declaration(expr), - for_loop(expr_no_constructors, statement.clone()), - block(statement).map_with_span(|block, span| { - StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) - }), - ))) - .map(|statement| StatementKind::Comptime(Box::new(statement))) + let comptime_statement = choice(( + declaration(expr), + for_loop(expr_no_constructors, statement.clone()), + block(statement).map_with_span(|block, span| { + StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) + }), + )) + .map_with_span(|kind, span| Box::new(Statement { kind, span })); + + keyword(Keyword::Comptime).ignore_then(comptime_statement).map(StatementKind::Comptime) } /// Comptime in an expression position only accepts entire blocks diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index fb60047b23..e017ea9e97 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1284,6 +1284,15 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { } #[test] + fn ban_mutable_globals() { + // Mutable globals are only allowed in a comptime context + let src = r#" + mut global FOO: Field = 0; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); + } + fn deny_inline_attribute_on_unconstrained() { let src = r#" #[inline(never)] diff --git a/test_programs/compile_success_empty/comptime_mut_global/Nargo.toml b/test_programs/compile_success_empty/comptime_mut_global/Nargo.toml new file mode 100644 index 0000000000..c29aa8d83b --- /dev/null +++ b/test_programs/compile_success_empty/comptime_mut_global/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_mut_global" +type = "bin" +authors = [""] +compiler_version = ">=0.28.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/comptime_mut_global/src/main.nr b/test_programs/compile_success_empty/comptime_mut_global/src/main.nr new file mode 100644 index 0000000000..fa739289d3 --- /dev/null +++ b/test_programs/compile_success_empty/comptime_mut_global/src/main.nr @@ -0,0 +1,30 @@ +comptime mut global COUNTER: Field = 0; + +comptime fn get_unique_id() -> Field { + let id = COUNTER; + COUNTER += 1; + id +} + +fn id1() -> Field { + comptime + { + get_unique_id() + } +} + +fn id2() -> Field { + comptime + { + get_unique_id() + } +} + +fn main() { + // Order of comptime evaluation between functions isn't guaranteed + // so we don't know if (id1 == 0 && id2 == 1) or if (id1 == 1 && id2 == 0). + // we only know they are not equal + let id1 = id1(); + let id2 = id2(); + assert(id1 != id2); +} diff --git a/test_programs/noir_test_success/comptime_globals/Nargo.toml b/test_programs/noir_test_success/comptime_globals/Nargo.toml new file mode 100644 index 0000000000..d4f349f537 --- /dev/null +++ b/test_programs/noir_test_success/comptime_globals/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_globals" +type = "bin" +authors = [""] +compiler_version = ">=0.27.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/noir_test_success/comptime_globals/src/main.nr b/test_programs/noir_test_success/comptime_globals/src/main.nr new file mode 100644 index 0000000000..efe9f0742b --- /dev/null +++ b/test_programs/noir_test_success/comptime_globals/src/main.nr @@ -0,0 +1,21 @@ +// Normal globals can be evaluated in a comptime context too, +// but comptime globals can only be evaluated in a comptime context. +comptime global FOO: Field = foo(); + +// Due to this function's mutability and branching, SSA currently fails +// to fold this function into a constant before the assert_constant check +// is evaluated before loop unrolling. +fn foo() -> Field { + let mut three = 3; + if three == 3 { 5 } else { 6 } +} + +#[test] +fn foo_global_constant() { + assert_constant(FOO); +} + +#[test(should_fail)] +fn foo_function_not_constant() { + assert_constant(foo()); +} diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index e41827c94a..8e05fe3f5c 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -103,7 +103,7 @@ impl super::FmtVisitor<'_> { StatementKind::Error => unreachable!(), StatementKind::Break => self.push_rewrite("break;".into(), span), StatementKind::Continue => self.push_rewrite("continue;".into(), span), - StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last), + StatementKind::Comptime(statement) => self.visit_stmt(statement.kind, span, is_last), } } }