Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 76 additions & 7 deletions compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use acvm::FieldElement;
use noirc_errors::Span;

use crate::{
ParsedModule, QuotedType,
BinaryTypeOperator, ParsedModule, QuotedType,
ast::{
ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression,
CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind,
Expand Down Expand Up @@ -416,7 +417,9 @@ pub trait Visitor {
true
}

fn visit_expression_type(&mut self, _: &UnresolvedTypeExpression, _: Span) {}
fn visit_expression_type(&mut self, _: &UnresolvedTypeExpression, _: Span) -> bool {
true
}

fn visit_format_string_type(
&mut self,
Expand All @@ -427,7 +430,9 @@ pub trait Visitor {
true
}

fn visit_string_type(&mut self, _: &UnresolvedTypeExpression, _: Span) {}
fn visit_string_type(&mut self, _: &UnresolvedTypeExpression, _: Span) -> bool {
true
}

fn visit_unspecified_type(&mut self, _: Span) {}

Expand Down Expand Up @@ -465,6 +470,30 @@ pub trait Visitor {
true
}

fn visit_unresolved_type_expression(&mut self, _: &UnresolvedTypeExpression) -> bool {
true
}

fn visit_variable_type_expression(&mut self, _: &Path) -> bool {
true
}

fn visit_constant_type_expression(&mut self, _value: FieldElement, _span: Span) {}

fn visit_binary_type_expression(
&mut self,
_lhs: &UnresolvedTypeExpression,
_op: BinaryTypeOperator,
_rhs: &UnresolvedTypeExpression,
_span: Span,
) -> bool {
true
}

fn visit_as_trait_path_type_expression(&mut self, _as_trait_path: &AsTraitPath) -> bool {
true
}

fn visit_pattern(&mut self, _: &Pattern) -> bool {
true
}
Expand Down Expand Up @@ -1357,6 +1386,7 @@ impl UnresolvedType {
unresolved_type,
self.location.span,
) {
unresolved_type_expression.accept(visitor);
unresolved_type.accept(visitor);
}
}
Expand Down Expand Up @@ -1405,21 +1435,28 @@ impl UnresolvedType {
}
}
UnresolvedTypeData::Expression(expr) => {
visitor.visit_expression_type(expr, self.location.span);
if visitor.visit_expression_type(expr, self.location.span) {
expr.accept(visitor);
}
}
UnresolvedTypeData::FormatString(expr, typ) => {
if visitor.visit_format_string_type(expr, typ, self.location.span) {
expr.accept(visitor);
typ.accept(visitor);
}
}
UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.location.span),
UnresolvedTypeData::String(expr) => {
if visitor.visit_string_type(expr, self.location.span) {
expr.accept(visitor);
}
}
UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.location.span),
UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.location.span),
UnresolvedTypeData::FieldElement => {
visitor.visit_field_element_type(self.location.span);
}
UnresolvedTypeData::Integer(signedness, size) => {
visitor.visit_integer_type(*signedness, *size, self.location.span);
UnresolvedTypeData::Integer(signdness, size) => {
visitor.visit_integer_type(*signdness, *size, self.location.span);
}
UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span),
UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span),
Expand Down Expand Up @@ -1498,6 +1535,38 @@ impl UnresolvedTraitConstraint {
}
}

impl UnresolvedTypeExpression {
pub fn accept(&self, visitor: &mut impl Visitor) {
if visitor.visit_unresolved_type_expression(self) {
self.accept_children(visitor);
}
}

pub fn accept_children(&self, visitor: &mut impl Visitor) {
match self {
UnresolvedTypeExpression::Variable(path) => {
if visitor.visit_variable_type_expression(path) {
path.accept(visitor);
}
}
UnresolvedTypeExpression::Constant(field_element, location) => {
visitor.visit_constant_type_expression(*field_element, location.span);
}
UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => {
if visitor.visit_binary_type_expression(lhs, *op, rhs, location.span) {
lhs.accept(visitor);
rhs.accept(visitor);
}
}
UnresolvedTypeExpression::AsTraitPath(as_trait_path) => {
if visitor.visit_as_trait_path_type_expression(as_trait_path) {
as_trait_path.accept(self.span(), visitor);
}
}
}
}
}

