Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: turbofish operator in struct constructor #5607

Merged
merged 16 commits into from
Jul 26, 2024
Merged
28 changes: 23 additions & 5 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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);
Expand All @@ -430,6 +430,24 @@ impl<'context> Elaborator<'context> {
}
};

let struct_generics = if let Some(turbofish_generics) = &last_segment.generics {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
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();

Expand All @@ -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))
Expand Down
28 changes: 18 additions & 10 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -404,28 +404,36 @@ 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<Vec<UnresolvedType>>,
span: Span,
) -> Option<Vec<Type>> {
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<UnresolvedType>,
) -> Vec<Type> {
let generics_with_types = generics.iter().zip(turbofish_generics);
vecmap(generics_with_types, |(generic, unresolved_type)| {
self.resolve_type_inner(unresolved_type, &generic.kind)
})
}

Expand All @@ -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,
});
Expand Down
35 changes: 31 additions & 4 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
x: T
}

fn main() {
let _ = Foo::<i32> { x: 1 };
let _ = Foo::<i32, i64> { 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<T> {
x: T
}

fn main() {
let x: Field = 0;
let _ = Foo::<i32> { 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#"
Expand Down
2 changes: 1 addition & 1 deletion test_programs/execution_success/slice_regex/Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "regex"
name = "slice_regex"
type = "bin"
authors = [""]
compiler_version = ">=0.31.0"
Expand Down
Loading