diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 7c173fbb900..7244be371af 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -454,9 +454,8 @@ impl Path { self.last_segment().ident } - pub fn first_name(&self) -> &str { - assert!(!self.segments.is_empty()); - &self.segments.first().unwrap().ident.0.contents + pub fn first_name(&self) -> Option<&str> { + self.segments.first().map(|segment| segment.ident.0.contents.as_str()) } pub fn last_name(&self) -> &str { diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index c4e536d2d5b..a68991becb7 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -107,7 +107,7 @@ impl<'context> Elaborator<'context> { pub(super) fn resolve_path(&mut self, mut path: Path) -> PathResolutionResult { let mut module_id = self.module_id(); - if path.kind == PathKind::Plain && path.first_name() == SELF_TYPE_NAME { + if path.kind == PathKind::Plain && path.first_name() == Some(SELF_TYPE_NAME) { if let Some(Type::Struct(struct_type, _)) = &self.self_type { let struct_type = struct_type.borrow(); if path.segments.len() == 1 { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b43ffa264fd..b296c4f1805 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -192,7 +192,7 @@ impl<'context> Elaborator<'context> { // Resolve Self::Foo to an associated type on the current trait or trait impl fn lookup_associated_type_on_self(&self, path: &Path) -> Option { - if path.segments.len() == 2 && path.first_name() == SELF_TYPE_NAME { + if path.segments.len() == 2 && path.first_name() == Some(SELF_TYPE_NAME) { if let Some(trait_id) = self.current_trait { let the_trait = self.interner.get_trait(trait_id); if let Some(typ) = the_trait.get_associated_type(path.last_name()) { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 1dd5eb6ead1..a0fea3aa774 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1221,7 +1221,6 @@ impl Type { | Type::Forall(_, _) | Type::Quoted(_) | Type::Slice(_) - | Type::InfixExpr(_, _, _) | Type::TraitAsType(..) => false, Type::CheckedCast { to, .. } => to.is_valid_for_program_input(), @@ -1241,6 +1240,10 @@ impl Type { .get_fields(generics) .into_iter() .all(|(_, field)| field.is_valid_for_program_input()), + + Type::InfixExpr(lhs, _, rhs) => { + lhs.is_valid_for_program_input() && rhs.is_valid_for_program_input() + } } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9786dacf109..b709436cccc 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -60,6 +60,15 @@ 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 + ) +} + +pub(crate) fn get_program_with_maybe_parser_errors( + src: &str, + allow_parser_errors: bool, +) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -72,7 +81,7 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); - if !has_parser_error(&errors) { + if allow_parser_errors || !has_parser_error(&errors) { let inner_attributes: Vec = program .items .iter() @@ -3645,3 +3654,41 @@ fn use_type_alias_to_generic_concrete_type_in_method_call() { "#; assert_no_errors(src); } + +#[test] +fn allows_struct_with_generic_infix_type_as_main_input_1() { + let src = r#" + struct Foo { + x: [u64; N * 2], + } + + fn main(_x: Foo<18>) {} + "#; + assert_no_errors(src); +} + +#[test] +fn allows_struct_with_generic_infix_type_as_main_input_2() { + let src = r#" + struct Foo { + x: [u64; N * 2], + } + + fn main(_x: Foo<2 * 9>) {} + "#; + assert_no_errors(src); +} + +#[test] +fn allows_struct_with_generic_infix_type_as_main_input_3() { + let src = r#" + struct Foo { + x: [u64; N * 2], + } + + global N = 9; + + fn main(_x: Foo) {} + "#; + assert_no_errors(src); +} diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 42900d094b8..0adf5c90bea 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1,6 +1,6 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::resolution::errors::ResolverError; -use crate::tests::get_program_errors; +use crate::tests::{get_program_errors, get_program_with_maybe_parser_errors}; use super::assert_no_errors; @@ -390,3 +390,19 @@ fn trait_bounds_which_are_dependent_on_generic_types_are_resolved_correctly() { "#; assert_no_errors(src); } + +#[test] +fn does_not_crash_on_as_trait_path_with_empty_path() { + let src = r#" + struct Foo { + x: , + } + + fn main() {} + "#; + + let (_, _, errors) = get_program_with_maybe_parser_errors( + src, true, // allow parser errors + ); + assert!(!errors.is_empty()); +}