impl Pattern {
pub fn accept(&self, visitor: &mut impl Visitor) {
if visitor.visit_pattern(self) {
Expand Down
63 changes: 60 additions & 3 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
ast::{
BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param,
Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics,
UnresolvedTraitConstraint, UnresolvedTypeData, UnsupportedNumericGenericType,
UnresolvedTraitConstraint, UnresolvedTypeData, UnsupportedNumericGenericType, Visitor,
},
graph::CrateId,
hir::{
Expand Down Expand Up @@ -64,6 +64,7 @@ mod traits;
pub mod types;
mod unquote;

use im::HashSet;
use iter_extended::vecmap;
use noirc_errors::{Located, Location};
pub(crate) use options::ElaboratorOptions;
Expand Down Expand Up @@ -368,8 +369,8 @@ impl<'context> Elaborator<'context> {
// re-collect the methods within into their proper module. This cannot be
// done during def collection since we need to be able to resolve the type of
// the impl since that determines the module we should collect into.
for ((_self_type, module), impls) in &mut items.impls {
self.collect_impls(*module, impls);
for ((self_type, module), impls) in &mut items.impls {
self.collect_impls(*module, impls, self_type);
}

// Bind trait impls to their trait. Collect trait functions, that have a
Expand Down Expand Up @@ -1477,10 +1478,13 @@ impl<'context> Elaborator<'context> {
&mut self,
module: LocalModuleId,
impls: &mut [(UnresolvedGenerics, Location, UnresolvedFunctions)],
self_type: &UnresolvedType,
) {
self.local_module = module;

for (generics, location, unresolved) in impls {
self.check_generics_appear_in_type(generics, self_type);

let old_generic_count = self.generics.len();
self.add_generics(generics);
self.declare_methods_on_struct(None, unresolved, *location);
Expand Down Expand Up @@ -2062,6 +2066,7 @@ impl<'context> Elaborator<'context> {
for (generics, _, function_set) in function_sets {
self.add_generics(generics);
let self_type = self.resolve_type(self_type.clone());

function_set.self_type = Some(self_type.clone());
self.self_type = Some(self_type);
self.define_function_metas_for_functions(function_set);
Expand Down Expand Up @@ -2201,6 +2206,46 @@ impl<'context> Elaborator<'context> {
})
}

/// Check that all the generics show up in `self_type` (if they don't, we produce an error)
fn check_generics_appear_in_type(
&mut self,
generics: &[UnresolvedGeneric],
self_type: &UnresolvedType,
) {
if generics.is_empty() {
return;
}

// Turn each generic into an Ident
let mut idents = HashSet::new();
for generic in generics {
match generic {
UnresolvedGeneric::Variable(ident) => {
idents.insert(ident.clone());
}
UnresolvedGeneric::Numeric { ident, typ: _ } => {
idents.insert(ident.clone());
}
UnresolvedGeneric::Resolved(quoted_type_id, span) => {
if let Type::NamedGeneric(_type_variable, name) =
self.interner.get_quoted_type(*quoted_type_id).follow_bindings()
{
idents.insert(Ident::new(name.to_string(), *span));
}
}
}
}

// Remove the ones that show up in `self_type`
let mut visitor = RemoveGenericsAppearingInTypeVisitor { idents: &mut idents };
self_type.accept(&mut visitor);

// The ones that remain are not mentioned in the impl: it's an error.
for ident in idents {
self.push_err(ResolverError::UnconstrainedTypeParameter { ident });
}
}

/// 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, location: Location) {
Expand All @@ -2219,3 +2264,15 @@ impl<'context> Elaborator<'context> {
(errored, ret)
}
}

struct RemoveGenericsAppearingInTypeVisitor<'a> {
idents: &'a mut HashSet<Ident>,
}

impl Visitor for RemoveGenericsAppearingInTypeVisitor<'_> {
fn visit_path(&mut self, path: &Path) {
if let Some(ident) = path.as_ident() {
self.idents.remove(ident);
}
}
}
14 changes: 13 additions & 1 deletion compiler/noirc_frontend/src/hir/resolution/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ pub enum ResolverError {
"Indexing an array or slice with a type other than `u32` is deprecated and will soon be an error"
)]
NonU32Index { location: Location },
#[error(
"The type parameter `{ident}` is not constrained by the impl trait, self type, or predicates"
)]
UnconstrainedTypeParameter { ident: Ident },
}

impl ResolverError {
Expand Down Expand Up @@ -270,7 +274,8 @@ impl ResolverError {
| ResolverError::LowLevelFunctionOutsideOfStdlib { ident }
| ResolverError::OracleMarkedAsConstrained { ident }
| ResolverError::NoPredicatesAttributeOnUnconstrained { ident }
| ResolverError::FoldAttributeOnUnconstrained { ident } => ident.location(),
| ResolverError::FoldAttributeOnUnconstrained { ident }
| ResolverError::UnconstrainedTypeParameter { ident } => ident.location(),
ResolverError::ArrayLengthInterpreter { error } => error.location(),
ResolverError::PathResolutionError(path_resolution_error) => {
path_resolution_error.location()
Expand Down Expand Up @@ -811,6 +816,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
*location,
)
},
ResolverError::UnconstrainedTypeParameter { ident} => {
Diagnostic::simple_error(
format!("The type parameter `{ident}` is not constrained by the impl trait, self type, or predicates"),
format!("Hint: remove the `{ident}` type parameter"),
ident.location(),
)
}
}
}
}
35 changes: 34 additions & 1 deletion compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4148,7 +4148,6 @@ fn int_min_global() {
let _x = MIN;
}
"#;

assert_no_errors!(src);
}

Expand Down Expand Up @@ -4540,3 +4539,37 @@ fn cannot_determine_type_of_generic_argument_in_enum_constructor() {
let features = vec![UnstableFeature::Enums];
check_monomorphization_error_using_features!(src, &features);
}

#[named]
#[test]
fn unconstrained_type_parameter_in_impl() {
let src = r#"
pub struct Foo<T> {}

impl<T, U> Foo<T> {}
^ The type parameter `U` is not constrained by the impl trait, self type, or predicates
~ Hint: remove the `U` type parameter

fn main() {
let _ = Foo {};
}
"#;
check_errors!(src);
}

#[named]
#[test]
fn unconstrained_numeric_generic_in_impl() {
let src = r#"
pub struct Foo {}

impl<let N: u32> Foo {}
^ The type parameter `N` is not constrained by the impl trait, self type, or predicates
~ Hint: remove the `N` type parameter

fn main() {
let _ = Foo {};
}
"#;
check_errors!(src);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[package]
name = "noirc_frontend_tests_unconstrained_numeric_generic_in_impl"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

pub struct Foo {}

impl<let N: u32> Foo {}

fn main() {
let _ = Foo {};
}

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10648545254261603633
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: The type parameter `N` is not constrained by the impl trait, self type, or predicates
┌─ src/main.nr:4:18
4 │ impl<let N: u32> Foo {}
│ - Hint: remove the `N` type parameter

Aborting due to 1 previous error
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[package]
name = "noirc_frontend_tests_unconstrained_type_parameter_in_impl"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

pub struct Foo<T> {}

impl<T, U> Foo<T> {}

fn main() {
let _ = Foo {};
}

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5158513121244495309
Loading
Loading