diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 60e400e9e07..1683409547e 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -350,7 +350,7 @@ impl<'context> Elaborator<'context> { &mut object, ); - self.resolve_turbofish_generics(&func_id, method_call.generics, span) + self.resolve_function_turbofish_generics(&func_id, method_call.generics, span) } else { None }; @@ -408,12 +408,12 @@ impl<'context> Elaborator<'context> { &mut self, constructor: ConstructorExpression, ) -> (HirExpression, Type) { - let exclude_last_segment = false; + let exclude_last_segment = true; self.check_unsupported_turbofish_usage(&constructor.type_name, exclude_last_segment); let span = constructor.type_name.span(); - let last_segment = constructor.type_name.last_ident(); - let is_self_type = last_segment.is_self_type_name(); + let last_segment = constructor.type_name.last_segment(); + let is_self_type = last_segment.ident.is_self_type_name(); let (r#type, struct_generics) = if let Some(struct_id) = constructor.struct_type { let typ = self.interner.get_struct(struct_id); @@ -430,6 +430,24 @@ impl<'context> Elaborator<'context> { } }; + let struct_generics = if let Some(turbofish_generics) = &last_segment.generics { + if turbofish_generics.len() == struct_generics.len() { + let struct_type = r#type.borrow(); + self.resolve_turbofish_generics(&struct_type.generics, turbofish_generics.clone()) + } else { + self.push_err(TypeCheckError::GenericCountMismatch { + item: format!("struct {}", last_segment.ident), + expected: struct_generics.len(), + found: turbofish_generics.len(), + span: Span::from(last_segment.ident.span().end()..last_segment.span.end()), + }); + + struct_generics + } + } else { + struct_generics + }; + let struct_type = r#type.clone(); let generics = struct_generics.clone(); @@ -444,7 +462,7 @@ impl<'context> Elaborator<'context> { }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(last_segment.span(), self.file); + let reference_location = Location::new(last_segment.ident.span(), self.file); self.interner.add_struct_reference(struct_id, reference_location, is_self_type); (expr, Type::Struct(struct_type, generics)) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 7e75eae7a16..7aab8d1a24c 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -15,7 +15,7 @@ use crate::{ }, macros_api::{HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - Shared, StructType, Type, TypeBindings, + ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -404,7 +404,7 @@ impl<'context> Elaborator<'context> { } /// Resolve generics using the expected kinds of the function we are calling - pub(super) fn resolve_turbofish_generics( + pub(super) fn resolve_function_turbofish_generics( &mut self, func_id: &FuncId, unresolved_turbofish: Option>, @@ -412,20 +412,28 @@ impl<'context> Elaborator<'context> { ) -> Option> { let direct_generics = self.interner.function_meta(func_id).direct_generics.clone(); - unresolved_turbofish.map(|option_inner| { - if option_inner.len() != direct_generics.len() { + unresolved_turbofish.map(|unresolved_turbofish| { + if unresolved_turbofish.len() != direct_generics.len() { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: direct_generics.len(), - actual_count: option_inner.len(), + actual_count: unresolved_turbofish.len(), span, }; self.push_err(type_check_err); } - let generics_with_types = direct_generics.iter().zip(option_inner); - vecmap(generics_with_types, |(generic, unresolved_type)| { - self.resolve_type_inner(unresolved_type, &generic.kind) - }) + self.resolve_turbofish_generics(&direct_generics, unresolved_turbofish) + }) + } + + pub(super) fn resolve_turbofish_generics( + &mut self, + generics: &[ResolvedGeneric], + turbofish_generics: Vec, + ) -> Vec { + let generics_with_types = generics.iter().zip(turbofish_generics); + vecmap(generics_with_types, |(generic, unresolved_type)| { + self.resolve_type_inner(unresolved_type, &generic.kind) }) } @@ -446,7 +454,7 @@ impl<'context> Elaborator<'context> { // and if the turbofish operator was used. let generics = definition_kind.and_then(|definition_kind| match &definition_kind { DefinitionKind::Function(function) => { - self.resolve_turbofish_generics(function, unresolved_turbofish, span) + self.resolve_function_turbofish_generics(function, unresolved_turbofish, span) } _ => None, }); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index de4f1a15eae..f2b83a48022 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2539,25 +2539,52 @@ fn trait_constraint_on_tuple_type() { } #[test] -fn turbofish_in_constructor_unsupported_yet() { +fn turbofish_in_constructor_generics_mismatch() { let src = r#" struct Foo { x: T } fn main() { - let _ = Foo:: { x: 1 }; + let _ = Foo:: { x: 1 }; } "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), )); } +#[test] +fn turbofish_in_constructor() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let x: Field = 0; + let _ = Foo:: { x: x }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i32"); + assert_eq!(expr_typ, "Field"); +} + #[test] fn turbofish_in_middle_of_variable_unsupported_yet() { let src = r#" diff --git a/test_programs/execution_success/slice_regex/Nargo.toml b/test_programs/execution_success/slice_regex/Nargo.toml index 5f2b90d7580..ac95636c74a 100644 --- a/test_programs/execution_success/slice_regex/Nargo.toml +++ b/test_programs/execution_success/slice_regex/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "regex" +name = "slice_regex" type = "bin" authors = [""] compiler_version = ">=0.31.0"