diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 639df0ef246..fb52a87b4d1 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -16,6 +16,7 @@ use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_evaluator::ssa::{SsaLogging, SsaProgramArtifact}; use noirc_frontend::debug::build_debug_crate_file; +use noirc_frontend::elaborator::{FrontendOptions, UnstableFeature}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::monomorphization::{ @@ -175,6 +176,11 @@ pub struct CompileOptions { /// Used internally to test for non-determinism in the compiler. #[clap(long, hide = true)] pub check_non_determinism: bool, + + /// Unstable features to enable for this current build + #[arg(value_parser = clap::value_parser!(UnstableFeature))] + #[clap(long, short = 'Z', value_delimiter = ',')] + pub unstable_features: Vec, } pub fn parse_expression_width(input: &str) -> Result { @@ -193,6 +199,16 @@ pub fn parse_expression_width(input: &str) -> Result FrontendOptions { + FrontendOptions { + debug_comptime_in_file: self.debug_comptime_in_file.as_deref(), + pedantic_solving: self.pedantic_solving, + enabled_unstable_features: &self.unstable_features, + } + } +} + #[derive(Debug)] pub enum CompileError { MonomorphizationError(MonomorphizationError), @@ -332,12 +348,7 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let diagnostics = CrateDefMap::collect_defs( - crate_id, - context, - options.debug_comptime_in_file.as_deref(), - options.pedantic_solving, - ); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.frontend_options()); let crate_files = context.crate_files(&crate_id); let warnings_and_errors: Vec = diagnostics .into_iter() diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index f9e36f7f3e0..e5231565041 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -33,7 +33,6 @@ strum.workspace = true strum_macros.workspace = true fxhash.workspace = true - [dev-dependencies] base64.workspace = true proptest.workspace = true diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index e3e93dbeb2b..58a467eef1b 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -95,9 +95,8 @@ impl<'context> Elaborator<'context> { self.usage_tracker, self.crate_graph, self.crate_id, - self.debug_comptime_in_file, self.interpreter_call_stack.clone(), - self.pedantic_solving, + self.options, self.elaborate_reasons.clone(), ); @@ -496,7 +495,7 @@ impl<'context> Elaborator<'context> { Some(DependencyId::Function(function)) => Some(function), _ => None, }; - Interpreter::new(self, self.crate_id, current_function, self.pedantic_solving) + Interpreter::new(self, self.crate_id, current_function) } pub(super) fn debug_comptime T>( @@ -504,7 +503,7 @@ impl<'context> Elaborator<'context> { location: Location, mut expr_f: F, ) { - if Some(location.file) == self.debug_comptime_in_file { + if Some(location.file) == self.options.debug_comptime_in_file { let displayed_expr = expr_f(self.interner); let error: CompilationError = InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(); diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 2bb36394f93..2dbbe73b327 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1107,6 +1107,8 @@ impl<'context> Elaborator<'context> { location: Location, ) -> (HirExpression, Type) { let span = location.span; + self.use_unstable_feature(super::UnstableFeature::Enums, span); + let (expression, typ) = self.elaborate_expression(match_expr.expression); let (let_, variable) = self.wrap_in_let(expression, typ); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index b8c4f892af1..7b553b88ac9 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -36,6 +36,7 @@ use crate::{ DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, + parser::{ParserError, ParserErrorReason}, token::SecondaryAttribute, EnumVariant, Shared, Type, TypeVariable, }; @@ -52,6 +53,7 @@ mod comptime; mod enums; mod expressions; mod lints; +mod options; mod path_resolution; mod patterns; mod scope; @@ -63,7 +65,9 @@ mod unquote; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Located, Location}; +use noirc_errors::{Located, Location, Span}; +pub(crate) use options::ElaboratorOptions; +pub use options::{FrontendOptions, UnstableFeature}; pub use path_resolution::Turbofish; use path_resolution::{PathResolution, PathResolutionItem}; use types::bind_ordered_generics; @@ -179,9 +183,6 @@ pub struct Elaborator<'context> { crate_id: CrateId, - /// The scope of --debug-comptime, or None if unset - debug_comptime_in_file: Option, - /// These are the globals that have yet to be elaborated. /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). @@ -195,8 +196,8 @@ pub struct Elaborator<'context> { /// that comptime value and any visibility errors were already reported. silence_field_visibility_errors: usize, - /// Use pedantic ACVM solving - pedantic_solving: bool, + /// Options from the nargo cli + options: ElaboratorOptions<'context>, /// Sometimes items are elaborated because a function attribute ran and generated items. /// The Elaborator keeps track of these reasons so that when an error is produced it will @@ -211,6 +212,7 @@ pub enum ElaborateReason { /// Evaluating `Module::add_item` AddingItemToModule, } + impl ElaborateReason { fn to_macro_error(self, error: CompilationError, location: Location) -> ComptimeError { match self { @@ -250,9 +252,8 @@ impl<'context> Elaborator<'context> { usage_tracker: &'context mut UsageTracker, crate_graph: &'context CrateGraph, crate_id: CrateId, - debug_comptime_in_file: Option, interpreter_call_stack: im::Vector, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, elaborate_reasons: im::Vector<(ElaborateReason, Location)>, ) -> Self { Self { @@ -275,13 +276,12 @@ impl<'context> Elaborator<'context> { trait_bounds: Vec::new(), function_context: vec![FunctionContext::default()], current_trait_impl: None, - debug_comptime_in_file, unresolved_globals: BTreeMap::new(), current_trait: None, interpreter_call_stack, in_comptime_context: false, silence_field_visibility_errors: 0, - pedantic_solving, + options, elaborate_reasons, } } @@ -289,8 +289,7 @@ impl<'context> Elaborator<'context> { pub fn from_context( context: &'context mut Context, crate_id: CrateId, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { Self::new( &mut context.def_interner, @@ -298,9 +297,8 @@ impl<'context> Elaborator<'context> { &mut context.usage_tracker, &context.crate_graph, crate_id, - debug_comptime_in_file, im::Vector::new(), - pedantic_solving, + options, im::Vector::new(), ) } @@ -309,28 +307,18 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self( - context, - crate_id, - items, - debug_comptime_in_file, - pedantic_solving, - ) - .errors + Self::elaborate_and_return_self(context, crate_id, items, options).errors } pub fn elaborate_and_return_self( context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { - let mut this = - Self::from_context(context, crate_id, debug_comptime_in_file, pedantic_solving); + let mut this = Self::from_context(context, crate_id, options); this.elaborate_items(items); this.check_and_pop_function_context(); this @@ -414,6 +402,11 @@ impl<'context> Elaborator<'context> { self.push_errors(self.interner.check_for_dependency_cycles()); } + /// True if we should use pedantic ACVM solving + pub fn pedantic_solving(&self) -> bool { + self.options.pedantic_solving + } + /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated /// back to the previous length. fn recover_generics(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { @@ -1941,8 +1934,12 @@ impl<'context> Elaborator<'context> { self.generics.clear(); let datatype = self.interner.get_type(*type_id); - let generics = datatype.borrow().generic_types(); - self.add_existing_generics(&typ.enum_def.generics, &datatype.borrow().generics); + let datatype_ref = datatype.borrow(); + let generics = datatype_ref.generic_types(); + self.add_existing_generics(&typ.enum_def.generics, &datatype_ref.generics); + + self.use_unstable_feature(UnstableFeature::Enums, datatype_ref.name.span()); + drop(datatype_ref); let self_type = Type::DataType(datatype.clone(), generics); let self_type_id = self.interner.push_quoted_type(self_type.clone()); @@ -2227,4 +2224,14 @@ impl<'context> Elaborator<'context> { _ => true, }) } + + /// Register a use of the given unstable feature. Errors if the feature has not + /// been explicitly enabled in this package. + pub fn use_unstable_feature(&mut self, feature: UnstableFeature, span: Span) { + if !self.options.enabled_unstable_features.contains(&feature) { + let reason = ParserErrorReason::ExperimentalFeature(feature); + let location = Location::new(span, self.file); + self.push_err(ParserError::with_reason(reason, location), self.file); + } + } } diff --git a/compiler/noirc_frontend/src/elaborator/options.rs b/compiler/noirc_frontend/src/elaborator/options.rs new file mode 100644 index 00000000000..285d1ddfe59 --- /dev/null +++ b/compiler/noirc_frontend/src/elaborator/options.rs @@ -0,0 +1,62 @@ +use std::str::FromStr; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum UnstableFeature { + Enums, + ArrayOwnership, +} + +impl std::fmt::Display for UnstableFeature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Enums => write!(f, "enums"), + Self::ArrayOwnership => write!(f, "array-ownership"), + } + } +} + +impl FromStr for UnstableFeature { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "enums" => Ok(Self::Enums), + "array-ownership" => Ok(Self::ArrayOwnership), + other => Err(format!("Unknown unstable feature '{other}'")), + } + } +} + +/// Generic options struct meant to resolve to ElaboratorOptions below when +/// we can resolve a file path to a file id later. This generic struct is used +/// so that FrontendOptions doesn't need to duplicate fields and methods with ElaboratorOptions. +#[derive(Copy, Clone)] +pub struct GenericOptions<'a, T> { + /// The scope of --debug-comptime, or None if unset + pub debug_comptime_in_file: Option, + + /// Use pedantic ACVM solving + pub pedantic_solving: bool, + + /// Unstable compiler features that were explicitly enabled. Any unstable features + /// that are not in this list result in an error when used. + pub enabled_unstable_features: &'a [UnstableFeature], +} + +/// Options from nargo_cli that need to be passed down to the elaborator +pub(crate) type ElaboratorOptions<'a> = GenericOptions<'a, fm::FileId>; + +/// This is the unresolved version of `ElaboratorOptions` +/// CLI options that need to be passed to the compiler frontend (the elaborator). +pub type FrontendOptions<'a> = GenericOptions<'a, &'a str>; + +impl<'a, T> GenericOptions<'a, T> { + /// A sane default of frontend options for running tests + pub fn test_default() -> GenericOptions<'static, T> { + GenericOptions { + debug_comptime_in_file: None, + pedantic_solving: true, + enabled_unstable_features: &[UnstableFeature::Enums], + } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index c9f608c2484..67f170c1600 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -67,9 +67,6 @@ pub struct Interpreter<'local, 'interner> { /// Stateful bigint calculator. bigint_solver: BigIntSolverWithId, - - /// Use pedantic ACVM solving - pedantic_solving: bool, } #[allow(unused)] @@ -78,8 +75,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { elaborator: &'local mut Elaborator<'interner>, crate_id: CrateId, current_function: Option, - pedantic_solving: bool, ) -> Self { + let pedantic_solving = elaborator.pedantic_solving(); let bigint_solver = BigIntSolverWithId::with_pedantic_solving(pedantic_solving); Self { elaborator, @@ -88,7 +85,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { bound_generics: Vec::new(), in_loop: false, bigint_solver, - pedantic_solving, } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 0221280ae1b..e880dd4b1c8 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -40,7 +40,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { arguments, return_type, location, - self.pedantic_solving, + self.elaborator.pedantic_solving(), ) } } diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 9cf12a1cca7..6d384860289 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -10,7 +10,7 @@ use noirc_errors::Location; use super::errors::InterpreterError; use super::value::Value; use super::Interpreter; -use crate::elaborator::Elaborator; +use crate::elaborator::{Elaborator, ElaboratorOptions}; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; @@ -60,13 +60,11 @@ pub(crate) fn with_interpreter( let main = context.get_main_function(&krate).expect("Expected 'main' function"); - let pedantic_solving = true; let mut elaborator = Elaborator::elaborate_and_return_self( &mut context, krate, collector.items, - None, - pedantic_solving, + ElaboratorOptions::test_default(), ); let errors = elaborator.errors.clone(); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index e409b37228a..ea2ac1123d7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -26,6 +26,7 @@ use crate::ast::{ UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, }; +use crate::elaborator::FrontendOptions; use crate::parser::{ParserError, SortedModule}; use noirc_errors::{CustomDiagnostic, Location, Span}; @@ -287,8 +288,7 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, + options: FrontendOptions, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; let crate_id = def_map.krate; @@ -301,12 +301,7 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs( - dep.crate_id, - context, - debug_comptime_in_file, - pedantic_solving, - )); + errors.extend(CrateDefMap::collect_defs(dep.crate_id, context, options)); let dep_def_map = context.def_map(&dep.crate_id).expect("ice: def map was just created"); @@ -470,21 +465,22 @@ impl DefCollector { } } - let debug_comptime_in_file = debug_comptime_in_file.and_then(|debug_comptime_in_file| { - let file = context.file_manager.find_by_path_suffix(debug_comptime_in_file); + let debug_comptime_in_file = options.debug_comptime_in_file.and_then(|file_suffix| { + let file = context.file_manager.find_by_path_suffix(file_suffix); file.unwrap_or_else(|error| { errors.push((CompilationError::DebugComptimeScopeNotFound(error), root_file_id)); None }) }); - let mut more_errors = Elaborator::elaborate( - context, - crate_id, - def_collector.items, + let cli_options = crate::elaborator::ElaboratorOptions { debug_comptime_in_file, - pedantic_solving, - ); + pedantic_solving: options.pedantic_solving, + enabled_unstable_features: options.enabled_unstable_features, + }; + + let mut more_errors = + Elaborator::elaborate(context, crate_id, def_collector.items, cli_options); errors.append(&mut more_errors); diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index cb48c2b6306..0dff7982489 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -1,3 +1,4 @@ +use crate::elaborator::FrontendOptions; use crate::graph::{CrateGraph, CrateId}; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; @@ -77,8 +78,7 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, + options: FrontendOptions, ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. @@ -120,8 +120,7 @@ impl CrateDefMap { context, ast, root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); errors.extend( diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 68f2c322993..99b1f975e4a 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -5,6 +5,7 @@ use crate::token::TokenKind; use small_ord_set::SmallOrdSet; use thiserror::Error; +use crate::elaborator::UnstableFeature; use iter_extended::vecmap; use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; @@ -75,8 +76,8 @@ pub enum ParserErrorReason { TraitImplVisibilityIgnored, #[error("comptime keyword is deprecated")] ComptimeDeprecated, - #[error("{0} are experimental and aren't fully supported yet")] - ExperimentalFeature(&'static str), + #[error("This requires the unstable feature '{0}' which is not enabled")] + ExperimentalFeature(UnstableFeature), #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] @@ -272,8 +273,11 @@ impl<'a> From<&'a ParserError> for Diagnostic { ), error.span(), ), - ParserErrorReason::ExperimentalFeature(_) => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span()) + ParserErrorReason::ExperimentalFeature(feature) => { + let secondary = format!( + "Pass -Z{feature} to nargo to enable this feature at your own risk." + ); + Diagnostic::simple_error(reason.to_string(), secondary, error.span()) } ParserErrorReason::TraitVisibilityIgnored => { Diagnostic::simple_warning(reason.to_string(), "".into(), error.span()) diff --git a/compiler/noirc_frontend/src/parser/parser/enums.rs b/compiler/noirc_frontend/src/parser/parser/enums.rs index e7ad09fcfb2..d051e392caa 100644 --- a/compiler/noirc_frontend/src/parser/parser/enums.rs +++ b/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -23,8 +23,6 @@ impl<'a> Parser<'a> { ) -> NoirEnumeration { let attributes = self.validate_secondary_attributes(attributes); - self.push_error(ParserErrorReason::ExperimentalFeature("Enums"), start_location); - let Some(name) = self.eat_ident() else { self.expected_identifier(); return self.empty_enum( @@ -218,7 +216,7 @@ mod tests { fn parse_unclosed_enum() { let src = "enum Foo {"; let (module, errors) = parse_program_with_dummy_file(src); - assert_eq!(errors.len(), 2); + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Enum(noir_enum) = &item.kind else { @@ -256,7 +254,8 @@ mod tests { assert_eq!("Foo", noir_enum.name.to_string()); assert_eq!(noir_enum.variants.len(), 1); - let error = &errors[1]; + assert_eq!(errors.len(), 1); + let error = &errors[0]; assert_eq!(error.to_string(), "Expected an identifier but found '42'"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index a0b16f90366..b178d2cd380 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -505,7 +505,6 @@ impl<'a> Parser<'a> { /// MatchExpression = 'match' ExpressionExceptConstructor '{' MatchRule* '}' pub(super) fn parse_match_expr(&mut self) -> Option { - let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Match) { return None; } @@ -520,10 +519,6 @@ impl<'a> Parser<'a> { Self::parse_match_rule, ); - self.push_error( - ParserErrorReason::ExperimentalFeature("Match expressions"), - start_location, - ); Some(ExpressionKind::Match(Box::new(MatchExpression { expression, rules }))) } diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index c15d8317a3c..c7e29c5dc61 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -295,8 +295,6 @@ impl<'a> Parser<'a> { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("loops"), start_location); - let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { @@ -321,8 +319,6 @@ impl<'a> Parser<'a> { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("while loops"), start_location); - let condition = self.parse_expression_except_constructor_or_error(); let block_start_location = self.current_token_location; diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index beaf205684a..7b1ffabd4e9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -17,6 +17,7 @@ mod visibility; // A test harness will allow for more expressive and readable tests use std::collections::BTreeMap; +use crate::elaborator::{FrontendOptions, UnstableFeature}; use fm::FileId; use iter_extended::vecmap; @@ -60,9 +61,18 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F } pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { - get_program_with_maybe_parser_errors( - src, false, // allow parser errors - ) + let allow_parser_errors = false; + get_program_with_maybe_parser_errors(src, allow_parser_errors, FrontendOptions::test_default()) +} + +pub(crate) fn get_program_using_features( + src: &str, + features: &[UnstableFeature], +) -> (ParsedModule, Context<'static, 'static>, Vec<(CompilationError, FileId)>) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + get_program_with_maybe_parser_errors(src, allow_parser_errors, options) } /// Compile a program. @@ -71,7 +81,8 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation pub(crate) fn get_program_with_maybe_parser_errors( src: &str, allow_parser_errors: bool, -) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { + options: FrontendOptions, +) -> (ParsedModule, Context<'static, 'static>, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -116,17 +127,13 @@ pub(crate) fn get_program_with_maybe_parser_errors( extern_prelude: BTreeMap::new(), }; - let debug_comptime_in_file = None; - let pedantic_solving = true; - // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect_crate_and_dependencies( def_map, &mut context, program.clone().into_sorted(), root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); } (program, context, errors) @@ -4424,3 +4431,47 @@ fn errors_if_while_body_type_is_not_unit() { panic!("Expected a TypeMismatch error"); }; } + +#[test] +fn errors_on_unspecified_unstable_enum() { + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + enum Foo { Bar } + + fn main() { + let _x = Foo::Bar; + } + "#; + + let no_features = &[]; + let errors = get_program_using_features(src, no_features).2; + assert_eq!(errors.len(), 1); + + let CompilationError::ParseError(error) = &errors[0].0 else { + panic!("Expected a ParseError experimental feature error"); + }; + + assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); +} + +#[test] +fn errors_on_unspecified_unstable_match() { + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + fn main() { + match 3 { + _ => (), + } + } + "#; + + let no_features = &[]; + let errors = get_program_using_features(src, no_features).2; + assert_eq!(errors.len(), 1); + + let CompilationError::ParseError(error) = &errors[0].0 else { + panic!("Expected a ParseError experimental feature error"); + }; + + assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); +} diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 7f252b556c2..78cda330cf1 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1,3 +1,5 @@ +use crate::elaborator::FrontendOptions; + use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::hir::resolution::errors::ResolverError; @@ -650,9 +652,9 @@ fn does_not_crash_on_as_trait_path_with_empty_path() { fn main() {} "#; - let (_, _, errors) = get_program_with_maybe_parser_errors( - src, true, // allow parser errors - ); + let allow_parser_errors = true; + let options = FrontendOptions::test_default(); + let (_, _, errors) = get_program_with_maybe_parser_errors(src, allow_parser_errors, options); assert!(!errors.is_empty()); } diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index afc6994823b..904e404d7c5 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -198,6 +198,9 @@ fn test_{test_name}(force_brillig: ForceBrillig, inliner_aggressiveness: Inliner // Allow more bytecode in exchange to catch illegal states. nargo.arg("--enable-brillig-debug-assertions"); + // Enable enums as an unstable feature + nargo.arg("-Zenums"); + if force_brillig.0 {{ nargo.arg("--force-brillig");