Skip to content

Commit

Permalink
chore: address some frontend tests TODOs (#7554)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Feb 28, 2025
1 parent ca21820 commit 3239a4a
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 119 deletions.
7 changes: 7 additions & 0 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,13 @@ impl FunctionReturnType {
FunctionReturnType::Ty(typ) => Cow::Borrowed(typ),
}
}

pub fn location(&self) -> Location {
match self {
FunctionReturnType::Default(location) => *location,
FunctionReturnType::Ty(typ) => typ.location,
}
}
}

impl Display for FunctionReturnType {
Expand Down
8 changes: 6 additions & 2 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,9 +1264,13 @@ impl<'context> Elaborator<'context> {
self.check_parent_traits_are_implemented(&trait_impl);
self.remove_trait_impl_assumed_trait_implementations(trait_impl.impl_id);

for (module, function, _) in &trait_impl.methods.functions {
for (module, function, noir_function) in &trait_impl.methods.functions {
self.local_module = *module;
let errors = check_trait_impl_method_matches_declaration(self.interner, *function);
let errors = check_trait_impl_method_matches_declaration(
self.interner,
*function,
noir_function,
);
self.push_errors(errors.into_iter().map(|error| error.into()));
}

Expand Down
13 changes: 8 additions & 5 deletions compiler/noirc_frontend/src/elaborator/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,19 @@ impl Elaborator<'_> {
let type_contains_unspecified = let_stmt.r#type.contains_unspecified();
let annotated_type = self.resolve_inferred_type(let_stmt.r#type);

let pattern_location = let_stmt.pattern.location();
let expr_location = let_stmt.expression.location;
let (expression, expr_type) =
self.elaborate_expression_with_target_type(let_stmt.expression, Some(&annotated_type));

// Require the top-level of a global's type to be fully-specified
if type_contains_unspecified && global_id.is_some() {
let expected_type = annotated_type.clone();
let error =
ResolverError::UnspecifiedGlobalType { location: expr_location, expected_type };
let error = ResolverError::UnspecifiedGlobalType {
pattern_location,
expr_location,
expected_type,
};
self.push_err(error);
}

Expand Down Expand Up @@ -202,19 +206,18 @@ impl Elaborator<'_> {
);

// Check that start range and end range have the same types
let range_location = start_location.merge(end_location);
self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch {
expected_typ: start_range_type.to_string(),
expr_typ: end_range_type.to_string(),
expr_location: range_location,
expr_location: end_location,
});

let expected_type = self.polymorphic_integer();

self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed {
typ: start_range_type.clone(),
place: "for loop",
location: range_location,
location: start_location,
});

self.interner.push_definition_type(identifier.id, start_range_type);
Expand Down
15 changes: 13 additions & 2 deletions compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ impl Elaborator<'_> {
pub(crate) fn check_trait_impl_method_matches_declaration(
interner: &mut NodeInterner,
function: FuncId,
noir_function: &NoirFunction,
) -> Vec<TypeCheckError> {
let meta = interner.function_meta(&function);
let modifiers = interner.function_modifiers(&function);
Expand Down Expand Up @@ -349,6 +350,8 @@ pub(crate) fn check_trait_impl_method_matches_declaration(
definition_type,
method_name,
&meta.parameters,
&meta.return_type,
noir_function,
meta.name.location,
&trait_info.name.0.contents,
&mut errors,
Expand All @@ -358,11 +361,14 @@ pub(crate) fn check_trait_impl_method_matches_declaration(
errors
}

#[allow(clippy::too_many_arguments)]
fn check_function_type_matches_expected_type(
expected: &Type,
actual: &Type,
method_name: &str,
actual_parameters: &Parameters,
actual_return_type: &FunctionReturnType,
noir_function: &NoirFunction,
location: Location,
trait_name: &str,
errors: &mut Vec<TypeCheckError>,
Expand All @@ -381,11 +387,16 @@ fn check_function_type_matches_expected_type(
if params_a.len() == params_b.len() {
for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() {
if a.try_unify(b, &mut bindings).is_err() {
let parameter_location = noir_function.def.parameters.get(i);
let parameter_location = parameter_location.map(|param| param.typ.location);
let parameter_location =
parameter_location.unwrap_or_else(|| actual_parameters.0[i].0.location());

errors.push(TypeCheckError::TraitMethodParameterTypeMismatch {
method_name: method_name.to_string(),
expected_typ: a.to_string(),
actual_typ: b.to_string(),
parameter_location: actual_parameters.0[i].0.location(),
parameter_location,
parameter_index: i + 1,
});
}
Expand All @@ -395,7 +406,7 @@ fn check_function_type_matches_expected_type(
errors.push(TypeCheckError::TypeMismatch {
expected_typ: ret_a.to_string(),
expr_typ: ret_b.to_string(),
expr_location: location,
expr_location: actual_return_type.location(),
});
}
} else {
Expand Down
12 changes: 6 additions & 6 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ impl Elaborator<'_> {

if !kind.unifies(&resolved_type.kind()) {
let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch {
expected_kind: kind.to_string(),
expr_kind: resolved_type.kind().to_string(),
expected_kind: kind.clone(),
expr_kind: resolved_type.kind(),
expr_location: location,
});
self.push_err(expected_typ_err);
Expand Down Expand Up @@ -523,8 +523,8 @@ impl Elaborator<'_> {
(Type::Constant(lhs, lhs_kind), Type::Constant(rhs, rhs_kind)) => {
if !lhs_kind.unifies(&rhs_kind) {
self.push_err(TypeCheckError::TypeKindMismatch {
expected_kind: lhs_kind.to_string(),
expr_kind: rhs_kind.to_string(),
expected_kind: lhs_kind,
expr_kind: rhs_kind,
expr_location: location,
});
return Type::Error;
Expand Down Expand Up @@ -557,8 +557,8 @@ impl Elaborator<'_> {
fn check_kind(&mut self, typ: Type, expected_kind: &Kind, location: Location) -> Type {
if !typ.kind().unifies(expected_kind) {
self.push_err(TypeCheckError::TypeKindMismatch {
expected_kind: expected_kind.to_string(),
expr_kind: typ.kind().to_string(),
expected_kind: expected_kind.clone(),
expr_kind: typ.kind(),
expr_location: location,
});
return Type::Error;
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/hir/def_collector/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub enum DefCollectorErrorKind {
impl DefCollectorErrorKind {
pub fn location(&self) -> Location {
match self {
DefCollectorErrorKind::Duplicate { first_def: ident, .. }
DefCollectorErrorKind::Duplicate { second_def: ident, .. }
| DefCollectorErrorKind::UnresolvedModuleDecl { mod_name: ident, .. }
| DefCollectorErrorKind::CannotReexportItemWithLessVisibility {
item_name: ident,
Expand Down Expand Up @@ -160,10 +160,10 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic {
let second_location = second_def.0.location();
let mut diag = Diagnostic::simple_error(
primary_message,
format!("First {} found here", &typ),
first_location,
format!("Second {} found here", &typ),
second_location,
);
diag.add_secondary(format!("Second {} found here", &typ), second_location);
diag.add_secondary(format!("First {} found here", &typ), first_location);
diag
}
}
Expand Down
34 changes: 20 additions & 14 deletions compiler/noirc_frontend/src/hir/resolution/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ pub enum ResolverError {
#[error("Only `comptime` globals can be mutable")]
MutableGlobal { location: Location },
#[error("Globals must have a specified type")]
UnspecifiedGlobalType { location: Location, expected_type: Type },
UnspecifiedGlobalType {
pattern_location: Location,
expr_location: Location,
expected_type: Type,
},
#[error("Global failed to evaluate")]
UnevaluatedGlobalType { location: Location },
#[error("Globals used in a type position must be non-negative")]
Expand Down Expand Up @@ -168,7 +172,7 @@ pub enum ResolverError {
AttributeFunctionIsNotAPath { function: String, location: Location },
#[error("Attribute function `{name}` is not in scope")]
AttributeFunctionNotInScope { name: String, location: Location },
#[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}")]
#[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}`")]
TraitNotImplemented {
impl_trait: String,
missing_trait: String,
Expand Down Expand Up @@ -197,7 +201,7 @@ pub enum ResolverError {
impl ResolverError {
pub fn location(&self) -> Location {
match self {
ResolverError::DuplicateDefinition { first_location: location, .. }
ResolverError::DuplicateDefinition { second_location: location, .. }
| ResolverError::UnconditionalRecursion { location, .. }
| ResolverError::PathIsNotIdent { location }
| ResolverError::Expected { location, .. }
Expand Down Expand Up @@ -233,7 +237,7 @@ impl ResolverError {
| ResolverError::WhileInConstrainedFn { location }
| ResolverError::JumpOutsideLoop { location, .. }
| ResolverError::MutableGlobal { location }
| ResolverError::UnspecifiedGlobalType { location, .. }
| ResolverError::UnspecifiedGlobalType { pattern_location: location, .. }
| ResolverError::UnevaluatedGlobalType { location }
| ResolverError::NegativeGlobalType { location, .. }
| ResolverError::NonIntegralGlobalType { location, .. }
Expand Down Expand Up @@ -292,10 +296,10 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
ResolverError::DuplicateDefinition { name, first_location, second_location} => {
let mut diag = Diagnostic::simple_error(
format!("duplicate definitions of {name} found"),
"first definition found here".to_string(),
*first_location,
"second definition found here".to_string(),
*second_location,
);
diag.add_secondary("second definition found here".to_string(), *second_location);
diag.add_secondary("first definition found here".to_string(), *first_location);
diag
}
ResolverError::UnusedVariable { ident } => {
Expand Down Expand Up @@ -573,12 +577,14 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
*location,
)
},
ResolverError::UnspecifiedGlobalType { location, expected_type } => {
Diagnostic::simple_error(
ResolverError::UnspecifiedGlobalType { pattern_location, expr_location, expected_type } => {
let mut diagnostic = Diagnostic::simple_error(
"Globals must have a specified type".to_string(),
format!("Inferred type is `{expected_type}`"),
*location,
)
String::new(),
*pattern_location,
);
diagnostic.add_secondary(format!("Inferred type is `{expected_type}`"), *expr_location);
diagnostic
},
ResolverError::UnevaluatedGlobalType { location } => {
Diagnostic::simple_error(
Expand Down Expand Up @@ -763,9 +769,9 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, location, missing_trait_location} => {
let mut diagnostic = Diagnostic::simple_error(
format!("The trait bound `{typ}: {the_trait}` is not satisfied"),
format!("The trait `{the_trait}` is not implemented for `{typ}")
format!("The trait `{the_trait}` is not implemented for `{typ}`")
, *location);
diagnostic.add_secondary(format!("required by this bound in `{impl_trait}"), *missing_trait_location);
diagnostic.add_secondary(format!("required by this bound in `{impl_trait}`"), *missing_trait_location);
diagnostic
},
ResolverError::LoopNotYetSupported { location } => {
Expand Down
38 changes: 32 additions & 6 deletions compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub enum TypeCheckError {
#[error("Expected type {expected} is not the same as {actual}")]
TypeMismatchWithSource { expected: Type, actual: Type, location: Location, source: Source },
#[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")]
TypeKindMismatch { expected_kind: String, expr_kind: String, expr_location: Location },
TypeKindMismatch { expected_kind: Kind, expr_kind: Kind, expr_location: Location },
#[error("Evaluating {to} resulted in {to_value}, but {from_value} was expected")]
TypeCanonicalizationMismatch {
to: Type,
Expand Down Expand Up @@ -369,11 +369,37 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic {
)
}
TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_location } => {
Diagnostic::simple_error(
format!("Expected kind {expected_kind}, found kind {expr_kind}"),
String::new(),
*expr_location,
)
// Try to improve the error message for some kind combinations
match (expected_kind, expr_kind) {
(Kind::Normal, Kind::Numeric(_)) => {
Diagnostic::simple_error(
"Expected type, found numeric generic".into(),
"not a type".into(),
*expr_location,
)
}
(Kind::Numeric(typ), Kind::Normal) => {
Diagnostic::simple_error(
"Type provided when a numeric generic was expected".into(),
format!("the numeric generic is not of type `{typ}`"),
*expr_location,
)
}
(Kind::Numeric(expected_type), Kind::Numeric(found_type)) => {
Diagnostic::simple_error(
format!("The numeric generic is not of type `{expected_type}`"),
format!("expected `{expected_type}`, found `{found_type}`"),
*expr_location,
)
}
_ => {
Diagnostic::simple_error(
format!("Expected kind {expected_kind}, found kind {expr_kind}"),
String::new(),
*expr_location,
)
}
}
}
TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, location } => {
Diagnostic::simple_error(
Expand Down
12 changes: 6 additions & 6 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,8 +858,8 @@ impl TypeVariable {
) -> Result<(), TypeCheckError> {
if !binding.kind().unifies(kind) {
return Err(TypeCheckError::TypeKindMismatch {
expected_kind: format!("{}", kind),
expr_kind: format!("{}", binding.kind()),
expected_kind: kind.clone(),
expr_kind: binding.kind(),
expr_location: location,
});
}
Expand Down Expand Up @@ -2144,8 +2144,8 @@ impl Type {
kind.ensure_value_fits(x, location)
} else {
Err(TypeCheckError::TypeKindMismatch {
expected_kind: format!("{}", constant_kind),
expr_kind: format!("{}", kind),
expected_kind: constant_kind,
expr_kind: kind.clone(),
expr_location: location,
})
}
Expand All @@ -2166,8 +2166,8 @@ impl Type {
op.function(lhs_value, rhs_value, &infix_kind, location)
} else {
Err(TypeCheckError::TypeKindMismatch {
expected_kind: format!("{}", kind),
expr_kind: format!("{}", infix_kind),
expected_kind: kind.clone(),
expr_kind: infix_kind,
expr_location: location,
})
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ impl NodeInterner {
id: type_id,
name: unresolved_trait.trait_def.name.clone(),
crate_id: unresolved_trait.crate_id,
location: unresolved_trait.trait_def.location,
location: unresolved_trait.trait_def.name.location(),
generics,
visibility: ItemVisibility::Private,
self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal),
Expand Down
22 changes: 18 additions & 4 deletions compiler/noirc_frontend/src/parser/parser/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,24 @@ impl Parser<'_> {
let visibility = self.parse_visibility();
(FunctionReturnType::Ty(self.parse_type_or_error()), visibility)
} else {
(
FunctionReturnType::Default(self.location_at_previous_token_end()),
Visibility::Private,
)
// This will return the span between `)` and `{`
//
// fn foo() { }
// ^^^
let mut location = self.previous_token_location.merge(self.current_token_location);

// Here we change it to this (if there's space)
//
// fn foo() { }
// ^
if location.span.end() - location.span.start() >= 3 {
location = Location::new(
Span::from(location.span.start() + 1..location.span.end() - 1),
location.file,
);
}

(FunctionReturnType::Default(location), Visibility::Private)
};

let where_clause = self.parse_where_clause();
Expand Down
Loading

0 comments on commit 3239a4a

Please sign in to comment.