From 4c012d3ddec8a4e025fafb66dbc61e71c701e45f Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 18 Sep 2024 17:30:06 -0400 Subject: [PATCH 01/56] wip: syncing TypeVariableKind with Kind, removing the temporary 'None' kind --- .../noirc_frontend/src/elaborator/types.rs | 22 ++++---- compiler/noirc_frontend/src/hir_def/types.rs | 54 +++++++++---------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index c11fc98fb1c..d706fcb3bcc 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -169,14 +169,12 @@ impl<'context> Elaborator<'context> { _ => (), } - if !kind.matches_opt(resolved_type.kind()) { + if !kind.unifies(&resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), expr_kind: resolved_type .kind() - .as_ref() - .map(Kind::to_string) - .unwrap_or("unknown".to_string()), + .to_string(), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -470,15 +468,13 @@ impl<'context> Elaborator<'context> { } fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { - if let Some(kind) = typ.kind() { - if !kind.unifies(expected_kind) { - self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: kind.to_string(), - expr_span: span, - }); - return Type::Error; - } + if !typ.kind().unifies(expected_kind) { + self.push_err(TypeCheckError::TypeKindMismatch { + expected_kind: expected_kind.to_string(), + expr_kind: typ.kind().to_string(), + expr_span: span, + }); + return Type::Error; } typ } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 7bb9fb83e70..5c4c6c406b4 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -150,10 +150,6 @@ impl Kind { matches!(self, Self::Numeric { .. }) } - pub(crate) fn matches_opt(&self, other: Option) -> bool { - other.as_ref().map_or(true, |other_kind| self.unifies(other_kind)) - } - pub(crate) fn u32() -> Self { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } @@ -544,6 +540,18 @@ pub enum TypeVariableKind { Integer, } +impl TypeVariableKind { + fn kind(&self) -> Kind { + // TODO: needs TypeVariableKind enum variants to be updated + // TODO: ensure that IntegerOrField and Integer are incompatible w/ Type::Constant or add to Kind + match self { + Self::Normal => _, + Self::IntegerOrField => _, + Self::Integer => _, + } + } +} + /// A TypeVariable is a mutable reference that is either /// bound to some type, or unbound with a given TypeVariableId. #[derive(PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] @@ -1130,15 +1138,15 @@ impl Type { } } - pub(crate) fn kind(&self) -> Option { + pub(crate) fn kind(&self) -> Kind { match self { - Type::NamedGeneric(_, _, kind) => Some(kind.clone()), - Type::Constant(_, kind) => Some(kind.clone()), - Type::TypeVariable(var, _) => match *var.borrow() { + Type::NamedGeneric(_, _, kind) => kind.clone(), + Type::Constant(_, kind) => kind.clone(), + Type::TypeVariable(var, type_var_kind) => match *var.borrow() { TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_) => None, + TypeBinding::Unbound(_) => type_var_kind.kind(), }, - Type::InfixExpr(lhs, _op, rhs) => Some(lhs.infix_kind(rhs)), + Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), Type::FieldElement | Type::Array(..) | Type::Slice(..) @@ -1155,26 +1163,18 @@ impl Type { | Type::MutableReference(..) | Type::Forall(..) | Type::Quoted(..) - | Type::Error => Some(Kind::Normal), + | Type::Error => Kind::Normal, } } - /// if both Kind's are equal to Some(_), return that Kind, - /// otherwise return a Kind error - /// if both Kind's are None, default to u32 - /// if exactly one Kind is None, return the other one + /// Unifies self and other kinds or fails with a Kind error fn infix_kind(&self, other: &Self) -> Kind { - match (self.kind(), other.kind()) { - (Some(self_kind), Some(other_kind)) => { - if self_kind == other_kind { - self_kind - } else { - Kind::Numeric(Box::new(Type::Error)) - } - } - (None, None) => Kind::u32(), - (Some(self_kind), None) => self_kind, - (None, Some(other_kind)) => other_kind, + let self_kind = self.kind(); + let other_kind = other.kind(); + if self_kind.unifies(&other_kind) { + self_kind + } else { + Kind::Numeric(Box::new(Type::Error)) } } @@ -1542,7 +1542,7 @@ impl Type { (Constant(value, kind), other) | (other, Constant(value, kind)) => { if let Some(other_value) = other.evaluate_to_u32() { - if *value == other_value && kind.matches_opt(other.kind()) { + if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { Err(UnificationError) From 93aee54b0c580e54df6e0ca2ddf2e3ea02f44291 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 19 Sep 2024 12:20:13 -0400 Subject: [PATCH 02/56] wip validating that op.function results fit in their kinds --- .../noirc_frontend/src/elaborator/types.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 31 ++++++++++++------- .../src/hir_def/types/arithmetic.rs | 11 ++++--- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d706fcb3bcc..d09ae1881af 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -450,7 +450,7 @@ impl<'context> Elaborator<'context> { }); return Type::Error; } - if let Some(result) = op.function(lhs, rhs) { + if let Some(result) = op.function(lhs, rhs, &lhs_kind) { Type::Constant(result, lhs_kind) } else { self.push_err(ResolverError::OverflowInType { lhs, op, rhs, span }); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 5c4c6c406b4..332c243b731 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -177,6 +177,11 @@ impl Kind { Err(UnificationError) } } + + // TODO! + fn ensure_value_fits(&self, value: u32) -> Option { + Some(value) + } } impl std::fmt::Display for Kind { @@ -542,13 +547,14 @@ pub enum TypeVariableKind { impl TypeVariableKind { fn kind(&self) -> Kind { + todo!() // TODO: needs TypeVariableKind enum variants to be updated // TODO: ensure that IntegerOrField and Integer are incompatible w/ Type::Constant or add to Kind - match self { - Self::Normal => _, - Self::IntegerOrField => _, - Self::Integer => _, - } + // match self { + // Self::Normal => _, + // Self::IntegerOrField => _, + // Self::Integer => _, + // } } } @@ -1713,11 +1719,11 @@ impl Type { match self.canonicalize() { Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x, _) => Some(x), + Type::Constant(x, kind) => kind.ensure_value_fits(x), Type::InfixExpr(lhs, op, rhs) => { - let lhs = lhs.evaluate_to_u32()?; - let rhs = rhs.evaluate_to_u32()?; - op.function(lhs, rhs) + let lhs_u32 = lhs.evaluate_to_u32()?; + let rhs_u32 = rhs.evaluate_to_u32()?; + op.function(lhs_u32, rhs_u32, &lhs.infix_kind(&rhs)) } _ => None, } @@ -2244,14 +2250,15 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - pub fn function(self, a: u32, b: u32) -> Option { - match self { + pub fn function(self, a: u32, b: u32, kind: &Kind) -> Option { + let result = match self { BinaryTypeOperator::Addition => a.checked_add(b), BinaryTypeOperator::Subtraction => a.checked_sub(b), BinaryTypeOperator::Multiplication => a.checked_mul(b), BinaryTypeOperator::Division => a.checked_div(b), BinaryTypeOperator::Modulo => a.checked_rem(b), - } + }; + result.and_then(|result| kind.ensure_value_fits(result)) } fn is_commutative(self) -> bool { diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 54b4c27a1f3..af94ef27535 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -20,8 +20,9 @@ impl Type { if let (Some(lhs_u32), Some(rhs_u32)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { - if let Some(result) = op.function(lhs_u32, rhs_u32) { - return Type::Constant(result, lhs.infix_kind(&rhs)); + let kind = lhs.infix_kind(&rhs); + if let Some(result) = op.function(lhs_u32, rhs_u32, &kind) { + return Type::Constant(result, kind); } } @@ -68,7 +69,7 @@ impl Type { queue.push(*rhs); } Type::Constant(new_constant, new_constant_kind) => { - if let Some(result) = op.function(constant, new_constant) { + if let Some(result) = op.function(constant, new_constant, &new_constant_kind) { constant = result; } else { let constant = Type::Constant(new_constant, new_constant_kind); @@ -205,7 +206,7 @@ impl Type { if l_op == Subtraction { op = op.inverse()?; } - let result = op.function(l_const, r_const)?; + let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); Some(Type::InfixExpr(l_type, l_op, Box::new(constant))) } @@ -218,7 +219,7 @@ impl Type { if op == Division && (r_const == 0 || l_const % r_const != 0) { None } else { - let result = op.function(l_const, r_const)?; + let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); Some(Type::InfixExpr(l_type, l_op, constant)) } From 1002bf48992bb2c4146cc0eac601d68aab9aba3b Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 19 Sep 2024 17:07:47 -0400 Subject: [PATCH 03/56] wip(todo!(..)'s remain): adding kinds checks to (try_)bind_to and TypeBindings, rename is_numeric on TypeVariableKind to distinguish it's numeric on _values_, add TypeVariableKind::Numeric --- compiler/noirc_frontend/src/elaborator/mod.rs | 4 +- .../src/elaborator/trait_impls.rs | 4 +- .../noirc_frontend/src/elaborator/traits.rs | 4 +- .../noirc_frontend/src/elaborator/types.rs | 21 ++- .../src/hir/comptime/interpreter.rs | 11 +- compiler/noirc_frontend/src/hir_def/types.rs | 178 +++++++++++++----- .../src/monomorphization/mod.rs | 26 +-- compiler/noirc_frontend/src/node_interner.rs | 10 +- 8 files changed, 171 insertions(+), 87 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index f9016b1ca65..79dcf602aea 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -471,9 +471,9 @@ impl<'context> Elaborator<'context> { let context = self.function_context.pop().expect("Imbalanced function_context pushes"); for typ in context.type_variables { - if let Type::TypeVariable(variable, kind) = typ.follow_bindings() { + if let Type::TypeVariable(variable, type_var_kind) = typ.follow_bindings() { let msg = "TypeChecker should only track defaultable type vars"; - variable.bind(kind.default_type().expect(msg)); + variable.bind(type_var_kind.default_type().expect(msg), &type_var_kind.kind()); } } diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index aa7e1cb89c5..e49a4c5272a 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -153,12 +153,12 @@ impl<'context> Elaborator<'context> { let override_meta = self.interner.function_meta(func_id); // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, .. }, + ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, ) in method.direct_generics.iter().zip(&override_meta.direct_generics) { let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); } let mut substituted_method_ids = HashSet::default(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 0faaf409e6c..8aaebe7b679 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -287,12 +287,12 @@ pub(crate) fn check_trait_impl_method_matches_declaration( // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, .. }, + ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); } let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 19c992cee91..f8a48ed4778 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -781,7 +781,7 @@ impl<'context> Elaborator<'context> { let expected = Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); - if let Err(error) = binding.try_bind(expected, span) { + if let Err(error) = binding.try_bind(expected, &Kind::Normal, span) { self.push_err(error); } ret @@ -923,13 +923,13 @@ impl<'context> Elaborator<'context> { span, }); - let use_impl = !lhs_type.is_numeric(); + let use_impl = !lhs_type.is_numeric_value(); // If this operator isn't valid for fields we have to possibly narrow // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. // Doing so also ensures a type error if Field is used. // The is_numeric check is to allow impls for custom types to bypass this. - if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { + if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric_value() { let target = Type::polymorphic_integer(self.interner); use crate::ast::BinaryOpKind::*; @@ -978,7 +978,7 @@ impl<'context> Elaborator<'context> { &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), || TypeCheckError::InvalidShiftSize { span }, ); - let use_impl = if lhs_type.is_numeric() { + let use_impl = if lhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) } else { @@ -1078,14 +1078,14 @@ impl<'context> Elaborator<'context> { // The `!` prefix operator is not valid for Field, so if this is a numeric // type we constrain it to just (non-Field) integer types. - if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric() { + if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } }); } - Ok((rhs_type.clone(), !rhs_type.is_numeric())) + Ok((rhs_type.clone(), !rhs_type.is_numeric_value())) } Integer(sign_x, bit_width_x) => { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { @@ -1167,7 +1167,7 @@ impl<'context> Elaborator<'context> { let object_type = object_type.substitute(&bindings); bindings.insert( the_trait.self_type_typevar.id(), - (the_trait.self_type_typevar.clone(), object_type.clone()), + (the_trait.self_type_typevar.clone(), Kind::Normal, object_type.clone()), ); self.interner.select_impl_for_expression( expr_id, @@ -1732,7 +1732,7 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) { // Avoid binding t = t if !arg.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.clone())); } } @@ -1751,7 +1751,7 @@ impl<'context> Elaborator<'context> { // Avoid binding t = t if !arg.typ.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.typ.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.typ.clone())); } } @@ -1760,7 +1760,8 @@ impl<'context> Elaborator<'context> { // to specify a redundant type annotation. if assumed { let self_type = the_trait.self_type_typevar.clone(); - bindings.insert(self_type.id(), (self_type, constraint.typ.clone())); + // self_type has Kind::Normal + bindings.insert(self_type.id(), (self_type, Kind::Normal, constraint.typ.clone())); } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index b5ed8126e33..d18a17c5a4a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -34,6 +34,7 @@ use crate::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, HirPattern, }, + types::Kind, }, macros_api::{HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, @@ -89,7 +90,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { // To match the monomorphizer, we need to call follow_bindings on each of // the instantiation bindings before we unbind the generics from the previous function. // This is because the instantiation bindings refer to variables from the call site. - for (_, binding) in instantiation_bindings.values_mut() { + for (_, _kind, binding) in instantiation_bindings.values_mut() { *binding = binding.follow_bindings(); } @@ -98,7 +99,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut impl_bindings = perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - for (_, binding) in impl_bindings.values_mut() { + for (_, _kind, binding) in impl_bindings.values_mut() { *binding = binding.follow_bindings(); } @@ -349,7 +350,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if let Some(bindings) = self.bound_generics.last() { for (var, binding) in bindings { - var.force_bind(binding.clone()); + var.force_bind(binding.clone(), &Kind::Normal); } } } @@ -360,11 +361,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { .last_mut() .expect("remember_bindings called with no bound_generics on the stack"); - for (var, binding) in main_bindings.values() { + for (var, _kind, binding) in main_bindings.values() { bound_generics.insert(var.clone(), binding.follow_bindings()); } - for (var, binding) in impl_bindings.values() { + for (var, _kind, binding) in impl_bindings.values() { bound_generics.insert(var.clone(), binding.follow_bindings()); } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 29d93af731e..a1c9f4be01b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -134,6 +134,7 @@ pub enum Type { /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { + // TODO: rename this and/or TypeVariableKind::Normal to non-numeric? Normal, Numeric(Box), } @@ -210,10 +211,10 @@ pub enum QuotedType { CtString, } -/// A list of TypeVariableIds to bind to a type. Storing the +/// A list of (TypeVariableId, Kind)'s to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. -pub type TypeBindings = HashMap; +pub type TypeBindings = HashMap; /// Represents a struct type in the type system. Each instance of this /// rust struct will be shared across all Type::Struct variants that represent @@ -237,7 +238,7 @@ pub struct StructType { /// Corresponds to generic lists such as `` in the source program. /// Used mainly for resolved types which no longer need information such -/// as names or kinds. +/// as names or kinds (i.e. they have Kind::Normal). pub type GenericTypeVars = Vec; /// Corresponds to generic lists such as `` with additional @@ -327,7 +328,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) .collect(); (typ.substitute(&substitutions), i) @@ -343,7 +344,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -455,7 +456,7 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) .collect(); self.typ.substitute(&substitutions) @@ -530,7 +531,7 @@ pub enum BinaryTypeOperator { #[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub enum TypeVariableKind { - /// Can bind to any type + /// Can bind to any type, except Type::Constant Normal, /// A generic integer or field type. This is a more specific kind of TypeVariable @@ -543,18 +544,19 @@ pub enum TypeVariableKind { /// A generic integer type. This is a more specific kind of TypeVariable /// that can only be bound to Type::Integer, or other polymorphic integers. Integer, + + /// Can bind to a Type::Constant of the given kind + Numeric(Box), } impl TypeVariableKind { - fn kind(&self) -> Kind { - todo!() - // TODO: needs TypeVariableKind enum variants to be updated - // TODO: ensure that IntegerOrField and Integer are incompatible w/ Type::Constant or add to Kind - // match self { - // Self::Normal => _, - // Self::IntegerOrField => _, - // Self::Integer => _, - // } + pub(crate) fn kind(&self) -> Kind { + match self { + Self::Normal => Kind::Normal, + Self::IntegerOrField => Kind::Normal, + Self::Integer => Kind::Normal, + Self::Numeric(typ) => Kind::Numeric(typ.clone()), + } } } @@ -577,7 +579,9 @@ impl TypeVariable { /// Panics if this TypeVariable is already Bound. /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. - pub fn bind(&self, typ: Type) { + pub fn bind(&self, typ: Type, kind: &Kind) { + todo!("check kind against typ"); + let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) @@ -589,7 +593,9 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, span: Span) -> Result<(), TypeCheckError> { + pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + todo!("check kind against typ"); + let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("Expected unbound, found bound to {binding}") @@ -620,7 +626,16 @@ impl TypeVariable { /// Forcibly bind a type variable to a new type - even if the type /// variable is already bound to a different type. This generally /// a logic error to use outside of monomorphization. - pub fn force_bind(&self, typ: Type) { + /// + /// Asserts that the given type is compatible with the given Kind + pub fn force_bind(&self, typ: Type, kind: &Kind) { + assert!( + typ.kind().unifies(kind), + "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + kind, + typ.kind() + ); + if !typ.occurs(self.id()) { *self.1.borrow_mut() = TypeBinding::Bound(typ); } @@ -679,6 +694,13 @@ impl std::fmt::Display for Type { write!(f, "{}", binding.borrow()) } } + Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + write!(f, "{}", typ) + } else { + write!(f, "{}", binding.borrow()) + } + } Type::Struct(s, args) => { let args = vecmap(args, |arg| arg.to_string()); if args.is_empty() { @@ -863,7 +885,9 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) } - pub fn is_numeric(&self) -> bool { + /// While Kind::is_numeric refers to numeric _types_, + /// this method checks for numeric _values_ + pub fn is_numeric_value(&self) -> bool { use Type::*; use TypeVariableKind as K; matches!( @@ -1119,9 +1143,10 @@ impl Type { } /// Takes a monomorphic type and generalizes it over each of the type variables in the - /// given type bindings, ignoring what each type variable is bound to in the TypeBindings. + /// given type bindings, ignoring what each type variable is bound to in the TypeBindings + /// and their Kind's pub(crate) fn generalize_from_substitutions(self, type_bindings: TypeBindings) -> Type { - let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _))| type_var); + let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _kind, _))| type_var); Type::Forall(polymorphic_type_vars, Box::new(self)) } @@ -1273,11 +1298,11 @@ impl Type { let this = self.substitute(bindings).follow_bindings(); match &this { Type::Integer(..) => { - bindings.insert(target_id, (var.clone(), this)); + bindings.insert(target_id, (var.clone(), Kind::Normal, this)); Ok(()) } Type::FieldElement if !only_integer => { - bindings.insert(target_id, (var.clone(), this)); + bindings.insert(target_id, (var.clone(), Kind::Normal, this)); Ok(()) } Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { @@ -1293,9 +1318,9 @@ impl Type { // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); - bindings.insert(*new_target_id, (self_var.clone(), clone)); + bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); } else { - bindings.insert(target_id, (var.clone(), this.clone())); + bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); } Ok(()) } @@ -1310,7 +1335,7 @@ impl Type { // Avoid infinitely recursive bindings TypeBinding::Unbound(id) if *id == target_id => Ok(()), TypeBinding::Unbound(_) => { - bindings.insert(target_id, (var.clone(), this.clone())); + bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); Ok(()) } } @@ -1331,7 +1356,7 @@ impl Type { TypeVariableKind::IntegerOrField }; let clone = Type::TypeVariable(var.clone(), clone_kind); - bindings.insert(*new_target_id, (self_var.clone(), clone)); + bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); Ok(()) } } @@ -1357,7 +1382,12 @@ impl Type { }; let this = self.substitute(bindings).follow_bindings(); - if let Some(binding) = this.get_inner_type_variable() { + if let Some((binding, kind)) = this.get_inner_type_variable() { + // TypeVariableKind::Normal can't bind to numeric Kind's + if kind.is_numeric() { + return Err(UnificationError) + } + match &*binding.borrow() { TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings), // Don't recursively bind the same id to itself @@ -1371,14 +1401,15 @@ impl Type { if this.occurs(target_id) { Err(UnificationError) } else { - bindings.insert(target_id, (var.clone(), this.clone())); + bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); Ok(()) } } - fn get_inner_type_variable(&self) -> Option> { + fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { match self { - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => Some(var.1.clone()), + Type::TypeVariable(var, type_var_kind) => Some((var.1.clone(), type_var_kind.kind())), + Type::NamedGeneric(var, _, kind) => Some((var.1.clone(), kind.clone())), _ => None, } } @@ -1446,6 +1477,15 @@ impl Type { }) } + (TypeVariable(var, Kind::Numeric(numeric_kind)), other) | (other, TypeVariable(var, Kind::Numeric(numeric_kind))) => { + todo!(); + + // something like: + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings) + }) + } + (Array(len_a, elem_a), Array(len_b, elem_b)) => { len_a.try_unify(len_b, bindings)?; elem_a.try_unify(elem_b, bindings) @@ -1600,7 +1640,12 @@ impl Type { // We may have already "bound" this type variable in this call to // try_unify, so check those bindings as well. match bindings.get(id) { - Some((_, binding)) => binding.clone().try_unify(self, bindings), + Some((_, kind, binding)) => { + if !kind.unifies(&binding.kind()) { + return Err(UnificationError); + } + binding.clone().try_unify(self, bindings) + } // Otherwise, bind it None => bind_variable(bindings), @@ -1703,17 +1748,17 @@ impl Type { /// Apply the given type bindings, making them permanently visible for each /// clone of each type variable bound. pub fn apply_type_bindings(bindings: TypeBindings) { - for (type_variable, binding) in bindings.values() { - type_variable.bind(binding.clone()); + for (type_variable, kind, binding) in bindings.values() { + type_variable.bind(binding.clone(), kind); } } /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some(binding) = self.get_inner_type_variable() { + if let Some((binding, kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return binding.evaluate_to_u32(); + return binding.evaluate_to_u32().and_then(|result| kind.ensure_value_fits(result)); } } @@ -1779,7 +1824,7 @@ impl Type { for var in typevars { bindings .entry(var.id()) - .or_insert_with(|| (var.clone(), interner.next_type_variable())); + .or_insert_with(|| (var.clone(), Kind::Normal, interner.next_type_variable())); } let instantiated = typ.force_substitute(&bindings); @@ -1799,7 +1844,7 @@ impl Type { .iter() .map(|var| { let new = interner.next_type_variable(); - (var.id(), (var.clone(), new)) + (var.id(), (var.clone(), Kind::Normal, new)) }) .collect(); @@ -1833,7 +1878,7 @@ impl Type { let replacements = typevars .iter() .zip(bindings) - .map(|(var, binding)| (var.id(), (var.clone(), binding))) + .map(|(var, binding)| (var.id(), (var.clone(), Kind::Normal, binding))) .collect(); let instantiated = typ.substitute(&replacements); @@ -1900,7 +1945,14 @@ impl Type { // type variables that have already been bound over. // This is needed for monomorphizing trait impl methods. match type_bindings.get(&binding.0) { - Some((_, replacement)) if substitute_bound_typevars => { + Some((_, kind, replacement)) if substitute_bound_typevars => { + assert!( + kind.unifies(&replacement.kind()), + "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + kind, + replacement.kind() + ); + recur_on_binding(binding.0, replacement) } _ => match &*binding.borrow() { @@ -1908,7 +1960,16 @@ impl Type { binding.substitute_helper(type_bindings, substitute_bound_typevars) } TypeBinding::Unbound(id) => match type_bindings.get(id) { - Some((_, replacement)) => recur_on_binding(binding.0, replacement), + Some((_, kind, replacement)) => { + assert!( + kind.unifies(&replacement.kind()), + "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + kind, + replacement.kind() + ); + + recur_on_binding(binding.0, replacement) + }, None => self.clone(), }, }, @@ -2162,12 +2223,18 @@ impl Type { typ.replace_named_generics_with_type_variables(); *self = typ; } - Type::TypeVariable(var, _) => { + Type::TypeVariable(var, type_var_kind) => { let var = var.borrow(); if let TypeBinding::Bound(binding) = &*var { let mut binding = binding.clone(); drop(var); binding.replace_named_generics_with_type_variables(); + assert!( + type_var_kind.kind().unifies(&binding.kind()), + "expected kind of bound TypeVariable ({:?}) to match the kind of its binding ({:?})", + type_var_kind.kind(), + binding.kind() + ); *self = binding; } } @@ -2179,7 +2246,7 @@ impl Type { generic.typ.replace_named_generics_with_type_variables(); } } - Type::NamedGeneric(var, _, _) => { + Type::NamedGeneric(var, _, kind) => { let type_binding = var.borrow(); if let TypeBinding::Bound(binding) = &*type_binding { let mut binding = binding.clone(); @@ -2188,7 +2255,11 @@ impl Type { *self = binding; } else { drop(type_binding); - *self = Type::TypeVariable(var.clone(), TypeVariableKind::Normal); + let type_var_kind = match kind { + Kind::Normal => TypeVariableKind::Normal, + Kind::Numeric(typ) => TypeVariableKind::Numeric(typ.clone()), + }; + *self = Type::TypeVariable(var.clone(), type_var_kind); } } Type::Function(args, ret, env, _unconstrained) => { @@ -2285,6 +2356,7 @@ impl TypeVariableKind { TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), TypeVariableKind::Integer => Some(Type::default_int_type()), TypeVariableKind::Normal => None, + TypeVariableKind::Numeric(typ) => Some(*typ.clone()), } } } @@ -2385,6 +2457,9 @@ impl std::fmt::Debug for Type { Type::TypeVariable(binding, TypeVariableKind::Integer) => { write!(f, "Int{:?}", binding) } + Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { + write!(f, "Numeric({:?}: {:?})", binding, typ) + } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); if args.is_empty() { @@ -2474,7 +2549,8 @@ impl std::fmt::Debug for StructType { impl std::hash::Hash for Type { fn hash(&self, state: &mut H) { - if let Some(variable) = self.get_inner_type_variable() { + if let Some((variable, kind)) = self.get_inner_type_variable() { + kind.hash(state); if let TypeBinding::Bound(typ) = &*variable.borrow() { typ.hash(state); return; @@ -2539,13 +2615,19 @@ impl std::hash::Hash for Type { impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { - if let Some(variable) = self.get_inner_type_variable() { + if let Some((variable, kind)) = self.get_inner_type_variable() { + if !kind.unifies(&other.kind()) { + return false; + } if let TypeBinding::Bound(typ) = &*variable.borrow() { return typ == other; } } - if let Some(variable) = other.get_inner_type_variable() { + if let Some((variable, other_kind)) = other.get_inner_type_variable() { + if !self.kind().unifies(&other_kind) { + return false; + } if let TypeBinding::Bound(typ) = &*variable.borrow() { return self == typ; } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 12cc3b55b1f..bdafba238b5 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -301,7 +301,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { if let Some((self_type, trait_id)) = self.interner.get_function_trait(&f) { let the_trait = self.interner.get_trait(trait_id); - the_trait.self_type_typevar.force_bind(self_type); + the_trait.self_type_typevar.force_bind(self_type, &types::Kind::Normal); } let meta = self.interner.function_meta(&f).clone(); @@ -857,7 +857,7 @@ impl<'interner> Monomorphizer<'interner> { // Ensure all instantiation bindings are bound. // This ensures even unused type variables like `fn foo() {}` have concrete types if let Some(bindings) = self.interner.try_get_instantiation_bindings(expr_id) { - for (_, binding) in bindings.values() { + for (_, _kind, binding) in bindings.values() { Self::check_type(binding, ident.location)?; } } @@ -967,7 +967,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _, _) => { + HirType::NamedGeneric(binding, _, kind) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -975,11 +975,11 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_or_field_type()); + binding.bind(HirType::default_int_or_field_type(), kind); ast::Type::Field } - HirType::TypeVariable(binding, kind) => { + HirType::TypeVariable(binding, type_var_kind) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -987,13 +987,13 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = match kind.default_type() { + let default = match type_var_kind.default_type() { Some(typ) => typ, None => return Err(MonomorphizationError::NoDefaultType { location }), }; let monomorphized_default = Self::convert_type(&default, location)?; - binding.bind(default); + binding.bind(default, &type_var_kind.kind()); monomorphized_default } @@ -1441,9 +1441,9 @@ impl<'interner> Monomorphizer<'interner> { fn follow_bindings(&self, bindings: &TypeBindings) -> TypeBindings { bindings .iter() - .map(|(id, (var, binding))| { + .map(|(id, (var, kind, binding))| { let binding2 = binding.follow_bindings(); - (*id, (var.clone(), binding2)) + (*id, (var.clone(), kind.clone(), binding2)) }) .collect() } @@ -1910,13 +1910,13 @@ fn unwrap_struct_type( } pub fn perform_instantiation_bindings(bindings: &TypeBindings) { - for (var, binding) in bindings.values() { - var.force_bind(binding.clone()); + for (var, kind, binding) in bindings.values() { + var.force_bind(binding.clone(), kind); } } pub fn undo_instantiation_bindings(bindings: TypeBindings) { - for (id, (var, _)) in bindings { + for (id, (var, _, _)) in bindings { var.unbind(id); } } @@ -1944,7 +1944,7 @@ pub fn perform_impl_bindings( interner.function_meta(&impl_method).typ.unwrap_forall().1.clone(); // Make each NamedGeneric in this type bindable by replacing it with a TypeVariable - // with the same internal id and binding. + // with the same internal id, binding. trait_method_type.replace_named_generics_with_type_variables(); impl_method_type.replace_named_generics_with_type_variables(); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index d3e5e270b3c..7fe5187d1bb 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -37,7 +37,7 @@ use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; use crate::hir_def::traits::{Trait, TraitConstraint}; -use crate::hir_def::types::{StructType, Type}; +use crate::hir_def::types::{Kind, StructType, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, @@ -1717,7 +1717,7 @@ impl NodeInterner { // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() - .map(|typevar| (typevar.id(), (typevar, self.next_type_variable()))) + .map(|typevar| (typevar.id(), (typevar, Kind::Normal, self.next_type_variable()))) .collect(); let instantiated_object_type = object_type.substitute(&substitutions); @@ -2208,11 +2208,11 @@ impl NodeInterner { let trait_generics = the_trait.generics.clone(); let self_type_var = the_trait.self_type_typevar.clone(); - bindings.insert(self_type_var.id(), (self_type_var, impl_self_type)); + bindings.insert(self_type_var.id(), (self_type_var, Kind::Normal, impl_self_type)); for (trait_generic, trait_impl_generic) in trait_generics.iter().zip(trait_impl_generics) { let type_var = trait_generic.type_var.clone(); - bindings.insert(type_var.id(), (type_var, trait_impl_generic.clone())); + bindings.insert(type_var.id(), (type_var, trait_generic.kind.clone(), trait_impl_generic.clone())); } // Now that the normal bindings are added, we still need to bind the associated types @@ -2221,7 +2221,7 @@ impl NodeInterner { for (trait_type, impl_type) in trait_associated_types.iter().zip(impl_associated_types) { let type_variable = trait_type.type_var.clone(); - bindings.insert(type_variable.id(), (type_variable, impl_type.typ.clone())); + bindings.insert(type_variable.id(), (type_variable, trait_type.kind.clone(), impl_type.typ.clone())); } bindings From 10a9bf13ff0f4ecdb9c302e96c95acb2bf2d989a Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 20 Sep 2024 12:17:19 -0400 Subject: [PATCH 04/56] wip debugging tests, implemented value size check, add assertions when binding kinds, implement unification for new TypeVariableKind --- compiler/noirc_frontend/src/hir_def/types.rs | 87 +++++++++++++++----- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a1c9f4be01b..176a5cd6121 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -179,9 +179,23 @@ impl Kind { } } - // TODO! + /// Ensure the given value fits in max(u32::MAX, max_of_self) fn ensure_value_fits(&self, value: u32) -> Option { - Some(value) + match self { + Self::Normal => Some(value), + Self::Numeric(typ) => { + match typ.integral_maximum_size() { + None => Some(value), + Some(maximum_size) => { + if value <= maximum_size { + Some(value) + } else { + None + } + } + } + }, + } } } @@ -580,7 +594,12 @@ impl TypeVariable { /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. pub fn bind(&self, typ: Type, kind: &Kind) { - todo!("check kind against typ"); + assert!( + typ.kind().unifies(kind), + "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + typ.kind(), + kind + ); let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { @@ -594,7 +613,12 @@ impl TypeVariable { } pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { - todo!("check kind against typ"); + assert!( + binding.kind().unifies(kind), + "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + binding.kind(), + kind + ); let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { @@ -631,7 +655,7 @@ impl TypeVariable { pub fn force_bind(&self, typ: Type, kind: &Kind) { assert!( typ.kind().unifies(kind), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + "expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", kind, typ.kind() ); @@ -1375,21 +1399,21 @@ impl Type { &self, var: &TypeVariable, bindings: &mut TypeBindings, + kind: Kind, ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), TypeBinding::Unbound(id) => *id, }; + if !self.kind().unifies(&kind) { + return Err(UnificationError) + } + let this = self.substitute(bindings).follow_bindings(); if let Some((binding, kind)) = this.get_inner_type_variable() { - // TypeVariableKind::Normal can't bind to numeric Kind's - if kind.is_numeric() { - return Err(UnificationError) - } - match &*binding.borrow() { - TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings), + TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings, kind), // Don't recursively bind the same id to itself TypeBinding::Unbound(id) if *id == target_id => return Ok(()), _ => (), @@ -1471,18 +1495,9 @@ impl Type { }) } - (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { + (TypeVariable(var, type_var_kind), other) | (other, TypeVariable(var, type_var_kind)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings) - }) - } - - (TypeVariable(var, Kind::Numeric(numeric_kind)), other) | (other, TypeVariable(var, Kind::Numeric(numeric_kind))) => { - todo!(); - - // something like: - other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings) + other.try_bind_to(var, bindings, type_var_kind.kind()) }) } @@ -2284,6 +2299,34 @@ impl Type { _ => None, } } + + fn integral_maximum_size(&self) -> Option { + match self { + Type::FieldElement => Some(u32::MAX), + Type::Integer(sign, num_bits) => { + let mut max_bit_size = num_bits.bit_size(); + if sign == &Signedness::Signed { + max_bit_size >>= 1; + } + if 32 <= max_bit_size { + Some(u32::MAX) + } else { + Some((1u32 << max_bit_size) - 1) + } + } + Type::TypeVariable(binding, TypeVariableKind::Integer | TypeVariableKind::IntegerOrField) => { + match &*binding.borrow() { + TypeBinding::Bound(typ) => typ.integral_maximum_size(), + TypeBinding::Unbound(_) => Some(u32::MAX), + } + } + Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), + Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), + Type::Bool => Some(1), + Type::NamedGeneric(_binding, _name, Kind::Numeric(typ)) => typ.integral_maximum_size(), + _ => None, + } + } } /// Wraps a given `expression` in `expression.as_slice()` From 1081c5084cbeef106cd49786c30b9ff2b5ee3840 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 20 Sep 2024 14:10:53 -0400 Subject: [PATCH 05/56] wip debugging, add max_value method for FieldElement, include kind in GenericType since it's always numeric, add (Type|Elaborator)::type_variable_with_kind, add stub 'evaluate_to_field_element', make integral_maximum_size work on FieldElement's --- acvm-repo/acir_field/src/field_element.rs | 4 + compiler/noirc_frontend/src/elaborator/mod.rs | 2 +- .../noirc_frontend/src/elaborator/patterns.rs | 7 +- .../noirc_frontend/src/elaborator/types.rs | 9 + .../src/hir/comptime/interpreter.rs | 7 +- compiler/noirc_frontend/src/hir_def/types.rs | 60 +- .../src/monomorphization/mod.rs | 11 +- compiler/noirc_frontend/src/node_interner.rs | 4 +- compiler/noirc_frontend/src/tests.rs | 6815 +++++++++-------- .../src/tests/name_shadowing.rs | 831 +- 10 files changed, 3904 insertions(+), 3846 deletions(-) diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 2323f008dbe..dc4d76e0b99 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -172,6 +172,10 @@ impl From for FieldElement { } impl FieldElement { + pub fn max_value() -> Self { + Self::zero() - Self::one() + } + pub fn from_repr(field: F) -> Self { Self(field) } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 79dcf602aea..c7367e96de7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -360,7 +360,7 @@ impl<'context> Elaborator<'context> { // Introduce all numeric generics into scope for generic in &all_generics { if let Kind::Numeric(typ) = &generic.kind { - let definition = DefinitionKind::GenericType(generic.type_var.clone()); + let definition = DefinitionKind::GenericType(generic.type_var.clone(), typ.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = self.add_variable_decl_inner(ident, false, false, false, definition); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 7afa3215566..c599d447b28 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::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - ResolvedGeneric, Shared, StructType, Type, TypeBindings, + ResolvedGeneric, Shared, StructType, Type, TypeBindings, TypeVariable, TypeVariableKind, }; use super::{Elaborator, ResolverMeta}; @@ -551,13 +551,14 @@ impl<'context> Elaborator<'context> { self.interner.add_global_reference(global_id, hir_ident.location); } - DefinitionKind::GenericType(_) => { + DefinitionKind::GenericType(_, ref numeric_typ) => { // Initialize numeric generics to a polymorphic integer type in case // they're used in expressions. We must do this here since type_check_variable // does not check definition kinds and otherwise expects parameters to // already be typed. if self.interner.definition_type(hir_ident.id) == Type::Error { - let typ = Type::polymorphic_integer_or_field(self.interner); + let type_var_kind = TypeVariableKind::Numeric(numeric_typ.clone()); + let typ = self.type_variable_with_kind(type_var_kind); self.interner.push_definition_type(hir_ident.id, typ); } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index f8a48ed4778..b2c7abad7f1 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -677,6 +677,15 @@ impl<'context> Elaborator<'context> { typ } + /// Return a fresh integer type variable and log it + /// in self.type_variables to default it later. + pub(super) fn type_variable_with_kind(&mut self, type_var_kind: TypeVariableKind) -> Type { + let typ = Type::type_variable_with_kind(self.interner, type_var_kind); + self.push_type_variable(typ.clone()); + typ + } + + /// Translates a (possibly Unspecified) UnresolvedType to a Type. /// Any UnresolvedType::Unspecified encountered are replaced with fresh type variables. pub(super) fn resolve_inferred_type(&mut self, typ: UnresolvedType) -> Type { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index d18a17c5a4a..52cf81f09f3 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -583,15 +583,16 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(value) } } - DefinitionKind::GenericType(type_variable) => { + DefinitionKind::GenericType(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => None, - TypeBinding::Bound(binding) => binding.evaluate_to_u32(), + // TODO: fix clone + TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()), }; if let Some(value) = value { let typ = self.elaborator.interner.id_type(id); - self.evaluate_integer((value as u128).into(), false, id) + self.evaluate_integer(value, false, id) } else { let location = self.elaborator.interner.expr_location(&id); let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 176a5cd6121..aee6d51ac6c 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -5,6 +5,7 @@ use std::{ rc::Rc, }; +use acvm::FieldElement; use crate::{ ast::IntegerBitSize, hir::type_check::{generics::TraitGenerics, TypeCheckError}, @@ -180,14 +181,14 @@ impl Kind { } /// Ensure the given value fits in max(u32::MAX, max_of_self) - fn ensure_value_fits(&self, value: u32) -> Option { + pub(crate) fn ensure_value_fits>(&self, value: T) -> Option { match self { Self::Normal => Some(value), Self::Numeric(typ) => { match typ.integral_maximum_size() { None => Some(value), Some(maximum_size) => { - if value <= maximum_size { + if value.clone().into() <= maximum_size { Some(value) } else { None @@ -722,7 +723,9 @@ impl std::fmt::Display for Type { if let TypeBinding::Unbound(_) = &*binding.borrow() { write!(f, "{}", typ) } else { - write!(f, "{}", binding.borrow()) + // TODO: revert after debugging + // write!(f, "{}", binding.borrow()) + write!(f, "{}:! {}", binding.borrow(), typ) } } Type::Struct(s, args) => { @@ -755,12 +758,17 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { + Type::NamedGeneric(binding, name, kind) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), + // TODO: revert after debugging + // TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_", kind), + TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_:! {:?}", kind), TypeBinding::Unbound(_) => write!(f, "{name}"), }, - Type::Constant(x, _kind) => write!(f, "{x}"), + + // TODO: revert after debugging + // Type::Constant(x, _kind) => write!(f, "{x}"), + Type::Constant(x, kind) => write!(f, "{x}:! {kind}"), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) @@ -811,7 +819,9 @@ impl std::fmt::Display for BinaryTypeOperator { impl std::fmt::Display for TypeVariableId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "_") + // TODO: revert after debugging + // TODO remove '!' + write!(f, "_!") } } @@ -860,18 +870,20 @@ impl Type { Type::TypeVariable(var, TypeVariableKind::Normal) } - pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { + pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: TypeVariableKind) -> Type { let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::IntegerOrField; let var = TypeVariable::unbound(id); - Type::TypeVariable(var, kind) + Type::TypeVariable(var, type_var_kind) + } + + pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { + let type_var_kind = TypeVariableKind::IntegerOrField; + Self::type_variable_with_kind(interner, type_var_kind) } pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { - let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::Integer; - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, kind) + let type_var_kind = TypeVariableKind::Integer; + Self::type_variable_with_kind(interner, type_var_kind) } /// A bit of an awkward name for this function - this function returns @@ -1602,6 +1614,7 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { + // TODO: replace evaluate_to_u32 if let Some(other_value) = other.evaluate_to_u32() { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) @@ -1789,6 +1802,11 @@ impl Type { } } + // TODO! + pub(crate) fn evaluate_to_field_element(&self) -> acvm::FieldElement { + unimplemented!("evaluate_to_field_element") + } + /// Iterate over the fields of this type. /// Panics if the type is not a struct or tuple. pub fn iter_fields(&self) -> impl Iterator { @@ -2300,29 +2318,25 @@ impl Type { } } - fn integral_maximum_size(&self) -> Option { + fn integral_maximum_size(&self) -> Option { match self { - Type::FieldElement => Some(u32::MAX), + Type::FieldElement => Some(FieldElement::max_value()), Type::Integer(sign, num_bits) => { let mut max_bit_size = num_bits.bit_size(); if sign == &Signedness::Signed { max_bit_size >>= 1; } - if 32 <= max_bit_size { - Some(u32::MAX) - } else { - Some((1u32 << max_bit_size) - 1) - } + Some(((1u128 << max_bit_size) - 1).into()) } Type::TypeVariable(binding, TypeVariableKind::Integer | TypeVariableKind::IntegerOrField) => { match &*binding.borrow() { TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_) => Some(u32::MAX), + TypeBinding::Unbound(_) => Some(FieldElement::max_value()), } } Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), - Type::Bool => Some(1), + Type::Bool => Some(FieldElement::from(1u128)), Type::NamedGeneric(_binding, _name, Kind::Numeric(typ)) => typ.integral_maximum_size(), _ => None, } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index bdafba238b5..117da91f509 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -21,7 +21,7 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Type, TypeBinding, TypeBindings, + Kind, Type, TypeBinding, TypeBindings, }; use acvm::{acir::AcirField, FieldElement}; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -916,17 +916,15 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Ident(ident) } }, - DefinitionKind::GenericType(type_variable) => { + DefinitionKind::GenericType(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => { unreachable!("Unbound type variable used in expression") } - TypeBinding::Bound(binding) => binding.evaluate_to_u32().unwrap_or_else(|| { + TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()).unwrap_or_else(|| { panic!("Non-numeric type variable used in expression expecting a value") }), }; - - let value = FieldElement::from(value as u128); let location = self.interner.id_location(expr_id); let typ = Self::convert_type(&typ, ident.location)?; ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) @@ -1769,7 +1767,8 @@ impl<'interner> Monomorphizer<'interner> { let lambda_name = "zeroed_lambda"; let parameters = vecmap(parameter_types, |parameter_type| { - (self.next_local_id(), false, "_".into(), parameter_type.clone()) + // TODO remove "??" + (self.next_local_id(), false, "_??".into(), parameter_type.clone()) }); let body = self.zeroed_value_of_type(ret_type, location); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 7fe5187d1bb..d4e3163dd3a 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -570,7 +570,7 @@ pub enum DefinitionKind { /// Generic types in functions (T, U in `fn foo(...)` are declared as variables /// in scope in case they resolve to numeric generics later. - GenericType(TypeVariable), + GenericType(TypeVariable, Box), } impl DefinitionKind { @@ -585,7 +585,7 @@ impl DefinitionKind { DefinitionKind::Function(_) => None, DefinitionKind::Global(_) => None, DefinitionKind::Local(id) => *id, - DefinitionKind::GenericType(_) => None, + DefinitionKind::GenericType(_, _) => None, } } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index e21db0790d7..6a5127d1f7c 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -125,733 +125,734 @@ fn assert_no_errors(src: &str) { } } -#[test] -fn check_trait_implemented_for_all_t() { - let src = " - trait Default { - fn default() -> Self; - } - - trait Eq { - fn eq(self, other: Self) -> bool; - } - - trait IsDefault { - fn is_default(self) -> bool; - } - - impl IsDefault for T where T: Default + Eq { - fn is_default(self) -> bool { - self.eq(T::default()) - } - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - impl Default for u64 { - fn default() -> Self { - 0 - } - } - - impl Default for Foo { - fn default() -> Self { - Foo { a: Default::default() } - } - } - - fn main(a: Foo) -> pub bool { - a.is_default() - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_implementation_duplicate_method() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Field; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - // Duplicate trait methods should not compile - fn default(x: Field, y: Field) -> Field { - y + 2 * x - } - // Duplicate trait methods should not compile - fn default(x: Field, y: Field) -> Field { - x + 2 * y - } - } - - fn main() {}"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_return_type() { - let src = " - trait Default { - fn default() -> Self; - } - - struct Foo { - } - - impl Default for Foo { - fn default() -> Field { - 0 - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_return_type2() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Self; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - fn default(x: Field, _y: Field) -> Field { - x - } - } - - fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_missing_implementation() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Self; - - fn method2(x: Field) -> Field; - - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - fn default(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_span: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_not_in_scope() { - let src = " - struct Foo { - bar: Field, - array: [Field; 2], - } - - // Default trait does not exist - impl Default for Foo { - fn default(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() { - } - - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_name() { - let src = " - trait Default { - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - // wrong trait name method should not compile - impl Default for Foo { - fn does_not_exist(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() { - }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for (err, _file_id) in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter() { - let src = " - trait Default { - fn default(x: Field) -> Self; - } - - struct Foo { - bar: u32, - } - - impl Default for Foo { - fn default(x: u32) -> Self { - Foo {bar: x} - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter2() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Self; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - fn default(x: Field, y: Foo) -> Self { - Self { bar: x, array: [x, y.bar] } - } - } - - fn main() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter_type() { - let src = " - pub trait Default { - fn default(x: Field, y: NotAType) -> Field; - } - - fn main(x: Field, y: Field) { - assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameters_count() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Self; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - fn default(x: Field) -> Self { - Self { bar: x, array: [x, x] } - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_impl_for_non_type() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Field; - } - - impl Default for main { - fn default(x: Field, y: Field) -> Field { - x + y - } - } - - fn main() {} - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(expected, "type"); - assert_eq!(got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_impl_struct_not_trait() { - let src = " - struct Foo { - bar: Field, - array: [Field; 2], - } - - struct Default { - x: Field, - z: Field, - } - - // Default is a struct not a trait - impl Default for Foo { - fn default(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() {} - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_declaration() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Self; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - - trait Default { - fn default(x: Field) -> Self; - } - - fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_implementation() { - let src = " - trait Default { - } - struct Foo { - bar: Field, - } - - impl Default for Foo { - } - impl Default for Foo { - } - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_implementation_with_alias() { - let src = " - trait Default { - } - - struct MyStruct { - } - - type MyType = MyStruct; - - impl Default for MyStruct { - } - - impl Default for MyType { - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn test_impl_self_within_default_def() { - let src = " - trait Bar { - fn ok(self) -> Self; - - fn ref_ok(self) -> Self { - self.ok() - } - } - - impl Bar for (T, T) where T: Bar { - fn ok(self) -> Self { - self - } - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_as_type_as_fn_parameter() { - let src = " - trait Eq { - fn eq(self, other: Self) -> bool; - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - fn test_eq(x: impl Eq) -> bool { - x.eq(x) - } - - fn main(a: Foo) -> pub bool { - test_eq(a) - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_as_type_as_two_fn_parameters() { - let src = " - trait Eq { - fn eq(self, other: Self) -> bool; - } - - trait Test { - fn test(self) -> bool; - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - impl Test for u64 { - fn test(self) -> bool { self == self } - } - - fn test_eq(x: impl Eq, y: impl Test) -> bool { - x.eq(x) == y.test() - } - - fn main(a: Foo, b: u64) -> pub bool { - test_eq(a, b) - }"; - assert_no_errors(src); -} +// TODO: re-enable! +// #[test] +// fn check_trait_implemented_for_all_t() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// trait IsDefault { +// fn is_default(self) -> bool; +// } +// +// impl IsDefault for T where T: Default + Eq { +// fn is_default(self) -> bool { +// self.eq(T::default()) +// } +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// impl Default for u64 { +// fn default() -> Self { +// 0 +// } +// } +// +// impl Default for Foo { +// fn default() -> Self { +// Foo { a: Default::default() } +// } +// } +// +// fn main(a: Foo) -> pub bool { +// a.is_default() +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_implementation_duplicate_method() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Field; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// // Duplicate trait methods should not compile +// fn default(x: Field, y: Field) -> Field { +// y + 2 * x +// } +// // Duplicate trait methods should not compile +// fn default(x: Field, y: Field) -> Field { +// x + 2 * y +// } +// } +// +// fn main() {}"; +// +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { +// typ, +// first_def, +// second_def, +// }) => { +// assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); +// assert_eq!(first_def, "default"); +// assert_eq!(second_def, "default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_return_type() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// struct Foo { +// } +// +// impl Default for Foo { +// fn default() -> Field { +// 0 +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, +// expr_typ, +// expr_span: _, +// }) => { +// assert_eq!(expected_typ, "Foo"); +// assert_eq!(expr_typ, "Field"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_return_type2() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Self; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// fn default(x: Field, _y: Field) -> Field { +// x +// } +// } +// +// fn main() { +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, +// expr_typ, +// expr_span: _, +// }) => { +// assert_eq!(expected_typ, "Foo"); +// assert_eq!(expr_typ, "Field"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_missing_implementation() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Self; +// +// fn method2(x: Field) -> Field; +// +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// fn default(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { +// trait_name, +// method_name, +// trait_impl_span: _, +// }) => { +// assert_eq!(trait_name, "Default"); +// assert_eq!(method_name, "method2"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_not_in_scope() { +// let src = " +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// // Default trait does not exist +// impl Default for Foo { +// fn default(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() { +// } +// +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { +// trait_path, +// }) => { +// assert_eq!(trait_path.as_string(), "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_name() { +// let src = " +// trait Default { +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// // wrong trait name method should not compile +// impl Default for Foo { +// fn does_not_exist(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() { +// }"; +// let compilation_errors = get_program_errors(src); +// assert!(!has_parser_error(&compilation_errors)); +// assert!( +// compilation_errors.len() == 1, +// "Expected 1 compilation error, got: {:?}", +// compilation_errors +// ); +// +// for (err, _file_id) in compilation_errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { +// trait_name, +// impl_method, +// }) => { +// assert_eq!(trait_name, "Default"); +// assert_eq!(impl_method, "does_not_exist"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter() { +// let src = " +// trait Default { +// fn default(x: Field) -> Self; +// } +// +// struct Foo { +// bar: u32, +// } +// +// impl Default for Foo { +// fn default(x: u32) -> Self { +// Foo {bar: x} +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { +// method_name, +// expected_typ, +// actual_typ, +// .. +// }) => { +// assert_eq!(method_name, "default"); +// assert_eq!(expected_typ, "Field"); +// assert_eq!(actual_typ, "u32"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter2() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Self; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// fn default(x: Field, y: Foo) -> Self { +// Self { bar: x, array: [x, y.bar] } +// } +// } +// +// fn main() { +// }"; +// +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { +// method_name, +// expected_typ, +// actual_typ, +// .. +// }) => { +// assert_eq!(method_name, "default"); +// assert_eq!(expected_typ, "Field"); +// assert_eq!(actual_typ, "Foo"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter_type() { +// let src = " +// pub trait Default { +// fn default(x: Field, y: NotAType) -> Field; +// } +// +// fn main(x: Field, y: Field) { +// assert(y == x); +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// +// // This is a duplicate error in the name resolver & type checker. +// // In the elaborator there is no duplicate and only 1 error is issued +// assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::PathResolutionError( +// PathResolutionError::Unresolved(ident), +// )) => { +// assert_eq!(ident, "NotAType"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameters_count() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Self; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// fn default(x: Field) -> Self { +// Self { bar: x, array: [x, x] } +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { +// actual_num_parameters, +// expected_num_parameters, +// trait_name, +// method_name, +// .. +// }) => { +// assert_eq!(actual_num_parameters, &1_usize); +// assert_eq!(expected_num_parameters, &2_usize); +// assert_eq!(method_name, "default"); +// assert_eq!(trait_name, "Default"); +// } +// _ => { +// panic!("No other errors are expected in this test case! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_impl_for_non_type() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Field; +// } +// +// impl Default for main { +// fn default(x: Field, y: Field) -> Field { +// x + y +// } +// } +// +// fn main() {} +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { +// assert_eq!(expected, "type"); +// assert_eq!(got, "function"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_impl_struct_not_trait() { +// let src = " +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// struct Default { +// x: Field, +// z: Field, +// } +// +// // Default is a struct not a trait +// impl Default for Foo { +// fn default(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() {} +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { +// not_a_trait_name, +// }) => { +// assert_eq!(not_a_trait_name.to_string(), "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_declaration() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Self; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// fn default(x: Field,y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// +// trait Default { +// fn default(x: Field) -> Self; +// } +// +// fn main() { +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { +// typ, +// first_def, +// second_def, +// }) => { +// assert_eq!(typ, &DuplicateType::Trait); +// assert_eq!(first_def, "Default"); +// assert_eq!(second_def, "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_implementation() { +// let src = " +// trait Default { +// } +// struct Foo { +// bar: Field, +// } +// +// impl Default for Foo { +// } +// impl Default for Foo { +// } +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { +// .. +// }) => (), +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { +// .. +// }) => (), +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_implementation_with_alias() { +// let src = " +// trait Default { +// } +// +// struct MyStruct { +// } +// +// type MyType = MyStruct; +// +// impl Default for MyStruct { +// } +// +// impl Default for MyType { +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { +// .. +// }) => (), +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { +// .. +// }) => (), +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn test_impl_self_within_default_def() { +// let src = " +// trait Bar { +// fn ok(self) -> Self; +// +// fn ref_ok(self) -> Self { +// self.ok() +// } +// } +// +// impl Bar for (T, T) where T: Bar { +// fn ok(self) -> Self { +// self +// } +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_as_type_as_fn_parameter() { +// let src = " +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// fn test_eq(x: impl Eq) -> bool { +// x.eq(x) +// } +// +// fn main(a: Foo) -> pub bool { +// test_eq(a) +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_as_type_as_two_fn_parameters() { +// let src = " +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// trait Test { +// fn test(self) -> bool; +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// impl Test for u64 { +// fn test(self) -> bool { self == self } +// } +// +// fn test_eq(x: impl Eq, y: impl Test) -> bool { +// x.eq(x) == y.test() +// } +// +// fn main(a: Foo, b: u64) -> pub bool { +// test_eq(a, b) +// }"; +// assert_no_errors(src); +// } fn get_program_captures(src: &str) -> Vec> { let (program, context, _errors) = get_program(src); @@ -908,312 +909,312 @@ fn get_lambda_captures( } } -#[test] -fn resolve_empty_function() { - let src = " - fn main() { - - } - "; - assert_no_errors(src); -} -#[test] -fn resolve_basic_function() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(y == x); - } - "#; - assert_no_errors(src); -} -#[test] -fn resolve_unused_var() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(x == x); - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } -} - -#[test] -fn resolve_unresolved_var() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(y == z); - } - "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } -} - -#[test] -fn unresolved_path() { - let src = " - fn main(x : Field) { - let _z = some::path::to::a::func(x); - } - "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } -} - -#[test] -fn resolve_literal_expr() { - let src = r#" - fn main(x : Field) { - let y = 5; - assert(y == x); - } - "#; - assert_no_errors(src); -} - -#[test] -fn multiple_resolution_errors() { - let src = r#" - fn main(x : Field) { - let y = foo::bar(x); - let z = y + a; - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } -} - -#[test] -fn resolve_prefix_expr() { - let src = r#" - fn main(x : Field) { - let _y = -x; - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_for_expr() { - let src = r#" - fn main(x : u64) { - for i in 1..20 { - let _z = x + i; - }; - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_call_expr() { - let src = r#" - fn main(x : Field) { - let _z = foo(x); - } - - fn foo(x : Field) -> Field { - x - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_shadowing() { - let src = r#" - fn main(x : Field) { - let x = foo(x); - let x = x; - let (x, x) = (x, x); - let _ = x; - } - - fn foo(x : Field) -> Field { - x - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_basic_closure() { - let src = r#" - fn main(x : Field) -> pub Field { - let closure = |y| y + x; - closure(x) - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_simplified_closure() { - // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { - let y = x; - let ret_capture = || { - y - }; - ret_capture() - } - - fn main(x: Field) { - assert(do_closure(x) == 100); - } - - "#; - let parsed_captures = get_program_captures(src); - let expected_captures = vec![vec!["y".to_string()]]; - assert_eq!(expected_captures, parsed_captures); -} - -#[test] -fn resolve_complex_closures() { - let src = r#" - fn main(x: Field) -> pub Field { - let closure_without_captures = |x: Field| -> Field { x + x }; - let a = closure_without_captures(1); - - let closure_capturing_a_param = |y: Field| -> Field { y + x }; - let b = closure_capturing_a_param(2); - - let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; - let c = closure_capturing_a_local_var(3); - - let closure_with_transitive_captures = |y: Field| -> Field { - let d = 5; - let nested_closure = |z: Field| -> Field { - let doubly_nested_closure = |w: Field| -> Field { w + x + b }; - a + z + y + d + x + doubly_nested_closure(4) + x + y - }; - let res = nested_closure(5); - res - }; - - a + b + c + closure_with_transitive_captures(6) - } - "#; - assert_no_errors(src); - - let expected_captures = vec![ - vec![], - vec!["x".to_string()], - vec!["b".to_string()], - vec!["x".to_string(), "b".to_string(), "a".to_string()], - vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], - vec!["x".to_string(), "b".to_string()], - ]; - - let parsed_captures = get_program_captures(src); - - assert_eq!(expected_captures, parsed_captures); -} - -#[test] -fn resolve_fmt_strings() { - let src = r#" - fn main() { - let string = f"this is i: {i}"; - println(string); - - println(f"I want to print {0}"); - - let new_val = 10; - println(f"random_string{new_val}{new_val}"); - } - fn println(x : T) -> T { - x - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { - name, - .. - }) => { - assert_eq!(name, "0"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_span, - }) => { - let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); - assert!( - a == "println(string)" - || a == "println(f\"I want to print {0}\")" - || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } -} +// #[test] +// fn resolve_empty_function() { +// let src = " +// fn main() { +// +// } +// "; +// assert_no_errors(src); +// } +// #[test] +// fn resolve_basic_function() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(y == x); +// } +// "#; +// assert_no_errors(src); +// } +// #[test] +// fn resolve_unused_var() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(x == x); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unused variable +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { +// assert_eq!(&ident.0.contents, "y"); +// } +// _ => unreachable!("we should only have an unused var error"), +// } +// } +// +// #[test] +// fn resolve_unresolved_var() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(y == z); +// } +// "#; +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { +// assert_eq!(name, "z"); +// } +// _ => unimplemented!("we should only have an unresolved variable"), +// } +// } +// +// #[test] +// fn unresolved_path() { +// let src = " +// fn main(x : Field) { +// let _z = some::path::to::a::func(x); +// } +// "; +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "some"); +// } +// _ => unimplemented!("we should only have an unresolved function"), +// }; +// } +// _ => unimplemented!(), +// } +// } +// } +// +// #[test] +// fn resolve_literal_expr() { +// let src = r#" +// fn main(x : Field) { +// let y = 5; +// assert(y == x); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn multiple_resolution_errors() { +// let src = r#" +// fn main(x : Field) { +// let y = foo::bar(x); +// let z = y + a; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); +// +// // Errors are: +// // `a` is undeclared +// // `z` is unused +// // `foo::bar` does not exist +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::UnusedVariable { ident } => { +// assert_eq!(&ident.0.contents, "z"); +// } +// ResolverError::VariableNotDeclared { name, .. } => { +// assert_eq!(name, "a"); +// } +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "foo"); +// } +// _ => unimplemented!(), +// }; +// } +// _ => unimplemented!(), +// } +// } +// } +// +// #[test] +// fn resolve_prefix_expr() { +// let src = r#" +// fn main(x : Field) { +// let _y = -x; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_for_expr() { +// let src = r#" +// fn main(x : u64) { +// for i in 1..20 { +// let _z = x + i; +// }; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_call_expr() { +// let src = r#" +// fn main(x : Field) { +// let _z = foo(x); +// } +// +// fn foo(x : Field) -> Field { +// x +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_shadowing() { +// let src = r#" +// fn main(x : Field) { +// let x = foo(x); +// let x = x; +// let (x, x) = (x, x); +// let _ = x; +// } +// +// fn foo(x : Field) -> Field { +// x +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_basic_closure() { +// let src = r#" +// fn main(x : Field) -> pub Field { +// let closure = |y| y + x; +// closure(x) +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_simplified_closure() { +// // based on bug https://github.com/noir-lang/noir/issues/1088 +// +// let src = r#"fn do_closure(x: Field) -> Field { +// let y = x; +// let ret_capture = || { +// y +// }; +// ret_capture() +// } +// +// fn main(x: Field) { +// assert(do_closure(x) == 100); +// } +// +// "#; +// let parsed_captures = get_program_captures(src); +// let expected_captures = vec![vec!["y".to_string()]]; +// assert_eq!(expected_captures, parsed_captures); +// } +// +// #[test] +// fn resolve_complex_closures() { +// let src = r#" +// fn main(x: Field) -> pub Field { +// let closure_without_captures = |x: Field| -> Field { x + x }; +// let a = closure_without_captures(1); +// +// let closure_capturing_a_param = |y: Field| -> Field { y + x }; +// let b = closure_capturing_a_param(2); +// +// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; +// let c = closure_capturing_a_local_var(3); +// +// let closure_with_transitive_captures = |y: Field| -> Field { +// let d = 5; +// let nested_closure = |z: Field| -> Field { +// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; +// a + z + y + d + x + doubly_nested_closure(4) + x + y +// }; +// let res = nested_closure(5); +// res +// }; +// +// a + b + c + closure_with_transitive_captures(6) +// } +// "#; +// assert_no_errors(src); +// +// let expected_captures = vec![ +// vec![], +// vec!["x".to_string()], +// vec!["b".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], +// vec!["x".to_string(), "b".to_string()], +// ]; +// +// let parsed_captures = get_program_captures(src); +// +// assert_eq!(expected_captures, parsed_captures); +// } +// +// #[test] +// fn resolve_fmt_strings() { +// let src = r#" +// fn main() { +// let string = f"this is i: {i}"; +// println(string); +// +// println(f"I want to print {0}"); +// +// let new_val = 10; +// println(f"random_string{new_val}{new_val}"); +// } +// fn println(x : T) -> T { +// x +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { +// name, .. +// }) => { +// assert_eq!(name, "i"); +// } +// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { +// name, +// .. +// }) => { +// assert_eq!(name, "0"); +// } +// CompilationError::TypeError(TypeCheckError::UnusedResultError { +// expr_type: _, +// expr_span, +// }) => { +// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); +// assert!( +// a == "println(string)" +// || a == "println(f\"I want to print {0}\")" +// || a == "println(f\"random_string{new_val}{new_val}\")" +// ); +// } +// _ => unimplemented!(), +// }; +// } +// } fn check_rewrite(src: &str, expected: &str) { let (_program, mut context, _errors) = get_program(src); @@ -1222,118 +1223,120 @@ fn check_rewrite(src: &str, expected: &str) { assert!(format!("{}", program) == expected); } -#[test] -fn simple_closure_with_no_captured_variables() { - let src = r#" - fn main() -> pub Field { - let x = 1; - let closure = || x; - closure() - } - "#; - - let expected_rewrite = r#"fn main$f0() -> Field { - let x$0 = 1; - let closure$3 = { - let closure_variable$2 = { - let env$1 = (x$l0); - (env$l1, lambda$f1) - }; - closure_variable$l2 - }; - { - let tmp$4 = closure$l3; - tmp$l4.1(tmp$l4.0) - } -} -fn lambda$f1(mut env$l1: (Field)) -> Field { - env$l1.0 -} -"#; - check_rewrite(src, expected_rewrite); -} - -#[test] -fn deny_cyclic_globals() { - let src = r#" - global A = B; - global B = A; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn deny_cyclic_type_aliases() { - let src = r#" - type A = B; - type B = A; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn ensure_nested_type_aliases_type_check() { - let src = r#" - type A = B; - type B = u8; - fn main() { - let _a: A = 0 as u16; - } - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn type_aliases_in_entry_point() { - let src = r#" - type Foo = u8; - fn main(_x: Foo) {} - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn operators_in_global_used_in_type() { - let src = r#" - global ONE: u32 = 1; - global COUNT: u32 = ONE + 2; - fn main() { - let _array: [Field; COUNT] = [1, 2, 3]; - } - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn break_and_continue_in_constrained_fn() { - let src = r#" - fn main() { - for i in 0 .. 10 { - if i == 2 { - continue; - } - if i == 5 { - break; - } - } - } - "#; - assert_eq!(get_program_errors(src).len(), 2); -} - -#[test] -fn break_and_continue_outside_loop() { - let src = r#" - unconstrained fn main() { - continue; - break; - } - "#; - assert_eq!(get_program_errors(src).len(), 2); -} - +// TODO: re-enable! +// #[test] +// fn simple_closure_with_no_captured_variables() { +// let src = r#" +// fn main() -> pub Field { +// let x = 1; +// let closure = || x; +// closure() +// } +// "#; +// +// let expected_rewrite = r#"fn main$f0() -> Field { +// let x$0 = 1; +// let closure$3 = { +// let closure_variable$2 = { +// let env$1 = (x$l0); +// (env$l1, lambda$f1) +// }; +// closure_variable$l2 +// }; +// { +// let tmp$4 = closure$l3; +// tmp$l4.1(tmp$l4.0) +// } +// } +// fn lambda$f1(mut env$l1: (Field)) -> Field { +// env$l1.0 +// } +// "#; +// check_rewrite(src, expected_rewrite); +// } +// +// #[test] +// fn deny_cyclic_globals() { +// let src = r#" +// global A = B; +// global B = A; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn deny_cyclic_type_aliases() { +// let src = r#" +// type A = B; +// type B = A; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn ensure_nested_type_aliases_type_check() { +// let src = r#" +// type A = B; +// type B = u8; +// fn main() { +// let _a: A = 0 as u16; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn type_aliases_in_entry_point() { +// let src = r#" +// type Foo = u8; +// fn main(_x: Foo) {} +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn operators_in_global_used_in_type() { +// let src = r#" +// global ONE: u32 = 1; +// global COUNT: u32 = ONE + 2; +// fn main() { +// let _array: [Field; COUNT] = [1, 2, 3]; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn break_and_continue_in_constrained_fn() { +// let src = r#" +// fn main() { +// for i in 0 .. 10 { +// if i == 2 { +// continue; +// } +// if i == 5 { +// break; +// } +// } +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 2); +// } +// +// #[test] +// fn break_and_continue_outside_loop() { +// let src = r#" +// unconstrained fn main() { +// continue; +// break; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 2); +// } + +// TODO: failing // Regression for #2540 #[test] fn for_loop_over_array() { @@ -1348,2213 +1351,2231 @@ fn for_loop_over_array() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -// Regression for #4545 -#[test] -fn type_aliases_in_main() { - let src = r#" - type Outer = [u8; N]; - fn main(_arg: Outer<1>) {} - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context - let src = r#" - mut global FOO: Field = 0; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn deny_inline_attribute_on_unconstrained() { - let src = r#" - #[no_predicates] - unconstrained pub fn foo(x: Field, y: Field) { - assert(x != y); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); -} - -#[test] -fn deny_fold_attribute_on_unconstrained() { - let src = r#" - #[fold] - unconstrained pub fn foo(x: Field, y: Field) { - assert(x != y); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); -} - -#[test] -fn specify_function_types_with_turbofish() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - impl Default for u64 { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - fn generic_func() -> (T, U) where T: Default, U: Default { - (T::default(), U::default()) - } - - fn main() { - let _ = generic_func::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn specify_method_types_with_turbofish() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - struct Foo { - inner: T - } - - impl Foo { - fn generic_method(_self: Self) -> U where U: Default { - U::default() - } - } - - fn main() { - let foo: Foo = Foo { inner: 1 }; - let _ = foo.generic_method::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn incorrect_turbofish_count_function_call() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - impl Default for u64 { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - fn generic_func() -> (T, U) where T: Default, U: Default { - (T::default(), U::default()) - } - - fn main() { - let _ = generic_func::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); -} - -#[test] -fn incorrect_turbofish_count_method_call() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - struct Foo { - inner: T - } - - impl Foo { - fn generic_method(_self: Self) -> U where U: Default { - U::default() - } - } - - fn main() { - let foo: Foo = Foo { inner: 1 }; - let _ = foo.generic_method::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); -} - -#[test] -fn struct_numeric_generic_in_function() { - let src = r#" - struct Foo { - inner: u64 - } - - pub fn bar() { } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} - -#[test] -fn struct_numeric_generic_in_struct() { - let src = r#" - pub struct Foo { - inner: u64 - } - - pub struct Bar { } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::UnsupportedNumericGenericType { .. } - ), - )); -} - -#[test] -fn bool_numeric_generic() { - let src = r#" - pub fn read() -> Field { - if N { - 0 - } else { - 1 - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} - -#[test] -fn numeric_generic_binary_operation_type_mismatch() { - let src = r#" - pub fn foo() -> bool { - let mut check: bool = true; - check = N; - check - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); -} - -#[test] -fn bool_generic_as_loop_bound() { - let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here - fields[i] = i + 1; - } - assert(fields[0] == 1); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2].0 - else { - panic!("Got an error other than a type mismatch"); - }; - - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); -} - -#[test] -fn numeric_generic_in_function_signature() { - let src = r#" - pub fn foo(arr: [Field; N]) -> [Field; N] { arr } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_as_struct_field_type_fails() { - let src = r#" - pub struct Foo { - a: Field, - b: N, - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn normal_generic_as_array_length() { - let src = r#" - pub struct Foo { - a: Field, - b: [Field; N], - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_as_param_type() { - let src = r#" - pub fn foo(x: I) -> I { - let _q: I = 5; - x - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_used_in_nested_type_fails() { - let src = r#" - pub struct Foo { - a: Field, - b: Bar, - } - struct Bar { - inner: N - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn normal_generic_used_in_nested_array_length_fail() { - let src = r#" - pub struct Foo { - a: Field, - b: Bar, - } - pub struct Bar { - inner: [Field; N] - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_used_in_nested_type_pass() { - // The order of these structs should not be changed to make sure - // that we are accurately resolving all struct generics before struct fields - let src = r#" - pub struct NestedNumeric { - a: Field, - b: InnerNumeric - } - pub struct InnerNumeric { - inner: [u64; N], - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_trait() { - // We want to make sure that `N` in `impl Deserialize` does - // not trigger `expected type, found numeric generic parameter N` as the trait - // does in fact expect a numeric generic. - let src = r#" - struct MyType { - a: Field, - b: Field, - c: Field, - d: T, - } - - impl Deserialize for MyType { - fn deserialize(fields: [Field; N], other: T) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2], d: other } - } - } - - trait Deserialize { - fn deserialize(fields: [Field; N], other: T) -> Self; - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_in_trait_impl_with_extra_impl_generics() { - let src = r#" - trait Default { - fn default() -> Self; - } - - struct MyType { - a: Field, - b: Field, - c: Field, - d: T, - } - - // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. - // `N` is used first in the trait impl generics (`Deserialize for MyType`). - // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind - // while `T` has a normal kind. - impl Deserialize for MyType where T: Default { - fn deserialize(fields: [Field; N]) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } - } - } - - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_where_clause() { - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_turbofish() { - let src = r#" - pub fn double() -> u32 { - // Used as an expression - N * 2 - } - - pub fn double_numeric_generics_test() { - // Example usage of a numeric generic arguments. - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); - } - "#; - assert_no_errors(src); -} - -#[test] -fn constant_used_with_numeric_generic() { - let src = r#" - struct ValueNote { - value: Field, - } - - trait Serialize { - fn serialize(self) -> [Field; N]; - } - - impl Serialize<1> for ValueNote { - fn serialize(self) -> [Field; 1] { - [self.value] - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn normal_generic_used_when_numeric_expected_in_where_clause() { - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - T::deserialize([0, 1]) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); -} - -#[test] -fn numeric_generics_type_kind_mismatch() { - let src = r#" - fn foo() -> u16 { - N as u16 - } - - global J: u16 = 10; - - fn bar() -> u16 { - foo::() - } - - global M: u16 = 3; - - fn main() { - let _ = bar::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generics_value_kind_mismatch_u32_u64() { - let src = r#" - struct BoundedVec { - storage: [T; MaxLen], - // can't be compared to MaxLen: u32 - // can't be used to index self.storage - len: u64, - } - - impl BoundedVec { - pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { - // We do this to avoid an unused variable warning on `self` - let _ = self.len; - for _ in 0..Len { } - } - - pub fn push(&mut self, elem: T) { - assert(self.len < MaxLen, "push out of bounds"); - self.storage[self.len] = elem; - self.len += 1; - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); -} - -#[test] -fn quote_code_fragments() { - // This test ensures we can quote (and unquote/splice) code fragments - // which by themselves are not valid code. They only need to be valid - // by the time they are unquoted into the macro's call site. - let src = r#" - fn main() { - comptime { - concat!(quote { assert( }, quote { false); }); - } - } - - comptime fn concat(a: Quoted, b: Quoted) -> Quoted { - quote { $a $b } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); -} - -#[test] -fn impl_stricter_than_trait_no_trait_method_constraints() { - // This test ensures that the error we get from the where clause on the trait impl method - // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. - let src = r#" - trait Serialize { - // We want to make sure we trigger the error when override a trait method - // which itself has no trait constraints. - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } - - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() - } - - struct MyType { - a: T, - b: T, - } - - impl Serialize<2> for MyType { - fn serialize(self) -> [Field; 2] where T: ToField { - [ self.a.to_field(), self.b.to_field() ] - } - } - - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); -} - -#[test] -fn impl_stricter_than_trait_different_generics() { - let src = r#" - trait Default { } - - // Object type of the trait constraint differs - trait Foo { - fn foo_good() where T: Default; - - fn foo_bad() where T: Default; - } - - impl Foo for () { - fn foo_good() where A: Default {} - - fn foo_bad() where B: Default {} - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_object_generics() { - let src = r#" - trait MyTrait { } - - trait OtherTrait {} - - struct Option { - inner: T - } - - struct OtherOption { - inner: Option, - } - - trait Bar { - fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; - - fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; - - fn array_good() where [T; 8]: MyTrait; - - fn array_bad() where [T; 8]: MyTrait; - - fn tuple_good() where (Option, Option): MyTrait; - - fn tuple_bad() where (Option, Option): MyTrait; - } - - impl Bar for () { - fn bar_good() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn bar_bad() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn array_good() where [A; 8]: MyTrait { } - - fn array_bad() where [B; 8]: MyTrait { } - - fn tuple_good() where (Option, Option): MyTrait { } - - fn tuple_bad() where (Option, Option): MyTrait { } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_trait() { - let src = r#" - trait Default { } - - trait OtherDefault { } - - struct Option { - inner: T - } - - trait Bar { - fn bar() where Option: Default; - } - - impl Bar for () { - // Trait constraint differs due to the trait even though the constraint - // types are the same. - fn bar() where Option: OtherDefault {} - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn trait_impl_where_clause_stricter_pass() { - let src = r#" - trait MyTrait { - fn good_foo() where H: OtherTrait; - - fn bad_foo() where H: OtherTrait; - } - - trait OtherTrait {} - - struct Option { - inner: T - } - - impl MyTrait for [T] where Option: MyTrait { - fn good_foo() where B: OtherTrait { } - - fn bad_foo() where A: OtherTrait { } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_trait_generics() { - let src = r#" - trait Foo { - fn foo() where T: T2; - } - - impl Foo for () { - // Should be A: T2 - fn foo() where A: T2 {} - } - - trait T2 {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_not_found_for_inner_impl() { - // We want to guarantee that we get a no impl found error - let src = r#" - trait Serialize { - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } - - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() - } - - struct MyType { - a: T, - b: T, - } - - impl Serialize<2> for MyType where T: ToField { - fn serialize(self) -> [Field; 2] { - [ self.a.to_field(), self.b.to_field() ] - } - } - - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); -} - -// Regression for #5388 -#[test] -fn comptime_let() { - let src = r#"fn main() { - comptime let my_var = 2; - assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn overflowing_u8() { - let src = r#" - fn main() { - let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁸` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_u8() { - let src = r#" - fn main() { - let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn overflowing_i8() { - let src = r#" - fn main() { - let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_i8() { - let src = r#" - fn main() { - let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls - let src = r#" - fn foo() -> [u8; N] { - [0; N] - } - - fn bar() -> [u8; N] { - foo::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); - - // Check for turbofish numeric generics used with method calls - let src = r#" - struct Foo { - a: T - } - - impl Foo { - fn static_method() -> [u8; N] { - [0; N] - } - - fn impl_method(self) -> [T; N] { - [self.a; N] - } - } - - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super() { - let src = r#" - fn some_func() {} - - mod foo { - use super::some_func; - - pub fn bar() { - some_func(); - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super_in_path() { - let src = r#" - fn some_func() {} - - mod foo { - pub fn func() { - super::some_func(); - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); -} - -#[test] -fn cannot_call_unconstrained_function_outside_of_unsafe() { - let src = r#" - fn main() { - foo(); - } - - unconstrained fn foo() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { - let src = r#" - fn main() { - let func = foo; - // Warning should trigger here - func(); - inner(func); - } - - fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here - x(); - } - - unconstrained fn foo() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; - } -} - -#[test] -fn missing_unsafe_block_when_needing_type_annotations() { - // This test is a regression check that even when an unsafe block is missing - // that we still appropriately continue type checking and infer type annotations. - let src = r#" - fn main() { - let z = BigNum { limbs: [2, 0, 0] }; - assert(z.__is_zero() == false); - } - - struct BigNum { - limbs: [u64; N], - } - - impl BigNum { - unconstrained fn __is_zero_impl(self) -> bool { - let mut result: bool = true; - for i in 0..N { - result = result & (self.limbs[i] == 0); - } - result - } - } - - trait BigNumTrait { - fn __is_zero(self) -> bool; - } - - impl BigNumTrait for BigNum { - fn __is_zero(self) -> bool { - self.__is_zero_impl() - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_pass_unconstrained_function_to_regular_function() { - let src = r#" - fn main() { - let func = foo; - expect_regular(func); - } - - unconstrained fn foo() {} - - fn expect_regular(_func: fn() -> ()) { - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_assign_unconstrained_and_regular_fn_to_variable() { - let src = r#" - fn main() { - let _func = if true { foo } else { bar }; - } - - fn foo() {} - unconstrained fn bar() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a context error, got {:?}", errors[0].0); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; -} - -#[test] -fn can_pass_regular_function_to_unconstrained_function() { - let src = r#" - fn main() { - let func = foo; - expect_unconstrained(func); - } - - fn foo() {} - - fn expect_unconstrained(_func: unconstrained fn() -> ()) {} - "#; - assert_no_errors(src); -} - -#[test] -fn cannot_pass_unconstrained_function_to_constrained_function() { - let src = r#" - fn main() { - let func = foo; - expect_regular(func); - } - - unconstrained fn foo() {} - - fn expect_regular(_func: fn() -> ()) {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; -} - -#[test] -fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { - let src = r#" - fn main() { - let _func: unconstrained fn() -> () = foo; - } - - fn foo() {} - "#; - assert_no_errors(src); -} - -#[test] -fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { - let src = r#" - fn main() { - let _ = Foo { func: foo }; - } - - fn foo() {} - - struct Foo { - func: unconstrained fn() -> (), - } - "#; - assert_no_errors(src); -} - -#[test] -fn trait_impl_generics_count_mismatch() { - let src = r#" - trait Foo {} - - impl Foo<()> for Field {} - - fn main() {}"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0].0 - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(item, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); -} - -#[test] -fn bit_not_on_untyped_integer() { - let src = r#" - fn main() { - let _: u32 = 3 & !1; - } - "#; - assert_no_errors(src); -} - -#[test] -fn duplicate_struct_field() { - let src = r#" - pub struct Foo { - x: i32, - x: i32, - } - - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { - first_def, - second_def, - }) = &errors[0].0 - else { - panic!("Expected a duplicate field error, got {:?}", errors[0].0); - }; - - assert_eq!(first_def.to_string(), "x"); - assert_eq!(second_def.to_string(), "x"); - - assert_eq!(first_def.span().start(), 30); - assert_eq!(second_def.span().start(), 46); -} - -#[test] -fn trait_constraint_on_tuple_type() { - let src = r#" - trait Foo { - fn foo(self, x: A) -> bool; - } - - pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { - x.foo(y) - } - - fn main() {}"#; - assert_no_errors(src); -} - -#[test] -fn turbofish_in_constructor_generics_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let _ = Foo:: { x: 1 }; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - 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#" - struct Foo { - x: T - } - - impl Foo { - fn new(x: T) -> Self { - Foo { x } - } - } - - fn main() { - let _ = Foo::::new(1); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), - )); -} - -#[test] -fn turbofish_in_struct_pattern() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - assert_no_errors(src); -} - -#[test] -fn turbofish_in_struct_pattern_errors_if_type_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; -} - -#[test] -fn turbofish_in_struct_pattern_generic_count_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0].0 - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(item, "struct Foo"); - assert_eq!(*expected, 1); - assert_eq!(*found, 2); -} - -#[test] -fn incorrect_generic_count_on_struct_impl() { - let src = r#" - struct Foo {} - impl Foo {} - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); -} - -#[test] -fn incorrect_generic_count_on_type_alias() { - let src = r#" - struct Foo {} - type Bar = Foo; - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); -} - -#[test] -fn uses_self_type_for_struct_function_call() { - let src = r#" - struct S { } - - impl S { - fn one() -> Field { - 1 - } - - fn two() -> Field { - Self::one() + Self::one() - } - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn uses_self_type_inside_trait() { - let src = r#" - trait Foo { - fn foo() -> Self { - Self::bar() - } - - fn bar() -> Self; - } - - impl Foo for Field { - fn bar() -> Self { - 1 - } - } - - fn main() { - let _: Field = Foo::foo(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn uses_self_type_in_trait_where_clause() { - let src = r#" - pub trait Trait { - fn trait_func() -> bool; - } - - pub trait Foo where Self: Trait { - fn foo(self) -> bool { - self.trait_func() - } - } - - struct Bar { - - } - - impl Foo for Bar { - - } - - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[0].0 - else { - panic!("Expected an unresolved method call error, got {:?}", errors[0].0); - }; - - assert_eq!(method_name, "trait_func"); -} - -#[test] -fn do_not_eagerly_error_on_cast_on_type_variable() { - let src = r#" - pub fn foo(x: T, f: fn(T) -> U) -> U { - f(x) - } - - fn main() { - let x: u8 = 1; - let _: Field = foo(x, |x| x as Field); - } - "#; - assert_no_errors(src); -} - -#[test] -fn error_on_cast_over_type_variable() { - let src = r#" - pub fn foo(x: T, f: fn(T) -> U) -> U { - f(x) - } - - fn main() { - let x = "a"; - let _: Field = foo(x, |x| x as Field); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); -} - -#[test] -fn trait_impl_for_a_type_that_implements_another_trait() { - let src = r#" - trait One { - fn one(self) -> i32; - } - - impl One for i32 { - fn one(self) -> i32 { - self - } - } - - trait Two { - fn two(self) -> i32; - } - - impl Two for T where T: One { - fn two(self) -> i32 { - self.one() + 1 - } - } - - pub fn use_it(t: T) -> i32 where T: Two { - Two::two(t) - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { - let src = r#" - trait One { - fn one(self) -> i32; - } - - impl One for i32 { - fn one(self) -> i32 { - let _ = self; - 1 - } - } - - trait Two { - fn two(self) -> i32; - } - - impl Two for T where T: One { - fn two(self) -> i32 { - self.one() + 1 - } - } - - impl Two for u32 { - fn two(self) -> i32 { - let _ = self; - 0 - } - } - - pub fn use_it(t: u32) -> i32 { - Two::two(t) - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn impl_missing_associated_type() { - let src = r#" - trait Foo { - type Assoc; - } - - impl Foo for () {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); -} - -#[test] -fn as_trait_path_syntax_resolves_outside_impl() { - let src = r#" - trait Foo { - type Assoc; - } - - struct Bar {} - - impl Foo for Bar { - type Assoc = i32; - } - - fn main() { - // AsTraitPath syntax is a bit silly when associated types - // are explicitly specified - let _: i64 = 1 as >::Assoc; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); -} - -#[test] -fn as_trait_path_syntax_no_impl() { - let src = r#" - trait Foo { - type Assoc; - } - - struct Bar {} - - impl Foo for Bar { - type Assoc = i32; - } - - fn main() { - let _: i64 = 1 as >::Assoc; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); -} - -#[test] -fn errors_on_unused_private_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - pub trait Foo { - } - } - - use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn errors_on_unused_pub_crate_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - pub trait Foo { - } - } - - pub(crate) use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn warns_on_use_of_private_exported_item() { - let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - use bar::baz; - - pub fn qux() { - baz(); - } - } - - fn main() { - foo::baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); + // TODO: cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); } +// TODO: re-enable! +// // Regression for #4545 +// #[test] +// fn type_aliases_in_main() { +// let src = r#" +// type Outer = [u8; N]; +// fn main(_arg: Outer<1>) {} +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn ban_mutable_globals() { +// // Mutable globals are only allowed in a comptime context +// let src = r#" +// mut global FOO: Field = 0; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn deny_inline_attribute_on_unconstrained() { +// let src = r#" +// #[no_predicates] +// unconstrained pub fn foo(x: Field, y: Field) { +// assert(x != y); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) +// )); +// } +// +// #[test] +// fn deny_fold_attribute_on_unconstrained() { +// let src = r#" +// #[fold] +// unconstrained pub fn foo(x: Field, y: Field) { +// assert(x != y); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) +// )); +// } +// +// #[test] +// fn specify_function_types_with_turbofish() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// impl Default for u64 { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// fn generic_func() -> (T, U) where T: Default, U: Default { +// (T::default(), U::default()) +// } +// +// fn main() { +// let _ = generic_func::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn specify_method_types_with_turbofish() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// struct Foo { +// inner: T +// } +// +// impl Foo { +// fn generic_method(_self: Self) -> U where U: Default { +// U::default() +// } +// } +// +// fn main() { +// let foo: Foo = Foo { inner: 1 }; +// let _ = foo.generic_method::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn incorrect_turbofish_count_function_call() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// impl Default for u64 { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// fn generic_func() -> (T, U) where T: Default, U: Default { +// (T::default(), U::default()) +// } +// +// fn main() { +// let _ = generic_func::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), +// )); +// } +// +// #[test] +// fn incorrect_turbofish_count_method_call() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// struct Foo { +// inner: T +// } +// +// impl Foo { +// fn generic_method(_self: Self) -> U where U: Default { +// U::default() +// } +// } +// +// fn main() { +// let foo: Foo = Foo { inner: 1 }; +// let _ = foo.generic_method::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), +// )); +// } +// +// #[test] +// fn struct_numeric_generic_in_function() { +// let src = r#" +// struct Foo { +// inner: u64 +// } +// +// pub fn bar() { } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } +// +// #[test] +// fn struct_numeric_generic_in_struct() { +// let src = r#" +// pub struct Foo { +// inner: u64 +// } +// +// pub struct Bar { } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::DefinitionError( +// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } +// ), +// )); +// } +// +// #[test] +// fn bool_numeric_generic() { +// let src = r#" +// pub fn read() -> Field { +// if N { +// 0 +// } else { +// 1 +// } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_binary_operation_type_mismatch() { +// let src = r#" +// pub fn foo() -> bool { +// let mut check: bool = true; +// check = N; +// check +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), +// )); +// } +// +// #[test] +// fn bool_generic_as_loop_bound() { +// let src = r#" +// pub fn read() { // error here +// let mut fields = [0; N]; // error here +// for i in 0..N { // error here +// fields[i] = i + 1; +// } +// assert(fields[0] == 1); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 3); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, expr_typ, .. +// }) = &errors[2].0 +// else { +// panic!("Got an error other than a type mismatch"); +// }; +// +// assert_eq!(expected_typ, "Field"); +// assert_eq!(expr_typ, "bool"); +// } +// +// #[test] +// fn numeric_generic_in_function_signature() { +// let src = r#" +// pub fn foo(arr: [Field; N]) -> [Field; N] { arr } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_as_struct_field_type_fails() { +// let src = r#" +// pub struct Foo { +// a: Field, +// b: N, +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn normal_generic_as_array_length() { +// let src = r#" +// pub struct Foo { +// a: Field, +// b: [Field; N], +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_as_param_type() { +// let src = r#" +// pub fn foo(x: I) -> I { +// let _q: I = 5; +// x +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 3); +// +// // Error from the parameter type +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// // Error from the let statement annotated type +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// // Error from the return type +// assert!(matches!( +// errors[2].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_used_in_nested_type_fails() { +// let src = r#" +// pub struct Foo { +// a: Field, +// b: Bar, +// } +// struct Bar { +// inner: N +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn normal_generic_used_in_nested_array_length_fail() { +// let src = r#" +// pub struct Foo { +// a: Field, +// b: Bar, +// } +// pub struct Bar { +// inner: [Field; N] +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_used_in_nested_type_pass() { +// // The order of these structs should not be changed to make sure +// // that we are accurately resolving all struct generics before struct fields +// let src = r#" +// pub struct NestedNumeric { +// a: Field, +// b: InnerNumeric +// } +// pub struct InnerNumeric { +// inner: [u64; N], +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_trait() { +// // We want to make sure that `N` in `impl Deserialize` does +// // not trigger `expected type, found numeric generic parameter N` as the trait +// // does in fact expect a numeric generic. +// let src = r#" +// struct MyType { +// a: Field, +// b: Field, +// c: Field, +// d: T, +// } +// +// impl Deserialize for MyType { +// fn deserialize(fields: [Field; N], other: T) -> Self { +// MyType { a: fields[0], b: fields[1], c: fields[2], d: other } +// } +// } +// +// trait Deserialize { +// fn deserialize(fields: [Field; N], other: T) -> Self; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_in_trait_impl_with_extra_impl_generics() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// struct MyType { +// a: Field, +// b: Field, +// c: Field, +// d: T, +// } +// +// // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. +// // `N` is used first in the trait impl generics (`Deserialize for MyType`). +// // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind +// // while `T` has a normal kind. +// impl Deserialize for MyType where T: Default { +// fn deserialize(fields: [Field; N]) -> Self { +// MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } +// } +// } +// +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_where_clause() { +// let src = r#" +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// +// pub fn read() -> T where T: Deserialize { +// let mut fields: [Field; N] = [0; N]; +// for i in 0..N { +// fields[i] = i as Field + 1; +// } +// T::deserialize(fields) +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_turbofish() { +// let src = r#" +// pub fn double() -> u32 { +// // Used as an expression +// N * 2 +// } +// +// pub fn double_numeric_generics_test() { +// // Example usage of a numeric generic arguments. +// assert(double::<9>() == 18); +// assert(double::<7 + 8>() == 30); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn constant_used_with_numeric_generic() { +// let src = r#" +// struct ValueNote { +// value: Field, +// } +// +// trait Serialize { +// fn serialize(self) -> [Field; N]; +// } +// +// impl Serialize<1> for ValueNote { +// fn serialize(self) -> [Field; 1] { +// [self.value] +// } +// } +// "#; +// assert_no_errors(src); +// } + +// TODO: failing #[test] -fn can_use_pub_use_item() { +fn normal_generic_used_when_numeric_expected_in_where_clause() { let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - pub use bar::baz; + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; } - fn main() { - foo::baz(); + pub fn read() -> T where T: Deserialize { + T::deserialize([0, 1]) } "#; - assert_no_errors(src); -} + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); -#[test] -fn warns_on_re_export_of_item_with_less_visibility() { let src = r#" - mod foo { - mod bar { - pub(crate) fn baz() {} - } - - pub use bar::baz; + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; } - fn main() { - foo::baz(); + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - + assert_eq!(errors.len(), 4); assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // N + assert!(matches!( + errors[3].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), )); } +// TODO: re-enable! +// #[test] +// fn numeric_generics_type_kind_mismatch() { +// let src = r#" +// fn foo() -> u16 { +// N as u16 +// } +// +// global J: u16 = 10; +// +// fn bar() -> u16 { +// foo::() +// } +// +// global M: u16 = 3; +// +// fn main() { +// let _ = bar::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generics_value_kind_mismatch_u32_u64() { +// let src = r#" +// struct BoundedVec { +// storage: [T; MaxLen], +// // can't be compared to MaxLen: u32 +// // can't be used to index self.storage +// len: u64, +// } +// +// impl BoundedVec { +// pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { +// // We do this to avoid an unused variable warning on `self` +// let _ = self.len; +// for _ in 0..Len { } +// } +// +// pub fn push(&mut self, elem: T) { +// assert(self.len < MaxLen, "push out of bounds"); +// self.storage[self.len] = elem; +// self.len += 1; +// } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IntegerBitWidth { +// bit_width_x: IntegerBitSize::SixtyFour, +// bit_width_y: IntegerBitSize::ThirtyTwo, +// .. +// }), +// )); +// } +// +// #[test] +// fn quote_code_fragments() { +// // This test ensures we can quote (and unquote/splice) code fragments +// // which by themselves are not valid code. They only need to be valid +// // by the time they are unquoted into the macro's call site. +// let src = r#" +// fn main() { +// comptime { +// concat!(quote { assert( }, quote { false); }); +// } +// } +// +// comptime fn concat(a: Quoted, b: Quoted) -> Quoted { +// quote { $a $b } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use InterpreterError::FailingConstraint; +// assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +// } + +// TODO: failing #[test] -fn unquoted_integer_as_integer_token() { +fn impl_stricter_than_trait_no_trait_method_constraints() { + // This test ensures that the error we get from the where clause on the trait impl method + // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. let src = r#" trait Serialize { - fn serialize() {} + // We want to make sure we trigger the error when override a trait method + // which itself has no trait constraints. + fn serialize(self) -> [Field; N]; } - #[attr] - pub fn foobar() {} - - comptime fn attr(_f: FunctionDefinition) -> Quoted { - let serialized_len = 1; - // We are testing that when we unquote $serialized_len, it's unquoted - // as the token `1` and not as something else that later won't be parsed correctly - // in the context of a generic argument. - quote { - impl Serialize<$serialized_len> for Field { - fn serialize() { } - } - } + trait ToField { + fn to_field(self) -> Field; } - fn main() {} - "#; - - assert_no_errors(src); -} + fn process_array(array: [Field; N]) -> Field { + array[0] + } -#[test] -fn errors_on_unused_function() { - let src = r#" - contract some_contract { - // This function is unused, but it's a contract entrypoint - // so it should not produce a warning - fn foo() -> pub Field { - 1 - } + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() } + struct MyType { + a: T, + b: T, + } - fn foo() { - bar(); + impl Serialize<2> for MyType { + fn serialize(self) -> [Field; 2] where T: ToField { + [ self.a.to_field(), self.b.to_field() ] + } } - fn bar() {} + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; + // TODO: cleanup + dbg!(&errors); - assert_eq!(ident.to_string(), "foo"); - assert_eq!(*item_type, "function"); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) + )); } +// TODO: re-enable! +// #[test] +// fn impl_stricter_than_trait_different_generics() { +// let src = r#" +// trait Default { } +// +// // Object type of the trait constraint differs +// trait Foo { +// fn foo_good() where T: Default; +// +// fn foo_bad() where T: Default; +// } +// +// impl Foo for () { +// fn foo_good() where A: Default {} +// +// fn foo_bad() where B: Default {} +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "B")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_object_generics() { +// let src = r#" +// trait MyTrait { } +// +// trait OtherTrait {} +// +// struct Option { +// inner: T +// } +// +// struct OtherOption { +// inner: Option, +// } +// +// trait Bar { +// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn array_good() where [T; 8]: MyTrait; +// +// fn array_bad() where [T; 8]: MyTrait; +// +// fn tuple_good() where (Option, Option): MyTrait; +// +// fn tuple_bad() where (Option, Option): MyTrait; +// } +// +// impl Bar for () { +// fn bar_good() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn bar_bad() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn array_good() where [A; 8]: MyTrait { } +// +// fn array_bad() where [B; 8]: MyTrait { } +// +// fn tuple_good() where (Option, Option): MyTrait { } +// +// fn tuple_bad() where (Option, Option): MyTrait { } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 3); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[1].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[2].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_trait() { +// let src = r#" +// trait Default { } +// +// trait OtherDefault { } +// +// struct Option { +// inner: T +// } +// +// trait Bar { +// fn bar() where Option: Default; +// } +// +// impl Bar for () { +// // Trait constraint differs due to the trait even though the constraint +// // types are the same. +// fn bar() where Option: OtherDefault {} +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); +// assert!(matches!(constraint_name.as_str(), "OtherDefault")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn trait_impl_where_clause_stricter_pass() { +// let src = r#" +// trait MyTrait { +// fn good_foo() where H: OtherTrait; +// +// fn bad_foo() where H: OtherTrait; +// } +// +// trait OtherTrait {} +// +// struct Option { +// inner: T +// } +// +// impl MyTrait for [T] where Option: MyTrait { +// fn good_foo() where B: OtherTrait { } +// +// fn bad_foo() where A: OtherTrait { } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "A")); +// assert!(matches!(constraint_name.as_str(), "OtherTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_trait_generics() { +// let src = r#" +// trait Foo { +// fn foo() where T: T2; +// } +// +// impl Foo for () { +// // Should be A: T2 +// fn foo() where A: T2 {} +// } +// +// trait T2 {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// constraint_generics, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "A")); +// assert!(matches!(constraint_name.as_str(), "T2")); +// assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_not_found_for_inner_impl() { +// // We want to guarantee that we get a no impl found error +// let src = r#" +// trait Serialize { +// fn serialize(self) -> [Field; N]; +// } +// +// trait ToField { +// fn to_field(self) -> Field; +// } +// +// fn process_array(array: [Field; N]) -> Field { +// array[0] +// } +// +// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { +// thing.serialize() +// } +// +// struct MyType { +// a: T, +// b: T, +// } +// +// impl Serialize<2> for MyType where T: ToField { +// fn serialize(self) -> [Field; 2] { +// [ self.a.to_field(), self.b.to_field() ] +// } +// } +// +// impl MyType { +// fn do_thing_with_serialization_with_extra_steps(self) -> Field { +// process_array(serialize_thing(self)) +// } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// &errors[0].0, +// CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) +// )); +// } +// +// // Regression for #5388 +// #[test] +// fn comptime_let() { +// let src = r#"fn main() { +// comptime let my_var = 2; +// assert_eq(my_var, 2); +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn overflowing_u8() { +// let src = r#" +// fn main() { +// let _: u8 = 256; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `2⁸` cannot fit into `u8` which has range `0..=255`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn underflowing_u8() { +// let src = r#" +// fn main() { +// let _: u8 = -1; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `-1` cannot fit into `u8` which has range `0..=255`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn overflowing_i8() { +// let src = r#" +// fn main() { +// let _: i8 = 128; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn underflowing_i8() { +// let src = r#" +// fn main() { +// let _: i8 = -129; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `-129` cannot fit into `i8` which has range `-128..=127`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } + +// TODO: failing #[test] -fn errors_on_unused_struct() { +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls let src = r#" - struct Foo {} - struct Bar {} + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; fn main() { - let _ = Bar {}; + let _ = bar::(); } "#; + assert_no_errors(src); - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(*item_type, "struct"); -} + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } -#[test] -fn errors_on_unused_trait() { - let src = r#" - trait Foo {} - trait Bar {} + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } - pub struct Baz { + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() } - impl Bar for Baz {} + global M: u32 = 3; fn main() { + let _ = bar::(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(*item_type, "trait"); + assert_no_errors(src); } +// TODO: re-enable! +// #[test] +// fn use_super() { +// let src = r#" +// fn some_func() {} +// +// mod foo { +// use super::some_func; +// +// pub fn bar() { +// some_func(); +// } +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn use_super_in_path() { +// let src = r#" +// fn some_func() {} +// +// mod foo { +// pub fn func() { +// super::some_func(); +// } +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn no_super() { +// let src = "use super::some_func;"; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( +// PathResolutionError::NoSuper(span), +// )) = &errors[0].0 +// else { +// panic!("Expected a 'no super' error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(span.start(), 4); +// assert_eq!(span.end(), 9); +// } +// +// #[test] +// fn cannot_call_unconstrained_function_outside_of_unsafe() { +// let src = r#" +// fn main() { +// foo(); +// } +// +// unconstrained fn foo() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { +// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { +// let src = r#" +// fn main() { +// let func = foo; +// // Warning should trigger here +// func(); +// inner(func); +// } +// +// fn inner(x: unconstrained fn() -> ()) { +// // Warning should trigger here +// x(); +// } +// +// unconstrained fn foo() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 2); +// +// for error in &errors { +// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { +// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); +// }; +// } +// } + +// TODO: failing #[test] -fn constrained_reference_to_unconstrained() { +fn missing_unsafe_block_when_needing_type_annotations() { + // This test is a regression check that even when an unsafe block is missing + // that we still appropriately continue type checking and infer type annotations. let src = r#" - fn main(mut x: u32, y: pub u32) { - let x_ref = &mut x; - if x == 5 { - unsafe { - mut_ref_input(x_ref, y); + fn main() { + let z = BigNum { limbs: [2, 0, 0] }; + assert(z.__is_zero() == false); + } + + struct BigNum { + limbs: [u64; N], + } + + impl BigNum { + unconstrained fn __is_zero_impl(self) -> bool { + let mut result: bool = true; + for i in 0..N { + result = result & (self.limbs[i] == 0); } + result } + } - assert(x == 10); + trait BigNumTrait { + fn __is_zero(self) -> bool; } - unconstrained fn mut_ref_input(x: &mut u32, y: u32) { - *x = y; + impl BigNumTrait for BigNum { + fn __is_zero(self) -> bool { + self.__is_zero_impl() + } } "#; - let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); }; } -#[test] -fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); -} - +// TODO: re-enable! +// #[test] +// fn cannot_pass_unconstrained_function_to_regular_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_regular(func); +// } +// +// unconstrained fn foo() {} +// +// fn expect_regular(_func: fn() -> ()) { +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { +// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn cannot_assign_unconstrained_and_regular_fn_to_variable() { +// let src = r#" +// fn main() { +// let _func = if true { foo } else { bar }; +// } +// +// fn foo() {} +// unconstrained fn bar() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { +// panic!("Expected a context error, got {:?}", errors[0].0); +// }; +// +// if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { +// assert_eq!(expected_typ, "fn() -> ()"); +// assert_eq!(expr_typ, "unconstrained fn() -> ()"); +// } else { +// panic!("Expected a type mismatch error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn can_pass_regular_function_to_unconstrained_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_unconstrained(func); +// } +// +// fn foo() {} +// +// fn expect_unconstrained(_func: unconstrained fn() -> ()) {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn cannot_pass_unconstrained_function_to_constrained_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_regular(func); +// } +// +// unconstrained fn foo() {} +// +// fn expect_regular(_func: fn() -> ()) {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { +// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { +// let src = r#" +// fn main() { +// let _func: unconstrained fn() -> () = foo; +// } +// +// fn foo() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { +// let src = r#" +// fn main() { +// let _ = Foo { func: foo }; +// } +// +// fn foo() {} +// +// struct Foo { +// func: unconstrained fn() -> (), +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn trait_impl_generics_count_mismatch() { +// let src = r#" +// trait Foo {} +// +// impl Foo<()> for Field {} +// +// fn main() {}"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// item, +// expected, +// found, +// .. +// }) = &errors[0].0 +// else { +// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(item, "Foo"); +// assert_eq!(*expected, 0); +// assert_eq!(*found, 1); +// } +// +// #[test] +// fn bit_not_on_untyped_integer() { +// let src = r#" +// fn main() { +// let _: u32 = 3 & !1; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn duplicate_struct_field() { +// let src = r#" +// pub struct Foo { +// x: i32, +// x: i32, +// } +// +// fn main() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { +// first_def, +// second_def, +// }) = &errors[0].0 +// else { +// panic!("Expected a duplicate field error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(first_def.to_string(), "x"); +// assert_eq!(second_def.to_string(), "x"); +// +// assert_eq!(first_def.span().start(), 30); +// assert_eq!(second_def.span().start(), 46); +// } +// +// #[test] +// fn trait_constraint_on_tuple_type() { +// let src = r#" +// trait Foo { +// fn foo(self, x: A) -> bool; +// } +// +// pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { +// x.foo(y) +// } +// +// fn main() {}"#; +// assert_no_errors(src); +// } +// +// #[test] +// fn turbofish_in_constructor_generics_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let _ = Foo:: { x: 1 }; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// 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#" +// struct Foo { +// x: T +// } +// +// impl Foo { +// fn new(x: T) -> Self { +// Foo { x } +// } +// } +// +// fn main() { +// let _ = Foo::::new(1); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), +// )); +// } +// +// #[test] +// fn turbofish_in_struct_pattern() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value: Field = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn turbofish_in_struct_pattern_errors_if_type_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value: Field = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 +// else { +// panic!("Expected a type mismatch error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn turbofish_in_struct_pattern_generic_count_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// item, +// expected, +// found, +// .. +// }) = &errors[0].0 +// else { +// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(item, "struct Foo"); +// assert_eq!(*expected, 1); +// assert_eq!(*found, 2); +// } +// +// #[test] +// fn incorrect_generic_count_on_struct_impl() { +// let src = r#" +// struct Foo {} +// impl Foo {} +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// found, expected, .. +// }) = errors[0].0 +// else { +// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(found, 1); +// assert_eq!(expected, 0); +// } +// +// #[test] +// fn incorrect_generic_count_on_type_alias() { +// let src = r#" +// struct Foo {} +// type Bar = Foo; +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// found, expected, .. +// }) = errors[0].0 +// else { +// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(found, 1); +// assert_eq!(expected, 0); +// } +// +// #[test] +// fn uses_self_type_for_struct_function_call() { +// let src = r#" +// struct S { } +// +// impl S { +// fn one() -> Field { +// 1 +// } +// +// fn two() -> Field { +// Self::one() + Self::one() +// } +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn uses_self_type_inside_trait() { +// let src = r#" +// trait Foo { +// fn foo() -> Self { +// Self::bar() +// } +// +// fn bar() -> Self; +// } +// +// impl Foo for Field { +// fn bar() -> Self { +// 1 +// } +// } +// +// fn main() { +// let _: Field = Foo::foo(); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn uses_self_type_in_trait_where_clause() { +// let src = r#" +// pub trait Trait { +// fn trait_func() -> bool; +// } +// +// pub trait Foo where Self: Trait { +// fn foo(self) -> bool { +// self.trait_func() +// } +// } +// +// struct Bar { +// +// } +// +// impl Foo for Bar { +// +// } +// +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = +// &errors[0].0 +// else { +// panic!("Expected an unresolved method call error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(method_name, "trait_func"); +// } +// +// #[test] +// fn do_not_eagerly_error_on_cast_on_type_variable() { +// let src = r#" +// pub fn foo(x: T, f: fn(T) -> U) -> U { +// f(x) +// } +// +// fn main() { +// let x: u8 = 1; +// let _: Field = foo(x, |x| x as Field); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn error_on_cast_over_type_variable() { +// let src = r#" +// pub fn foo(x: T, f: fn(T) -> U) -> U { +// f(x) +// } +// +// fn main() { +// let x = "a"; +// let _: Field = foo(x, |x| x as Field); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) +// )); +// } +// +// #[test] +// fn trait_impl_for_a_type_that_implements_another_trait() { +// let src = r#" +// trait One { +// fn one(self) -> i32; +// } +// +// impl One for i32 { +// fn one(self) -> i32 { +// self +// } +// } +// +// trait Two { +// fn two(self) -> i32; +// } +// +// impl Two for T where T: One { +// fn two(self) -> i32 { +// self.one() + 1 +// } +// } +// +// pub fn use_it(t: T) -> i32 where T: Two { +// Two::two(t) +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { +// let src = r#" +// trait One { +// fn one(self) -> i32; +// } +// +// impl One for i32 { +// fn one(self) -> i32 { +// let _ = self; +// 1 +// } +// } +// +// trait Two { +// fn two(self) -> i32; +// } +// +// impl Two for T where T: One { +// fn two(self) -> i32 { +// self.one() + 1 +// } +// } +// +// impl Two for u32 { +// fn two(self) -> i32 { +// let _ = self; +// 0 +// } +// } +// +// pub fn use_it(t: u32) -> i32 { +// Two::two(t) +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn impl_missing_associated_type() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// impl Foo for () {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) +// )); +// } +// +// #[test] +// fn as_trait_path_syntax_resolves_outside_impl() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// struct Bar {} +// +// impl Foo for Bar { +// type Assoc = i32; +// } +// +// fn main() { +// // AsTraitPath syntax is a bit silly when associated types +// // are explicitly specified +// let _: i64 = 1 as >::Assoc; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use CompilationError::TypeError; +// use TypeCheckError::TypeMismatch; +// let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { +// panic!("Expected TypeMismatch error, found {:?}", errors[0].0); +// }; +// +// assert_eq!(expected_typ, "i64".to_string()); +// assert_eq!(expr_typ, "i32".to_string()); +// } +// +// #[test] +// fn as_trait_path_syntax_no_impl() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// struct Bar {} +// +// impl Foo for Bar { +// type Assoc = i32; +// } +// +// fn main() { +// let _: i64 = 1 as >::Assoc; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use CompilationError::TypeError; +// assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); +// } +// +// #[test] +// fn errors_on_unused_private_import() { +// let src = r#" +// mod foo { +// pub fn bar() {} +// pub fn baz() {} +// +// pub trait Foo { +// } +// } +// +// use foo::bar; +// use foo::baz; +// use foo::Foo; +// +// impl Foo for Field { +// } +// +// fn main() { +// baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "bar"); +// assert_eq!(*item_type, "import"); +// } +// +// #[test] +// fn errors_on_unused_pub_crate_import() { +// let src = r#" +// mod foo { +// pub fn bar() {} +// pub fn baz() {} +// +// pub trait Foo { +// } +// } +// +// pub(crate) use foo::bar; +// use foo::baz; +// use foo::Foo; +// +// impl Foo for Field { +// } +// +// fn main() { +// baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "bar"); +// assert_eq!(*item_type, "import"); +// } +// +// #[test] +// fn warns_on_use_of_private_exported_item() { +// let src = r#" +// mod foo { +// mod bar { +// pub fn baz() {} +// } +// +// use bar::baz; +// +// pub fn qux() { +// baz(); +// } +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::ResolverError(ResolverError::PathResolutionError( +// PathResolutionError::Private(..), +// )) +// )); +// } +// +// #[test] +// fn can_use_pub_use_item() { +// let src = r#" +// mod foo { +// mod bar { +// pub fn baz() {} +// } +// +// pub use bar::baz; +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn warns_on_re_export_of_item_with_less_visibility() { +// let src = r#" +// mod foo { +// mod bar { +// pub(crate) fn baz() {} +// } +// +// pub use bar::baz; +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::DefinitionError( +// DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } +// ) +// )); +// } +// +// #[test] +// fn unquoted_integer_as_integer_token() { +// let src = r#" +// trait Serialize { +// fn serialize() {} +// } +// +// #[attr] +// pub fn foobar() {} +// +// comptime fn attr(_f: FunctionDefinition) -> Quoted { +// let serialized_len = 1; +// // We are testing that when we unquote $serialized_len, it's unquoted +// // as the token `1` and not as something else that later won't be parsed correctly +// // in the context of a generic argument. +// quote { +// impl Serialize<$serialized_len> for Field { +// fn serialize() { } +// } +// } +// } +// +// fn main() {} +// "#; +// +// assert_no_errors(src); +// } +// +// #[test] +// fn errors_on_unused_function() { +// let src = r#" +// contract some_contract { +// // This function is unused, but it's a contract entrypoint +// // so it should not produce a warning +// fn foo() -> pub Field { +// 1 +// } +// } +// +// +// fn foo() { +// bar(); +// } +// +// fn bar() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "foo"); +// assert_eq!(*item_type, "function"); +// } +// +// #[test] +// fn errors_on_unused_struct() { +// let src = r#" +// struct Foo {} +// struct Bar {} +// +// fn main() { +// let _ = Bar {}; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "Foo"); +// assert_eq!(*item_type, "struct"); +// } +// +// #[test] +// fn errors_on_unused_trait() { +// let src = r#" +// trait Foo {} +// trait Bar {} +// +// pub struct Baz { +// } +// +// impl Bar for Baz {} +// +// fn main() { +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "Foo"); +// assert_eq!(*item_type, "trait"); +// } +// +// #[test] +// fn constrained_reference_to_unconstrained() { +// let src = r#" +// fn main(mut x: u32, y: pub u32) { +// let x_ref = &mut x; +// if x == 5 { +// unsafe { +// mut_ref_input(x_ref, y); +// } +// } +// +// assert(x == 10); +// } +// +// unconstrained fn mut_ref_input(x: &mut u32, y: u32) { +// *x = y; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = +// &errors[0].0 +// else { +// panic!("Expected an error about passing a constrained reference to unconstrained"); +// }; +// } +// +// #[test] +// fn comptime_type_in_runtime_code() { +// let source = "pub fn foo(_f: FunctionDefinition) {}"; +// let errors = get_program_errors(source); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) +// )); +// } + +// TODO: failing #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { let source = r#" @@ -3574,119 +3595,121 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { assert_eq!(errors.len(), 0); } -#[test] -fn cannot_mutate_immutable_variable() { - let src = r#" - fn main() { - let array = [1]; - mutate(&mut array); - } - - fn mutate(_: &mut [Field; 1]) {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); -} - -#[test] -fn cannot_mutate_immutable_variable_on_member_access() { - let src = r#" - struct Foo { - x: Field - } - - fn main() { - let foo = Foo { x: 0 }; - mutate(&mut foo.x); - } - - fn mutate(foo: &mut Field) { - *foo = 1; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); -} - -#[test] -fn does_not_crash_when_passing_mutable_undefined_variable() { - let src = r#" - fn main() { - mutate(&mut undefined); - } - - fn mutate(foo: &mut Field) { - *foo = 1; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); -} - -#[test] -fn infer_globals_to_u32_from_type_use() { - let src = r#" - global ARRAY_LEN = 3; - global STR_LEN = 2; - global FMT_STR_LEN = 2; - - fn main() { - let _a: [u32; ARRAY_LEN] = [1, 2, 3]; - let _b: str = "hi"; - let _c: fmtstr = f"hi"; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn non_u32_in_array_length() { - let src = r#" - global ARRAY_LEN: u8 = 3; - - fn main() { - let _a: [u32; ARRAY_LEN] = [1, 2, 3]; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); -} - +// TODO: re-enable! +// #[test] +// fn cannot_mutate_immutable_variable() { +// let src = r#" +// fn main() { +// let array = [1]; +// mutate(&mut array); +// } +// +// fn mutate(_: &mut [Field; 1]) {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = +// &errors[0].0 +// else { +// panic!("Expected a CannotMutateImmutableVariable error"); +// }; +// +// assert_eq!(name, "array"); +// } +// +// #[test] +// fn cannot_mutate_immutable_variable_on_member_access() { +// let src = r#" +// struct Foo { +// x: Field +// } +// +// fn main() { +// let foo = Foo { x: 0 }; +// mutate(&mut foo.x); +// } +// +// fn mutate(foo: &mut Field) { +// *foo = 1; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = +// &errors[0].0 +// else { +// panic!("Expected a CannotMutateImmutableVariable error"); +// }; +// +// assert_eq!(name, "foo"); +// } +// +// #[test] +// fn does_not_crash_when_passing_mutable_undefined_variable() { +// let src = r#" +// fn main() { +// mutate(&mut undefined); +// } +// +// fn mutate(foo: &mut Field) { +// *foo = 1; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = +// &errors[0].0 +// else { +// panic!("Expected a VariableNotDeclared error"); +// }; +// +// assert_eq!(name, "undefined"); +// } +// +// #[test] +// fn infer_globals_to_u32_from_type_use() { +// let src = r#" +// global ARRAY_LEN = 3; +// global STR_LEN = 2; +// global FMT_STR_LEN = 2; +// +// fn main() { +// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; +// let _b: str = "hi"; +// let _c: fmtstr = f"hi"; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn non_u32_in_array_length() { +// let src = r#" +// global ARRAY_LEN: u8 = 3; +// +// fn main() { +// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) +// )); +// } + +// TODO: failing #[test] fn use_non_u32_generic_in_struct() { let src = r#" @@ -3698,9 +3721,14 @@ fn use_non_u32_generic_in_struct() { "#; let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + assert_eq!(errors.len(), 0); } +// TODO: failing #[test] fn use_numeric_generic_in_trait_method() { let src = r#" @@ -3717,7 +3745,8 @@ fn use_numeric_generic_in_trait_method() { } fn main() { - let _ = Bar{}.foo([1,2,3]); + let bytes: [u8; 3] = [1,2,3]; + let _ = Bar{}.foo(bytes); } "#; diff --git a/compiler/noirc_frontend/src/tests/name_shadowing.rs b/compiler/noirc_frontend/src/tests/name_shadowing.rs index b0d83510039..d67cd519103 100644 --- a/compiler/noirc_frontend/src/tests/name_shadowing.rs +++ b/compiler/noirc_frontend/src/tests/name_shadowing.rs @@ -2,418 +2,419 @@ use super::get_program_errors; use std::collections::HashSet; -#[test] -fn test_name_shadowing() { - let src = " - trait Default { - fn default() -> Self; - } - - impl Default for bool { - fn default() -> bool { - false - } - } - - impl Default for Field { - fn default() -> Field { - 0 - } - } - - impl Default for [T; N] where T: Default { - fn default() -> [T; N] { - [Default::default(); N] - } - } - - impl Default for (T, U) where T: Default, U: Default { - fn default() -> (T, U) { - (Default::default(), Default::default()) - } - } - - fn drop_var(_x: T, y: U) -> U { y } - - mod local_module { - use crate::{Default, drop_var}; - - global LOCAL_GLOBAL_N: Field = 0; - - global LOCAL_GLOBAL_M: Field = 1; - - struct LocalStruct { - field1: A, - field2: B, - field3: [A; N], - field4: ([A; N], [B; M]), - field5: &mut A, - } - - impl Default for LocalStruct where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - field1: Default::default(), - field2: Default::default(), - field3: Default::default(), - field4: Default::default(), - field5: mut_field, - } - } - } - - trait DefinedInLocalModule1 { - fn trait_fn1(self, x: A); - fn trait_fn2(self, y: B); - fn trait_fn3(&mut self, x: A, y: B); - fn trait_fn4(self, x: [A; 0], y: [B]); - fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl DefinedInLocalModule1 for LocalStruct { - fn trait_fn1(self, _x: A) { drop_var(self, ()) } - fn trait_fn2(self, _y: B) { drop_var(self, ()) } - fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); - let x: Field = 0; - assert(x == 0); - let x: Field = 1; - assert(x == 1); - [] - } - } - - mod library { - use crate::{Default, drop_var}; - - mod library2 { - use crate::{Default, drop_var}; - - global IMPORT_GLOBAL_N_2: Field = 4; - - global IMPORT_GLOBAL_M_2: Field = 5; - - // When we re-export this type from another library and then use it in - // main, we get a panic - struct ReExportMeFromAnotherLib1 { - x : Field, - } - - struct PubLibLocalStruct3 { - pub_field1: A, - pub_field2: B, - pub_field3: [A; N], - pub_field4: ([A; N], [B; M]), - pub_field5: &mut A, - } - - impl Default for PubLibLocalStruct3 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - pub_field1: Default::default(), - pub_field2: Default::default(), - pub_field3: Default::default(), - pub_field4: Default::default(), - pub_field5: mut_field, - } - } - } - - trait PubLibDefinedInLocalModule3 { - fn pub_trait_fn1(self, x: A); - fn pub_trait_fn2(self, y: B); - fn pub_trait_fn3(&mut self, x: A, y: B); - fn pub_trait_fn4(self, x: [A; 0], y: [B]); - fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { - fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); - [] - } - } - - // Re-export - use library2::ReExportMeFromAnotherLib1; - - global IMPORT_GLOBAL_N_1: Field = 2; - - global IMPORT_GLOBAL_M_1: Field = 3; - - struct LibLocalStruct1 { - lib_field1: A, - lib_field2: B, - lib_field3: [A; N], - lib_field4: ([A; N], [B; M]), - lib_field5: &mut A, - } - - impl Default for LibLocalStruct1 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - lib_field1: Default::default(), - lib_field2: Default::default(), - lib_field3: Default::default(), - lib_field4: Default::default(), - lib_field5: mut_field, - } - } - } - - trait LibDefinedInLocalModule1 { - fn lib_trait_fn1(self, x: A); - fn lib_trait_fn2(self, y: B); - fn lib_trait_fn3(&mut self, x: A, y: B); - fn lib_trait_fn4(self, x: [A; 0], y: [B]); - fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl LibDefinedInLocalModule1 for LibLocalStruct1 { - fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); - [] - } - } - - mod library3 { - use crate::{Default, drop_var}; - - global IMPORT_GLOBAL_N_3: Field = 6; - - global IMPORT_GLOBAL_M_3: Field = 7; - - struct ReExportMeFromAnotherLib2 { - x : Field, - } - - struct PubCrateLibLocalStruct2 { - crate_field1: A, - crate_field2: B, - crate_field3: [A; N], - crate_field4: ([A; N], [B; M]), - crate_field5: &mut A, - } - - impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - crate_field1: Default::default(), - crate_field2: Default::default(), - crate_field3: Default::default(), - crate_field4: Default::default(), - crate_field5: mut_field, - } - } - } - - trait PubCrateLibDefinedInLocalModule2 { - fn crate_trait_fn1(self, x: A); - fn crate_trait_fn2(self, y: B); - fn crate_trait_fn3(&mut self, x: A, y: B); - fn crate_trait_fn4(self, x: [A; 0], y: [B]); - fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { - fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } - fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } - } - - pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); - [] - } - } - - - use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; - - use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; - - // overlapping - // use library::library2::ReExportMeFromAnotherLib1; - use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; - - use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; - - - fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { - assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); - assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); - assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); - assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); - - let x: LocalStruct = Default::default(); - assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); - assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); - - let x: LibLocalStruct1 = Default::default(); - assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); - assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); - - let x: PubLibLocalStruct3 = Default::default(); - assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); - assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); - - let x: PubCrateLibLocalStruct2 = Default::default(); - assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); - assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); - - assert(drop_var(local_fn2((0, 1), [], []), true)); - assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); - assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); - assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); - }"; - - // NOTE: these names must be "replacement-unique", i.e. - // replacing one in a discinct name should do nothing - let names_to_collapse = [ - "DefinedInLocalModule1", - "IMPORT_GLOBAL_M_1", - "IMPORT_GLOBAL_M_2", - "IMPORT_GLOBAL_M_3", - "IMPORT_GLOBAL_N_1", - "IMPORT_GLOBAL_N_2", - "IMPORT_GLOBAL_N_3", - "LOCAL_GLOBAL_M", - "LOCAL_GLOBAL_N", - "LibDefinedInLocalModule1", - "LibLocalStruct1", - "Liblocal_fn1", - "LocalStruct", - "PubCrateLibDefinedInLocalModule2", - "PubCrateLibLocalStruct2", - "PubCrateLiblocal_fn2", - "PubLibDefinedInLocalModule3", - "PubLibLocalStruct3", - "PubLiblocal_fn3", - "ReExportMeFromAnotherLib1", - "ReExportMeFromAnotherLib2", - "local_fn4", - "crate_field1", - "crate_field2", - "crate_field3", - "crate_field4", - "crate_field5", - "crate_trait_fn1", - "crate_trait_fn2", - "crate_trait_fn3", - "crate_trait_fn4", - "crate_trait_fn5", - "crate_trait_fn6", - "crate_trait_fn7", - "field1", - "field2", - "field3", - "field4", - "field5", - "lib_field1", - "lib_field2", - "lib_field3", - "lib_field4", - "lib_field5", - "lib_trait_fn1", - "lib_trait_fn2", - "lib_trait_fn3", - "lib_trait_fn4", - "lib_trait_fn5", - "lib_trait_fn6", - "lib_trait_fn7", - "pub_field1", - "pub_field2", - "pub_field3", - "pub_field4", - "pub_field5", - "pub_trait_fn1", - "pub_trait_fn2", - "pub_trait_fn3", - "pub_trait_fn4", - "pub_trait_fn5", - "pub_trait_fn6", - "pub_trait_fn7", - "trait_fn1", - "trait_fn2", - "trait_fn3", - "trait_fn4", - "trait_fn5", - "trait_fn6", - "trait_fn7", - ]; - - // TODO(https://github.com/noir-lang/noir/issues/4973): - // Name resolution panic from name shadowing test - let cases_to_skip = [ - (1, 21), - (2, 11), - (2, 21), - (3, 11), - (3, 18), - (3, 21), - (4, 21), - (5, 11), - (5, 21), - (6, 11), - (6, 18), - (6, 21), - ]; - let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); - - for (i, x) in names_to_collapse.iter().enumerate() { - for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { - if !cases_to_skip.contains(&(i, j)) { - dbg!((i, j)); - - let modified_src = src.replace(x, y); - let errors = get_program_errors(&modified_src); - assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); - } - } - } -} +// TODO: re-enable! +// #[test] +// fn test_name_shadowing() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for bool { +// fn default() -> bool { +// false +// } +// } +// +// impl Default for Field { +// fn default() -> Field { +// 0 +// } +// } +// +// impl Default for [T; N] where T: Default { +// fn default() -> [T; N] { +// [Default::default(); N] +// } +// } +// +// impl Default for (T, U) where T: Default, U: Default { +// fn default() -> (T, U) { +// (Default::default(), Default::default()) +// } +// } +// +// fn drop_var(_x: T, y: U) -> U { y } +// +// mod local_module { +// use crate::{Default, drop_var}; +// +// global LOCAL_GLOBAL_N: Field = 0; +// +// global LOCAL_GLOBAL_M: Field = 1; +// +// struct LocalStruct { +// field1: A, +// field2: B, +// field3: [A; N], +// field4: ([A; N], [B; M]), +// field5: &mut A, +// } +// +// impl Default for LocalStruct where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// field1: Default::default(), +// field2: Default::default(), +// field3: Default::default(), +// field4: Default::default(), +// field5: mut_field, +// } +// } +// } +// +// trait DefinedInLocalModule1 { +// fn trait_fn1(self, x: A); +// fn trait_fn2(self, y: B); +// fn trait_fn3(&mut self, x: A, y: B); +// fn trait_fn4(self, x: [A; 0], y: [B]); +// fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl DefinedInLocalModule1 for LocalStruct { +// fn trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); +// let x: Field = 0; +// assert(x == 0); +// let x: Field = 1; +// assert(x == 1); +// [] +// } +// } +// +// mod library { +// use crate::{Default, drop_var}; +// +// mod library2 { +// use crate::{Default, drop_var}; +// +// global IMPORT_GLOBAL_N_2: Field = 4; +// +// global IMPORT_GLOBAL_M_2: Field = 5; +// +// // When we re-export this type from another library and then use it in +// // main, we get a panic +// struct ReExportMeFromAnotherLib1 { +// x : Field, +// } +// +// struct PubLibLocalStruct3 { +// pub_field1: A, +// pub_field2: B, +// pub_field3: [A; N], +// pub_field4: ([A; N], [B; M]), +// pub_field5: &mut A, +// } +// +// impl Default for PubLibLocalStruct3 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// pub_field1: Default::default(), +// pub_field2: Default::default(), +// pub_field3: Default::default(), +// pub_field4: Default::default(), +// pub_field5: mut_field, +// } +// } +// } +// +// trait PubLibDefinedInLocalModule3 { +// fn pub_trait_fn1(self, x: A); +// fn pub_trait_fn2(self, y: B); +// fn pub_trait_fn3(&mut self, x: A, y: B); +// fn pub_trait_fn4(self, x: [A; 0], y: [B]); +// fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { +// fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); +// [] +// } +// } +// +// // Re-export +// use library2::ReExportMeFromAnotherLib1; +// +// global IMPORT_GLOBAL_N_1: Field = 2; +// +// global IMPORT_GLOBAL_M_1: Field = 3; +// +// struct LibLocalStruct1 { +// lib_field1: A, +// lib_field2: B, +// lib_field3: [A; N], +// lib_field4: ([A; N], [B; M]), +// lib_field5: &mut A, +// } +// +// impl Default for LibLocalStruct1 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// lib_field1: Default::default(), +// lib_field2: Default::default(), +// lib_field3: Default::default(), +// lib_field4: Default::default(), +// lib_field5: mut_field, +// } +// } +// } +// +// trait LibDefinedInLocalModule1 { +// fn lib_trait_fn1(self, x: A); +// fn lib_trait_fn2(self, y: B); +// fn lib_trait_fn3(&mut self, x: A, y: B); +// fn lib_trait_fn4(self, x: [A; 0], y: [B]); +// fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl LibDefinedInLocalModule1 for LibLocalStruct1 { +// fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); +// [] +// } +// } +// +// mod library3 { +// use crate::{Default, drop_var}; +// +// global IMPORT_GLOBAL_N_3: Field = 6; +// +// global IMPORT_GLOBAL_M_3: Field = 7; +// +// struct ReExportMeFromAnotherLib2 { +// x : Field, +// } +// +// struct PubCrateLibLocalStruct2 { +// crate_field1: A, +// crate_field2: B, +// crate_field3: [A; N], +// crate_field4: ([A; N], [B; M]), +// crate_field5: &mut A, +// } +// +// impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// crate_field1: Default::default(), +// crate_field2: Default::default(), +// crate_field3: Default::default(), +// crate_field4: Default::default(), +// crate_field5: mut_field, +// } +// } +// } +// +// trait PubCrateLibDefinedInLocalModule2 { +// fn crate_trait_fn1(self, x: A); +// fn crate_trait_fn2(self, y: B); +// fn crate_trait_fn3(&mut self, x: A, y: B); +// fn crate_trait_fn4(self, x: [A; 0], y: [B]); +// fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { +// fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } +// fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } +// } +// +// pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); +// [] +// } +// } +// +// +// use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; +// +// use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; +// +// // overlapping +// // use library::library2::ReExportMeFromAnotherLib1; +// use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; +// +// use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; +// +// +// fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { +// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); +// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); +// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); +// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); +// +// let x: LocalStruct = Default::default(); +// assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); +// assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); +// +// let x: LibLocalStruct1 = Default::default(); +// assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); +// assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); +// +// let x: PubLibLocalStruct3 = Default::default(); +// assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); +// assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); +// +// let x: PubCrateLibLocalStruct2 = Default::default(); +// assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); +// assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); +// +// assert(drop_var(local_fn2((0, 1), [], []), true)); +// assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); +// assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); +// assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); +// }"; +// +// // NOTE: these names must be "replacement-unique", i.e. +// // replacing one in a discinct name should do nothing +// let names_to_collapse = [ +// "DefinedInLocalModule1", +// "IMPORT_GLOBAL_M_1", +// "IMPORT_GLOBAL_M_2", +// "IMPORT_GLOBAL_M_3", +// "IMPORT_GLOBAL_N_1", +// "IMPORT_GLOBAL_N_2", +// "IMPORT_GLOBAL_N_3", +// "LOCAL_GLOBAL_M", +// "LOCAL_GLOBAL_N", +// "LibDefinedInLocalModule1", +// "LibLocalStruct1", +// "Liblocal_fn1", +// "LocalStruct", +// "PubCrateLibDefinedInLocalModule2", +// "PubCrateLibLocalStruct2", +// "PubCrateLiblocal_fn2", +// "PubLibDefinedInLocalModule3", +// "PubLibLocalStruct3", +// "PubLiblocal_fn3", +// "ReExportMeFromAnotherLib1", +// "ReExportMeFromAnotherLib2", +// "local_fn4", +// "crate_field1", +// "crate_field2", +// "crate_field3", +// "crate_field4", +// "crate_field5", +// "crate_trait_fn1", +// "crate_trait_fn2", +// "crate_trait_fn3", +// "crate_trait_fn4", +// "crate_trait_fn5", +// "crate_trait_fn6", +// "crate_trait_fn7", +// "field1", +// "field2", +// "field3", +// "field4", +// "field5", +// "lib_field1", +// "lib_field2", +// "lib_field3", +// "lib_field4", +// "lib_field5", +// "lib_trait_fn1", +// "lib_trait_fn2", +// "lib_trait_fn3", +// "lib_trait_fn4", +// "lib_trait_fn5", +// "lib_trait_fn6", +// "lib_trait_fn7", +// "pub_field1", +// "pub_field2", +// "pub_field3", +// "pub_field4", +// "pub_field5", +// "pub_trait_fn1", +// "pub_trait_fn2", +// "pub_trait_fn3", +// "pub_trait_fn4", +// "pub_trait_fn5", +// "pub_trait_fn6", +// "pub_trait_fn7", +// "trait_fn1", +// "trait_fn2", +// "trait_fn3", +// "trait_fn4", +// "trait_fn5", +// "trait_fn6", +// "trait_fn7", +// ]; +// +// // TODO(https://github.com/noir-lang/noir/issues/4973): +// // Name resolution panic from name shadowing test +// let cases_to_skip = [ +// (1, 21), +// (2, 11), +// (2, 21), +// (3, 11), +// (3, 18), +// (3, 21), +// (4, 21), +// (5, 11), +// (5, 21), +// (6, 11), +// (6, 18), +// (6, 21), +// ]; +// let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); +// +// for (i, x) in names_to_collapse.iter().enumerate() { +// for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { +// if !cases_to_skip.contains(&(i, j)) { +// dbg!((i, j)); +// +// let modified_src = src.replace(x, y); +// let errors = get_program_errors(&modified_src); +// assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); +// } +// } +// } +// } From a9aba8c88518ffc5b2cf4dbd55379a2b163185be Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 23 Sep 2024 10:09:18 -0400 Subject: [PATCH 06/56] wip: debugging --- .../noirc_frontend/src/elaborator/types.rs | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b2c7abad7f1..1ba6c7869cb 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -59,6 +59,18 @@ impl<'context> Elaborator<'context> { /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { + // TODO: cleanup + let dbg_this = match &typ.typ { + UnresolvedTypeData::Integer(..) => true, + UnresolvedTypeData::Named(..) => true, + UnresolvedTypeData::FieldElement => true, + UnresolvedTypeData::Expression(..) => true, + _ => false, + }; + if dbg_this { + dbg!("resolve_type_inner", &typ, kind); + } + use crate::ast::UnresolvedTypeData::*; let span = typ.span; @@ -228,6 +240,9 @@ impl<'context> Elaborator<'context> { return self_type; } } else if name == WILDCARD_TYPE { + // TODO: cleanup + dbg!("resolve_named_type: WILDCARD_TYPE", &name); + return self.interner.next_type_variable(); } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { @@ -685,13 +700,25 @@ impl<'context> Elaborator<'context> { typ } - /// Translates a (possibly Unspecified) UnresolvedType to a Type. /// Any UnresolvedType::Unspecified encountered are replaced with fresh type variables. pub(super) fn resolve_inferred_type(&mut self, typ: UnresolvedType) -> Type { + // TODO: cleanup + // + // match &typ.typ { + // UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), + // _ => self.resolve_type(typ), + // } + // match &typ.typ { - UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), - _ => self.resolve_type(typ), + UnresolvedTypeData::Unspecified => { + dbg!("resolve_inferred_type: unspecified", &typ); + self.interner.next_type_variable() + } + _ => { + // dbg!("resolve_inferred_type: specified", &typ); + self.resolve_type(typ) + } } } @@ -784,6 +811,8 @@ impl<'context> Elaborator<'context> { return self.bind_function_type(typ.clone(), args, span); } + // TODO: cleanup + dbg!("bind_function_type", &binding); let ret = self.interner.next_type_variable(); let args = vecmap(args, |(arg, _, _)| arg); let env_type = self.interner.next_type_variable(); @@ -819,7 +848,7 @@ impl<'context> Elaborator<'context> { Type::TypeVariable(_, _) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. - let expected = Type::polymorphic_integer_or_field(self.interner); + let expected = self.polymorphic_integer_or_field(); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), span, @@ -939,7 +968,7 @@ impl<'context> Elaborator<'context> { // Doing so also ensures a type error if Field is used. // The is_numeric check is to allow impls for custom types to bypass this. if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric_value() { - let target = Type::polymorphic_integer(self.interner); + let target = self.polymorphic_integer(); use crate::ast::BinaryOpKind::*; use TypeCheckError::*; @@ -988,7 +1017,7 @@ impl<'context> Elaborator<'context> { || TypeCheckError::InvalidShiftSize { span }, ); let use_impl = if lhs_type.is_numeric_value() { - let integer_type = Type::polymorphic_integer(self.interner); + let integer_type = self.polymorphic_integer(); self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) } else { true From c056a0f0cf4c098011a8d8f5cedebcdd3e7892c5 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 23 Sep 2024 17:20:17 -0400 Subject: [PATCH 07/56] feat: kind-based size checks, evaluating to FieldElement, integral_maximum_size --- compiler/noirc_frontend/src/elaborator/mod.rs | 6 +- .../noirc_frontend/src/elaborator/patterns.rs | 7 +- .../src/elaborator/trait_impls.rs | 4 +- .../noirc_frontend/src/elaborator/traits.rs | 4 +- .../noirc_frontend/src/elaborator/types.rs | 91 +- .../src/hir/comptime/interpreter.rs | 22 +- compiler/noirc_frontend/src/hir_def/types.rs | 321 +- .../src/monomorphization/mod.rs | 39 +- compiler/noirc_frontend/src/node_interner.rs | 14 +- compiler/noirc_frontend/src/tests.rs | 6805 ++++++++--------- .../src/tests/name_shadowing.rs | 831 +- 11 files changed, 4018 insertions(+), 4126 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index c7367e96de7..f9016b1ca65 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -360,7 +360,7 @@ impl<'context> Elaborator<'context> { // Introduce all numeric generics into scope for generic in &all_generics { if let Kind::Numeric(typ) = &generic.kind { - let definition = DefinitionKind::GenericType(generic.type_var.clone(), typ.clone()); + let definition = DefinitionKind::GenericType(generic.type_var.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = self.add_variable_decl_inner(ident, false, false, false, definition); @@ -471,9 +471,9 @@ impl<'context> Elaborator<'context> { let context = self.function_context.pop().expect("Imbalanced function_context pushes"); for typ in context.type_variables { - if let Type::TypeVariable(variable, type_var_kind) = typ.follow_bindings() { + if let Type::TypeVariable(variable, kind) = typ.follow_bindings() { let msg = "TypeChecker should only track defaultable type vars"; - variable.bind(type_var_kind.default_type().expect(msg), &type_var_kind.kind()); + variable.bind(kind.default_type().expect(msg)); } } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index c599d447b28..7afa3215566 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::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - ResolvedGeneric, Shared, StructType, Type, TypeBindings, TypeVariable, TypeVariableKind, + ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -551,14 +551,13 @@ impl<'context> Elaborator<'context> { self.interner.add_global_reference(global_id, hir_ident.location); } - DefinitionKind::GenericType(_, ref numeric_typ) => { + DefinitionKind::GenericType(_) => { // Initialize numeric generics to a polymorphic integer type in case // they're used in expressions. We must do this here since type_check_variable // does not check definition kinds and otherwise expects parameters to // already be typed. if self.interner.definition_type(hir_ident.id) == Type::Error { - let type_var_kind = TypeVariableKind::Numeric(numeric_typ.clone()); - let typ = self.type_variable_with_kind(type_var_kind); + let typ = Type::polymorphic_integer_or_field(self.interner); self.interner.push_definition_type(hir_ident.id, typ); } } diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index e49a4c5272a..aa7e1cb89c5 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -153,12 +153,12 @@ impl<'context> Elaborator<'context> { let override_meta = self.interner.function_meta(func_id); // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, + ResolvedGeneric { type_var: trait_fn_generic, .. }, ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, ) in method.direct_generics.iter().zip(&override_meta.direct_generics) { let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); } let mut substituted_method_ids = HashSet::default(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 8aaebe7b679..0faaf409e6c 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -287,12 +287,12 @@ pub(crate) fn check_trait_impl_method_matches_declaration( // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, + ResolvedGeneric { type_var: trait_fn_generic, .. }, ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); } let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index f94b5348f80..708917d1d42 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -59,18 +59,6 @@ impl<'context> Elaborator<'context> { /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { - // TODO: cleanup - let dbg_this = match &typ.typ { - UnresolvedTypeData::Integer(..) => true, - UnresolvedTypeData::Named(..) => true, - UnresolvedTypeData::FieldElement => true, - UnresolvedTypeData::Expression(..) => true, - _ => false, - }; - if dbg_this { - dbg!("resolve_type_inner", &typ, kind); - } - use crate::ast::UnresolvedTypeData::*; let span = typ.span; @@ -181,12 +169,14 @@ impl<'context> Elaborator<'context> { _ => (), } - if !kind.unifies(&resolved_type.kind()) { + if !kind.matches_opt(resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), expr_kind: resolved_type .kind() - .to_string(), + .as_ref() + .map(Kind::to_string) + .unwrap_or("unknown".to_string()), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -240,9 +230,6 @@ impl<'context> Elaborator<'context> { return self_type; } } else if name == WILDCARD_TYPE { - // TODO: cleanup - dbg!("resolve_named_type: WILDCARD_TYPE", &name); - return self.interner.next_type_variable(); } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { @@ -483,13 +470,15 @@ impl<'context> Elaborator<'context> { } fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { - if !typ.kind().unifies(expected_kind) { - self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: typ.kind().to_string(), - expr_span: span, - }); - return Type::Error; + if let Some(kind) = typ.kind() { + if !kind.unifies(expected_kind) { + self.push_err(TypeCheckError::TypeKindMismatch { + expected_kind: expected_kind.to_string(), + expr_kind: kind.to_string(), + expr_span: span, + }); + return Type::Error; + } } typ } @@ -708,33 +697,12 @@ impl<'context> Elaborator<'context> { typ } - /// Return a fresh integer type variable and log it - /// in self.type_variables to default it later. - pub(super) fn type_variable_with_kind(&mut self, type_var_kind: TypeVariableKind) -> Type { - let typ = Type::type_variable_with_kind(self.interner, type_var_kind); - self.push_type_variable(typ.clone()); - typ - } - /// Translates a (possibly Unspecified) UnresolvedType to a Type. /// Any UnresolvedType::Unspecified encountered are replaced with fresh type variables. pub(super) fn resolve_inferred_type(&mut self, typ: UnresolvedType) -> Type { - // TODO: cleanup - // - // match &typ.typ { - // UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), - // _ => self.resolve_type(typ), - // } - // match &typ.typ { - UnresolvedTypeData::Unspecified => { - dbg!("resolve_inferred_type: unspecified", &typ); - self.interner.next_type_variable() - } - _ => { - // dbg!("resolve_inferred_type: specified", &typ); - self.resolve_type(typ) - } + UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), + _ => self.resolve_type(typ), } } @@ -827,15 +795,13 @@ impl<'context> Elaborator<'context> { return self.bind_function_type(typ.clone(), args, span); } - // TODO: cleanup - dbg!("bind_function_type", &binding); let ret = self.interner.next_type_variable(); let args = vecmap(args, |(arg, _, _)| arg); let env_type = self.interner.next_type_variable(); let expected = Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); - if let Err(error) = binding.try_bind(expected, &Kind::Normal, span) { + if let Err(error) = binding.try_bind(expected, span) { self.push_err(error); } ret @@ -864,7 +830,7 @@ impl<'context> Elaborator<'context> { Type::TypeVariable(_, _) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. - let expected = self.polymorphic_integer_or_field(); + let expected = Type::polymorphic_integer_or_field(self.interner); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), span, @@ -977,14 +943,14 @@ impl<'context> Elaborator<'context> { span, }); - let use_impl = !lhs_type.is_numeric_value(); + let use_impl = !lhs_type.is_numeric(); // If this operator isn't valid for fields we have to possibly narrow // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. // Doing so also ensures a type error if Field is used. // The is_numeric check is to allow impls for custom types to bypass this. - if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric_value() { - let target = self.polymorphic_integer(); + if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { + let target = Type::polymorphic_integer(self.interner); use crate::ast::BinaryOpKind::*; use TypeCheckError::*; @@ -1032,8 +998,8 @@ impl<'context> Elaborator<'context> { &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), || TypeCheckError::InvalidShiftSize { span }, ); - let use_impl = if lhs_type.is_numeric_value() { - let integer_type = self.polymorphic_integer(); + let use_impl = if lhs_type.is_numeric() { + let integer_type = Type::polymorphic_integer(self.interner); self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) } else { true @@ -1132,14 +1098,14 @@ impl<'context> Elaborator<'context> { // The `!` prefix operator is not valid for Field, so if this is a numeric // type we constrain it to just (non-Field) integer types. - if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { + if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } }); } - Ok((rhs_type.clone(), !rhs_type.is_numeric_value())) + Ok((rhs_type.clone(), !rhs_type.is_numeric())) } Integer(sign_x, bit_width_x) => { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { @@ -1221,7 +1187,7 @@ impl<'context> Elaborator<'context> { let object_type = object_type.substitute(&bindings); bindings.insert( the_trait.self_type_typevar.id(), - (the_trait.self_type_typevar.clone(), Kind::Normal, object_type.clone()), + (the_trait.self_type_typevar.clone(), object_type.clone()), ); self.interner.select_impl_for_expression( expr_id, @@ -1786,7 +1752,7 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) { // Avoid binding t = t if !arg.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.clone())); } } @@ -1805,7 +1771,7 @@ impl<'context> Elaborator<'context> { // Avoid binding t = t if !arg.typ.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.typ.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.typ.clone())); } } @@ -1814,8 +1780,7 @@ impl<'context> Elaborator<'context> { // to specify a redundant type annotation. if assumed { let self_type = the_trait.self_type_typevar.clone(); - // self_type has Kind::Normal - bindings.insert(self_type.id(), (self_type, Kind::Normal, constraint.typ.clone())); + bindings.insert(self_type.id(), (self_type, constraint.typ.clone())); } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index ea68757a0ff..6c5d1557050 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -34,11 +34,10 @@ use crate::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, HirPattern, }, - types::Kind, }, macros_api::{HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, - Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, + Kind, Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; use super::errors::{IResult, InterpreterError}; @@ -90,7 +89,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { // To match the monomorphizer, we need to call follow_bindings on each of // the instantiation bindings before we unbind the generics from the previous function. // This is because the instantiation bindings refer to variables from the call site. - for (_, _kind, binding) in instantiation_bindings.values_mut() { + for (_, binding) in instantiation_bindings.values_mut() { *binding = binding.follow_bindings(); } @@ -99,7 +98,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut impl_bindings = perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - for (_, _kind, binding) in impl_bindings.values_mut() { + for (_, binding) in impl_bindings.values_mut() { *binding = binding.follow_bindings(); } @@ -350,7 +349,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if let Some(bindings) = self.bound_generics.last() { for (var, binding) in bindings { - var.force_bind(binding.clone(), &Kind::Normal); + var.force_bind(binding.clone()); } } } @@ -361,11 +360,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { .last_mut() .expect("remember_bindings called with no bound_generics on the stack"); - for (var, _kind, binding) in main_bindings.values() { + for (var, binding) in main_bindings.values() { bound_generics.insert(var.clone(), binding.follow_bindings()); } - for (var, _kind, binding) in impl_bindings.values() { + for (var, binding) in impl_bindings.values() { bound_generics.insert(var.clone(), binding.follow_bindings()); } } @@ -583,11 +582,14 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(value) } } - DefinitionKind::GenericType(type_variable, numeric_typ) => { + DefinitionKind::GenericType(type_variable) => { + // TODO: DefinitionKind::GenericType(_, numeric_typ) after sync-TypeVariableKind + let numeric_typ = Box::new(Type::FieldElement); + let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => None, - // TODO: fix clone - TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()), + // TODO: remove clone if possible + TypeBinding::Bound(binding) => binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())), }; if let Some(value) = value { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index aee6d51ac6c..06dd0077d98 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -135,7 +135,6 @@ pub enum Type { /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { - // TODO: rename this and/or TypeVariableKind::Normal to non-numeric? Normal, Numeric(Box), } @@ -152,6 +151,10 @@ impl Kind { matches!(self, Self::Numeric { .. }) } + pub(crate) fn matches_opt(&self, other: Option) -> bool { + other.as_ref().map_or(true, |other_kind| self.unifies(other_kind)) + } + pub(crate) fn u32() -> Self { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } @@ -198,6 +201,13 @@ impl Kind { }, } } + + fn integral_maximum_size(&self) -> Option { + match self { + Self::Normal => None, + Self::Numeric(typ) => typ.integral_maximum_size(), + } + } } impl std::fmt::Display for Kind { @@ -226,10 +236,10 @@ pub enum QuotedType { CtString, } -/// A list of (TypeVariableId, Kind)'s to bind to a type. Storing the +/// A list of TypeVariableIds to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. -pub type TypeBindings = HashMap; +pub type TypeBindings = HashMap; /// Represents a struct type in the type system. Each instance of this /// rust struct will be shared across all Type::Struct variants that represent @@ -253,7 +263,7 @@ pub struct StructType { /// Corresponds to generic lists such as `` in the source program. /// Used mainly for resolved types which no longer need information such -/// as names or kinds (i.e. they have Kind::Normal). +/// as names or kinds. pub type GenericTypeVars = Vec; /// Corresponds to generic lists such as `` with additional @@ -343,7 +353,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); (typ.substitute(&substitutions), i) @@ -359,7 +369,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -471,7 +481,7 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); self.typ.substitute(&substitutions) @@ -546,7 +556,7 @@ pub enum BinaryTypeOperator { #[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub enum TypeVariableKind { - /// Can bind to any type, except Type::Constant + /// Can bind to any type Normal, /// A generic integer or field type. This is a more specific kind of TypeVariable @@ -559,20 +569,6 @@ pub enum TypeVariableKind { /// A generic integer type. This is a more specific kind of TypeVariable /// that can only be bound to Type::Integer, or other polymorphic integers. Integer, - - /// Can bind to a Type::Constant of the given kind - Numeric(Box), -} - -impl TypeVariableKind { - pub(crate) fn kind(&self) -> Kind { - match self { - Self::Normal => Kind::Normal, - Self::IntegerOrField => Kind::Normal, - Self::Integer => Kind::Normal, - Self::Numeric(typ) => Kind::Numeric(typ.clone()), - } - } } /// A TypeVariable is a mutable reference that is either @@ -594,14 +590,7 @@ impl TypeVariable { /// Panics if this TypeVariable is already Bound. /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. - pub fn bind(&self, typ: Type, kind: &Kind) { - assert!( - typ.kind().unifies(kind), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - typ.kind(), - kind - ); - + pub fn bind(&self, typ: Type) { let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) @@ -613,14 +602,7 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { - assert!( - binding.kind().unifies(kind), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - binding.kind(), - kind - ); - + pub fn try_bind(&self, binding: Type, span: Span) -> Result<(), TypeCheckError> { let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("Expected unbound, found bound to {binding}") @@ -651,16 +633,7 @@ impl TypeVariable { /// Forcibly bind a type variable to a new type - even if the type /// variable is already bound to a different type. This generally /// a logic error to use outside of monomorphization. - /// - /// Asserts that the given type is compatible with the given Kind - pub fn force_bind(&self, typ: Type, kind: &Kind) { - assert!( - typ.kind().unifies(kind), - "expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - typ.kind() - ); - + pub fn force_bind(&self, typ: Type) { if !typ.occurs(self.id()) { *self.1.borrow_mut() = TypeBinding::Bound(typ); } @@ -719,15 +692,6 @@ impl std::fmt::Display for Type { write!(f, "{}", binding.borrow()) } } - Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", typ) - } else { - // TODO: revert after debugging - // write!(f, "{}", binding.borrow()) - write!(f, "{}:! {}", binding.borrow(), typ) - } - } Type::Struct(s, args) => { let args = vecmap(args, |arg| arg.to_string()); if args.is_empty() { @@ -758,17 +722,12 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, kind) => match &*binding.borrow() { + Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), - // TODO: revert after debugging - // TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_", kind), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_:! {:?}", kind), + TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_) => write!(f, "{name}"), }, - - // TODO: revert after debugging - // Type::Constant(x, _kind) => write!(f, "{x}"), - Type::Constant(x, kind) => write!(f, "{x}:! {kind}"), + Type::Constant(x, _kind) => write!(f, "{x}"), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) @@ -819,9 +778,7 @@ impl std::fmt::Display for BinaryTypeOperator { impl std::fmt::Display for TypeVariableId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // TODO: revert after debugging - // TODO remove '!' - write!(f, "_!") + write!(f, "_") } } @@ -870,20 +827,18 @@ impl Type { Type::TypeVariable(var, TypeVariableKind::Normal) } - pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: TypeVariableKind) -> Type { + pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { let id = interner.next_type_variable_id(); + let kind = TypeVariableKind::IntegerOrField; let var = TypeVariable::unbound(id); - Type::TypeVariable(var, type_var_kind) - } - - pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { - let type_var_kind = TypeVariableKind::IntegerOrField; - Self::type_variable_with_kind(interner, type_var_kind) + Type::TypeVariable(var, kind) } pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { - let type_var_kind = TypeVariableKind::Integer; - Self::type_variable_with_kind(interner, type_var_kind) + let id = interner.next_type_variable_id(); + let kind = TypeVariableKind::Integer; + let var = TypeVariable::unbound(id); + Type::TypeVariable(var, kind) } /// A bit of an awkward name for this function - this function returns @@ -921,9 +876,7 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) } - /// While Kind::is_numeric refers to numeric _types_, - /// this method checks for numeric _values_ - pub fn is_numeric_value(&self) -> bool { + pub fn is_numeric(&self) -> bool { use Type::*; use TypeVariableKind as K; matches!( @@ -1179,10 +1132,9 @@ impl Type { } /// Takes a monomorphic type and generalizes it over each of the type variables in the - /// given type bindings, ignoring what each type variable is bound to in the TypeBindings - /// and their Kind's + /// given type bindings, ignoring what each type variable is bound to in the TypeBindings. pub(crate) fn generalize_from_substitutions(self, type_bindings: TypeBindings) -> Type { - let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _kind, _))| type_var); + let polymorphic_type_vars = vecmap(type_bindings, |(_, (type_var, _))| type_var); Type::Forall(polymorphic_type_vars, Box::new(self)) } @@ -1205,15 +1157,15 @@ impl Type { } } - pub(crate) fn kind(&self) -> Kind { + pub(crate) fn kind(&self) -> Option { match self { - Type::NamedGeneric(_, _, kind) => kind.clone(), - Type::Constant(_, kind) => kind.clone(), - Type::TypeVariable(var, type_var_kind) => match *var.borrow() { + Type::NamedGeneric(_, _, kind) => Some(kind.clone()), + Type::Constant(_, kind) => Some(kind.clone()), + Type::TypeVariable(var, _) => match *var.borrow() { TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_) => type_var_kind.kind(), + TypeBinding::Unbound(_) => None, }, - Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), + Type::InfixExpr(lhs, _op, rhs) => Some(lhs.infix_kind(rhs)), Type::FieldElement | Type::Array(..) | Type::Slice(..) @@ -1230,18 +1182,26 @@ impl Type { | Type::MutableReference(..) | Type::Forall(..) | Type::Quoted(..) - | Type::Error => Kind::Normal, + | Type::Error => Some(Kind::Normal), } } - /// Unifies self and other kinds or fails with a Kind error + /// if both Kind's are equal to Some(_), return that Kind, + /// otherwise return a Kind error + /// if both Kind's are None, default to u32 + /// if exactly one Kind is None, return the other one fn infix_kind(&self, other: &Self) -> Kind { - let self_kind = self.kind(); - let other_kind = other.kind(); - if self_kind.unifies(&other_kind) { - self_kind - } else { - Kind::Numeric(Box::new(Type::Error)) + match (self.kind(), other.kind()) { + (Some(self_kind), Some(other_kind)) => { + if self_kind == other_kind { + self_kind + } else { + Kind::Numeric(Box::new(Type::Error)) + } + } + (None, None) => Kind::u32(), + (Some(self_kind), None) => self_kind, + (None, Some(other_kind)) => other_kind, } } @@ -1334,11 +1294,11 @@ impl Type { let this = self.substitute(bindings).follow_bindings(); match &this { Type::Integer(..) => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this)); + bindings.insert(target_id, (var.clone(), this)); Ok(()) } Type::FieldElement if !only_integer => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this)); + bindings.insert(target_id, (var.clone(), this)); Ok(()) } Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { @@ -1354,9 +1314,9 @@ impl Type { // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); - bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); + bindings.insert(*new_target_id, (self_var.clone(), clone)); } else { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + bindings.insert(target_id, (var.clone(), this.clone())); } Ok(()) } @@ -1371,7 +1331,7 @@ impl Type { // Avoid infinitely recursive bindings TypeBinding::Unbound(id) if *id == target_id => Ok(()), TypeBinding::Unbound(_) => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + bindings.insert(target_id, (var.clone(), this.clone())); Ok(()) } } @@ -1392,7 +1352,7 @@ impl Type { TypeVariableKind::IntegerOrField }; let clone = Type::TypeVariable(var.clone(), clone_kind); - bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); + bindings.insert(*new_target_id, (self_var.clone(), clone)); Ok(()) } } @@ -1411,21 +1371,16 @@ impl Type { &self, var: &TypeVariable, bindings: &mut TypeBindings, - kind: Kind, ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), TypeBinding::Unbound(id) => *id, }; - if !self.kind().unifies(&kind) { - return Err(UnificationError) - } - let this = self.substitute(bindings).follow_bindings(); - if let Some((binding, kind)) = this.get_inner_type_variable() { + if let Some(binding) = this.get_inner_type_variable() { match &*binding.borrow() { - TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings, kind), + TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings), // Don't recursively bind the same id to itself TypeBinding::Unbound(id) if *id == target_id => return Ok(()), _ => (), @@ -1437,15 +1392,14 @@ impl Type { if this.occurs(target_id) { Err(UnificationError) } else { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + bindings.insert(target_id, (var.clone(), this.clone())); Ok(()) } } - fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { + fn get_inner_type_variable(&self) -> Option> { match self { - Type::TypeVariable(var, type_var_kind) => Some((var.1.clone(), type_var_kind.kind())), - Type::NamedGeneric(var, _, kind) => Some((var.1.clone(), kind.clone())), + Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => Some(var.1.clone()), _ => None, } } @@ -1507,9 +1461,9 @@ impl Type { }) } - (TypeVariable(var, type_var_kind), other) | (other, TypeVariable(var, type_var_kind)) => { + (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings, type_var_kind.kind()) + other.try_bind_to(var, bindings) }) } @@ -1614,9 +1568,8 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - // TODO: replace evaluate_to_u32 if let Some(other_value) = other.evaluate_to_u32() { - if *value == other_value && kind.unifies(&other.kind()) { + if *value == other_value && kind.matches_opt(other.kind()) { Ok(()) } else { Err(UnificationError) @@ -1668,12 +1621,7 @@ impl Type { // We may have already "bound" this type variable in this call to // try_unify, so check those bindings as well. match bindings.get(id) { - Some((_, kind, binding)) => { - if !kind.unifies(&binding.kind()) { - return Err(UnificationError); - } - binding.clone().try_unify(self, bindings) - } + Some((_, binding)) => binding.clone().try_unify(self, bindings), // Otherwise, bind it None => bind_variable(bindings), @@ -1776,15 +1724,20 @@ impl Type { /// Apply the given type bindings, making them permanently visible for each /// clone of each type variable bound. pub fn apply_type_bindings(bindings: TypeBindings) { - for (type_variable, kind, binding) in bindings.values() { - type_variable.bind(binding.clone(), kind); + for (type_variable, binding) in bindings.values() { + type_variable.bind(binding.clone()); } } /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some((binding, kind)) = self.get_inner_type_variable() { + // TODO: after sync-TypeVariableKind + // if let Some((binding, kind)) = self.get_inner_type_variable() { + if let Some(binding) = self.get_inner_type_variable() { + // TODO: from get_inner_type_variable after sync-TypeVariableKind + let kind = Kind::Numeric(Box::new(Type::FieldElement)); + if let TypeBinding::Bound(binding) = &*binding.borrow() { return binding.evaluate_to_u32().and_then(|result| kind.ensure_value_fits(result)); } @@ -1802,11 +1755,12 @@ impl Type { } } - // TODO! - pub(crate) fn evaluate_to_field_element(&self) -> acvm::FieldElement { - unimplemented!("evaluate_to_field_element") + // TODO: implement! + pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { + self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) + } - + /// Iterate over the fields of this type. /// Panics if the type is not a struct or tuple. pub fn iter_fields(&self) -> impl Iterator { @@ -1857,7 +1811,7 @@ impl Type { for var in typevars { bindings .entry(var.id()) - .or_insert_with(|| (var.clone(), Kind::Normal, interner.next_type_variable())); + .or_insert_with(|| (var.clone(), interner.next_type_variable())); } let instantiated = typ.force_substitute(&bindings); @@ -1877,7 +1831,7 @@ impl Type { .iter() .map(|var| { let new = interner.next_type_variable(); - (var.id(), (var.clone(), Kind::Normal, new)) + (var.id(), (var.clone(), new)) }) .collect(); @@ -1911,7 +1865,7 @@ impl Type { let replacements = typevars .iter() .zip(bindings) - .map(|(var, binding)| (var.id(), (var.clone(), Kind::Normal, binding))) + .map(|(var, binding)| (var.id(), (var.clone(), binding))) .collect(); let instantiated = typ.substitute(&replacements); @@ -1978,14 +1932,7 @@ impl Type { // type variables that have already been bound over. // This is needed for monomorphizing trait impl methods. match type_bindings.get(&binding.0) { - Some((_, kind, replacement)) if substitute_bound_typevars => { - assert!( - kind.unifies(&replacement.kind()), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - replacement.kind() - ); - + Some((_, replacement)) if substitute_bound_typevars => { recur_on_binding(binding.0, replacement) } _ => match &*binding.borrow() { @@ -1993,16 +1940,7 @@ impl Type { binding.substitute_helper(type_bindings, substitute_bound_typevars) } TypeBinding::Unbound(id) => match type_bindings.get(id) { - Some((_, kind, replacement)) => { - assert!( - kind.unifies(&replacement.kind()), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - replacement.kind() - ); - - recur_on_binding(binding.0, replacement) - }, + Some((_, replacement)) => recur_on_binding(binding.0, replacement), None => self.clone(), }, }, @@ -2256,18 +2194,12 @@ impl Type { typ.replace_named_generics_with_type_variables(); *self = typ; } - Type::TypeVariable(var, type_var_kind) => { + Type::TypeVariable(var, _) => { let var = var.borrow(); if let TypeBinding::Bound(binding) = &*var { let mut binding = binding.clone(); drop(var); binding.replace_named_generics_with_type_variables(); - assert!( - type_var_kind.kind().unifies(&binding.kind()), - "expected kind of bound TypeVariable ({:?}) to match the kind of its binding ({:?})", - type_var_kind.kind(), - binding.kind() - ); *self = binding; } } @@ -2279,7 +2211,7 @@ impl Type { generic.typ.replace_named_generics_with_type_variables(); } } - Type::NamedGeneric(var, _, kind) => { + Type::NamedGeneric(var, _, _) => { let type_binding = var.borrow(); if let TypeBinding::Bound(binding) = &*type_binding { let mut binding = binding.clone(); @@ -2288,11 +2220,7 @@ impl Type { *self = binding; } else { drop(type_binding); - let type_var_kind = match kind { - Kind::Normal => TypeVariableKind::Normal, - Kind::Numeric(typ) => TypeVariableKind::Numeric(typ.clone()), - }; - *self = Type::TypeVariable(var.clone(), type_var_kind); + *self = Type::TypeVariable(var.clone(), TypeVariableKind::Normal); } } Type::Function(args, ret, env, _unconstrained) => { @@ -2320,7 +2248,7 @@ impl Type { fn integral_maximum_size(&self) -> Option { match self { - Type::FieldElement => Some(FieldElement::max_value()), + Type::FieldElement => None, Type::Integer(sign, num_bits) => { let mut max_bit_size = num_bits.bit_size(); if sign == &Signedness::Signed { @@ -2328,17 +2256,51 @@ impl Type { } Some(((1u128 << max_bit_size) - 1).into()) } + Type::Bool => Some(FieldElement::from(1u128)), + + Type::TypeVariable(_binding, TypeVariableKind::Normal) => None, Type::TypeVariable(binding, TypeVariableKind::Integer | TypeVariableKind::IntegerOrField) => { match &*binding.borrow() { TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_) => Some(FieldElement::max_value()), + TypeBinding::Unbound(_) => None, } } - Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), + + // TODO: after sync-TypeVariableKind + // TODO: include a similar assertion as with Type::NamedGeneric + // Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), + Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), - Type::Bool => Some(FieldElement::from(1u128)), - Type::NamedGeneric(_binding, _name, Kind::Numeric(typ)) => typ.integral_maximum_size(), - _ => None, + Type::NamedGeneric(binding, _name, kind) => { + let binding_maximum_size = match &*binding.borrow() { + TypeBinding::Bound(typ) => typ.integral_maximum_size(), + TypeBinding::Unbound(_) => None, + }; + let kind_maximum_size = kind.integral_maximum_size(); + assert!( + binding_maximum_size.is_none() || kind_maximum_size.is_none() || binding_maximum_size == kind_maximum_size, + "NamedGeneric binding has incompatible maximum size ({:?}) with its kind ({:?})", + binding_maximum_size, + kind_maximum_size + ); + binding_maximum_size.or(kind_maximum_size) + }, + Type::MutableReference(typ) => typ.integral_maximum_size(), + Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs).integral_maximum_size(), + Type::Constant(_, kind) => kind.integral_maximum_size(), + + Type::Array(..) + | Type::Slice(..) + | Type::String(..) + | Type::FmtString(..) + | Type::Unit + | Type::Tuple(..) + | Type::Struct(..) + | Type::TraitAsType(..) + | Type::Function(..) + | Type::Forall(..) + | Type::Quoted(..) + | Type::Error => None, } } } @@ -2413,7 +2375,6 @@ impl TypeVariableKind { TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), TypeVariableKind::Integer => Some(Type::default_int_type()), TypeVariableKind::Normal => None, - TypeVariableKind::Numeric(typ) => Some(*typ.clone()), } } } @@ -2514,9 +2475,6 @@ impl std::fmt::Debug for Type { Type::TypeVariable(binding, TypeVariableKind::Integer) => { write!(f, "Int{:?}", binding) } - Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { - write!(f, "Numeric({:?}: {:?})", binding, typ) - } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); if args.is_empty() { @@ -2606,8 +2564,7 @@ impl std::fmt::Debug for StructType { impl std::hash::Hash for Type { fn hash(&self, state: &mut H) { - if let Some((variable, kind)) = self.get_inner_type_variable() { - kind.hash(state); + if let Some(variable) = self.get_inner_type_variable() { if let TypeBinding::Bound(typ) = &*variable.borrow() { typ.hash(state); return; @@ -2672,19 +2629,13 @@ impl std::hash::Hash for Type { impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { - if let Some((variable, kind)) = self.get_inner_type_variable() { - if !kind.unifies(&other.kind()) { - return false; - } + if let Some(variable) = self.get_inner_type_variable() { if let TypeBinding::Bound(typ) = &*variable.borrow() { return typ == other; } } - if let Some((variable, other_kind)) = other.get_inner_type_variable() { - if !self.kind().unifies(&other_kind) { - return false; - } + if let Some(variable) = other.get_inner_type_variable() { if let TypeBinding::Bound(typ) = &*variable.borrow() { return self == typ; } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 117da91f509..7177f1f3e6c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -301,7 +301,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { if let Some((self_type, trait_id)) = self.interner.get_function_trait(&f) { let the_trait = self.interner.get_trait(trait_id); - the_trait.self_type_typevar.force_bind(self_type, &types::Kind::Normal); + the_trait.self_type_typevar.force_bind(self_type); } let meta = self.interner.function_meta(&f).clone(); @@ -857,7 +857,7 @@ impl<'interner> Monomorphizer<'interner> { // Ensure all instantiation bindings are bound. // This ensures even unused type variables like `fn foo() {}` have concrete types if let Some(bindings) = self.interner.try_get_instantiation_bindings(expr_id) { - for (_, _kind, binding) in bindings.values() { + for (_, binding) in bindings.values() { Self::check_type(binding, ident.location)?; } } @@ -916,12 +916,18 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Ident(ident) } }, - DefinitionKind::GenericType(type_variable, numeric_typ) => { + // TODO: After sync kinds PR + // DefinitionKind::GenericType(type_variable, numeric_typ) => { + DefinitionKind::GenericType(type_variable) => { + // TODO: After sync kinds PR + let numeric_typ = Box::new(Type::FieldElement); + let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => { unreachable!("Unbound type variable used in expression") } - TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()).unwrap_or_else(|| { + // TODO: remove clone if possible + TypeBinding::Bound(binding) => binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())).unwrap_or_else(|| { panic!("Non-numeric type variable used in expression expecting a value") }), }; @@ -965,7 +971,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _, kind) => { + HirType::NamedGeneric(binding, _, _) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -973,11 +979,11 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_or_field_type(), kind); + binding.bind(HirType::default_int_or_field_type()); ast::Type::Field } - HirType::TypeVariable(binding, type_var_kind) => { + HirType::TypeVariable(binding, kind) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -985,13 +991,13 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = match type_var_kind.default_type() { + let default = match kind.default_type() { Some(typ) => typ, None => return Err(MonomorphizationError::NoDefaultType { location }), }; let monomorphized_default = Self::convert_type(&default, location)?; - binding.bind(default, &type_var_kind.kind()); + binding.bind(default); monomorphized_default } @@ -1439,9 +1445,9 @@ impl<'interner> Monomorphizer<'interner> { fn follow_bindings(&self, bindings: &TypeBindings) -> TypeBindings { bindings .iter() - .map(|(id, (var, kind, binding))| { + .map(|(id, (var, binding))| { let binding2 = binding.follow_bindings(); - (*id, (var.clone(), kind.clone(), binding2)) + (*id, (var.clone(), binding2)) }) .collect() } @@ -1767,8 +1773,7 @@ impl<'interner> Monomorphizer<'interner> { let lambda_name = "zeroed_lambda"; let parameters = vecmap(parameter_types, |parameter_type| { - // TODO remove "??" - (self.next_local_id(), false, "_??".into(), parameter_type.clone()) + (self.next_local_id(), false, "_".into(), parameter_type.clone()) }); let body = self.zeroed_value_of_type(ret_type, location); @@ -1909,13 +1914,13 @@ fn unwrap_struct_type( } pub fn perform_instantiation_bindings(bindings: &TypeBindings) { - for (var, kind, binding) in bindings.values() { - var.force_bind(binding.clone(), kind); + for (var, binding) in bindings.values() { + var.force_bind(binding.clone()); } } pub fn undo_instantiation_bindings(bindings: TypeBindings) { - for (id, (var, _, _)) in bindings { + for (id, (var, _)) in bindings { var.unbind(id); } } @@ -1943,7 +1948,7 @@ pub fn perform_impl_bindings( interner.function_meta(&impl_method).typ.unwrap_forall().1.clone(); // Make each NamedGeneric in this type bindable by replacing it with a TypeVariable - // with the same internal id, binding. + // with the same internal id and binding. trait_method_type.replace_named_generics_with_type_variables(); impl_method_type.replace_named_generics_with_type_variables(); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b3acaef2c62..75178df319d 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -38,7 +38,7 @@ use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; use crate::hir_def::traits::{Trait, TraitConstraint}; -use crate::hir_def::types::{Kind, StructType, Type}; +use crate::hir_def::types::{StructType, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, @@ -580,7 +580,7 @@ pub enum DefinitionKind { /// Generic types in functions (T, U in `fn foo(...)` are declared as variables /// in scope in case they resolve to numeric generics later. - GenericType(TypeVariable, Box), + GenericType(TypeVariable), } impl DefinitionKind { @@ -595,7 +595,7 @@ impl DefinitionKind { DefinitionKind::Function(_) => None, DefinitionKind::Global(_) => None, DefinitionKind::Local(id) => *id, - DefinitionKind::GenericType(_, _) => None, + DefinitionKind::GenericType(_) => None, } } } @@ -1731,7 +1731,7 @@ impl NodeInterner { // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() - .map(|typevar| (typevar.id(), (typevar, Kind::Normal, self.next_type_variable()))) + .map(|typevar| (typevar.id(), (typevar, self.next_type_variable()))) .collect(); let instantiated_object_type = object_type.substitute(&substitutions); @@ -2222,11 +2222,11 @@ impl NodeInterner { let trait_generics = the_trait.generics.clone(); let self_type_var = the_trait.self_type_typevar.clone(); - bindings.insert(self_type_var.id(), (self_type_var, Kind::Normal, impl_self_type)); + bindings.insert(self_type_var.id(), (self_type_var, impl_self_type)); for (trait_generic, trait_impl_generic) in trait_generics.iter().zip(trait_impl_generics) { let type_var = trait_generic.type_var.clone(); - bindings.insert(type_var.id(), (type_var, trait_generic.kind.clone(), trait_impl_generic.clone())); + bindings.insert(type_var.id(), (type_var, trait_impl_generic.clone())); } // Now that the normal bindings are added, we still need to bind the associated types @@ -2235,7 +2235,7 @@ impl NodeInterner { for (trait_type, impl_type) in trait_associated_types.iter().zip(impl_associated_types) { let type_variable = trait_type.type_var.clone(); - bindings.insert(type_variable.id(), (type_variable, trait_type.kind.clone(), impl_type.typ.clone())); + bindings.insert(type_variable.id(), (type_variable, impl_type.typ.clone())); } bindings diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 8947487c4a0..dc54fd624e4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -125,734 +125,733 @@ fn assert_no_errors(src: &str) { } } -// TODO: re-enable! -// #[test] -// fn check_trait_implemented_for_all_t() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait IsDefault { -// fn is_default(self) -> bool; -// } -// -// impl IsDefault for T where T: Default + Eq { -// fn is_default(self) -> bool { -// self.eq(T::default()) -// } -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Default for u64 { -// fn default() -> Self { -// 0 -// } -// } -// -// impl Default for Foo { -// fn default() -> Self { -// Foo { a: Default::default() } -// } -// } -// -// fn main(a: Foo) -> pub bool { -// a.is_default() -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_implementation_duplicate_method() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// y + 2 * x -// } -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// x + 2 * y -// } -// } -// -// fn main() {}"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); -// assert_eq!(first_def, "default"); -// assert_eq!(second_def, "default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// struct Foo { -// } -// -// impl Default for Foo { -// fn default() -> Field { -// 0 -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type2() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, _y: Field) -> Field { -// x -// } -// } -// -// fn main() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_missing_implementation() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// -// fn method2(x: Field) -> Field; -// -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { -// trait_name, -// method_name, -// trait_impl_span: _, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(method_name, "method2"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_not_in_scope() { -// let src = " -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// // Default trait does not exist -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// } -// -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { -// trait_path, -// }) => { -// assert_eq!(trait_path.as_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_name() { -// let src = " -// trait Default { -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// // wrong trait name method should not compile -// impl Default for Foo { -// fn does_not_exist(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// }"; -// let compilation_errors = get_program_errors(src); -// assert!(!has_parser_error(&compilation_errors)); -// assert!( -// compilation_errors.len() == 1, -// "Expected 1 compilation error, got: {:?}", -// compilation_errors -// ); -// -// for (err, _file_id) in compilation_errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { -// trait_name, -// impl_method, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(impl_method, "does_not_exist"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter() { -// let src = " -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// struct Foo { -// bar: u32, -// } -// -// impl Default for Foo { -// fn default(x: u32) -> Self { -// Foo {bar: x} -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "u32"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter2() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, y: Foo) -> Self { -// Self { bar: x, array: [x, y.bar] } -// } -// } -// -// fn main() { -// }"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "Foo"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter_type() { -// let src = " -// pub trait Default { -// fn default(x: Field, y: NotAType) -> Field; -// } -// -// fn main(x: Field, y: Field) { -// assert(y == x); -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// -// // This is a duplicate error in the name resolver & type checker. -// // In the elaborator there is no duplicate and only 1 error is issued -// assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Unresolved(ident), -// )) => { -// assert_eq!(ident, "NotAType"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameters_count() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field) -> Self { -// Self { bar: x, array: [x, x] } -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { -// actual_num_parameters, -// expected_num_parameters, -// trait_name, -// method_name, -// .. -// }) => { -// assert_eq!(actual_num_parameters, &1_usize); -// assert_eq!(expected_num_parameters, &2_usize); -// assert_eq!(method_name, "default"); -// assert_eq!(trait_name, "Default"); -// } -// _ => { -// panic!("No other errors are expected in this test case! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_impl_for_non_type() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// impl Default for main { -// fn default(x: Field, y: Field) -> Field { -// x + y -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { -// assert_eq!(expected, "type"); -// assert_eq!(got, "function"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_impl_struct_not_trait() { -// let src = " -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// struct Default { -// x: Field, -// z: Field, -// } -// -// // Default is a struct not a trait -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { -// not_a_trait_name, -// }) => { -// assert_eq!(not_a_trait_name.to_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_declaration() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field,y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// fn main() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::Trait); -// assert_eq!(first_def, "Default"); -// assert_eq!(second_def, "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation() { -// let src = " -// trait Default { -// } -// struct Foo { -// bar: Field, -// } -// -// impl Default for Foo { -// } -// impl Default for Foo { -// } -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation_with_alias() { -// let src = " -// trait Default { -// } -// -// struct MyStruct { -// } -// -// type MyType = MyStruct; -// -// impl Default for MyStruct { -// } -// -// impl Default for MyType { -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn test_impl_self_within_default_def() { -// let src = " -// trait Bar { -// fn ok(self) -> Self; -// -// fn ref_ok(self) -> Self { -// self.ok() -// } -// } -// -// impl Bar for (T, T) where T: Bar { -// fn ok(self) -> Self { -// self -// } -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_fn_parameter() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// fn test_eq(x: impl Eq) -> bool { -// x.eq(x) -// } -// -// fn main(a: Foo) -> pub bool { -// test_eq(a) -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_two_fn_parameters() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait Test { -// fn test(self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Test for u64 { -// fn test(self) -> bool { self == self } -// } -// -// fn test_eq(x: impl Eq, y: impl Test) -> bool { -// x.eq(x) == y.test() -// } -// -// fn main(a: Foo, b: u64) -> pub bool { -// test_eq(a, b) -// }"; -// assert_no_errors(src); -// } +#[test] +fn check_trait_implemented_for_all_t() { + let src = " + trait Default { + fn default() -> Self; + } + + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait IsDefault { + fn is_default(self) -> bool; + } + + impl IsDefault for T where T: Default + Eq { + fn is_default(self) -> bool { + self.eq(T::default()) + } + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Default for u64 { + fn default() -> Self { + 0 + } + } + + impl Default for Foo { + fn default() -> Self { + Foo { a: Default::default() } + } + } + + fn main(a: Foo) -> pub bool { + a.is_default() + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_implementation_duplicate_method() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + y + 2 * x + } + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + x + 2 * y + } + } + + fn main() {}"; + + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); + assert_eq!(first_def, "default"); + assert_eq!(second_def, "default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_return_type() { + let src = " + trait Default { + fn default() -> Self; + } + + struct Foo { + } + + impl Default for Foo { + fn default() -> Field { + 0 + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_return_type2() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, _y: Field) -> Field { + x + } + } + + fn main() { + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_missing_implementation() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field; + + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { + trait_name, + method_name, + trait_impl_span: _, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(method_name, "method2"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_not_in_scope() { + let src = " + struct Foo { + bar: Field, + array: [Field; 2], + } + + // Default trait does not exist + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() { + } + + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { + trait_path, + }) => { + assert_eq!(trait_path.as_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_name() { + let src = " + trait Default { + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + // wrong trait name method should not compile + impl Default for Foo { + fn does_not_exist(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() { + }"; + let compilation_errors = get_program_errors(src); + assert!(!has_parser_error(&compilation_errors)); + assert!( + compilation_errors.len() == 1, + "Expected 1 compilation error, got: {:?}", + compilation_errors + ); + + for (err, _file_id) in compilation_errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { + trait_name, + impl_method, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(impl_method, "does_not_exist"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter() { + let src = " + trait Default { + fn default(x: Field) -> Self; + } + + struct Foo { + bar: u32, + } + + impl Default for Foo { + fn default(x: u32) -> Self { + Foo {bar: x} + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "u32"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter2() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, y: Foo) -> Self { + Self { bar: x, array: [x, y.bar] } + } + } + + fn main() { + }"; + + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "Foo"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter_type() { + let src = " + pub trait Default { + fn default(x: Field, y: NotAType) -> Field; + } + + fn main(x: Field, y: Field) { + assert(y == x); + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + + // This is a duplicate error in the name resolver & type checker. + // In the elaborator there is no duplicate and only 1 error is issued + assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Unresolved(ident), + )) => { + assert_eq!(ident, "NotAType"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameters_count() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field) -> Self { + Self { bar: x, array: [x, x] } + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { + actual_num_parameters, + expected_num_parameters, + trait_name, + method_name, + .. + }) => { + assert_eq!(actual_num_parameters, &1_usize); + assert_eq!(expected_num_parameters, &2_usize); + assert_eq!(method_name, "default"); + assert_eq!(trait_name, "Default"); + } + _ => { + panic!("No other errors are expected in this test case! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_impl_for_non_type() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; + } + + impl Default for main { + fn default(x: Field, y: Field) -> Field { + x + y + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { + assert_eq!(expected, "type"); + assert_eq!(got, "function"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_impl_struct_not_trait() { + let src = " + struct Foo { + bar: Field, + array: [Field; 2], + } + + struct Default { + x: Field, + z: Field, + } + + // Default is a struct not a trait + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { + not_a_trait_name, + }) => { + assert_eq!(not_a_trait_name.to_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_declaration() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + + trait Default { + fn default(x: Field) -> Self; + } + + fn main() { + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::Trait); + assert_eq!(first_def, "Default"); + assert_eq!(second_def, "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_implementation() { + let src = " + trait Default { + } + struct Foo { + bar: Field, + } + + impl Default for Foo { + } + impl Default for Foo { + } + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_implementation_with_alias() { + let src = " + trait Default { + } + + struct MyStruct { + } + + type MyType = MyStruct; + + impl Default for MyStruct { + } + + impl Default for MyType { + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn test_impl_self_within_default_def() { + let src = " + trait Bar { + fn ok(self) -> Self; + + fn ref_ok(self) -> Self { + self.ok() + } + } + + impl Bar for (T, T) where T: Bar { + fn ok(self) -> Self { + self + } + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_fn_parameter() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + fn test_eq(x: impl Eq) -> bool { + x.eq(x) + } + + fn main(a: Foo) -> pub bool { + test_eq(a) + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_two_fn_parameters() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait Test { + fn test(self) -> bool; + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Test for u64 { + fn test(self) -> bool { self == self } + } + + fn test_eq(x: impl Eq, y: impl Test) -> bool { + x.eq(x) == y.test() + } + + fn main(a: Foo, b: u64) -> pub bool { + test_eq(a, b) + }"; + assert_no_errors(src); +} fn get_program_captures(src: &str) -> Vec> { let (program, context, _errors) = get_program(src); @@ -909,434 +908,432 @@ fn get_lambda_captures( } } -// #[test] -// fn resolve_empty_function() { -// let src = " -// fn main() { -// -// } -// "; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_basic_function() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_unused_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(x == x); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unused variable -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { -// assert_eq!(&ident.0.contents, "y"); -// } -// _ => unreachable!("we should only have an unused var error"), -// } -// } -// -// #[test] -// fn resolve_unresolved_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == z); -// } -// "#; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { -// assert_eq!(name, "z"); -// } -// _ => unimplemented!("we should only have an unresolved variable"), -// } -// } -// -// #[test] -// fn unresolved_path() { -// let src = " -// fn main(x : Field) { -// let _z = some::path::to::a::func(x); -// } -// "; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "some"); -// } -// _ => unimplemented!("we should only have an unresolved function"), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_literal_expr() { -// let src = r#" -// fn main(x : Field) { -// let y = 5; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn multiple_resolution_errors() { -// let src = r#" -// fn main(x : Field) { -// let y = foo::bar(x); -// let z = y + a; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); -// -// // Errors are: -// // `a` is undeclared -// // `z` is unused -// // `foo::bar` does not exist -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::UnusedVariable { ident } => { -// assert_eq!(&ident.0.contents, "z"); -// } -// ResolverError::VariableNotDeclared { name, .. } => { -// assert_eq!(name, "a"); -// } -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "foo"); -// } -// _ => unimplemented!(), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_prefix_expr() { -// let src = r#" -// fn main(x : Field) { -// let _y = -x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_for_expr() { -// let src = r#" -// fn main(x : u64) { -// for i in 1..20 { -// let _z = x + i; -// }; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_call_expr() { -// let src = r#" -// fn main(x : Field) { -// let _z = foo(x); -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_shadowing() { -// let src = r#" -// fn main(x : Field) { -// let x = foo(x); -// let x = x; -// let (x, x) = (x, x); -// let _ = x; -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_basic_closure() { -// let src = r#" -// fn main(x : Field) -> pub Field { -// let closure = |y| y + x; -// closure(x) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_simplified_closure() { -// // based on bug https://github.com/noir-lang/noir/issues/1088 -// -// let src = r#"fn do_closure(x: Field) -> Field { -// let y = x; -// let ret_capture = || { -// y -// }; -// ret_capture() -// } -// -// fn main(x: Field) { -// assert(do_closure(x) == 100); -// } -// -// "#; -// let parsed_captures = get_program_captures(src); -// let expected_captures = vec![vec!["y".to_string()]]; -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_complex_closures() { -// let src = r#" -// fn main(x: Field) -> pub Field { -// let closure_without_captures = |x: Field| -> Field { x + x }; -// let a = closure_without_captures(1); -// -// let closure_capturing_a_param = |y: Field| -> Field { y + x }; -// let b = closure_capturing_a_param(2); -// -// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; -// let c = closure_capturing_a_local_var(3); -// -// let closure_with_transitive_captures = |y: Field| -> Field { -// let d = 5; -// let nested_closure = |z: Field| -> Field { -// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; -// a + z + y + d + x + doubly_nested_closure(4) + x + y -// }; -// let res = nested_closure(5); -// res -// }; -// -// a + b + c + closure_with_transitive_captures(6) -// } -// "#; -// assert_no_errors(src); -// -// let expected_captures = vec![ -// vec![], -// vec!["x".to_string()], -// vec!["b".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], -// vec!["x".to_string(), "b".to_string()], -// ]; -// -// let parsed_captures = get_program_captures(src); -// -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_fmt_strings() { -// let src = r#" -// fn main() { -// let string = f"this is i: {i}"; -// println(string); -// -// println(f"I want to print {0}"); -// -// let new_val = 10; -// println(f"random_string{new_val}{new_val}"); -// } -// fn println(x : T) -> T { -// x -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { -// name, .. -// }) => { -// assert_eq!(name, "i"); -// } -// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { -// name, -// .. -// }) => { -// assert_eq!(name, "0"); -// } -// CompilationError::TypeError(TypeCheckError::UnusedResultError { -// expr_type: _, -// expr_span, -// }) => { -// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); -// assert!( -// a == "println(string)" -// || a == "println(f\"I want to print {0}\")" -// || a == "println(f\"random_string{new_val}{new_val}\")" -// ); -// } -// _ => unimplemented!(), -// }; -// } -// } +#[test] +fn resolve_empty_function() { + let src = " + fn main() { -fn check_rewrite(src: &str, expected: &str) { - let (_program, mut context, _errors) = get_program(src); - let main_func_id = context.def_interner.find_function("main").unwrap(); - let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); - assert!(format!("{}", program) == expected); -} + } + "; + assert_no_errors(src); +} +#[test] +fn resolve_basic_function() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == x); + } + "#; + assert_no_errors(src); +} +#[test] +fn resolve_unused_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(x == x); + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unused variable + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { + assert_eq!(&ident.0.contents, "y"); + } + _ => unreachable!("we should only have an unused var error"), + } +} + +#[test] +fn resolve_unresolved_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == z); + } + "#; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { + assert_eq!(name, "z"); + } + _ => unimplemented!("we should only have an unresolved variable"), + } +} + +#[test] +fn unresolved_path() { + let src = " + fn main(x : Field) { + let _z = some::path::to::a::func(x); + } + "; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "some"); + } + _ => unimplemented!("we should only have an unresolved function"), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_literal_expr() { + let src = r#" + fn main(x : Field) { + let y = 5; + assert(y == x); + } + "#; + assert_no_errors(src); +} + +#[test] +fn multiple_resolution_errors() { + let src = r#" + fn main(x : Field) { + let y = foo::bar(x); + let z = y + a; + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); + + // Errors are: + // `a` is undeclared + // `z` is unused + // `foo::bar` does not exist + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::UnusedVariable { ident } => { + assert_eq!(&ident.0.contents, "z"); + } + ResolverError::VariableNotDeclared { name, .. } => { + assert_eq!(name, "a"); + } + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "foo"); + } + _ => unimplemented!(), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_prefix_expr() { + let src = r#" + fn main(x : Field) { + let _y = -x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_for_expr() { + let src = r#" + fn main(x : u64) { + for i in 1..20 { + let _z = x + i; + }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_call_expr() { + let src = r#" + fn main(x : Field) { + let _z = foo(x); + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_shadowing() { + let src = r#" + fn main(x : Field) { + let x = foo(x); + let x = x; + let (x, x) = (x, x); + let _ = x; + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_basic_closure() { + let src = r#" + fn main(x : Field) -> pub Field { + let closure = |y| y + x; + closure(x) + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_simplified_closure() { + // based on bug https://github.com/noir-lang/noir/issues/1088 + + let src = r#"fn do_closure(x: Field) -> Field { + let y = x; + let ret_capture = || { + y + }; + ret_capture() + } + + fn main(x: Field) { + assert(do_closure(x) == 100); + } + + "#; + let parsed_captures = get_program_captures(src); + let expected_captures = vec![vec!["y".to_string()]]; + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_complex_closures() { + let src = r#" + fn main(x: Field) -> pub Field { + let closure_without_captures = |x: Field| -> Field { x + x }; + let a = closure_without_captures(1); + + let closure_capturing_a_param = |y: Field| -> Field { y + x }; + let b = closure_capturing_a_param(2); + + let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; + let c = closure_capturing_a_local_var(3); + + let closure_with_transitive_captures = |y: Field| -> Field { + let d = 5; + let nested_closure = |z: Field| -> Field { + let doubly_nested_closure = |w: Field| -> Field { w + x + b }; + a + z + y + d + x + doubly_nested_closure(4) + x + y + }; + let res = nested_closure(5); + res + }; + + a + b + c + closure_with_transitive_captures(6) + } + "#; + assert_no_errors(src); + + let expected_captures = vec![ + vec![], + vec!["x".to_string()], + vec!["b".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], + vec!["x".to_string(), "b".to_string()], + ]; + + let parsed_captures = get_program_captures(src); + + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_fmt_strings() { + let src = r#" + fn main() { + let string = f"this is i: {i}"; + println(string); + + println(f"I want to print {0}"); + + let new_val = 10; + println(f"random_string{new_val}{new_val}"); + } + fn println(x : T) -> T { + x + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { + name, .. + }) => { + assert_eq!(name, "i"); + } + CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { + name, + .. + }) => { + assert_eq!(name, "0"); + } + CompilationError::TypeError(TypeCheckError::UnusedResultError { + expr_type: _, + expr_span, + }) => { + let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); + assert!( + a == "println(string)" + || a == "println(f\"I want to print {0}\")" + || a == "println(f\"random_string{new_val}{new_val}\")" + ); + } + _ => unimplemented!(), + }; + } +} + +fn check_rewrite(src: &str, expected: &str) { + let (_program, mut context, _errors) = get_program(src); + let main_func_id = context.def_interner.find_function("main").unwrap(); + let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); + assert!(format!("{}", program) == expected); +} + +#[test] +fn simple_closure_with_no_captured_variables() { + let src = r#" + fn main() -> pub Field { + let x = 1; + let closure = || x; + closure() + } + "#; + + let expected_rewrite = r#"fn main$f0() -> Field { + let x$0 = 1; + let closure$3 = { + let closure_variable$2 = { + let env$1 = (x$l0); + (env$l1, lambda$f1) + }; + closure_variable$l2 + }; + { + let tmp$4 = closure$l3; + tmp$l4.1(tmp$l4.0) + } +} +fn lambda$f1(mut env$l1: (Field)) -> Field { + env$l1.0 +} +"#; + check_rewrite(src, expected_rewrite); +} + +#[test] +fn deny_cyclic_globals() { + let src = r#" + global A = B; + global B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_cyclic_type_aliases() { + let src = r#" + type A = B; + type B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn ensure_nested_type_aliases_type_check() { + let src = r#" + type A = B; + type B = u8; + fn main() { + let _a: A = 0 as u16; + } + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn type_aliases_in_entry_point() { + let src = r#" + type Foo = u8; + fn main(_x: Foo) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn operators_in_global_used_in_type() { + let src = r#" + global ONE: u32 = 1; + global COUNT: u32 = ONE + 2; + fn main() { + let _array: [Field; COUNT] = [1, 2, 3]; + } + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn break_and_continue_in_constrained_fn() { + let src = r#" + fn main() { + for i in 0 .. 10 { + if i == 2 { + continue; + } + if i == 5 { + break; + } + } + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} + +#[test] +fn break_and_continue_outside_loop() { + let src = r#" + unconstrained fn main() { + continue; + break; + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} -// TODO: re-enable! -// #[test] -// fn simple_closure_with_no_captured_variables() { -// let src = r#" -// fn main() -> pub Field { -// let x = 1; -// let closure = || x; -// closure() -// } -// "#; -// -// let expected_rewrite = r#"fn main$f0() -> Field { -// let x$0 = 1; -// let closure$3 = { -// let closure_variable$2 = { -// let env$1 = (x$l0); -// (env$l1, lambda$f1) -// }; -// closure_variable$l2 -// }; -// { -// let tmp$4 = closure$l3; -// tmp$l4.1(tmp$l4.0) -// } -// } -// fn lambda$f1(mut env$l1: (Field)) -> Field { -// env$l1.0 -// } -// "#; -// check_rewrite(src, expected_rewrite); -// } -// -// #[test] -// fn deny_cyclic_globals() { -// let src = r#" -// global A = B; -// global B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_cyclic_type_aliases() { -// let src = r#" -// type A = B; -// type B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn ensure_nested_type_aliases_type_check() { -// let src = r#" -// type A = B; -// type B = u8; -// fn main() { -// let _a: A = 0 as u16; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn type_aliases_in_entry_point() { -// let src = r#" -// type Foo = u8; -// fn main(_x: Foo) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn operators_in_global_used_in_type() { -// let src = r#" -// global ONE: u32 = 1; -// global COUNT: u32 = ONE + 2; -// fn main() { -// let _array: [Field; COUNT] = [1, 2, 3]; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn break_and_continue_in_constrained_fn() { -// let src = r#" -// fn main() { -// for i in 0 .. 10 { -// if i == 2 { -// continue; -// } -// if i == 5 { -// break; -// } -// } -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } -// -// #[test] -// fn break_and_continue_outside_loop() { -// let src = r#" -// unconstrained fn main() { -// continue; -// break; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } - -// TODO: failing // Regression for #2540 #[test] fn for_loop_over_array() { @@ -1351,2231 +1348,2213 @@ fn for_loop_over_array() { } "#; let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +// Regression for #4545 +#[test] +fn type_aliases_in_main() { + let src = r#" + type Outer = [u8; N]; + fn main(_arg: Outer<1>) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn ban_mutable_globals() { + // Mutable globals are only allowed in a comptime context + let src = r#" + mut global FOO: Field = 0; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_inline_attribute_on_unconstrained() { + let src = r#" + #[no_predicates] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn deny_fold_attribute_on_unconstrained() { + let src = r#" + #[fold] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn specify_function_types_with_turbofish() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } - // TODO: cleanup - dbg!(&errors); + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } -// TODO: re-enable! -// // Regression for #4545 -// #[test] -// fn type_aliases_in_main() { -// let src = r#" -// type Outer = [u8; N]; -// fn main(_arg: Outer<1>) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn ban_mutable_globals() { -// // Mutable globals are only allowed in a comptime context -// let src = r#" -// mut global FOO: Field = 0; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_inline_attribute_on_unconstrained() { -// let src = r#" -// #[no_predicates] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn deny_fold_attribute_on_unconstrained() { -// let src = r#" -// #[fold] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn specify_function_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn specify_method_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn incorrect_turbofish_count_function_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn incorrect_turbofish_count_method_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_function() { -// let src = r#" -// struct Foo { -// inner: u64 -// } -// -// pub fn bar() { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_struct() { -// let src = r#" -// pub struct Foo { -// inner: u64 -// } -// -// pub struct Bar { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } -// ), -// )); -// } -// -// #[test] -// fn bool_numeric_generic() { -// let src = r#" -// pub fn read() -> Field { -// if N { -// 0 -// } else { -// 1 -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_binary_operation_type_mismatch() { -// let src = r#" -// pub fn foo() -> bool { -// let mut check: bool = true; -// check = N; -// check -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), -// )); -// } -// -// #[test] -// fn bool_generic_as_loop_bound() { -// let src = r#" -// pub fn read() { // error here -// let mut fields = [0; N]; // error here -// for i in 0..N { // error here -// fields[i] = i + 1; -// } -// assert(fields[0] == 1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, expr_typ, .. -// }) = &errors[2].0 -// else { -// panic!("Got an error other than a type mismatch"); -// }; -// -// assert_eq!(expected_typ, "Field"); -// assert_eq!(expr_typ, "bool"); -// } -// -// #[test] -// fn numeric_generic_in_function_signature() { -// let src = r#" -// pub fn foo(arr: [Field; N]) -> [Field; N] { arr } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_as_struct_field_type_fails() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: N, -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_as_array_length() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: [Field; N], -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_as_param_type() { -// let src = r#" -// pub fn foo(x: I) -> I { -// let _q: I = 5; -// x -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// -// // Error from the parameter type -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the let statement annotated type -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the return type -// assert!(matches!( -// errors[2].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_fails() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: Bar, -// } -// struct Bar { -// inner: N -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_used_in_nested_array_length_fail() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: Bar, -// } -// pub struct Bar { -// inner: [Field; N] -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_pass() { -// // The order of these structs should not be changed to make sure -// // that we are accurately resolving all struct generics before struct fields -// let src = r#" -// pub struct NestedNumeric { -// a: Field, -// b: InnerNumeric -// } -// pub struct InnerNumeric { -// inner: [u64; N], -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_trait() { -// // We want to make sure that `N` in `impl Deserialize` does -// // not trigger `expected type, found numeric generic parameter N` as the trait -// // does in fact expect a numeric generic. -// let src = r#" -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// impl Deserialize for MyType { -// fn deserialize(fields: [Field; N], other: T) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: other } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N], other: T) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_in_trait_impl_with_extra_impl_generics() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. -// // `N` is used first in the trait impl generics (`Deserialize for MyType`). -// // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind -// // while `T` has a normal kind. -// impl Deserialize for MyType where T: Default { -// fn deserialize(fields: [Field; N]) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_where_clause() { -// let src = r#" -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// -// pub fn read() -> T where T: Deserialize { -// let mut fields: [Field; N] = [0; N]; -// for i in 0..N { -// fields[i] = i as Field + 1; -// } -// T::deserialize(fields) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_turbofish() { -// let src = r#" -// pub fn double() -> u32 { -// // Used as an expression -// N * 2 -// } -// -// pub fn double_numeric_generics_test() { -// // Example usage of a numeric generic arguments. -// assert(double::<9>() == 18); -// assert(double::<7 + 8>() == 30); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn constant_used_with_numeric_generic() { -// let src = r#" -// struct ValueNote { -// value: Field, -// } -// -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// impl Serialize<1> for ValueNote { -// fn serialize(self) -> [Field; 1] { -// [self.value] -// } -// } -// "#; -// assert_no_errors(src); -// } - -// TODO: failing #[test] -fn normal_generic_used_when_numeric_expected_in_where_clause() { +fn specify_method_types_with_turbofish() { let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } + trait Default { + fn default() -> Self; + } - pub fn read() -> T where T: Deserialize { - T::deserialize([0, 1]) + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn incorrect_turbofish_count_function_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn incorrect_turbofish_count_method_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn struct_numeric_generic_in_function() { + let src = r#" + struct Foo { + inner: u64 + } + + pub fn bar() { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn struct_numeric_generic_in_struct() { + let src = r#" + pub struct Foo { + inner: u64 + } + + pub struct Bar { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::UnsupportedNumericGenericType { .. } + ), + )); +} + +#[test] +fn bool_numeric_generic() { + let src = r#" + pub fn read() -> Field { + if N { + 0 + } else { + 1 + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn numeric_generic_binary_operation_type_mismatch() { + let src = r#" + pub fn foo() -> bool { + let mut check: bool = true; + check = N; + check + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), + )); +} + +#[test] +fn bool_generic_as_loop_bound() { + let src = r#" + pub fn read() { // error here + let mut fields = [0; N]; // error here + for i in 0..N { // error here + fields[i] = i + 1; + } + assert(fields[0] == 1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); + + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[2].0 + else { + panic!("Got an error other than a type mismatch"); + }; + + assert_eq!(expected_typ, "Field"); + assert_eq!(expr_typ, "bool"); +} + +#[test] +fn numeric_generic_in_function_signature() { + let src = r#" + pub fn foo(arr: [Field; N]) -> [Field; N] { arr } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_as_struct_field_type_fails() { + let src = r#" + pub struct Foo { + a: Field, + b: N, + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_as_array_length() { + let src = r#" + pub struct Foo { + a: Field, + b: [Field; N], + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_as_param_type() { + let src = r#" + pub fn foo(x: I) -> I { + let _q: I = 5; + x + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + + // Error from the parameter type + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the let statement annotated type + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the return type + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_fails() { + let src = r#" + pub struct Foo { + a: Field, + b: Bar, + } + struct Bar { + inner: N + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_used_in_nested_array_length_fail() { + let src = r#" + pub struct Foo { + a: Field, + b: Bar, + } + pub struct Bar { + inner: [Field; N] + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_pass() { + // The order of these structs should not be changed to make sure + // that we are accurately resolving all struct generics before struct fields + let src = r#" + pub struct NestedNumeric { + a: Field, + b: InnerNumeric + } + pub struct InnerNumeric { + inner: [u64; N], + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_trait() { + // We want to make sure that `N` in `impl Deserialize` does + // not trigger `expected type, found numeric generic parameter N` as the trait + // does in fact expect a numeric generic. + let src = r#" + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + impl Deserialize for MyType { + fn deserialize(fields: [Field; N], other: T) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: other } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N], other: T) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_in_trait_impl_with_extra_impl_generics() { + let src = r#" + trait Default { + fn default() -> Self; + } + + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. + // `N` is used first in the trait impl generics (`Deserialize for MyType`). + // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind + // while `T` has a normal kind. + impl Deserialize for MyType where T: Default { + fn deserialize(fields: [Field; N]) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_turbofish() { + let src = r#" + pub fn double() -> u32 { + // Used as an expression + N * 2 + } + + pub fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); + } + "#; + assert_no_errors(src); +} + +#[test] +fn constant_used_with_numeric_generic() { + let src = r#" + struct ValueNote { + value: Field, + } + + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + impl Serialize<1> for ValueNote { + fn serialize(self) -> [Field; 1] { + [self.value] + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn normal_generic_used_when_numeric_expected_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + T::deserialize([0, 1]) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 4); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // N + assert!(matches!( + errors[3].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + )); +} + +#[test] +fn numeric_generics_type_kind_mismatch() { + let src = r#" + fn foo() -> u16 { + N as u16 + } + + global J: u16 = 10; + + fn bar() -> u16 { + foo::() + } + + global M: u16 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generics_value_kind_mismatch_u32_u64() { + let src = r#" + struct BoundedVec { + storage: [T; MaxLen], + // can't be compared to MaxLen: u32 + // can't be used to index self.storage + len: u64, + } + + impl BoundedVec { + pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { + // We do this to avoid an unused variable warning on `self` + let _ = self.len; + for _ in 0..Len { } + } + + pub fn push(&mut self, elem: T) { + assert(self.len < MaxLen, "push out of bounds"); + self.storage[self.len] = elem; + self.len += 1; + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IntegerBitWidth { + bit_width_x: IntegerBitSize::SixtyFour, + bit_width_y: IntegerBitSize::ThirtyTwo, + .. + }), + )); +} + +#[test] +fn quote_code_fragments() { + // This test ensures we can quote (and unquote/splice) code fragments + // which by themselves are not valid code. They only need to be valid + // by the time they are unquoted into the macro's call site. + let src = r#" + fn main() { + comptime { + concat!(quote { assert( }, quote { false); }); + } + } + + comptime fn concat(a: Quoted, b: Quoted) -> Quoted { + quote { $a $b } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use InterpreterError::FailingConstraint; + assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +} + +#[test] +fn impl_stricter_than_trait_no_trait_method_constraints() { + // This test ensures that the error we get from the where clause on the trait impl method + // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. + let src = r#" + trait Serialize { + // We want to make sure we trigger the error when override a trait method + // which itself has no trait constraints. + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType { + fn serialize(self) -> [Field; 2] where T: ToField { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) + )); +} + +#[test] +fn impl_stricter_than_trait_different_generics() { + let src = r#" + trait Default { } + + // Object type of the trait constraint differs + trait Foo { + fn foo_good() where T: Default; + + fn foo_bad() where T: Default; + } + + impl Foo for () { + fn foo_good() where A: Default {} + + fn foo_bad() where B: Default {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_object_generics() { + let src = r#" + trait MyTrait { } + + trait OtherTrait {} + + struct Option { + inner: T + } + + struct OtherOption { + inner: Option, + } + + trait Bar { + fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; + + fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + + fn array_good() where [T; 8]: MyTrait; + + fn array_bad() where [T; 8]: MyTrait; + + fn tuple_good() where (Option, Option): MyTrait; + + fn tuple_bad() where (Option, Option): MyTrait; + } + + impl Bar for () { + fn bar_good() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn bar_bad() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn array_good() where [A; 8]: MyTrait { } + + fn array_bad() where [B; 8]: MyTrait { } + + fn tuple_good() where (Option, Option): MyTrait { } + + fn tuple_bad() where (Option, Option): MyTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[1].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[2].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait() { + let src = r#" + trait Default { } + + trait OtherDefault { } + + struct Option { + inner: T + } + + trait Bar { + fn bar() where Option: Default; + } + + impl Bar for () { + // Trait constraint differs due to the trait even though the constraint + // types are the same. + fn bar() where Option: OtherDefault {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "OtherDefault")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn trait_impl_where_clause_stricter_pass() { + let src = r#" + trait MyTrait { + fn good_foo() where H: OtherTrait; + + fn bad_foo() where H: OtherTrait; + } + + trait OtherTrait {} + + struct Option { + inner: T + } + + impl MyTrait for [T] where Option: MyTrait { + fn good_foo() where B: OtherTrait { } + + fn bad_foo() where A: OtherTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "OtherTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait_generics() { + let src = r#" + trait Foo { + fn foo() where T: T2; + } + + impl Foo for () { + // Should be A: T2 + fn foo() where A: T2 {} + } + + trait T2 {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + constraint_generics, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "T2")); + assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_not_found_for_inner_impl() { + // We want to guarantee that we get a no impl found error + let src = r#" + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType where T: ToField { + fn serialize(self) -> [Field; 2] { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) + )); +} + +// Regression for #5388 +#[test] +fn comptime_let() { + let src = r#"fn main() { + comptime let my_var = 2; + assert_eq(my_var, 2); + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn overflowing_u8() { + let src = r#" + fn main() { + let _: u8 = 256; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁸` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_u8() { + let src = r#" + fn main() { + let _: u8 = -1; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-1` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn overflowing_i8() { + let src = r#" + fn main() { + let _: i8 = 128; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_i8() { + let src = r#" + fn main() { + let _: i8 = -129; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-129` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls + let src = r#" + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); + + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } + + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } + + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } + + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super() { + let src = r#" + fn some_func() {} + + mod foo { + use super::some_func; + + pub fn bar() { + some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super_in_path() { + let src = r#" + fn some_func() {} + + mod foo { + pub fn func() { + super::some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn no_super() { + let src = "use super::some_func;"; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( + PathResolutionError::NoSuper(span), + )) = &errors[0].0 + else { + panic!("Expected a 'no super' error, got {:?}", errors[0].0); + }; + + assert_eq!(span.start(), 4); + assert_eq!(span.end(), 9); +} + +#[test] +fn cannot_call_unconstrained_function_outside_of_unsafe() { + let src = r#" + fn main() { + foo(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { + let src = r#" + fn main() { + let func = foo; + // Warning should trigger here + func(); + inner(func); + } + + fn inner(x: unconstrained fn() -> ()) { + // Warning should trigger here + x(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + for error in &errors { + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; + } +} + +#[test] +fn missing_unsafe_block_when_needing_type_annotations() { + // This test is a regression check that even when an unsafe block is missing + // that we still appropriately continue type checking and infer type annotations. + let src = r#" + fn main() { + let z = BigNum { limbs: [2, 0, 0] }; + assert(z.__is_zero() == false); + } + + struct BigNum { + limbs: [u64; N], + } + + impl BigNum { + unconstrained fn __is_zero_impl(self) -> bool { + let mut result: bool = true; + for i in 0..N { + result = result & (self.limbs[i] == 0); + } + result + } + } + + trait BigNumTrait { + fn __is_zero(self) -> bool; + } + + impl BigNumTrait for BigNum { + fn __is_zero(self) -> bool { + self.__is_zero_impl() + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_pass_unconstrained_function_to_regular_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) { + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_assign_unconstrained_and_regular_fn_to_variable() { + let src = r#" + fn main() { + let _func = if true { foo } else { bar }; + } + + fn foo() {} + unconstrained fn bar() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { + panic!("Expected a context error, got {:?}", errors[0].0); + }; + + if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { + assert_eq!(expected_typ, "fn() -> ()"); + assert_eq!(expr_typ, "unconstrained fn() -> ()"); + } else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_pass_regular_function_to_unconstrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_unconstrained(func); + } + + fn foo() {} + + fn expect_unconstrained(_func: unconstrained fn() -> ()) {} + "#; + assert_no_errors(src); +} + +#[test] +fn cannot_pass_unconstrained_function_to_constrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { + let src = r#" + fn main() { + let _func: unconstrained fn() -> () = foo; + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { + let src = r#" + fn main() { + let _ = Foo { func: foo }; + } + + fn foo() {} + + struct Foo { + func: unconstrained fn() -> (), + } + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_generics_count_mismatch() { + let src = r#" + trait Foo {} + + impl Foo<()> for Field {} + + fn main() {}"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "Foo"); + assert_eq!(*expected, 0); + assert_eq!(*found, 1); +} + +#[test] +fn bit_not_on_untyped_integer() { + let src = r#" + fn main() { + let _: u32 = 3 & !1; + } + "#; + assert_no_errors(src); +} + +#[test] +fn duplicate_struct_field() { + let src = r#" + pub struct Foo { + x: i32, + x: i32, + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { + first_def, + second_def, + }) = &errors[0].0 + else { + panic!("Expected a duplicate field error, got {:?}", errors[0].0); + }; + + assert_eq!(first_def.to_string(), "x"); + assert_eq!(second_def.to_string(), "x"); + + assert_eq!(first_def.span().start(), 30); + assert_eq!(second_def.span().start(), 46); +} + +#[test] +fn trait_constraint_on_tuple_type() { + let src = r#" + trait Foo { + fn foo(self, x: A) -> bool; + } + + pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { + x.foo(y) + } + + fn main() {}"#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_constructor_generics_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let _ = Foo:: { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + 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#" + struct Foo { + x: T + } + + impl Foo { + fn new(x: T) -> Self { + Foo { x } + } + } + + fn main() { + let _ = Foo::::new(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + )); +} + +#[test] +fn turbofish_in_struct_pattern() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn turbofish_in_struct_pattern_generic_count_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "struct Foo"); + assert_eq!(*expected, 1); + assert_eq!(*found, 2); +} + +#[test] +fn incorrect_generic_count_on_struct_impl() { + let src = r#" + struct Foo {} + impl Foo {} + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn incorrect_generic_count_on_type_alias() { + let src = r#" + struct Foo {} + type Bar = Foo; + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn uses_self_type_for_struct_function_call() { + let src = r#" + struct S { } + + impl S { + fn one() -> Field { + 1 + } + + fn two() -> Field { + Self::one() + Self::one() + } + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_inside_trait() { + let src = r#" + trait Foo { + fn foo() -> Self { + Self::bar() + } + + fn bar() -> Self; + } + + impl Foo for Field { + fn bar() -> Self { + 1 + } + } + + fn main() { + let _: Field = Foo::foo(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_in_trait_where_clause() { + let src = r#" + pub trait Trait { + fn trait_func() -> bool; + } + + pub trait Foo where Self: Trait { + fn foo(self) -> bool { + self.trait_func() + } + } + + struct Bar { + + } + + impl Foo for Bar { + + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = + &errors[0].0 + else { + panic!("Expected an unresolved method call error, got {:?}", errors[0].0); + }; + + assert_eq!(method_name, "trait_func"); +} + +#[test] +fn do_not_eagerly_error_on_cast_on_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x: u8 = 1; + let _: Field = foo(x, |x| x as Field); + } + "#; + assert_no_errors(src); +} + +#[test] +fn error_on_cast_over_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x = "a"; + let _: Field = foo(x, |x| x as Field); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + self + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + pub fn use_it(t: T) -> i32 where T: Two { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + let _ = self; + 1 + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + impl Two for u32 { + fn two(self) -> i32 { + let _ = self; + 0 + } + } + + pub fn use_it(t: u32) -> i32 { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn impl_missing_associated_type() { + let src = r#" + trait Foo { + type Assoc; + } + + impl Foo for () {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) + )); +} + +#[test] +fn as_trait_path_syntax_resolves_outside_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + // AsTraitPath syntax is a bit silly when associated types + // are explicitly specified + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + use TypeCheckError::TypeMismatch; + let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { + panic!("Expected TypeMismatch error, found {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i64".to_string()); + assert_eq!(expr_typ, "i32".to_string()); +} + +#[test] +fn as_trait_path_syntax_no_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); +} + +#[test] +fn errors_on_unused_private_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn errors_on_unused_pub_crate_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + pub(crate) use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); } "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn warns_on_use_of_private_exported_item() { let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } + mod foo { + mod bar { + pub fn baz() {} + } - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; + use bar::baz; + + pub fn qux() { + baz(); } - T::deserialize(fields) + } + + fn main() { + foo::baz(); } "#; + let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N + assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated + assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + &errors[0].0, + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(..), + )) )); } -// TODO: re-enable! -// #[test] -// fn numeric_generics_type_kind_mismatch() { -// let src = r#" -// fn foo() -> u16 { -// N as u16 -// } -// -// global J: u16 = 10; -// -// fn bar() -> u16 { -// foo::() -// } -// -// global M: u16 = 3; -// -// fn main() { -// let _ = bar::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generics_value_kind_mismatch_u32_u64() { -// let src = r#" -// struct BoundedVec { -// storage: [T; MaxLen], -// // can't be compared to MaxLen: u32 -// // can't be used to index self.storage -// len: u64, -// } -// -// impl BoundedVec { -// pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { -// // We do this to avoid an unused variable warning on `self` -// let _ = self.len; -// for _ in 0..Len { } -// } -// -// pub fn push(&mut self, elem: T) { -// assert(self.len < MaxLen, "push out of bounds"); -// self.storage[self.len] = elem; -// self.len += 1; -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IntegerBitWidth { -// bit_width_x: IntegerBitSize::SixtyFour, -// bit_width_y: IntegerBitSize::ThirtyTwo, -// .. -// }), -// )); -// } -// -// #[test] -// fn quote_code_fragments() { -// // This test ensures we can quote (and unquote/splice) code fragments -// // which by themselves are not valid code. They only need to be valid -// // by the time they are unquoted into the macro's call site. -// let src = r#" -// fn main() { -// comptime { -// concat!(quote { assert( }, quote { false); }); -// } -// } -// -// comptime fn concat(a: Quoted, b: Quoted) -> Quoted { -// quote { $a $b } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use InterpreterError::FailingConstraint; -// assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); -// } - -// TODO: failing #[test] -fn impl_stricter_than_trait_no_trait_method_constraints() { - // This test ensures that the error we get from the where clause on the trait impl method - // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. +fn can_use_pub_use_item() { let src = r#" - trait Serialize { - // We want to make sure we trigger the error when override a trait method - // which itself has no trait constraints. - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } + mod foo { + mod bar { + pub fn baz() {} + } - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() + pub use bar::baz; } - struct MyType { - a: T, - b: T, + fn main() { + foo::baz(); } + "#; + assert_no_errors(src); +} - impl Serialize<2> for MyType { - fn serialize(self) -> [Field; 2] where T: ToField { - [ self.a.to_field(), self.b.to_field() ] +#[test] +fn warns_on_re_export_of_item_with_less_visibility() { + let src = r#" + mod foo { + mod bar { + pub(crate) fn baz() {} } + + pub use bar::baz; } - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } + fn main() { + foo::baz(); } "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - assert_eq!(errors.len(), 1); + assert!(matches!( &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) + CompilationError::DefinitionError( + DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } + ) )); } -// TODO: re-enable! -// #[test] -// fn impl_stricter_than_trait_different_generics() { -// let src = r#" -// trait Default { } -// -// // Object type of the trait constraint differs -// trait Foo { -// fn foo_good() where T: Default; -// -// fn foo_bad() where T: Default; -// } -// -// impl Foo for () { -// fn foo_good() where A: Default {} -// -// fn foo_bad() where B: Default {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_object_generics() { -// let src = r#" -// trait MyTrait { } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// struct OtherOption { -// inner: Option, -// } -// -// trait Bar { -// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn array_good() where [T; 8]: MyTrait; -// -// fn array_bad() where [T; 8]: MyTrait; -// -// fn tuple_good() where (Option, Option): MyTrait; -// -// fn tuple_bad() where (Option, Option): MyTrait; -// } -// -// impl Bar for () { -// fn bar_good() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn bar_bad() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn array_good() where [A; 8]: MyTrait { } -// -// fn array_bad() where [B; 8]: MyTrait { } -// -// fn tuple_good() where (Option, Option): MyTrait { } -// -// fn tuple_bad() where (Option, Option): MyTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[1].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[2].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait() { -// let src = r#" -// trait Default { } -// -// trait OtherDefault { } -// -// struct Option { -// inner: T -// } -// -// trait Bar { -// fn bar() where Option: Default; -// } -// -// impl Bar for () { -// // Trait constraint differs due to the trait even though the constraint -// // types are the same. -// fn bar() where Option: OtherDefault {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "OtherDefault")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn trait_impl_where_clause_stricter_pass() { -// let src = r#" -// trait MyTrait { -// fn good_foo() where H: OtherTrait; -// -// fn bad_foo() where H: OtherTrait; -// } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// impl MyTrait for [T] where Option: MyTrait { -// fn good_foo() where B: OtherTrait { } -// -// fn bad_foo() where A: OtherTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "OtherTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait_generics() { -// let src = r#" -// trait Foo { -// fn foo() where T: T2; -// } -// -// impl Foo for () { -// // Should be A: T2 -// fn foo() where A: T2 {} -// } -// -// trait T2 {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// constraint_generics, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "T2")); -// assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_not_found_for_inner_impl() { -// // We want to guarantee that we get a no impl found error -// let src = r#" -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// trait ToField { -// fn to_field(self) -> Field; -// } -// -// fn process_array(array: [Field; N]) -> Field { -// array[0] -// } -// -// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { -// thing.serialize() -// } -// -// struct MyType { -// a: T, -// b: T, -// } -// -// impl Serialize<2> for MyType where T: ToField { -// fn serialize(self) -> [Field; 2] { -// [ self.a.to_field(), self.b.to_field() ] -// } -// } -// -// impl MyType { -// fn do_thing_with_serialization_with_extra_steps(self) -> Field { -// process_array(serialize_thing(self)) -// } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) -// )); -// } -// -// // Regression for #5388 -// #[test] -// fn comptime_let() { -// let src = r#"fn main() { -// comptime let my_var = 2; -// assert_eq(my_var, 2); -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn overflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = 256; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁸` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = -1; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-1` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn overflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = 128; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = -129; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-129` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } - -// TODO: failing #[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls +fn unquoted_integer_as_integer_token() { let src = r#" - fn foo() -> [u8; N] { - [0; N] + trait Serialize { + fn serialize() {} } - fn bar() -> [u8; N] { - foo::() + #[attr] + pub fn foobar() {} + + comptime fn attr(_f: FunctionDefinition) -> Quoted { + let serialized_len = 1; + // We are testing that when we unquote $serialized_len, it's unquoted + // as the token `1` and not as something else that later won't be parsed correctly + // in the context of a generic argument. + quote { + impl Serialize<$serialized_len> for Field { + fn serialize() { } + } + } } - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } + fn main() {} "#; + assert_no_errors(src); +} - // Check for turbofish numeric generics used with method calls +#[test] +fn errors_on_unused_function() { let src = r#" - struct Foo { - a: T + contract some_contract { + // This function is unused, but it's a contract entrypoint + // so it should not produce a warning + fn foo() -> pub Field { + 1 + } } - impl Foo { - fn static_method() -> [u8; N] { - [0; N] - } - fn impl_method(self) -> [T; N] { - [self.a; N] - } + fn foo() { + bar(); } - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() - } + fn bar() {} + "#; - global M: u32 = 3; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "foo"); + assert_eq!(*item_type, "function"); +} + +#[test] +fn errors_on_unused_struct() { + let src = r#" + struct Foo {} + struct Bar {} fn main() { - let _ = bar::(); + let _ = Bar {}; } "#; - assert_no_errors(src); + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "struct"); } -// TODO: re-enable! -// #[test] -// fn use_super() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// use super::some_func; -// -// pub fn bar() { -// some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn use_super_in_path() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// pub fn func() { -// super::some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn no_super() { -// let src = "use super::some_func;"; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( -// PathResolutionError::NoSuper(span), -// )) = &errors[0].0 -// else { -// panic!("Expected a 'no super' error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(span.start(), 4); -// assert_eq!(span.end(), 9); -// } -// -// #[test] -// fn cannot_call_unconstrained_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// foo(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// let func = foo; -// // Warning should trigger here -// func(); -// inner(func); -// } -// -// fn inner(x: unconstrained fn() -> ()) { -// // Warning should trigger here -// x(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); -// -// for error in &errors { -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// } - -// TODO: failing #[test] -fn missing_unsafe_block_when_needing_type_annotations() { - // This test is a regression check that even when an unsafe block is missing - // that we still appropriately continue type checking and infer type annotations. +fn errors_on_unused_trait() { let src = r#" - fn main() { - let z = BigNum { limbs: [2, 0, 0] }; - assert(z.__is_zero() == false); + trait Foo {} + trait Bar {} + + pub struct Baz { } - struct BigNum { - limbs: [u64; N], + impl Bar for Baz {} + + fn main() { } + "#; - impl BigNum { - unconstrained fn __is_zero_impl(self) -> bool { - let mut result: bool = true; - for i in 0..N { - result = result & (self.limbs[i] == 0); + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "trait"); +} + +#[test] +fn constrained_reference_to_unconstrained() { + let src = r#" + fn main(mut x: u32, y: pub u32) { + let x_ref = &mut x; + if x == 5 { + unsafe { + mut_ref_input(x_ref, y); } - result } - } - trait BigNumTrait { - fn __is_zero(self) -> bool; + assert(x == 10); } - impl BigNumTrait for BigNum { - fn __is_zero(self) -> bool { - self.__is_zero_impl() - } + unconstrained fn mut_ref_input(x: &mut u32, y: u32) { + *x = y; } "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = + &errors[0].0 + else { + panic!("Expected an error about passing a constrained reference to unconstrained"); }; } -// TODO: re-enable! -// #[test] -// fn cannot_pass_unconstrained_function_to_regular_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) { -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_assign_unconstrained_and_regular_fn_to_variable() { -// let src = r#" -// fn main() { -// let _func = if true { foo } else { bar }; -// } -// -// fn foo() {} -// unconstrained fn bar() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { -// panic!("Expected a context error, got {:?}", errors[0].0); -// }; -// -// if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { -// assert_eq!(expected_typ, "fn() -> ()"); -// assert_eq!(expr_typ, "unconstrained fn() -> ()"); -// } else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_pass_regular_function_to_unconstrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_unconstrained(func); -// } -// -// fn foo() {} -// -// fn expect_unconstrained(_func: unconstrained fn() -> ()) {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn cannot_pass_unconstrained_function_to_constrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { -// let src = r#" -// fn main() { -// let _func: unconstrained fn() -> () = foo; -// } -// -// fn foo() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { -// let src = r#" -// fn main() { -// let _ = Foo { func: foo }; -// } -// -// fn foo() {} -// -// struct Foo { -// func: unconstrained fn() -> (), -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn trait_impl_generics_count_mismatch() { -// let src = r#" -// trait Foo {} -// -// impl Foo<()> for Field {} -// -// fn main() {}"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "Foo"); -// assert_eq!(*expected, 0); -// assert_eq!(*found, 1); -// } -// -// #[test] -// fn bit_not_on_untyped_integer() { -// let src = r#" -// fn main() { -// let _: u32 = 3 & !1; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn duplicate_struct_field() { -// let src = r#" -// pub struct Foo { -// x: i32, -// x: i32, -// } -// -// fn main() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { -// first_def, -// second_def, -// }) = &errors[0].0 -// else { -// panic!("Expected a duplicate field error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(first_def.to_string(), "x"); -// assert_eq!(second_def.to_string(), "x"); -// -// assert_eq!(first_def.span().start(), 30); -// assert_eq!(second_def.span().start(), 46); -// } -// -// #[test] -// fn trait_constraint_on_tuple_type() { -// let src = r#" -// trait Foo { -// fn foo(self, x: A) -> bool; -// } -// -// pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { -// x.foo(y) -// } -// -// fn main() {}"#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_constructor_generics_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let _ = Foo:: { x: 1 }; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// 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#" -// struct Foo { -// x: T -// } -// -// impl Foo { -// fn new(x: T) -> Self { -// Foo { x } -// } -// } -// -// fn main() { -// let _ = Foo::::new(1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), -// )); -// } -// -// #[test] -// fn turbofish_in_struct_pattern() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_struct_pattern_errors_if_type_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 -// else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn turbofish_in_struct_pattern_generic_count_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "struct Foo"); -// assert_eq!(*expected, 1); -// assert_eq!(*found, 2); -// } -// -// #[test] -// fn incorrect_generic_count_on_struct_impl() { -// let src = r#" -// struct Foo {} -// impl Foo {} -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// -// #[test] -// fn incorrect_generic_count_on_type_alias() { -// let src = r#" -// struct Foo {} -// type Bar = Foo; -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// -// #[test] -// fn uses_self_type_for_struct_function_call() { -// let src = r#" -// struct S { } -// -// impl S { -// fn one() -> Field { -// 1 -// } -// -// fn two() -> Field { -// Self::one() + Self::one() -// } -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn uses_self_type_inside_trait() { -// let src = r#" -// trait Foo { -// fn foo() -> Self { -// Self::bar() -// } -// -// fn bar() -> Self; -// } -// -// impl Foo for Field { -// fn bar() -> Self { -// 1 -// } -// } -// -// fn main() { -// let _: Field = Foo::foo(); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn uses_self_type_in_trait_where_clause() { -// let src = r#" -// pub trait Trait { -// fn trait_func() -> bool; -// } -// -// pub trait Foo where Self: Trait { -// fn foo(self) -> bool { -// self.trait_func() -// } -// } -// -// struct Bar { -// -// } -// -// impl Foo for Bar { -// -// } -// -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected an unresolved method call error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(method_name, "trait_func"); -// } -// -// #[test] -// fn do_not_eagerly_error_on_cast_on_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x: u8 = 1; -// let _: Field = foo(x, |x| x as Field); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn error_on_cast_over_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x = "a"; -// let _: Field = foo(x, |x| x as Field); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) -// )); -// } -// -// #[test] -// fn trait_impl_for_a_type_that_implements_another_trait() { -// let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// self -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 -// } -// } -// -// pub fn use_it(t: T) -> i32 where T: Two { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { -// let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// let _ = self; -// 1 -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 -// } -// } -// -// impl Two for u32 { -// fn two(self) -> i32 { -// let _ = self; -// 0 -// } -// } -// -// pub fn use_it(t: u32) -> i32 { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn impl_missing_associated_type() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// impl Foo for () {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) -// )); -// } -// -// #[test] -// fn as_trait_path_syntax_resolves_outside_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// // AsTraitPath syntax is a bit silly when associated types -// // are explicitly specified -// let _: i64 = 1 as >::Assoc; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// use TypeCheckError::TypeMismatch; -// let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { -// panic!("Expected TypeMismatch error, found {:?}", errors[0].0); -// }; -// -// assert_eq!(expected_typ, "i64".to_string()); -// assert_eq!(expr_typ, "i32".to_string()); -// } -// -// #[test] -// fn as_trait_path_syntax_no_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// let _: i64 = 1 as >::Assoc; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); -// } -// -// #[test] -// fn errors_on_unused_private_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// pub trait Foo { -// } -// } -// -// use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); -// } -// -// #[test] -// fn errors_on_unused_pub_crate_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// pub trait Foo { -// } -// } -// -// pub(crate) use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); -// } -// -// #[test] -// fn warns_on_use_of_private_exported_item() { -// let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} -// } -// -// use bar::baz; -// -// pub fn qux() { -// baz(); -// } -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Private(..), -// )) -// )); -// } -// -// #[test] -// fn can_use_pub_use_item() { -// let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} -// } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn warns_on_re_export_of_item_with_less_visibility() { -// let src = r#" -// mod foo { -// mod bar { -// pub(crate) fn baz() {} -// } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } -// ) -// )); -// } -// -// #[test] -// fn unquoted_integer_as_integer_token() { -// let src = r#" -// trait Serialize { -// fn serialize() {} -// } -// -// #[attr] -// pub fn foobar() {} -// -// comptime fn attr(_f: FunctionDefinition) -> Quoted { -// let serialized_len = 1; -// // We are testing that when we unquote $serialized_len, it's unquoted -// // as the token `1` and not as something else that later won't be parsed correctly -// // in the context of a generic argument. -// quote { -// impl Serialize<$serialized_len> for Field { -// fn serialize() { } -// } -// } -// } -// -// fn main() {} -// "#; -// -// assert_no_errors(src); -// } -// -// #[test] -// fn errors_on_unused_function() { -// let src = r#" -// contract some_contract { -// // This function is unused, but it's a contract entrypoint -// // so it should not produce a warning -// fn foo() -> pub Field { -// 1 -// } -// } -// -// -// fn foo() { -// bar(); -// } -// -// fn bar() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "foo"); -// assert_eq!(*item_type, "function"); -// } -// -// #[test] -// fn errors_on_unused_struct() { -// let src = r#" -// struct Foo {} -// struct Bar {} -// -// fn main() { -// let _ = Bar {}; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "Foo"); -// assert_eq!(*item_type, "struct"); -// } -// -// #[test] -// fn errors_on_unused_trait() { -// let src = r#" -// trait Foo {} -// trait Bar {} -// -// pub struct Baz { -// } -// -// impl Bar for Baz {} -// -// fn main() { -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "Foo"); -// assert_eq!(*item_type, "trait"); -// } -// -// #[test] -// fn constrained_reference_to_unconstrained() { -// let src = r#" -// fn main(mut x: u32, y: pub u32) { -// let x_ref = &mut x; -// if x == 5 { -// unsafe { -// mut_ref_input(x_ref, y); -// } -// } -// -// assert(x == 10); -// } -// -// unconstrained fn mut_ref_input(x: &mut u32, y: u32) { -// *x = y; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = -// &errors[0].0 -// else { -// panic!("Expected an error about passing a constrained reference to unconstrained"); -// }; -// } -// -// #[test] -// fn comptime_type_in_runtime_code() { -// let source = "pub fn foo(_f: FunctionDefinition) {}"; -// let errors = get_program_errors(source); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) -// )); -// } - -// TODO: failing +#[test] +fn comptime_type_in_runtime_code() { + let source = "pub fn foo(_f: FunctionDefinition) {}"; + let errors = get_program_errors(source); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) + )); +} + #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { let source = r#" @@ -3595,121 +3574,119 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { assert_eq!(errors.len(), 0); } -// TODO: re-enable! -// #[test] -// fn cannot_mutate_immutable_variable() { -// let src = r#" -// fn main() { -// let array = [1]; -// mutate(&mut array); -// } -// -// fn mutate(_: &mut [Field; 1]) {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a CannotMutateImmutableVariable error"); -// }; -// -// assert_eq!(name, "array"); -// } -// -// #[test] -// fn cannot_mutate_immutable_variable_on_member_access() { -// let src = r#" -// struct Foo { -// x: Field -// } -// -// fn main() { -// let foo = Foo { x: 0 }; -// mutate(&mut foo.x); -// } -// -// fn mutate(foo: &mut Field) { -// *foo = 1; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a CannotMutateImmutableVariable error"); -// }; -// -// assert_eq!(name, "foo"); -// } -// -// #[test] -// fn does_not_crash_when_passing_mutable_undefined_variable() { -// let src = r#" -// fn main() { -// mutate(&mut undefined); -// } -// -// fn mutate(foo: &mut Field) { -// *foo = 1; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a VariableNotDeclared error"); -// }; -// -// assert_eq!(name, "undefined"); -// } -// -// #[test] -// fn infer_globals_to_u32_from_type_use() { -// let src = r#" -// global ARRAY_LEN = 3; -// global STR_LEN = 2; -// global FMT_STR_LEN = 2; -// -// fn main() { -// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; -// let _b: str = "hi"; -// let _c: fmtstr = f"hi"; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn non_u32_in_array_length() { -// let src = r#" -// global ARRAY_LEN: u8 = 3; -// -// fn main() { -// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) -// )); -// } - -// TODO: failing +#[test] +fn cannot_mutate_immutable_variable() { + let src = r#" + fn main() { + let array = [1]; + mutate(&mut array); + } + + fn mutate(_: &mut [Field; 1]) {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "array"); +} + +#[test] +fn cannot_mutate_immutable_variable_on_member_access() { + let src = r#" + struct Foo { + x: Field + } + + fn main() { + let foo = Foo { x: 0 }; + mutate(&mut foo.x); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "foo"); +} + +#[test] +fn does_not_crash_when_passing_mutable_undefined_variable() { + let src = r#" + fn main() { + mutate(&mut undefined); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = + &errors[0].0 + else { + panic!("Expected a VariableNotDeclared error"); + }; + + assert_eq!(name, "undefined"); +} + +#[test] +fn infer_globals_to_u32_from_type_use() { + let src = r#" + global ARRAY_LEN = 3; + global STR_LEN = 2; + global FMT_STR_LEN = 2; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + let _b: str = "hi"; + let _c: fmtstr = f"hi"; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn non_u32_in_array_length() { + let src = r#" + global ARRAY_LEN: u8 = 3; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) + )); +} + #[test] fn use_non_u32_generic_in_struct() { let src = r#" @@ -3721,14 +3698,9 @@ fn use_non_u32_generic_in_struct() { "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } -// TODO: failing #[test] fn use_numeric_generic_in_trait_method() { let src = r#" @@ -3745,8 +3717,7 @@ fn use_numeric_generic_in_trait_method() { } fn main() { - let bytes: [u8; 3] = [1,2,3]; - let _ = Bar{}.foo(bytes); + let _ = Bar{}.foo([1,2,3]); } "#; diff --git a/compiler/noirc_frontend/src/tests/name_shadowing.rs b/compiler/noirc_frontend/src/tests/name_shadowing.rs index d67cd519103..b0d83510039 100644 --- a/compiler/noirc_frontend/src/tests/name_shadowing.rs +++ b/compiler/noirc_frontend/src/tests/name_shadowing.rs @@ -2,419 +2,418 @@ use super::get_program_errors; use std::collections::HashSet; -// TODO: re-enable! -// #[test] -// fn test_name_shadowing() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for bool { -// fn default() -> bool { -// false -// } -// } -// -// impl Default for Field { -// fn default() -> Field { -// 0 -// } -// } -// -// impl Default for [T; N] where T: Default { -// fn default() -> [T; N] { -// [Default::default(); N] -// } -// } -// -// impl Default for (T, U) where T: Default, U: Default { -// fn default() -> (T, U) { -// (Default::default(), Default::default()) -// } -// } -// -// fn drop_var(_x: T, y: U) -> U { y } -// -// mod local_module { -// use crate::{Default, drop_var}; -// -// global LOCAL_GLOBAL_N: Field = 0; -// -// global LOCAL_GLOBAL_M: Field = 1; -// -// struct LocalStruct { -// field1: A, -// field2: B, -// field3: [A; N], -// field4: ([A; N], [B; M]), -// field5: &mut A, -// } -// -// impl Default for LocalStruct where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// field1: Default::default(), -// field2: Default::default(), -// field3: Default::default(), -// field4: Default::default(), -// field5: mut_field, -// } -// } -// } -// -// trait DefinedInLocalModule1 { -// fn trait_fn1(self, x: A); -// fn trait_fn2(self, y: B); -// fn trait_fn3(&mut self, x: A, y: B); -// fn trait_fn4(self, x: [A; 0], y: [B]); -// fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl DefinedInLocalModule1 for LocalStruct { -// fn trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// let x: Field = 0; -// assert(x == 0); -// let x: Field = 1; -// assert(x == 1); -// [] -// } -// } -// -// mod library { -// use crate::{Default, drop_var}; -// -// mod library2 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_2: Field = 4; -// -// global IMPORT_GLOBAL_M_2: Field = 5; -// -// // When we re-export this type from another library and then use it in -// // main, we get a panic -// struct ReExportMeFromAnotherLib1 { -// x : Field, -// } -// -// struct PubLibLocalStruct3 { -// pub_field1: A, -// pub_field2: B, -// pub_field3: [A; N], -// pub_field4: ([A; N], [B; M]), -// pub_field5: &mut A, -// } -// -// impl Default for PubLibLocalStruct3 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// pub_field1: Default::default(), -// pub_field2: Default::default(), -// pub_field3: Default::default(), -// pub_field4: Default::default(), -// pub_field5: mut_field, -// } -// } -// } -// -// trait PubLibDefinedInLocalModule3 { -// fn pub_trait_fn1(self, x: A); -// fn pub_trait_fn2(self, y: B); -// fn pub_trait_fn3(&mut self, x: A, y: B); -// fn pub_trait_fn4(self, x: [A; 0], y: [B]); -// fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { -// fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// [] -// } -// } -// -// // Re-export -// use library2::ReExportMeFromAnotherLib1; -// -// global IMPORT_GLOBAL_N_1: Field = 2; -// -// global IMPORT_GLOBAL_M_1: Field = 3; -// -// struct LibLocalStruct1 { -// lib_field1: A, -// lib_field2: B, -// lib_field3: [A; N], -// lib_field4: ([A; N], [B; M]), -// lib_field5: &mut A, -// } -// -// impl Default for LibLocalStruct1 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// lib_field1: Default::default(), -// lib_field2: Default::default(), -// lib_field3: Default::default(), -// lib_field4: Default::default(), -// lib_field5: mut_field, -// } -// } -// } -// -// trait LibDefinedInLocalModule1 { -// fn lib_trait_fn1(self, x: A); -// fn lib_trait_fn2(self, y: B); -// fn lib_trait_fn3(&mut self, x: A, y: B); -// fn lib_trait_fn4(self, x: [A; 0], y: [B]); -// fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl LibDefinedInLocalModule1 for LibLocalStruct1 { -// fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// [] -// } -// } -// -// mod library3 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_3: Field = 6; -// -// global IMPORT_GLOBAL_M_3: Field = 7; -// -// struct ReExportMeFromAnotherLib2 { -// x : Field, -// } -// -// struct PubCrateLibLocalStruct2 { -// crate_field1: A, -// crate_field2: B, -// crate_field3: [A; N], -// crate_field4: ([A; N], [B; M]), -// crate_field5: &mut A, -// } -// -// impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// crate_field1: Default::default(), -// crate_field2: Default::default(), -// crate_field3: Default::default(), -// crate_field4: Default::default(), -// crate_field5: mut_field, -// } -// } -// } -// -// trait PubCrateLibDefinedInLocalModule2 { -// fn crate_trait_fn1(self, x: A); -// fn crate_trait_fn2(self, y: B); -// fn crate_trait_fn3(&mut self, x: A, y: B); -// fn crate_trait_fn4(self, x: [A; 0], y: [B]); -// fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { -// fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// } -// -// pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// [] -// } -// } -// -// -// use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; -// -// use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; -// -// // overlapping -// // use library::library2::ReExportMeFromAnotherLib1; -// use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; -// -// use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; -// -// -// fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// -// let x: LocalStruct = Default::default(); -// assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// -// let x: LibLocalStruct1 = Default::default(); -// assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// -// let x: PubLibLocalStruct3 = Default::default(); -// assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// -// let x: PubCrateLibLocalStruct2 = Default::default(); -// assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// -// assert(drop_var(local_fn2((0, 1), [], []), true)); -// assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); -// assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); -// assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); -// }"; -// -// // NOTE: these names must be "replacement-unique", i.e. -// // replacing one in a discinct name should do nothing -// let names_to_collapse = [ -// "DefinedInLocalModule1", -// "IMPORT_GLOBAL_M_1", -// "IMPORT_GLOBAL_M_2", -// "IMPORT_GLOBAL_M_3", -// "IMPORT_GLOBAL_N_1", -// "IMPORT_GLOBAL_N_2", -// "IMPORT_GLOBAL_N_3", -// "LOCAL_GLOBAL_M", -// "LOCAL_GLOBAL_N", -// "LibDefinedInLocalModule1", -// "LibLocalStruct1", -// "Liblocal_fn1", -// "LocalStruct", -// "PubCrateLibDefinedInLocalModule2", -// "PubCrateLibLocalStruct2", -// "PubCrateLiblocal_fn2", -// "PubLibDefinedInLocalModule3", -// "PubLibLocalStruct3", -// "PubLiblocal_fn3", -// "ReExportMeFromAnotherLib1", -// "ReExportMeFromAnotherLib2", -// "local_fn4", -// "crate_field1", -// "crate_field2", -// "crate_field3", -// "crate_field4", -// "crate_field5", -// "crate_trait_fn1", -// "crate_trait_fn2", -// "crate_trait_fn3", -// "crate_trait_fn4", -// "crate_trait_fn5", -// "crate_trait_fn6", -// "crate_trait_fn7", -// "field1", -// "field2", -// "field3", -// "field4", -// "field5", -// "lib_field1", -// "lib_field2", -// "lib_field3", -// "lib_field4", -// "lib_field5", -// "lib_trait_fn1", -// "lib_trait_fn2", -// "lib_trait_fn3", -// "lib_trait_fn4", -// "lib_trait_fn5", -// "lib_trait_fn6", -// "lib_trait_fn7", -// "pub_field1", -// "pub_field2", -// "pub_field3", -// "pub_field4", -// "pub_field5", -// "pub_trait_fn1", -// "pub_trait_fn2", -// "pub_trait_fn3", -// "pub_trait_fn4", -// "pub_trait_fn5", -// "pub_trait_fn6", -// "pub_trait_fn7", -// "trait_fn1", -// "trait_fn2", -// "trait_fn3", -// "trait_fn4", -// "trait_fn5", -// "trait_fn6", -// "trait_fn7", -// ]; -// -// // TODO(https://github.com/noir-lang/noir/issues/4973): -// // Name resolution panic from name shadowing test -// let cases_to_skip = [ -// (1, 21), -// (2, 11), -// (2, 21), -// (3, 11), -// (3, 18), -// (3, 21), -// (4, 21), -// (5, 11), -// (5, 21), -// (6, 11), -// (6, 18), -// (6, 21), -// ]; -// let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); -// -// for (i, x) in names_to_collapse.iter().enumerate() { -// for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { -// if !cases_to_skip.contains(&(i, j)) { -// dbg!((i, j)); -// -// let modified_src = src.replace(x, y); -// let errors = get_program_errors(&modified_src); -// assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); -// } -// } -// } -// } +#[test] +fn test_name_shadowing() { + let src = " + trait Default { + fn default() -> Self; + } + + impl Default for bool { + fn default() -> bool { + false + } + } + + impl Default for Field { + fn default() -> Field { + 0 + } + } + + impl Default for [T; N] where T: Default { + fn default() -> [T; N] { + [Default::default(); N] + } + } + + impl Default for (T, U) where T: Default, U: Default { + fn default() -> (T, U) { + (Default::default(), Default::default()) + } + } + + fn drop_var(_x: T, y: U) -> U { y } + + mod local_module { + use crate::{Default, drop_var}; + + global LOCAL_GLOBAL_N: Field = 0; + + global LOCAL_GLOBAL_M: Field = 1; + + struct LocalStruct { + field1: A, + field2: B, + field3: [A; N], + field4: ([A; N], [B; M]), + field5: &mut A, + } + + impl Default for LocalStruct where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + field1: Default::default(), + field2: Default::default(), + field3: Default::default(), + field4: Default::default(), + field5: mut_field, + } + } + } + + trait DefinedInLocalModule1 { + fn trait_fn1(self, x: A); + fn trait_fn2(self, y: B); + fn trait_fn3(&mut self, x: A, y: B); + fn trait_fn4(self, x: [A; 0], y: [B]); + fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl DefinedInLocalModule1 for LocalStruct { + fn trait_fn1(self, _x: A) { drop_var(self, ()) } + fn trait_fn2(self, _y: B) { drop_var(self, ()) } + fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + let x: Field = 0; + assert(x == 0); + let x: Field = 1; + assert(x == 1); + [] + } + } + + mod library { + use crate::{Default, drop_var}; + + mod library2 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_2: Field = 4; + + global IMPORT_GLOBAL_M_2: Field = 5; + + // When we re-export this type from another library and then use it in + // main, we get a panic + struct ReExportMeFromAnotherLib1 { + x : Field, + } + + struct PubLibLocalStruct3 { + pub_field1: A, + pub_field2: B, + pub_field3: [A; N], + pub_field4: ([A; N], [B; M]), + pub_field5: &mut A, + } + + impl Default for PubLibLocalStruct3 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + pub_field1: Default::default(), + pub_field2: Default::default(), + pub_field3: Default::default(), + pub_field4: Default::default(), + pub_field5: mut_field, + } + } + } + + trait PubLibDefinedInLocalModule3 { + fn pub_trait_fn1(self, x: A); + fn pub_trait_fn2(self, y: B); + fn pub_trait_fn3(&mut self, x: A, y: B); + fn pub_trait_fn4(self, x: [A; 0], y: [B]); + fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { + fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + [] + } + } + + // Re-export + use library2::ReExportMeFromAnotherLib1; + + global IMPORT_GLOBAL_N_1: Field = 2; + + global IMPORT_GLOBAL_M_1: Field = 3; + + struct LibLocalStruct1 { + lib_field1: A, + lib_field2: B, + lib_field3: [A; N], + lib_field4: ([A; N], [B; M]), + lib_field5: &mut A, + } + + impl Default for LibLocalStruct1 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + lib_field1: Default::default(), + lib_field2: Default::default(), + lib_field3: Default::default(), + lib_field4: Default::default(), + lib_field5: mut_field, + } + } + } + + trait LibDefinedInLocalModule1 { + fn lib_trait_fn1(self, x: A); + fn lib_trait_fn2(self, y: B); + fn lib_trait_fn3(&mut self, x: A, y: B); + fn lib_trait_fn4(self, x: [A; 0], y: [B]); + fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl LibDefinedInLocalModule1 for LibLocalStruct1 { + fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + [] + } + } + + mod library3 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_3: Field = 6; + + global IMPORT_GLOBAL_M_3: Field = 7; + + struct ReExportMeFromAnotherLib2 { + x : Field, + } + + struct PubCrateLibLocalStruct2 { + crate_field1: A, + crate_field2: B, + crate_field3: [A; N], + crate_field4: ([A; N], [B; M]), + crate_field5: &mut A, + } + + impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + crate_field1: Default::default(), + crate_field2: Default::default(), + crate_field3: Default::default(), + crate_field4: Default::default(), + crate_field5: mut_field, + } + } + } + + trait PubCrateLibDefinedInLocalModule2 { + fn crate_trait_fn1(self, x: A); + fn crate_trait_fn2(self, y: B); + fn crate_trait_fn3(&mut self, x: A, y: B); + fn crate_trait_fn4(self, x: [A; 0], y: [B]); + fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { + fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + } + + pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + [] + } + } + + + use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; + + use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; + + // overlapping + // use library::library2::ReExportMeFromAnotherLib1; + use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; + + use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; + + + fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + + let x: LocalStruct = Default::default(); + assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + + let x: LibLocalStruct1 = Default::default(); + assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + + let x: PubLibLocalStruct3 = Default::default(); + assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + + let x: PubCrateLibLocalStruct2 = Default::default(); + assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + + assert(drop_var(local_fn2((0, 1), [], []), true)); + assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); + assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); + assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); + }"; + + // NOTE: these names must be "replacement-unique", i.e. + // replacing one in a discinct name should do nothing + let names_to_collapse = [ + "DefinedInLocalModule1", + "IMPORT_GLOBAL_M_1", + "IMPORT_GLOBAL_M_2", + "IMPORT_GLOBAL_M_3", + "IMPORT_GLOBAL_N_1", + "IMPORT_GLOBAL_N_2", + "IMPORT_GLOBAL_N_3", + "LOCAL_GLOBAL_M", + "LOCAL_GLOBAL_N", + "LibDefinedInLocalModule1", + "LibLocalStruct1", + "Liblocal_fn1", + "LocalStruct", + "PubCrateLibDefinedInLocalModule2", + "PubCrateLibLocalStruct2", + "PubCrateLiblocal_fn2", + "PubLibDefinedInLocalModule3", + "PubLibLocalStruct3", + "PubLiblocal_fn3", + "ReExportMeFromAnotherLib1", + "ReExportMeFromAnotherLib2", + "local_fn4", + "crate_field1", + "crate_field2", + "crate_field3", + "crate_field4", + "crate_field5", + "crate_trait_fn1", + "crate_trait_fn2", + "crate_trait_fn3", + "crate_trait_fn4", + "crate_trait_fn5", + "crate_trait_fn6", + "crate_trait_fn7", + "field1", + "field2", + "field3", + "field4", + "field5", + "lib_field1", + "lib_field2", + "lib_field3", + "lib_field4", + "lib_field5", + "lib_trait_fn1", + "lib_trait_fn2", + "lib_trait_fn3", + "lib_trait_fn4", + "lib_trait_fn5", + "lib_trait_fn6", + "lib_trait_fn7", + "pub_field1", + "pub_field2", + "pub_field3", + "pub_field4", + "pub_field5", + "pub_trait_fn1", + "pub_trait_fn2", + "pub_trait_fn3", + "pub_trait_fn4", + "pub_trait_fn5", + "pub_trait_fn6", + "pub_trait_fn7", + "trait_fn1", + "trait_fn2", + "trait_fn3", + "trait_fn4", + "trait_fn5", + "trait_fn6", + "trait_fn7", + ]; + + // TODO(https://github.com/noir-lang/noir/issues/4973): + // Name resolution panic from name shadowing test + let cases_to_skip = [ + (1, 21), + (2, 11), + (2, 21), + (3, 11), + (3, 18), + (3, 21), + (4, 21), + (5, 11), + (5, 21), + (6, 11), + (6, 18), + (6, 21), + ]; + let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); + + for (i, x) in names_to_collapse.iter().enumerate() { + for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { + if !cases_to_skip.contains(&(i, j)) { + dbg!((i, j)); + + let modified_src = src.replace(x, y); + let errors = get_program_errors(&modified_src); + assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); + } + } + } +} From ca62c45150f03ea141b097e4fa5bf3c1792904da Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 24 Sep 2024 13:37:02 -0400 Subject: [PATCH 08/56] wip debugging missing kinds, splitting out kind size checks, add kind check during monomorphization, add missing lsp case --- acvm-repo/acir_field/src/field_element.rs | 4 -- .../noirc_frontend/src/elaborator/patterns.rs | 2 +- .../src/hir/comptime/interpreter.rs | 5 +- compiler/noirc_frontend/src/hir_def/types.rs | 63 +++---------------- .../src/monomorphization/mod.rs | 12 +++- tooling/lsp/src/requests/inlay_hint.rs | 7 +++ 6 files changed, 27 insertions(+), 66 deletions(-) diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index dc4d76e0b99..2323f008dbe 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -172,10 +172,6 @@ impl From for FieldElement { } impl FieldElement { - pub fn max_value() -> Self { - Self::zero() - Self::one() - } - pub fn from_repr(field: F) -> Self { Self(field) } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index c599d447b28..b3799f3deb1 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::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - ResolvedGeneric, Shared, StructType, Type, TypeBindings, TypeVariable, TypeVariableKind, + ResolvedGeneric, Shared, StructType, Type, TypeBindings, TypeVariableKind, }; use super::{Elaborator, ResolverMeta}; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index ea68757a0ff..cc0b44952bd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -586,13 +586,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { DefinitionKind::GenericType(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => None, - // TODO: fix clone - TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()), + TypeBinding::Bound(binding) => binding.evaluate_to_u32(), }; if let Some(value) = value { let typ = self.elaborator.interner.id_type(id); - self.evaluate_integer(value, false, id) + self.evaluate_integer((value as u128).into(), false, id) } else { let location = self.elaborator.interner.expr_location(&id); let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index aee6d51ac6c..a0182f491a9 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -5,7 +5,6 @@ use std::{ rc::Rc, }; -use acvm::FieldElement; use crate::{ ast::IntegerBitSize, hir::type_check::{generics::TraitGenerics, TypeCheckError}, @@ -179,25 +178,6 @@ impl Kind { Err(UnificationError) } } - - /// Ensure the given value fits in max(u32::MAX, max_of_self) - pub(crate) fn ensure_value_fits>(&self, value: T) -> Option { - match self { - Self::Normal => Some(value), - Self::Numeric(typ) => { - match typ.integral_maximum_size() { - None => Some(value), - Some(maximum_size) => { - if value.clone().into() <= maximum_size { - Some(value) - } else { - None - } - } - } - }, - } - } } impl std::fmt::Display for Kind { @@ -1784,15 +1764,15 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some((binding, kind)) = self.get_inner_type_variable() { + if let Some((binding, _kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return binding.evaluate_to_u32().and_then(|result| kind.ensure_value_fits(result)); + return binding.evaluate_to_u32(); } } match self.canonicalize() { Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x, kind) => kind.ensure_value_fits(x), + Type::Constant(x, _kind) => Some(x), Type::InfixExpr(lhs, op, rhs) => { let lhs_u32 = lhs.evaluate_to_u32()?; let rhs_u32 = rhs.evaluate_to_u32()?; @@ -1802,11 +1782,6 @@ impl Type { } } - // TODO! - pub(crate) fn evaluate_to_field_element(&self) -> acvm::FieldElement { - unimplemented!("evaluate_to_field_element") - } - /// Iterate over the fields of this type. /// Panics if the type is not a struct or tuple. pub fn iter_fields(&self) -> impl Iterator { @@ -2317,30 +2292,6 @@ impl Type { _ => None, } } - - fn integral_maximum_size(&self) -> Option { - match self { - Type::FieldElement => Some(FieldElement::max_value()), - Type::Integer(sign, num_bits) => { - let mut max_bit_size = num_bits.bit_size(); - if sign == &Signedness::Signed { - max_bit_size >>= 1; - } - Some(((1u128 << max_bit_size) - 1).into()) - } - Type::TypeVariable(binding, TypeVariableKind::Integer | TypeVariableKind::IntegerOrField) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_) => Some(FieldElement::max_value()), - } - } - Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), - Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), - Type::Bool => Some(FieldElement::from(1u128)), - Type::NamedGeneric(_binding, _name, Kind::Numeric(typ)) => typ.integral_maximum_size(), - _ => None, - } - } } /// Wraps a given `expression` in `expression.as_slice()` @@ -2378,15 +2329,15 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - pub fn function(self, a: u32, b: u32, kind: &Kind) -> Option { - let result = match self { + // TODO: the Kind is included since it'll be needed for size checks + pub fn function(self, a: u32, b: u32, _kind: &Kind) -> Option { + match self { BinaryTypeOperator::Addition => a.checked_add(b), BinaryTypeOperator::Subtraction => a.checked_sub(b), BinaryTypeOperator::Multiplication => a.checked_mul(b), BinaryTypeOperator::Division => a.checked_div(b), BinaryTypeOperator::Modulo => a.checked_rem(b), - }; - result.and_then(|result| kind.ensure_value_fits(result)) + } } fn is_commutative(self) -> bool { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 117da91f509..2fc43c3df80 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -921,13 +921,21 @@ impl<'interner> Monomorphizer<'interner> { TypeBinding::Unbound(_) => { unreachable!("Unbound type variable used in expression") } - TypeBinding::Bound(binding) => Kind::Numeric(numeric_typ.clone()).ensure_value_fits(binding.evaluate_to_field_element()).unwrap_or_else(|| { + TypeBinding::Bound(binding) => binding.evaluate_to_u32().unwrap_or_else(|| { panic!("Non-numeric type variable used in expression expecting a value") }), }; let location = self.interner.id_location(expr_id); + + // TODO: check that numeric_typ matches typ + // TODO: possible to remove clones? + if !Kind::Numeric(numeric_typ.clone()).unifies(&Kind::Numeric(Box::new(typ.clone()))) { + let message = "ICE: Generic's kind does not match expected type"; + return Err(MonomorphizationError::InternalError { location, message }); + } + let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) + ast::Expression::Literal(ast::Literal::Integer((value as u128).into(), false, typ, location)) } }; diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index 2eef4f6e262..a56e7029a79 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -477,6 +477,13 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File push_type_variable_parts(binding, parts, files); } } + Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + push_type_parts(&typ, parts, files); + } else { + push_type_variable_parts(binding, parts, files); + } + } Type::FieldElement | Type::Integer(..) From b59241eaebd69b1ba07e81c8eb8c5139dc53ee50 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 24 Sep 2024 15:11:35 -0400 Subject: [PATCH 09/56] wip implementing Jake's suggestions, incl. moving TypeVariableKind into TypeBinding::Unbound so that all generics have kinds --- compiler/noirc_frontend/src/hir_def/types.rs | 252 ++++++++++--------- 1 file changed, 131 insertions(+), 121 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a0182f491a9..352c14da99b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -78,7 +78,7 @@ pub enum Type { /// is a process that replaces each NamedGeneric in a generic function with a TypeVariable. /// Doing this at each call site of a generic function is how they can be called with /// different argument types each time. - TypeVariable(TypeVariable, TypeVariableKind), + TypeVariable(TypeVariable), /// `impl Trait` when used in a type position. /// These are only matched based on the TraitId. The trait name parameter is only @@ -526,7 +526,7 @@ pub enum BinaryTypeOperator { #[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub enum TypeVariableKind { - /// Can bind to any type, except Type::Constant + /// Can bind to any type, except Type::Constant and Type::InfixExpr Normal, /// A generic integer or field type. This is a more specific kind of TypeVariable @@ -540,7 +540,7 @@ pub enum TypeVariableKind { /// that can only be bound to Type::Integer, or other polymorphic integers. Integer, - /// Can bind to a Type::Constant of the given kind + /// Can bind to a Type::Constant or Type::InfixExpr of the given kind Numeric(Box), } @@ -561,8 +561,8 @@ impl TypeVariableKind { pub struct TypeVariable(TypeVariableId, Shared); impl TypeVariable { - pub fn unbound(id: TypeVariableId) -> Self { - TypeVariable(id, Shared::new(TypeBinding::Unbound(id))) + pub fn unbound(id: TypeVariableId, type_var_kind: TypeVariableKind) -> Self { + TypeVariable(id, Shared::new(TypeBinding::Unbound(id, type_var_kind))) } pub fn id(&self) -> TypeVariableId { @@ -575,6 +575,7 @@ impl TypeVariable { /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. pub fn bind(&self, typ: Type, kind: &Kind) { + // TODO: remove before review assert!( typ.kind().unifies(kind), "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", @@ -586,7 +587,7 @@ impl TypeVariable { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) } - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; assert!(!typ.occurs(id), "{self:?} occurs within {typ:?}"); @@ -594,6 +595,7 @@ impl TypeVariable { } pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + // TODO: remove before review assert!( binding.kind().unifies(kind), "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", @@ -605,7 +607,7 @@ impl TypeVariable { TypeBinding::Bound(binding) => { unreachable!("Expected unbound, found bound to {binding}") } - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; if binding.occurs(id) { @@ -624,8 +626,8 @@ impl TypeVariable { /// Unbind this type variable, setting it to Unbound(id). /// /// This is generally a logic error to use outside of monomorphization. - pub fn unbind(&self, id: TypeVariableId) { - *self.1.borrow_mut() = TypeBinding::Unbound(id); + pub fn unbind(&self, id: TypeVariableId, type_var_kind: TypeVariableKind) { + *self.1.borrow_mut() = TypeBinding::Unbound(id, type_var_kind); } /// Forcibly bind a type variable to a new type - even if the type @@ -634,6 +636,7 @@ impl TypeVariable { /// /// Asserts that the given type is compatible with the given Kind pub fn force_bind(&self, typ: Type, kind: &Kind) { + // TODO: remove before review assert!( typ.kind().unifies(kind), "expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", @@ -652,12 +655,12 @@ impl TypeVariable { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum TypeBinding { Bound(Type), - Unbound(TypeVariableId), + Unbound(TypeVariableId, TypeVariableKind), } impl TypeBinding { pub fn is_unbound(&self) -> bool { - matches!(self, TypeBinding::Unbound(_)) + matches!(self, TypeBinding::Unbound(_, _)) } } @@ -681,31 +684,23 @@ impl std::fmt::Display for Type { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), }, - Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", Type::default_int_type()) - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - // Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is - // what they bind to by default anyway. It is less confusing than displaying it - // as a generic. - write!(f, "Field") - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", typ) - } else { - // TODO: revert after debugging - // write!(f, "{}", binding.borrow()) - write!(f, "{}:! {}", binding.borrow(), typ) + Type::TypeVariable(var) => { + match &*binding.borrow() { + TypeBinding::Unbound(_, type_var_kind) => { + match type_var_kind { + TypeVariableKind::Normal => write!(f, "{}", var.borrow()), + TypeVariableKind::Integer => write!(f, "{}", Type::default_int_type()), + TypeVariableKind::IntegerOrField => write!(f, "Field"), + + // TODO: after debugging + // TypeVariableKind::Numeric(typ) => write!(f, "_"), + TypeVariableKind::Numeric(typ) => write!(f, "_!({:?})", typ), + + } + } + TypeBinding::Bound(binding) => { + write!(f, "{}", binding) + } } } Type::Struct(s, args) => { @@ -742,8 +737,8 @@ impl std::fmt::Display for Type { TypeBinding::Bound(binding) => binding.fmt(f), // TODO: revert after debugging // TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_", kind), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_:! {:?}", kind), - TypeBinding::Unbound(_) => write!(f, "{name}"), + TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_:! {:?}", kind), + TypeBinding::Unbound(_, _) => write!(f, "{name}"), }, // TODO: revert after debugging @@ -809,7 +804,7 @@ impl std::fmt::Display for TypeBinding { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TypeBinding::Bound(typ) => typ.fmt(f), - TypeBinding::Unbound(id) => id.fmt(f), + TypeBinding::Unbound(id, _) => id.fmt(f), } } } @@ -845,15 +840,14 @@ impl Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) } - pub fn type_variable(id: TypeVariableId) -> Type { - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, TypeVariableKind::Normal) - } - pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: TypeVariableKind) -> Type { let id = interner.next_type_variable_id(); - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, type_var_kind) + let var = TypeVariable::unbound(id, type_var_kind); + Type::TypeVariable(var) + } + + pub fn type_variable(id: TypeVariableId) -> Type { + Self::type_variable_with_kind(interner, TypeVariableKind::Normal) } pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { @@ -872,9 +866,9 @@ impl Type { /// they shouldn't be bound over until monomorphization. pub fn is_bindable(&self) -> bool { match self { - Type::TypeVariable(binding, _) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.is_bindable(), - TypeBinding::Unbound(_) => true, + TypeBinding::Unbound(_, _) => true, }, Type::Alias(alias, args) => alias.borrow().get_type(args).is_bindable(), _ => false, @@ -906,14 +900,21 @@ impl Type { pub fn is_numeric_value(&self) -> bool { use Type::*; use TypeVariableKind as K; - matches!( - self.follow_bindings(), - FieldElement | Integer(..) | Bool | TypeVariable(_, K::Integer | K::IntegerOrField) - ) + match self.follow_bindings() { + FieldElement => true, + Integer(..) => true, + Bool => true, + TypeVariable(var) => { + match &*var.borrow() { + TypeBinding::Bound(typ) => typ.is_numeric_value(), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, K::Integer | K::IntegerOrField), + } + } + } } pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { - // Return whether the named generic has a TypeKind::Numeric and save its name + // Return whether the named generic has a Kind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { if let Type::NamedGeneric(_, name, Kind::Numeric { .. }) = typ { found_names.push(name.to_string()); @@ -933,7 +934,7 @@ impl Type { | Type::Forall(_, _) | Type::Quoted(_) => {} - Type::TypeVariable(type_var, _) => { + Type::TypeVariable(type_var) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { named_generic_is_numeric(typ, found_names); } @@ -1016,7 +1017,7 @@ impl Type { | Type::Error => true, Type::FmtString(_, _) - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _, _) | Type::MutableReference(_) @@ -1059,7 +1060,7 @@ impl Type { | Type::Bool | Type::Unit | Type::Constant(_, _) - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::NamedGeneric(_, _, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1108,7 +1109,7 @@ impl Type { | Type::InfixExpr(..) | Type::Error => true, - Type::TypeVariable(type_var, _) | Type::NamedGeneric(type_var, _, _) => { + Type::TypeVariable(type_var) | Type::NamedGeneric(type_var, _, _) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { typ.is_valid_for_unconstrained_boundary() } else { @@ -1148,10 +1149,10 @@ impl Type { pub fn generic_count(&self) -> usize { match self { Type::Forall(generics, _) => generics.len(), - Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _, _) => { + Type::TypeVariable(type_variable) | Type::NamedGeneric(type_variable, _, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), - TypeBinding::Unbound(_) => 0, + TypeBinding::Unbound(_, _) => 0, } } _ => 0, @@ -1189,9 +1190,9 @@ impl Type { match self { Type::NamedGeneric(_, _, kind) => kind.clone(), Type::Constant(_, kind) => kind.clone(), - Type::TypeVariable(var, type_var_kind) => match *var.borrow() { + Type::TypeVariable(var) => match *var.borrow() { TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_) => type_var_kind.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.kind(), }, Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), Type::FieldElement @@ -1250,7 +1251,7 @@ impl Type { .expect("Cannot have variable sized strings as a parameter to main"), Type::FmtString(_, _) | Type::Unit - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::TraitAsType(..) | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _, _) @@ -1308,9 +1309,13 @@ impl Type { ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; + if !self.kind().unifies(&TypeVariableKind::IntegerOrField.kind()) { + return Err(UnificationError) + } + let this = self.substitute(bindings).follow_bindings(); match &this { Type::Integer(..) => { @@ -1328,13 +1333,18 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id) => { + TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id, type_var_kind) => { if only_integer { + let mut var_clone = var.clone(); + TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; + + // TODO: set kind on var + *var.1.borrow() = TypeVariableKind::Integer; // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. - let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); - bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); + let clone = Type::TypeVariable(var_clone); + bindings.insert(new_target_id, (self_var.clone(), TypeVariableKind::Integer.kind(), clone)); } else { bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); } @@ -1349,9 +1359,10 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(_) => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(_, type_var_kind) => { + TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; + bindings.insert(target_id, (var.clone(), type_var_kind.kind(), this.clone())); Ok(()) } } @@ -1363,16 +1374,19 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id) => { + TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id, type_var_kind) => { + let mut var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = if only_integer { TypeVariableKind::Integer } else { TypeVariableKind::IntegerOrField }; - let clone = Type::TypeVariable(var.clone(), clone_kind); - bindings.insert(*new_target_id, (self_var.clone(), Kind::Normal, clone)); + clone_kind.kind().unify(&type_var_kind.kind())?; + *var_clone.borrow() = clone_kind; + let clone = Type::TypeVariable(var_clone); + bindings.insert(*new_target_id, (self_var.clone(), clone_kind.kind(), clone)); Ok(()) } } @@ -1395,7 +1409,7 @@ impl Type { ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), - TypeBinding::Unbound(id) => *id, + TypeBinding::Unbound(id, _) => *id, }; if !self.kind().unifies(&kind) { @@ -1407,7 +1421,7 @@ impl Type { match &*binding.borrow() { TypeBinding::Bound(typ) => return typ.try_bind_to(var, bindings, kind), // Don't recursively bind the same id to itself - TypeBinding::Unbound(id) if *id == target_id => return Ok(()), + TypeBinding::Unbound(id, _) if *id == target_id => return Ok(()), _ => (), } } @@ -1424,7 +1438,7 @@ impl Type { fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { match self { - Type::TypeVariable(var, type_var_kind) => Some((var.1.clone(), type_var_kind.kind())), + Type::TypeVariable(var) => Some((var.1.clone(), var.kind())), Type::NamedGeneric(var, _, kind) => Some((var.1.clone(), kind.clone())), _ => None, } @@ -1471,6 +1485,15 @@ impl Type { alias.try_unify(other, bindings) } + // TODO + // (TypeVariable(var), other) | (other, TypeVariable(var)) => { + // match var.1 { + // TypeBinding::Bound(typ) => _, + // TypeBinding::Unbound(id, type_var_kind) => _, + // } + // + // } + (TypeVariable(var, Kind::IntegerOrField), other) | (other, TypeVariable(var, Kind::IntegerOrField)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { @@ -1493,6 +1516,7 @@ impl Type { }) } + (Array(len_a, elem_a), Array(len_b, elem_b)) => { len_a.try_unify(len_b, bindings)?; elem_a.try_unify(elem_b, bindings) @@ -1644,7 +1668,7 @@ impl Type { match &*type_variable.borrow() { // If it is already bound, unify against what it is bound to TypeBinding::Bound(link) => link.try_unify(self, bindings), - TypeBinding::Unbound(id) => { + TypeBinding::Unbound(id, _) => { // We may have already "bound" this type variable in this call to // try_unify, so check those bindings as well. match bindings.get(id) { @@ -1898,7 +1922,7 @@ impl Type { fn type_variable_id(&self) -> Option { match self { - Type::TypeVariable(variable, _) | Type::NamedGeneric(variable, _, _) => { + Type::TypeVariable(variable) | Type::NamedGeneric(variable, _, _) => { Some(variable.0) } _ => None, @@ -1967,7 +1991,7 @@ impl Type { TypeBinding::Bound(binding) => { binding.substitute_helper(type_bindings, substitute_bound_typevars) } - TypeBinding::Unbound(id) => match type_bindings.get(id) { + TypeBinding::Unbound(id, _) => match type_bindings.get(id) { Some((_, kind, replacement)) => { assert!( kind.unifies(&replacement.kind()), @@ -2003,7 +2027,7 @@ impl Type { let fields = fields.substitute_helper(type_bindings, substitute_bound_typevars); Type::FmtString(Box::new(size), Box::new(fields)) } - Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding, _) => { + Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding) => { substitute_binding(binding) } // Do not substitute_helper fields, it can lead to infinite recursion @@ -2092,12 +2116,12 @@ impl Type { || args.named.iter().any(|arg| arg.typ.occurs(target_id)) } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), - Type::NamedGeneric(type_var, _, _) | Type::TypeVariable(type_var, _) => { + Type::NamedGeneric(type_var, _, _) | Type::TypeVariable(type_var) => { match &*type_var.borrow() { TypeBinding::Bound(binding) => { type_var.id() == target_id || binding.occurs(target_id) } - TypeBinding::Unbound(id) => *id == target_id, + TypeBinding::Unbound(id, _) => *id == target_id, } } Type::Forall(typevars, typ) => { @@ -2150,7 +2174,7 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var, _) | NamedGeneric(var, _, _) => { + TypeVariable(var) | NamedGeneric(var, _, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -2189,7 +2213,7 @@ impl Type { } pub fn from_generics(generics: &GenericTypeVars) -> Vec { - vecmap(generics, |var| Type::TypeVariable(var.clone(), TypeVariableKind::Normal)) + vecmap(generics, |var| Type::TypeVariable(var.clone())) } /// Replace any `Type::NamedGeneric` in this type with a `Type::TypeVariable` @@ -2231,18 +2255,11 @@ impl Type { typ.replace_named_generics_with_type_variables(); *self = typ; } - Type::TypeVariable(var, type_var_kind) => { + Type::TypeVariable(var) => { let var = var.borrow(); if let TypeBinding::Bound(binding) = &*var { let mut binding = binding.clone(); drop(var); - binding.replace_named_generics_with_type_variables(); - assert!( - type_var_kind.kind().unifies(&binding.kind()), - "expected kind of bound TypeVariable ({:?}) to match the kind of its binding ({:?})", - type_var_kind.kind(), - binding.kind() - ); *self = binding; } } @@ -2263,11 +2280,7 @@ impl Type { *self = binding; } else { drop(type_binding); - let type_var_kind = match kind { - Kind::Normal => TypeVariableKind::Normal, - Kind::Numeric(typ) => TypeVariableKind::Numeric(typ.clone()), - }; - *self = Type::TypeVariable(var.clone(), type_var_kind); + *self = Type::TypeVariable(var.clone()); } } Type::Function(args, ret, env, _unconstrained) => { @@ -2396,16 +2409,12 @@ impl From<&Type> for PrintableType { } Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() }, }, - Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_type().into(), + TypeBinding::Unbound(_, TypeVariableKind::Integer) => Type::default_int_type().into(), + TypeBinding::Unbound(_, TypeVariableKind::IntegerOrField) => Type::default_int_or_field_type().into(), + TypeBinding::Unbound(_, TypeVariableKind::Numeric(typ)) => (**typ).into(), }, - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_or_field_type().into(), - } - } Type::Bool => PrintableType::Boolean, Type::String(size) => { let size = size.evaluate_to_u32().expect("Cannot print variable sized strings"); @@ -2424,7 +2433,6 @@ impl From<&Type> for PrintableType { Type::Alias(alias, args) => alias.borrow().get_type(args).into(), Type::TraitAsType(..) => unreachable!(), Type::Tuple(types) => PrintableType::Tuple { types: vecmap(types, |typ| typ.into()) }, - Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), Type::Function(arguments, return_type, env, unconstrained) => PrintableType::Function { @@ -2458,15 +2466,17 @@ impl std::fmt::Debug for Type { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), }, - Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{:?}", var), - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - write!(f, "IntOrField{:?}", binding) - } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - write!(f, "Int{:?}", binding) - } - Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { - write!(f, "Numeric({:?}: {:?})", binding, typ) + Type::TypeVariable(var) => { + if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { + match type_var_kind { + TypeVariableKind::Normal => write!(f, "{:?}", var), + TypeVariableKind::IntegerOrField => write!(f, "IntOrField{:?}", binding), + TypeVariableKind::Integer => write!(f, "Int{:?}", binding), + TypeVariableKind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), + } + } else { + write!(f, "{}", binding.borrow()) + } } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); @@ -2594,7 +2604,7 @@ impl std::hash::Hash for Type { alias.hash(state); args.hash(state); } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, ..) => var.hash(state), + Type::TypeVariable(var) | Type::NamedGeneric(var, ..) => var.hash(state), Type::TraitAsType(trait_id, _, args) => { trait_id.hash(state); args.hash(state); @@ -2624,7 +2634,7 @@ impl std::hash::Hash for Type { impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { if let Some((variable, kind)) = self.get_inner_type_variable() { - if !kind.unifies(&other.kind()) { + if kind != other.kind() { return false; } if let TypeBinding::Bound(typ) = &*variable.borrow() { @@ -2633,7 +2643,7 @@ impl PartialEq for Type { } if let Some((variable, other_kind)) = other.get_inner_type_variable() { - if !self.kind().unifies(&other_kind) { + if self.kind() != other_kind { return false; } if let TypeBinding::Bound(typ) = &*variable.borrow() { @@ -2689,8 +2699,8 @@ impl PartialEq for Type { // still want them to be equal for canonicalization checks in arithmetic generics. // Without this we'd fail the `serialize` test. ( - NamedGeneric(lhs_var, _, _) | TypeVariable(lhs_var, _), - NamedGeneric(rhs_var, _, _) | TypeVariable(rhs_var, _), + NamedGeneric(lhs_var, _, _) | TypeVariable(lhs_var), + NamedGeneric(rhs_var, _, _) | TypeVariable(rhs_var), ) => lhs_var.id() == rhs_var.id(), _ => false, } From 953f28523d86760db681ba1d6fa963e1bf94ec05 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 24 Sep 2024 16:48:42 -0400 Subject: [PATCH 10/56] wip: add UnresolvedGeneric::type_variable_kind, propagating changes to TypeVariable/TypeBinding, add TypeVariable::{kind, borrow_mut, is_integer, is_integer_or_field, set_type_variable_kind} --- compiler/noirc_frontend/src/ast/expression.rs | 12 + .../src/hir/comptime/hir_to_display_ast.rs | 6 +- .../src/hir/comptime/interpreter.rs | 9 +- .../src/hir/comptime/interpreter/builtin.rs | 5 +- .../src/hir/def_collector/dc_mod.rs | 4 +- compiler/noirc_frontend/src/hir/mod.rs | 3 +- compiler/noirc_frontend/src/hir_def/traits.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 239 +++++++++++++----- .../src/monomorphization/mod.rs | 32 ++- 9 files changed, 228 insertions(+), 84 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index c581ea9d62a..2db2d79ebc9 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -84,6 +84,18 @@ impl UnresolvedGeneric { } } + pub fn type_variable_kind(&self) -> TypeVariableKind { + match self { + UnresolvedGeneric::Variable(_) => TypeVariableKind::Normal, + UnresolvedGeneric::Numeric { typ, .. } => { + let typ = self.resolve_numeric_kind_type(typ)?; + TypeVariableKind::Numeric(Box::new(typ)) + } + // TODO: do we need kind info here? + UnresolvedGeneric::Resolved(..) => TypeVariableKind::Normal, + } + } + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 972826f5b7c..39a03322f06 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -315,10 +315,10 @@ impl Type { let name = Path::from_ident(type_def.name.clone()); UnresolvedTypeData::Named(name, generics, false) } - Type::TypeVariable(binding, kind) => match &*binding.borrow() { + Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => return typ.to_display_ast(), - TypeBinding::Unbound(id) => { - let name = format!("var_{:?}_{}", kind, id); + TypeBinding::Unbound(id, type_var_kind) => { + let name = format!("var_{:?}_{}", type_var_kind, id); let path = Path::from_single(name, Span::empty(0)); let expression = UnresolvedTypeExpression::Variable(path); UnresolvedTypeData::Expression(expression) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index cc0b44952bd..fb651888890 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -585,7 +585,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } DefinitionKind::GenericType(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_) => None, + TypeBinding::Unbound(_, _) => None, TypeBinding::Bound(binding) => binding.evaluate_to_u32(), }; @@ -594,7 +594,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.evaluate_integer((value as u128).into(), false, id) } else { let location = self.elaborator.interner.expr_location(&id); - let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); + let typ = Type::TypeVariable(type_variable.clone()); Err(InterpreterError::NonIntegerArrayLength { typ, location }) } } @@ -753,14 +753,15 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::I64(value)) } } - } else if let Type::TypeVariable(variable, TypeVariableKind::IntegerOrField) = &typ { + } else if let Type::TypeVariable(variable) = &typ && variable.is_integer_or_field() { Ok(Value::Field(value)) - } else if let Type::TypeVariable(variable, TypeVariableKind::Integer) = &typ { + } else if let Type::TypeVariable(variable) = &typ && variable.is_integer() { let value: u64 = value .try_to_u64() .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) + } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4678d29a452..1de616dae08 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -401,7 +401,8 @@ fn struct_def_add_generic( } } - let type_var = TypeVariable::unbound(interner.next_type_variable_id()); + let type_var_kind = TypeVariableKind::Normal; + let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); let span = generic_location.span; let kind = Kind::Normal; let typ = Type::NamedGeneric(type_var.clone(), name.clone(), kind.clone()); @@ -1200,7 +1201,7 @@ fn zeroed(return_type: Type) -> IResult { Ok(Value::Pointer(Shared::new(element), false)) } // Optimistically assume we can resolve this type later or that the value is unused - Type::TypeVariable(_, _) + Type::TypeVariable(_) | Type::Forall(_, _) | Type::Constant(..) | Type::InfixExpr(..) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 508765f943c..cf4e00ade5f 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -524,7 +524,7 @@ impl<'a> ModCollector<'a> { associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id), + type_var: TypeVariable::unbound(type_variable_id, TypeVariableKind::Numeric(Box::new(typ))), kind: Kind::Numeric(Box::new(typ)), span: name.span(), }); @@ -545,7 +545,7 @@ impl<'a> ModCollector<'a> { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id), + type_var: TypeVariable::unbound(type_variable_id, TypeVariableKind::Normal), kind: Kind::Normal, span: name.span(), }); diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index c631edfa889..a8fa5036790 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -280,7 +280,8 @@ impl Context<'_, '_> { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); - let type_var = TypeVariable::unbound(id); + let type_var_kind = generic.type_variable_kind(); + let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); let span = ident.0.span(); diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 0572ba403a1..b5b1f70e225 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -168,7 +168,7 @@ impl Trait { }); TraitConstraint { - typ: Type::TypeVariable(self.self_type_typevar.clone(), TypeVariableKind::Normal), + typ: Type::TypeVariable(self.self_type_typevar.clone()), trait_generics: TraitGenerics { ordered, named }, trait_id: self.id, span, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 352c14da99b..f17d0b25581 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -623,6 +623,11 @@ impl TypeVariable { self.1.borrow() } + /// Borrows this TypeVariable to (e.g.) manually match on the inner TypeBinding. + pub fn borrow_mut(&self) -> std::cell::RefMut { + self.1.borrow_mut() + } + /// Unbind this type variable, setting it to Unbound(id). /// /// This is generally a logic error to use outside of monomorphization. @@ -648,6 +653,45 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } } + + fn kind(&self) -> Kind { + match &*self.borrow() { + TypeBinding::Bound(binding) => binding.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.kind(), + } + } + + /// Check that if bound, it's an integer + /// and if unbound, that it's a TypeVariableKind::Integer + pub(crate) fn is_integer(&self) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, TypeVariableKind::Integer), + } + } + + /// Check that if bound, it's an integer or field + /// and if unbound, that it's a TypeVariableKind::IntegerOrField + pub(crate) fn is_integer_or_field(&self) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..) | Type::FieldElement), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, TypeVariableKind::IntegerOrField), + } + } + + /// Set the TypeVariableKind if unbound, + /// else unify the TypeVariableKind::kind() with the expected Kind + fn set_type_variable_kind(&self, new_type_var_kind: TypeVariableKind) -> Result<(), UnificationError> { + match &mut*self.borrow_mut() { + TypeBinding::Bound(binding) => { + binding.kind().unify(&new_type_var_kind.kind()) + } + TypeBinding::Unbound(_, ref mut type_var_kind) => { + *type_var_kind = new_type_var_kind; + Ok(()) + } + } + } } /// TypeBindings are the mutable insides of a TypeVariable. @@ -685,6 +729,7 @@ impl std::fmt::Display for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var) => { + let binding = var.1; match &*binding.borrow() { TypeBinding::Unbound(_, type_var_kind) => { match type_var_kind { @@ -847,7 +892,8 @@ impl Type { } pub fn type_variable(id: TypeVariableId) -> Type { - Self::type_variable_with_kind(interner, TypeVariableKind::Normal) + let var = TypeVariable::unbound(id, TypeVariableKind::Normal); + Type::TypeVariable(var) } pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { @@ -1326,7 +1372,7 @@ impl Type { bindings.insert(target_id, (var.clone(), Kind::Normal, this)); Ok(()) } - Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { + Type::TypeVariable(self_var) => { let borrow = self_var.borrow(); match &*borrow { TypeBinding::Bound(typ) => { @@ -1334,48 +1380,29 @@ impl Type { } // Avoid infinitely recursive bindings TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id, type_var_kind) => { + TypeBinding::Unbound(ref new_target_id, TypeVariableKind::IntegerOrField) => { + let type_var_kind = TypeVariableKind::IntegerOrField; if only_integer { let mut var_clone = var.clone(); TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; - - // TODO: set kind on var - *var.1.borrow() = TypeVariableKind::Integer; + var.set_type_variable_kind(TypeVariableKind::Integer)?; // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var_clone); - bindings.insert(new_target_id, (self_var.clone(), TypeVariableKind::Integer.kind(), clone)); + bindings.insert(*new_target_id, (self_var.clone(), type_var_kind.kind(), clone)); } else { bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); } Ok(()) } - } - } - Type::TypeVariable(self_var, TypeVariableKind::Integer) => { - let borrow = self_var.borrow(); - match &*borrow { - TypeBinding::Bound(typ) => { - typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - } - // Avoid infinitely recursive bindings - TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(_, type_var_kind) => { + TypeBinding::Unbound(new_target_id, TypeVariableKind::Integer) => { + let type_var_kind = TypeVariableKind::Integer; TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; bindings.insert(target_id, (var.clone(), type_var_kind.kind(), this.clone())); Ok(()) } - } - } - Type::TypeVariable(self_var, TypeVariableKind::Normal) => { - let borrow = self_var.borrow(); - match &*borrow { - TypeBinding::Bound(typ) => { - typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - } - // Avoid infinitely recursive bindings - TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(new_target_id, type_var_kind) => { + TypeBinding::Unbound(new_target_id, TypeVariableKind::Normal) => { + let type_var_kind = TypeVariableKind::Normal; let mut var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = if only_integer { @@ -1384,13 +1411,82 @@ impl Type { TypeVariableKind::IntegerOrField }; clone_kind.kind().unify(&type_var_kind.kind())?; - *var_clone.borrow() = clone_kind; + var_clone.set_type_variable_kind(clone_kind)?; let clone = Type::TypeVariable(var_clone); bindings.insert(*new_target_id, (self_var.clone(), clone_kind.kind(), clone)); Ok(()) } + TypeBinding::Unbound(new_target_id, TypeVariableKind::Numeric(typ)) => { + Err(UnificationError) + } } } + + // TODO: remove after testing + // Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { + // let borrow = self_var.borrow(); + // match &*borrow { + // TypeBinding::Bound(typ) => { + // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + // } + // // Avoid infinitely recursive bindings + // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + // TypeBinding::Unbound(new_target_id, type_var_kind) => { + // if only_integer { + // let mut var_clone = var.clone(); + // TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; + // var.set_type_variable_kind(TypeVariableKind::Integer)?; + // // Integer is more specific than IntegerOrField so we bind the type + // // variable to Integer instead. + // let clone = Type::TypeVariable(var_clone); + // bindings.insert(new_target_id, (self_var.clone(), TypeVariableKind::Integer.kind(), clone)); + // } else { + // bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + // } + // Ok(()) + // } + // } + // } + // Type::TypeVariable(self_var, TypeVariableKind::Integer) => { + // let borrow = self_var.borrow(); + // match &*borrow { + // TypeBinding::Bound(typ) => { + // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + // } + // // Avoid infinitely recursive bindings + // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + // TypeBinding::Unbound(_, type_var_kind) => { + // TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; + // bindings.insert(target_id, (var.clone(), type_var_kind.kind(), this.clone())); + // Ok(()) + // } + // } + // } + // Type::TypeVariable(self_var, TypeVariableKind::Normal) => { + // let borrow = self_var.borrow(); + // match &*borrow { + // TypeBinding::Bound(typ) => { + // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + // } + // // Avoid infinitely recursive bindings + // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), + // TypeBinding::Unbound(new_target_id, type_var_kind) => { + // let mut var_clone = var.clone(); + // // Bind to the most specific type variable kind + // let clone_kind = if only_integer { + // TypeVariableKind::Integer + // } else { + // TypeVariableKind::IntegerOrField + // }; + // clone_kind.kind().unify(&type_var_kind.kind())?; + // var_clone.set_type_variable_kind(clone_kind)?; + // let clone = Type::TypeVariable(var_clone); + // bindings.insert(*new_target_id, (self_var.clone(), clone_kind.kind(), clone)); + // Ok(()) + // } + // } + // } + _ => Err(UnificationError), } } @@ -1485,36 +1581,62 @@ impl Type { alias.try_unify(other, bindings) } - // TODO - // (TypeVariable(var), other) | (other, TypeVariable(var)) => { - // match var.1 { - // TypeBinding::Bound(typ) => _, - // TypeBinding::Unbound(id, type_var_kind) => _, - // } - // - // } - - (TypeVariable(var, Kind::IntegerOrField), other) - | (other, TypeVariable(var, Kind::IntegerOrField)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = false; - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) - } - - (TypeVariable(var, Kind::Integer), other) - | (other, TypeVariable(var, Kind::Integer)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = true; - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) + (TypeVariable(var), other) | (other, TypeVariable(var)) => { + match &*var.borrow() { + TypeBinding::Bound(typ) => { + if typ.is_numeric_value() { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = matches!(typ, Type::Integer(..)); + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } else { + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings, typ.kind()) + }) + } + } + TypeBinding::Unbound(id, Kind::IntegerOrField) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = false; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } + TypeBinding::Unbound(id, Kind::Integer) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = true; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } + TypeBinding::Unbound(id, type_var_kind) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings, type_var_kind.kind()) + }) + } + } } - (TypeVariable(var, type_var_kind), other) | (other, TypeVariable(var, type_var_kind)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings, type_var_kind.kind()) - }) - } + // TODO: remove after testing + // (TypeVariable(var, Kind::IntegerOrField), other) + // | (other, TypeVariable(var, Kind::IntegerOrField)) => { + // other.try_unify_to_type_variable(var, bindings, |bindings| { + // let only_integer = false; + // other.try_bind_to_polymorphic_int(var, bindings, only_integer) + // }) + // } + // + // (TypeVariable(var, Kind::Integer), other) + // | (other, TypeVariable(var, Kind::Integer)) => { + // other.try_unify_to_type_variable(var, bindings, |bindings| { + // let only_integer = true; + // other.try_bind_to_polymorphic_int(var, bindings, only_integer) + // }) + // } + // + // (TypeVariable(var, type_var_kind), other) | (other, TypeVariable(var, type_var_kind)) => { + // other.try_unify_to_type_variable(var, bindings, |bindings| { + // other.try_bind_to(var, bindings, type_var_kind.kind()) + // }) + // } (Array(len_a, elem_a), Array(len_b, elem_b)) => { @@ -2467,6 +2589,7 @@ impl std::fmt::Debug for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var) => { + let binding = var.1; if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { match type_var_kind { TypeVariableKind::Normal => write!(f, "{:?}", var), diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2fc43c3df80..c8c12f32d06 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -918,7 +918,7 @@ impl<'interner> Monomorphizer<'interner> { }, DefinitionKind::GenericType(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { - TypeBinding::Unbound(_) => { + TypeBinding::Unbound(_, _) => { unreachable!("Unbound type variable used in expression") } TypeBinding::Bound(binding) => binding.evaluate_to_u32().unwrap_or_else(|| { @@ -985,10 +985,13 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Field } - HirType::TypeVariable(binding, type_var_kind) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - return Self::convert_type(binding, location); - } + HirType::TypeVariable(binding) => { + let type_var_kind = match &*binding.borrow() { + TypeBinding::Bound(ref binding) => { + return Self::convert_type(binding, location); + } + TypeBinding::Unbound(_, type_var_kind) => type_var_kind, + }; // Default any remaining unbound type variables. // This should only happen if the variable in question is unused @@ -1092,22 +1095,25 @@ impl<'interner> Monomorphizer<'interner> { HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), HirType::Slice(element) => Self::check_type(element.as_ref(), location), HirType::NamedGeneric(binding, _, _) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + if let TypeBinding::Bound(ref binding) = &*binding.borrow() { return Self::check_type(binding, location); } Ok(()) } - HirType::TypeVariable(binding, kind) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - return Self::check_type(binding, location); - } + HirType::TypeVariable(binding) => { + let type_var_kind = match &*binding.borrow() { + TypeBinding::Bound(binding) => { + return Self::check_type(binding, location); + } + TypeBinding::Unbound(_, type_var_kind) => type_var_kind, + }; // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = match kind.default_type() { + let default = match type_var_kind.default_type() { Some(typ) => typ, None => return Err(MonomorphizationError::NoDefaultType { location }), }; @@ -1923,8 +1929,8 @@ pub fn perform_instantiation_bindings(bindings: &TypeBindings) { } pub fn undo_instantiation_bindings(bindings: TypeBindings) { - for (id, (var, _, _)) in bindings { - var.unbind(id); + for (id, (var, kind, _)) in bindings { + var.unbind(id, kind); } } From 2a11bbeae53c60ae4071af9ab449838774317401 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 25 Sep 2024 15:17:04 -0400 Subject: [PATCH 11/56] wip debugging, move UnsupportedNumericGenericType into its own struct so that it can resolve to a DefCollectorErrorKind or ResolverError, remove kind argument from NamedGeneric and UnresolvedGeneric, include the Kind in bound_generics, combine TypeVariableKind and Kind, cleanup --- compiler/noirc_frontend/src/ast/expression.rs | 28 +- compiler/noirc_frontend/src/elaborator/mod.rs | 51 +-- .../noirc_frontend/src/elaborator/patterns.rs | 6 +- .../src/elaborator/trait_impls.rs | 7 +- .../noirc_frontend/src/elaborator/traits.rs | 12 +- .../noirc_frontend/src/elaborator/types.rs | 62 +-- .../src/hir/comptime/hir_to_display_ast.rs | 4 +- .../src/hir/comptime/interpreter.rs | 41 +- .../src/hir/comptime/interpreter/builtin.rs | 11 +- .../src/hir/def_collector/dc_crate.rs | 9 +- .../src/hir/def_collector/dc_mod.rs | 6 +- .../src/hir/def_collector/errors.rs | 29 +- compiler/noirc_frontend/src/hir/mod.rs | 13 +- .../src/hir/resolution/errors.rs | 16 +- compiler/noirc_frontend/src/hir_def/traits.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 359 +++++++----------- .../src/monomorphization/mod.rs | 18 +- compiler/noirc_frontend/src/node_interner.rs | 24 +- 18 files changed, 304 insertions(+), 394 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 2db2d79ebc9..5a2d7c22786 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -1,11 +1,12 @@ use std::borrow::Cow; use std::fmt::Display; +use thiserror::Error; + use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; @@ -75,6 +76,13 @@ pub enum UnresolvedGeneric { Resolved(QuotedTypeId, Span), } +#[derive(Error, PartialEq, Eq, Debug, Clone)] +#[error("The only supported types of numeric generics are integers, fields, and booleans")] +pub struct UnsupportedNumericGenericType { + pub ident: Ident, + pub typ: UnresolvedTypeData +} + impl UnresolvedGeneric { pub fn span(&self) -> Span { match self { @@ -84,19 +92,7 @@ impl UnresolvedGeneric { } } - pub fn type_variable_kind(&self) -> TypeVariableKind { - match self { - UnresolvedGeneric::Variable(_) => TypeVariableKind::Normal, - UnresolvedGeneric::Numeric { typ, .. } => { - let typ = self.resolve_numeric_kind_type(typ)?; - TypeVariableKind::Numeric(Box::new(typ)) - } - // TODO: do we need kind info here? - UnresolvedGeneric::Resolved(..) => TypeVariableKind::Normal, - } - } - - pub fn kind(&self) -> Result { + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), UnresolvedGeneric::Numeric { typ, .. } => { @@ -112,14 +108,14 @@ impl UnresolvedGeneric { fn resolve_numeric_kind_type( &self, typ: &UnresolvedType, - ) -> Result { + ) -> Result { use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; match typ.typ { FieldElement => Ok(Type::FieldElement), Integer(sign, bits) => Ok(Type::Integer(sign, bits)), // Only fields and integers are supported for numeric kinds - _ => Err(DefCollectorErrorKind::UnsupportedNumericGenericType { + _ => Err(UnsupportedNumericGenericType { ident: self.ident().clone(), typ: typ.typ.clone(), }), diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index c7367e96de7..fec4984fcf4 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -33,7 +33,7 @@ use crate::{ Shared, Type, TypeVariable, }; use crate::{ - ast::{TraitBound, UnresolvedGeneric, UnresolvedGenerics}, + ast::{TraitBound, UnresolvedGeneric, UnresolvedGenerics, UnsupportedNumericGenericType}, graph::CrateId, hir::{ def_collector::{dc_crate::CollectedItems, errors::DefCollectorErrorKind}, @@ -359,7 +359,7 @@ impl<'context> Elaborator<'context> { fn introduce_generics_into_scope(&mut self, all_generics: Vec) { // Introduce all numeric generics into scope for generic in &all_generics { - if let Kind::Numeric(typ) = &generic.kind { + if let Kind::Numeric(typ) = &generic.kind() { let definition = DefinitionKind::GenericType(generic.type_var.clone(), typ.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = @@ -471,9 +471,9 @@ impl<'context> Elaborator<'context> { let context = self.function_context.pop().expect("Imbalanced function_context pushes"); for typ in context.type_variables { - if let Type::TypeVariable(variable, type_var_kind) = typ.follow_bindings() { + if let Type::TypeVariable(variable) = typ.follow_bindings() { let msg = "TypeChecker should only track defaultable type vars"; - variable.bind(type_var_kind.default_type().expect(msg), &type_var_kind.kind()); + variable.bind(variable.kind().default_type().expect(msg), &variable.kind()); } } @@ -511,11 +511,11 @@ impl<'context> Elaborator<'context> { trait_constraints: &mut Vec, ) -> Type { let new_generic_id = self.interner.next_type_variable_id(); - let new_generic = TypeVariable::unbound(new_generic_id); + let new_generic = TypeVariable::unbound(new_generic_id, Kind::Normal); generics.push(new_generic.clone()); let name = format!("impl {trait_path}"); - let generic_type = Type::NamedGeneric(new_generic, Rc::new(name), Kind::Normal); + let generic_type = Type::NamedGeneric(new_generic, Rc::new(name)); let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics }; if let Some(new_constraint) = self.resolve_trait_bound(&trait_bound, generic_type.clone()) { @@ -530,19 +530,26 @@ impl<'context> Elaborator<'context> { pub fn add_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { vecmap(generics, |generic| { let mut is_error = false; - let (type_var, name, kind) = match self.resolve_generic(generic) { + let (type_var, name) = match self.resolve_generic(generic) { Ok(values) => values, Err(error) => { self.push_err(error); is_error = true; let id = self.interner.next_type_variable_id(); - (TypeVariable::unbound(id), Rc::new("(error)".into()), Kind::Normal) + let kind = match generic.kind() { + Ok(kind) => kind, + Err(error) => { + self.push_err(error); + Kind::Normal + } + }; + (TypeVariable::unbound(id, kind), Rc::new("(error)".into())) } }; let span = generic.span(); let name_owned = name.as_ref().clone(); - let resolved_generic = ResolvedGeneric { name, type_var, kind, span }; + let resolved_generic = ResolvedGeneric { name, type_var, span }; // Check for name collisions of this generic // Checking `is_error` here prevents DuplicateDefinition errors when @@ -567,24 +574,22 @@ impl<'context> Elaborator<'context> { fn resolve_generic( &mut self, generic: &UnresolvedGeneric, - ) -> Result<(TypeVariable, Rc, Kind), ResolverError> { + ) -> Result<(TypeVariable, Rc), ResolverError> { // Map the generic to a fresh type variable match generic { UnresolvedGeneric::Variable(_) | UnresolvedGeneric::Numeric { .. } => { let id = self.interner.next_type_variable_id(); - let typevar = TypeVariable::unbound(id); + let typevar = TypeVariable::unbound(id, generic.kind()?); let ident = generic.ident(); - - let kind = self.resolve_generic_kind(generic); let name = Rc::new(ident.0.contents.clone()); - Ok((typevar, name, kind)) + Ok((typevar, name)) } // An already-resolved generic is only possible if it is the result of a // previous macro call being inserted into a generics list. UnresolvedGeneric::Resolved(id, span) => { match self.interner.get_quoted_type(*id).follow_bindings() { - Type::NamedGeneric(type_variable, name, kind) => { - Ok((type_variable, name, kind)) + Type::NamedGeneric(type_variable, name) => { + Ok((type_variable.clone(), name)) } other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { span: *span, @@ -600,17 +605,17 @@ impl<'context> Elaborator<'context> { /// sure only primitive numeric types are being used. pub(super) fn resolve_generic_kind(&mut self, generic: &UnresolvedGeneric) -> Kind { if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = typ.clone(); - let typ = if typ.is_type_expression() { - self.resolve_type_inner(typ, &Kind::Numeric(Box::new(Type::default_int_type()))) + let unresolved_typ = typ.clone(); + let typ = if unresolved_typ.is_type_expression() { + self.resolve_type_inner(unresolved_typ.clone(), &Kind::Numeric(Box::new(Type::default_int_type()))) } else { - self.resolve_type(typ.clone()) + self.resolve_type(unresolved_typ.clone()) }; if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = ResolverError::UnsupportedNumericGenericType { + let unsupported_typ_err = ResolverError::UnsupportedNumericGenericType(UnsupportedNumericGenericType { ident: ident.clone(), - typ: typ.clone(), - }; + typ: unresolved_typ.typ.clone(), + }); self.push_err(unsupported_typ_err); } Kind::Numeric(Box::new(typ)) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index b3799f3deb1..819767053cc 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::{Expression, ExpressionKind, HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - ResolvedGeneric, Shared, StructType, Type, TypeBindings, TypeVariableKind, + Kind, ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -482,7 +482,7 @@ impl<'context> Elaborator<'context> { ) -> 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) + self.resolve_type_inner(unresolved_type, &generic.kind()) }) } @@ -557,7 +557,7 @@ impl<'context> Elaborator<'context> { // does not check definition kinds and otherwise expects parameters to // already be typed. if self.interner.definition_type(hir_ident.id) == Type::Error { - let type_var_kind = TypeVariableKind::Numeric(numeric_typ.clone()); + let type_var_kind = Kind::Numeric(numeric_typ.clone()); let typ = self.type_variable_with_kind(type_var_kind); self.interner.push_definition_type(hir_ident.id, typ); } diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index e49a4c5272a..9c223a556a0 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -153,11 +153,12 @@ impl<'context> Elaborator<'context> { let override_meta = self.interner.function_meta(func_id); // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, - ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, + ResolvedGeneric { type_var: trait_fn_generic, .. }, + ResolvedGeneric { name, type_var: impl_fn_generic, .. }, ) in method.direct_generics.iter().zip(&override_meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); + let trait_fn_kind = trait_fn_generic.kind(); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 8aaebe7b679..7aaa0040f81 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -15,7 +15,7 @@ use crate::{ }, node_interner::{FuncId, ReferenceId, TraitId}, token::Attributes, - Kind, ResolvedGeneric, Type, TypeBindings, TypeVariableKind, + ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; @@ -83,7 +83,7 @@ impl<'context> Elaborator<'context> { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); let self_type = - Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); + Type::TypeVariable(self_typevar.clone()); let name_span = the_trait.name.span(); this.add_existing_generic( @@ -93,7 +93,6 @@ impl<'context> Elaborator<'context> { name: Rc::new("Self".to_owned()), type_var: self_typevar, span: name_span, - kind: Kind::Normal, }, ); this.self_type = Some(self_type.clone()); @@ -287,11 +286,12 @@ pub(crate) fn check_trait_impl_method_matches_declaration( // Substitute each generic on the trait function with the corresponding generic on the impl function for ( - ResolvedGeneric { type_var: trait_fn_generic, kind: trait_fn_kind, .. }, - ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. }, + ResolvedGeneric { type_var: trait_fn_generic, .. }, + ResolvedGeneric { name, type_var: impl_fn_generic, .. }, ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone()); + let trait_fn_kind = trait_fn_generic.kind(); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index f94b5348f80..23757e39292 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -37,7 +37,7 @@ use crate::{ TraitImplKind, TraitMethodId, }, Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, TypeVariable, - TypeVariableKind, UnificationError, + UnificationError, }; use super::{lints, Elaborator}; @@ -138,7 +138,7 @@ impl<'context> Elaborator<'context> { let env = Box::new(self.resolve_type_inner(*env, kind)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { Type::Function(args, ret, env, unconstrained) } _ => { @@ -346,7 +346,7 @@ impl<'context> Elaborator<'context> { let ordered_args = expected_kinds.iter().zip(args.ordered_args); let ordered = - vecmap(ordered_args, |(generic, typ)| self.resolve_type_inner(typ, &generic.kind)); + vecmap(ordered_args, |(generic, typ)| self.resolve_type_inner(typ, &generic.kind())); let mut associated = Vec::new(); @@ -390,7 +390,7 @@ impl<'context> Elaborator<'context> { let expected = required_args.remove(index); seen_args.insert(name.0.contents.clone(), name.span()); - let typ = self.resolve_type_inner(typ, &expected.kind); + let typ = self.resolve_type_inner(typ, &expected.kind()); resolved.push(NamedType { name, typ }); } @@ -409,7 +409,7 @@ impl<'context> Elaborator<'context> { let name = path.last_name(); if let Some(generic) = self.find_generic(name) { let generic = generic.clone(); - return Some(Type::NamedGeneric(generic.type_var, generic.name, generic.kind)); + return Some(Type::NamedGeneric(generic.type_var, generic.name)); } } else if let Some(typ) = self.lookup_associated_type_on_self(path) { return Some(typ); @@ -591,7 +591,7 @@ impl<'context> Elaborator<'context> { } for constraint in self.trait_bounds.clone() { - if let Type::NamedGeneric(_, name, _) = &constraint.typ { + if let Type::NamedGeneric(_, name) = &constraint.typ { // if `path` is `T::method_name`, we're looking for constraint of the form `T: SomeTrait` if path.segments[0].ident.0.contents != name.as_str() { continue; @@ -710,7 +710,7 @@ impl<'context> Elaborator<'context> { /// Return a fresh integer type variable and log it /// in self.type_variables to default it later. - pub(super) fn type_variable_with_kind(&mut self, type_var_kind: TypeVariableKind) -> Type { + pub(super) fn type_variable_with_kind(&mut self, type_var_kind: Kind) -> Type { let typ = Type::type_variable_with_kind(self.interner, type_var_kind); self.push_type_variable(typ.clone()); typ @@ -822,7 +822,7 @@ impl<'context> Elaborator<'context> { // Could do a single unification for the entire function type, but matching beforehand // lets us issue a more precise error on the individual argument that fails to type check. match function { - Type::TypeVariable(binding, TypeVariableKind::Normal) => { + Type::TypeVariable(binding) if binding.kind() == Kind::Normal => { if let TypeBinding::Bound(typ) = &*binding.borrow() { return self.bind_function_type(typ.clone(), args, span); } @@ -857,11 +857,11 @@ impl<'context> Elaborator<'context> { match from.follow_bindings() { Type::Integer(..) | Type::FieldElement - | Type::TypeVariable(_, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(_, TypeVariableKind::Integer) | Type::Bool => (), - Type::TypeVariable(_, _) => { + Type::TypeVariable(var) if var.is_integer() || var.is_integer_or_field() => (), + + Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. let expected = self.polymorphic_integer_or_field(); @@ -912,8 +912,8 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first to follow any type // bindings. - (TypeVariable(var, _), other) | (other, TypeVariable(var, _)) => { - if let TypeBinding::Bound(binding) = &*var.borrow() { + (TypeVariable(var), other) | (other, TypeVariable(var)) => { + if let TypeBinding::Bound(ref binding) = &*var.borrow() { return self.comparator_operand_type_rules(other, binding, op, span); } @@ -980,7 +980,7 @@ impl<'context> Elaborator<'context> { let use_impl = !lhs_type.is_numeric_value(); // If this operator isn't valid for fields we have to possibly narrow - // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. + // Kind::IntegerOrField to Kind::Integer. // Doing so also ensures a type error if Field is used. // The is_numeric check is to allow impls for custom types to bypass this. if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric_value() { @@ -1025,7 +1025,7 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first so that we follow any type // bindings. - (TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => { + (TypeVariable(int), other) | (other, TypeVariable(int)) => { if op.kind == BinaryOpKind::ShiftLeft || op.kind == BinaryOpKind::ShiftRight { self.unify( rhs_type, @@ -1040,7 +1040,7 @@ impl<'context> Elaborator<'context> { }; return Ok((lhs_type.clone(), use_impl)); } - if let TypeBinding::Bound(binding) = &*int.borrow() { + if let TypeBinding::Bound(ref binding) = &*int.borrow() { return self.infix_operand_type_rules(binding, op, other, span); } let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); @@ -1125,8 +1125,8 @@ impl<'context> Elaborator<'context> { // Matches on TypeVariable must be first so that we follow any type // bindings. - TypeVariable(int, _) => { - if let TypeBinding::Bound(binding) = &*int.borrow() { + TypeVariable(int) => { + if let TypeBinding::Bound(ref binding) = &*int.borrow() { return self.prefix_operand_type_rules(op, binding, span); } @@ -1302,7 +1302,7 @@ impl<'context> Elaborator<'context> { }); None } - Type::NamedGeneric(_, _, _) => { + Type::NamedGeneric(_, _) => { self.lookup_method_in_trait_constraints(object_type, method_name, span) } // Mutable references to another type should resolve to methods of their element type. @@ -1318,7 +1318,7 @@ impl<'context> Elaborator<'context> { Type::Error => None, // The type variable must be unbound at this point since follow_bindings was called - Type::TypeVariable(_, TypeVariableKind::Normal) => { + Type::TypeVariable(var) if var.kind() == Kind::Normal => { self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); None } @@ -1665,9 +1665,9 @@ impl<'context> Elaborator<'context> { | Type::Bool | Type::Unit | Type::Error - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::Constant(..) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::Quoted(_) | Type::Forall(_, _) => (), @@ -1681,7 +1681,7 @@ impl<'context> Elaborator<'context> { } Type::Array(length, element_type) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(element_type, found); @@ -1706,7 +1706,7 @@ impl<'context> Elaborator<'context> { Type::Struct(struct_type, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name, _) = generic { + if let Type::NamedGeneric(type_variable, name) = generic { if struct_type.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } @@ -1717,7 +1717,7 @@ impl<'context> Elaborator<'context> { } Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name, _) = generic { + if let Type::NamedGeneric(type_variable, name) = generic { if alias.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } @@ -1728,12 +1728,12 @@ impl<'context> Elaborator<'context> { } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } } Type::FmtString(length, fields) => { - if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(fields, found); @@ -1786,7 +1786,7 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) { // Avoid binding t = t if !arg.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.clone())); } } @@ -1805,7 +1805,7 @@ impl<'context> Elaborator<'context> { // Avoid binding t = t if !arg.typ.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind.clone(), arg.typ.clone())); + bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.typ.clone())); } } @@ -1814,8 +1814,8 @@ impl<'context> Elaborator<'context> { // to specify a redundant type annotation. if assumed { let self_type = the_trait.self_type_typevar.clone(); - // self_type has Kind::Normal - bindings.insert(self_type.id(), (self_type, Kind::Normal, constraint.typ.clone())); + let kind = the_trait.self_type_typevar.kind(); + bindings.insert(self_type.id(), (self_type, kind, constraint.typ.clone())); } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 39a03322f06..c3c8e51456d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -333,7 +333,7 @@ impl Type { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::TraitAsType(name, generics) } - Type::NamedGeneric(_var, name, _kind) => { + Type::NamedGeneric(_var, name) => { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } @@ -373,7 +373,7 @@ impl Type { match self.follow_bindings() { Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), - Type::NamedGeneric(_var, name, _kind) => { + Type::NamedGeneric(_var, name) => { let path = Path::from_single(name.as_ref().clone(), span); UnresolvedTypeExpression::Variable(path) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index fb651888890..c53bb02d369 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -38,7 +38,7 @@ use crate::{ }, macros_api::{HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, - Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, + Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; @@ -63,7 +63,7 @@ pub struct Interpreter<'local, 'interner> { /// Since the interpreter monomorphizes as it interprets, we can bind over the same generic /// multiple times. Without this map, when one of these inner functions exits we would /// unbind the generic completely instead of resetting it to its previous binding. - bound_generics: Vec>, + bound_generics: Vec>, } #[allow(unused)] @@ -336,8 +336,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn unbind_generics_from_previous_function(&mut self) { if let Some(bindings) = self.bound_generics.last() { - for var in bindings.keys() { - var.unbind(var.id()); + for (var, (_, kind)) in bindings { + var.unbind(var.id(), kind.clone()); } } // Push a new bindings list for the current function @@ -349,8 +349,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.bound_generics.pop(); if let Some(bindings) = self.bound_generics.last() { - for (var, binding) in bindings { - var.force_bind(binding.clone(), &Kind::Normal); + for (var, (binding, kind)) in bindings { + var.force_bind(binding.clone(), kind); } } } @@ -361,12 +361,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { .last_mut() .expect("remember_bindings called with no bound_generics on the stack"); - for (var, _kind, binding) in main_bindings.values() { - bound_generics.insert(var.clone(), binding.follow_bindings()); + for (var, kind, binding) in main_bindings.values() { + bound_generics.insert(var.clone(), (binding.follow_bindings(), kind.clone())); } - for (var, _kind, binding) in impl_bindings.values() { - bound_generics.insert(var.clone(), binding.follow_bindings()); + for (var, kind, binding) in impl_bindings.values() { + bound_generics.insert(var.clone(), (binding.follow_bindings(), kind.clone())); } } @@ -753,15 +753,18 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::I64(value)) } } - } else if let Type::TypeVariable(variable) = &typ && variable.is_integer_or_field() { - Ok(Value::Field(value)) - } else if let Type::TypeVariable(variable) = &typ && variable.is_integer() { - let value: u64 = value - .try_to_u64() - .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; - Ok(Value::U64(value)) - + } else if let Type::TypeVariable(variable) = &typ { + if variable.is_integer_or_field() { + Ok(Value::Field(value)) + } else if variable.is_integer() { + let value: u64 = value + .try_to_u64() + .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; + let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + Ok(Value::U64(value)) + } else { + Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) + } } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 1de616dae08..5ac7b04b0dd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -401,12 +401,11 @@ fn struct_def_add_generic( } } - let type_var_kind = TypeVariableKind::Normal; + let type_var_kind = Kind::Normal; let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); let span = generic_location.span; - let kind = Kind::Normal; - let typ = Type::NamedGeneric(type_var.clone(), name.clone(), kind.clone()); - let new_generic = ResolvedGeneric { name, type_var, span, kind }; + let typ = Type::NamedGeneric(type_var.clone(), name.clone()); + let new_generic = ResolvedGeneric { name, type_var, span }; the_struct.generics.push(new_generic); Ok(Value::Type(typ)) @@ -424,7 +423,7 @@ fn struct_def_as_type( let struct_def = struct_def_rc.borrow(); let generics = vecmap(&struct_def.generics, |generic| { - Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone()) + Type::NamedGeneric(generic.type_var.clone(), generic.name.clone()) }); drop(struct_def); @@ -1208,7 +1207,7 @@ fn zeroed(return_type: Type) -> IResult { | Type::Quoted(_) | Type::Error | Type::TraitAsType(..) - | Type::NamedGeneric(_, _, _) => Ok(Value::Zeroed(return_type)), + | Type::NamedGeneric(_, _) => Ok(Value::Zeroed(return_type)), } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index ab045c52169..899cd335e53 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -23,7 +23,7 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, + UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, }; use crate::parser::{ParserError, SortedModule}; @@ -237,12 +237,19 @@ impl From for CompilationError { CompilationError::ResolverError(value) } } + impl From for CompilationError { fn from(value: TypeCheckError) -> Self { CompilationError::TypeError(value) } } +impl From for CompilationError { + fn from(value: UnsupportedNumericGenericType) -> Self { + Self::ResolverError(value.into()) + } +} + impl DefCollector { pub fn new(def_map: CrateDefMap) -> DefCollector { DefCollector { diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index cf4e00ade5f..d75a7371244 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -524,8 +524,7 @@ impl<'a> ModCollector<'a> { associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id, TypeVariableKind::Numeric(Box::new(typ))), - kind: Kind::Numeric(Box::new(typ)), + type_var: TypeVariable::unbound(type_variable_id, Kind::Numeric(Box::new(typ))), span: name.span(), }); } @@ -545,8 +544,7 @@ impl<'a> ModCollector<'a> { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id, TypeVariableKind::Normal), - kind: Kind::Normal, + type_var: TypeVariable::unbound(type_variable_id, Kind::Normal), span: name.span(), }); } diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index f931a7cdf41..ed72fe6930d 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -1,4 +1,4 @@ -use crate::ast::{Ident, ItemVisibility, Path, UnresolvedTypeData}; +use crate::ast::{Ident, ItemVisibility, Path, UnsupportedNumericGenericType}; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::generics::TraitGenerics; @@ -73,8 +73,6 @@ pub enum DefCollectorErrorKind { TraitImplOrphaned { span: Span }, #[error("macro error : {0:?}")] MacroError(MacroError), - #[error("The only supported types of numeric generics are integers, fields, and booleans")] - UnsupportedNumericGenericType { ident: Ident, typ: UnresolvedTypeData }, #[error("impl has stricter requirements than trait")] ImplIsStricterThanTrait { constraint_typ: crate::Type, @@ -84,6 +82,8 @@ pub enum DefCollectorErrorKind { trait_method_name: String, trait_method_span: Span, }, + #[error("{0}")] + UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), } /// An error struct that macro processors can return. @@ -100,6 +100,19 @@ impl DefCollectorErrorKind { } } +impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { + fn from(error: &'a UnsupportedNumericGenericType) -> Diagnostic { + let name = &error.ident.0.contents; + let typ = &error.typ; + + Diagnostic::simple_error( + format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), + "Unsupported numeric generic type".to_string(), + error.ident.0.span(), + ) + } +} + impl fmt::Display for DuplicateType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -279,15 +292,6 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { DefCollectorErrorKind::MacroError(macro_error) => { Diagnostic::simple_error(macro_error.primary_message.clone(), macro_error.secondary_message.clone().unwrap_or_default(), macro_error.span.unwrap_or_default()) }, - DefCollectorErrorKind::UnsupportedNumericGenericType { ident, typ } => { - let name = &ident.0.contents; - - Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers and fields"), - "Unsupported numeric generic type".to_string(), - ident.0.span(), - ) - } DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_span, trait_method_name, trait_method_span } => { let constraint = format!("{}{}", constraint_name, constraint_generics); @@ -299,6 +303,7 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_span); diag } + DefCollectorErrorKind::UnsupportedNumericGenericType(err) => err.into(), } } } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index a8fa5036790..506177e859d 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -280,7 +280,11 @@ impl Context<'_, '_> { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); - let type_var_kind = generic.type_variable_kind(); + + let type_var_kind = generic.kind().unwrap_or_else(|err| { + errors.push((err.into(), file_id)); + Kind::Numeric(Box::new(Type::Error)) + }); let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); let span = ident.0.span(); @@ -288,12 +292,7 @@ impl Context<'_, '_> { // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - let kind = generic.kind().unwrap_or_else(|err| { - errors.push((err.into(), file_id)); - Kind::Numeric(Box::new(Type::Error)) - }); - - ResolvedGeneric { name, type_var, kind, span } + ResolvedGeneric { name, type_var, span } }) } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 1e7f29527e2..108cc250778 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -2,7 +2,7 @@ pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::{ast::Ident, hir::comptime::InterpreterError, parser::ParserError, Type}; +use crate::{ast::{Ident, UnsupportedNumericGenericType}, hir::comptime::InterpreterError, parser::ParserError, Type}; use super::import::PathResolutionError; @@ -100,8 +100,6 @@ pub enum ResolverError { NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, - #[error("The only supported types of numeric generics are integers, fields, and booleans")] - UnsupportedNumericGenericType { ident: Ident, typ: Type }, #[error("expected type, found numeric generic parameter")] NumericGenericUsedForType { name: String, span: Span }, #[error("Invalid array length construction")] @@ -130,6 +128,8 @@ pub enum ResolverError { MutatingComptimeInNonComptimeContext { name: String, span: Span }, #[error("Failed to parse `{statement}` as an expression")] InvalidInternedStatementInExpr { statement: String, span: Span }, + #[error("{0}")] + UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), } impl ResolverError { @@ -428,15 +428,6 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } - ResolverError::UnsupportedNumericGenericType { ident , typ } => { - let name = &ident.0.contents; - - Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), - "Unsupported numeric generic type".to_string(), - ident.0.span(), - ) - } ResolverError::NumericGenericUsedForType { name, span } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), @@ -529,6 +520,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::UnsupportedNumericGenericType(err) => err.into(), } } } diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index b5b1f70e225..32240e0c987 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -8,7 +8,7 @@ use crate::{ node_interner::{FuncId, TraitId, TraitMethodId}, Generics, Type, TypeBindings, TypeVariable, }; -use crate::{ResolvedGeneric, TypeVariableKind}; +use crate::ResolvedGeneric; use fm::FileId; use noirc_errors::{Location, Span}; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index f17d0b25581..a9802d616a0 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -87,7 +87,7 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc, Kind), + NamedGeneric(TypeVariable, Rc), /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -134,8 +134,21 @@ pub enum Type { /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { - // TODO: rename this and/or TypeVariableKind::Normal to non-numeric? + /// Can bind to any type, except Type::Constant and Type::InfixExpr Normal, + + /// A generic integer or field type. This is a more specific kind of TypeVariable + /// that can only be bound to Type::Field, Type::Integer, or other polymorphic integers. + /// This is the type of undecorated integer literals like `46`. Typing them in this way + /// allows them to be polymorphic over the actual integer/field type used without requiring + /// type annotations on each integer literal. + IntegerOrField, + + /// A generic integer type. This is a more specific kind of TypeVariable + /// that can only be bound to Type::Integer, or other polymorphic integers. + Integer, + + /// Can bind to a Type::Constant or Type::InfixExpr of the given kind Numeric(Box), } @@ -178,12 +191,26 @@ impl Kind { Err(UnificationError) } } + + /// Returns the default type this type variable should be bound to if it is still unbound + /// during monomorphization. + pub(crate) fn default_type(&self) -> Option { + match self { + Kind::IntegerOrField => Some(Type::default_int_or_field_type()), + Kind::Integer => Some(Type::default_int_type()), + Kind::Normal => None, + Kind::Numeric(typ) => Some(*typ.clone()), + } + } + } impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Kind::Normal => write!(f, "normal"), + Kind::Integer => write!(f, "int"), + Kind::IntegerOrField => write!(f, "intOrField"), Kind::Numeric(typ) => write!(f, "numeric {}", typ), } } @@ -245,17 +272,20 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub kind: Kind, pub span: Span, } impl ResolvedGeneric { pub fn as_named_generic(self) -> Type { - Type::NamedGeneric(self.type_var, self.name, self.kind) + Type::NamedGeneric(self.type_var, self.name) + } + + pub(crate) fn kind(&self) -> Kind { + self.type_var.kind() } pub(crate) fn is_numeric(&self) -> bool { - self.kind.is_numeric() + self.kind().is_numeric() } } @@ -323,7 +353,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) .collect(); (typ.substitute(&substitutions), i) @@ -339,7 +369,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -451,7 +481,7 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.kind.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) .collect(); self.typ.substitute(&substitutions) @@ -524,44 +554,13 @@ pub enum BinaryTypeOperator { Modulo, } -#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] -pub enum TypeVariableKind { - /// Can bind to any type, except Type::Constant and Type::InfixExpr - Normal, - - /// A generic integer or field type. This is a more specific kind of TypeVariable - /// that can only be bound to Type::Field, Type::Integer, or other polymorphic integers. - /// This is the type of undecorated integer literals like `46`. Typing them in this way - /// allows them to be polymorphic over the actual integer/field type used without requiring - /// type annotations on each integer literal. - IntegerOrField, - - /// A generic integer type. This is a more specific kind of TypeVariable - /// that can only be bound to Type::Integer, or other polymorphic integers. - Integer, - - /// Can bind to a Type::Constant or Type::InfixExpr of the given kind - Numeric(Box), -} - -impl TypeVariableKind { - pub(crate) fn kind(&self) -> Kind { - match self { - Self::Normal => Kind::Normal, - Self::IntegerOrField => Kind::Normal, - Self::Integer => Kind::Normal, - Self::Numeric(typ) => Kind::Numeric(typ.clone()), - } - } -} - /// A TypeVariable is a mutable reference that is either /// bound to some type, or unbound with a given TypeVariableId. #[derive(PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub struct TypeVariable(TypeVariableId, Shared); impl TypeVariable { - pub fn unbound(id: TypeVariableId, type_var_kind: TypeVariableKind) -> Self { + pub fn unbound(id: TypeVariableId, type_var_kind: Kind) -> Self { TypeVariable(id, Shared::new(TypeBinding::Unbound(id, type_var_kind))) } @@ -631,7 +630,7 @@ impl TypeVariable { /// Unbind this type variable, setting it to Unbound(id). /// /// This is generally a logic error to use outside of monomorphization. - pub fn unbind(&self, id: TypeVariableId, type_var_kind: TypeVariableKind) { + pub fn unbind(&self, id: TypeVariableId, type_var_kind: Kind) { *self.1.borrow_mut() = TypeBinding::Unbound(id, type_var_kind); } @@ -654,37 +653,37 @@ impl TypeVariable { } } - fn kind(&self) -> Kind { + pub(crate) fn kind(&self) -> Kind { match &*self.borrow() { TypeBinding::Bound(binding) => binding.kind(), - TypeBinding::Unbound(_, type_var_kind) => type_var_kind.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), } } /// Check that if bound, it's an integer - /// and if unbound, that it's a TypeVariableKind::Integer + /// and if unbound, that it's a Kind::Integer pub(crate) fn is_integer(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, TypeVariableKind::Integer), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::Integer), } } /// Check that if bound, it's an integer or field - /// and if unbound, that it's a TypeVariableKind::IntegerOrField + /// and if unbound, that it's a Kind::IntegerOrField pub(crate) fn is_integer_or_field(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..) | Type::FieldElement), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, TypeVariableKind::IntegerOrField), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), } } - /// Set the TypeVariableKind if unbound, - /// else unify the TypeVariableKind::kind() with the expected Kind - fn set_type_variable_kind(&self, new_type_var_kind: TypeVariableKind) -> Result<(), UnificationError> { + /// Set the Kind if unbound, + /// else unify the Kind::kind() with the expected Kind + fn set_type_variable_kind(&self, new_type_var_kind: Kind) -> Result<(), UnificationError> { match &mut*self.borrow_mut() { TypeBinding::Bound(binding) => { - binding.kind().unify(&new_type_var_kind.kind()) + binding.kind().unify(&new_type_var_kind) } TypeBinding::Unbound(_, ref mut type_var_kind) => { *type_var_kind = new_type_var_kind; @@ -699,7 +698,7 @@ impl TypeVariable { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum TypeBinding { Bound(Type), - Unbound(TypeVariableId, TypeVariableKind), + Unbound(TypeVariableId, Kind), } impl TypeBinding { @@ -729,17 +728,17 @@ impl std::fmt::Display for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var) => { - let binding = var.1; + let binding = &var.1; match &*binding.borrow() { TypeBinding::Unbound(_, type_var_kind) => { match type_var_kind { - TypeVariableKind::Normal => write!(f, "{}", var.borrow()), - TypeVariableKind::Integer => write!(f, "{}", Type::default_int_type()), - TypeVariableKind::IntegerOrField => write!(f, "Field"), + Kind::Normal => write!(f, "{}", var.borrow()), + Kind::Integer => write!(f, "{}", Type::default_int_type()), + Kind::IntegerOrField => write!(f, "Field"), // TODO: after debugging - // TypeVariableKind::Numeric(typ) => write!(f, "_"), - TypeVariableKind::Numeric(typ) => write!(f, "_!({:?})", typ), + // Kind::Numeric(typ) => write!(f, "_"), + Kind::Numeric(typ) => write!(f, "_!({:?})", typ), } } @@ -778,11 +777,11 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, kind) => match &*binding.borrow() { + Type::NamedGeneric(binding, name) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), // TODO: revert after debugging // TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_", kind), - TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_:! {:?}", kind), + TypeBinding::Unbound(_, kind) if name.is_empty() => write!(f, "_:! {:?}", kind), TypeBinding::Unbound(_, _) => write!(f, "{name}"), }, @@ -885,24 +884,24 @@ impl Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) } - pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: TypeVariableKind) -> Type { + pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: Kind) -> Type { let id = interner.next_type_variable_id(); let var = TypeVariable::unbound(id, type_var_kind); Type::TypeVariable(var) } pub fn type_variable(id: TypeVariableId) -> Type { - let var = TypeVariable::unbound(id, TypeVariableKind::Normal); + let var = TypeVariable::unbound(id, Kind::Normal); Type::TypeVariable(var) } pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { - let type_var_kind = TypeVariableKind::IntegerOrField; + let type_var_kind = Kind::IntegerOrField; Self::type_variable_with_kind(interner, type_var_kind) } pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { - let type_var_kind = TypeVariableKind::Integer; + let type_var_kind = Kind::Integer; Self::type_variable_with_kind(interner, type_var_kind) } @@ -945,7 +944,7 @@ impl Type { /// this method checks for numeric _values_ pub fn is_numeric_value(&self) -> bool { use Type::*; - use TypeVariableKind as K; + use Kind as K; match self.follow_bindings() { FieldElement => true, Integer(..) => true, @@ -956,15 +955,20 @@ impl Type { TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, K::Integer | K::IntegerOrField), } } + _ => false, } } pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { // Return whether the named generic has a Kind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { - if let Type::NamedGeneric(_, name, Kind::Numeric { .. }) = typ { - found_names.push(name.to_string()); - true + if let Type::NamedGeneric(var, name) = typ { + if var.kind().is_numeric() { + found_names.push(name.to_string()); + true + } else { + false + } } else { false } @@ -986,7 +990,7 @@ impl Type { } } - Type::NamedGeneric(_, _, _) => { + Type::NamedGeneric(_, _) => { named_generic_is_numeric(self, found_names); } @@ -1064,7 +1068,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -1107,7 +1111,7 @@ impl Type { | Type::Unit | Type::Constant(_, _) | Type::TypeVariable(_) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1155,7 +1159,7 @@ impl Type { | Type::InfixExpr(..) | Type::Error => true, - Type::TypeVariable(type_var) | Type::NamedGeneric(type_var, _, _) => { + Type::TypeVariable(type_var) | Type::NamedGeneric(type_var, _) => { if let TypeBinding::Bound(typ) = &*type_var.borrow() { typ.is_valid_for_unconstrained_boundary() } else { @@ -1195,7 +1199,7 @@ impl Type { pub fn generic_count(&self) -> usize { match self { Type::Forall(generics, _) => generics.len(), - Type::TypeVariable(type_variable) | Type::NamedGeneric(type_variable, _, _) => { + Type::TypeVariable(type_variable) | Type::NamedGeneric(type_variable, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), TypeBinding::Unbound(_, _) => 0, @@ -1234,11 +1238,11 @@ impl Type { pub(crate) fn kind(&self) -> Kind { match self { - Type::NamedGeneric(_, _, kind) => kind.clone(), + Type::NamedGeneric(var, _) => var.kind(), Type::Constant(_, kind) => kind.clone(), - Type::TypeVariable(var) => match *var.borrow() { + Type::TypeVariable(var) => match &*var.borrow() { TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_, type_var_kind) => type_var_kind.kind(), + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), }, Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs), Type::FieldElement @@ -1299,7 +1303,7 @@ impl Type { | Type::Unit | Type::TypeVariable(_) | Type::TraitAsType(..) - | Type::NamedGeneric(_, _, _) + | Type::NamedGeneric(_, _) | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -1358,7 +1362,7 @@ impl Type { TypeBinding::Unbound(id, _) => *id, }; - if !self.kind().unifies(&TypeVariableKind::IntegerOrField.kind()) { + if !self.kind().unifies(&Kind::IntegerOrField) { return Err(UnificationError) } @@ -1380,119 +1384,53 @@ impl Type { } // Avoid infinitely recursive bindings TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(ref new_target_id, TypeVariableKind::IntegerOrField) => { - let type_var_kind = TypeVariableKind::IntegerOrField; + TypeBinding::Unbound(ref new_target_id, Kind::IntegerOrField) => { + let type_var_kind = Kind::IntegerOrField; if only_integer { - let mut var_clone = var.clone(); - TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; - var.set_type_variable_kind(TypeVariableKind::Integer)?; + let var_clone = var.clone(); + Kind::Integer.unify(&type_var_kind)?; + var.set_type_variable_kind(Kind::Integer)?; // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var_clone); - bindings.insert(*new_target_id, (self_var.clone(), type_var_kind.kind(), clone)); + bindings.insert(*new_target_id, (self_var.clone(), type_var_kind, clone)); } else { bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); } Ok(()) } - TypeBinding::Unbound(new_target_id, TypeVariableKind::Integer) => { - let type_var_kind = TypeVariableKind::Integer; - TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; - bindings.insert(target_id, (var.clone(), type_var_kind.kind(), this.clone())); + TypeBinding::Unbound(_new_target_id, Kind::Integer) => { + let type_var_kind = Kind::Integer; + Kind::Integer.unify(&type_var_kind)?; + bindings.insert(target_id, (var.clone(), type_var_kind, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, TypeVariableKind::Normal) => { - let type_var_kind = TypeVariableKind::Normal; - let mut var_clone = var.clone(); + TypeBinding::Unbound(new_target_id, Kind::Normal) => { + let type_var_kind = Kind::Normal; + let var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = if only_integer { - TypeVariableKind::Integer + Kind::Integer } else { - TypeVariableKind::IntegerOrField + Kind::IntegerOrField }; - clone_kind.kind().unify(&type_var_kind.kind())?; - var_clone.set_type_variable_kind(clone_kind)?; + clone_kind.unify(&type_var_kind)?; + var_clone.set_type_variable_kind(clone_kind.clone())?; let clone = Type::TypeVariable(var_clone); - bindings.insert(*new_target_id, (self_var.clone(), clone_kind.kind(), clone)); + bindings.insert(*new_target_id, (self_var.clone(), clone_kind, clone)); Ok(()) } - TypeBinding::Unbound(new_target_id, TypeVariableKind::Numeric(typ)) => { + TypeBinding::Unbound(_new_target_id, Kind::Numeric(_typ)) => { Err(UnificationError) } } } - - // TODO: remove after testing - // Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { - // let borrow = self_var.borrow(); - // match &*borrow { - // TypeBinding::Bound(typ) => { - // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - // } - // // Avoid infinitely recursive bindings - // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - // TypeBinding::Unbound(new_target_id, type_var_kind) => { - // if only_integer { - // let mut var_clone = var.clone(); - // TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; - // var.set_type_variable_kind(TypeVariableKind::Integer)?; - // // Integer is more specific than IntegerOrField so we bind the type - // // variable to Integer instead. - // let clone = Type::TypeVariable(var_clone); - // bindings.insert(new_target_id, (self_var.clone(), TypeVariableKind::Integer.kind(), clone)); - // } else { - // bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); - // } - // Ok(()) - // } - // } - // } - // Type::TypeVariable(self_var, TypeVariableKind::Integer) => { - // let borrow = self_var.borrow(); - // match &*borrow { - // TypeBinding::Bound(typ) => { - // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - // } - // // Avoid infinitely recursive bindings - // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - // TypeBinding::Unbound(_, type_var_kind) => { - // TypeVariableKind::Integer.kind().unify(&type_var_kind.kind())?; - // bindings.insert(target_id, (var.clone(), type_var_kind.kind(), this.clone())); - // Ok(()) - // } - // } - // } - // Type::TypeVariable(self_var, TypeVariableKind::Normal) => { - // let borrow = self_var.borrow(); - // match &*borrow { - // TypeBinding::Bound(typ) => { - // typ.try_bind_to_polymorphic_int(var, bindings, only_integer) - // } - // // Avoid infinitely recursive bindings - // TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - // TypeBinding::Unbound(new_target_id, type_var_kind) => { - // let mut var_clone = var.clone(); - // // Bind to the most specific type variable kind - // let clone_kind = if only_integer { - // TypeVariableKind::Integer - // } else { - // TypeVariableKind::IntegerOrField - // }; - // clone_kind.kind().unify(&type_var_kind.kind())?; - // var_clone.set_type_variable_kind(clone_kind)?; - // let clone = Type::TypeVariable(var_clone); - // bindings.insert(*new_target_id, (self_var.clone(), clone_kind.kind(), clone)); - // Ok(()) - // } - // } - // } - _ => Err(UnificationError), } } /// Try to bind the given type variable to self. Although the given type variable - /// is expected to be of TypeVariableKind::Normal, this binding can still fail + /// is expected to be of Kind::Normal, this binding can still fail /// if the given type variable occurs within `self` as that would create a recursive type. /// /// If successful, the binding is placed in the @@ -1535,7 +1473,7 @@ impl Type { fn get_inner_type_variable(&self) -> Option<(Shared, Kind)> { match self { Type::TypeVariable(var) => Some((var.1.clone(), var.kind())), - Type::NamedGeneric(var, _, kind) => Some((var.1.clone(), kind.clone())), + Type::NamedGeneric(var, _) => Some((var.1.clone(), var.kind())), _ => None, } } @@ -1561,7 +1499,6 @@ impl Type { bindings: &mut TypeBindings, ) -> Result<(), UnificationError> { use Type::*; - use TypeVariableKind as Kind; let lhs = match self { Type::InfixExpr(..) => Cow::Owned(self.canonicalize()), @@ -1595,50 +1532,26 @@ impl Type { }) } } - TypeBinding::Unbound(id, Kind::IntegerOrField) => { + TypeBinding::Unbound(_id, Kind::IntegerOrField) => { other.try_unify_to_type_variable(var, bindings, |bindings| { let only_integer = false; other.try_bind_to_polymorphic_int(var, bindings, only_integer) }) } - TypeBinding::Unbound(id, Kind::Integer) => { + TypeBinding::Unbound(_id, Kind::Integer) => { other.try_unify_to_type_variable(var, bindings, |bindings| { let only_integer = true; other.try_bind_to_polymorphic_int(var, bindings, only_integer) }) } - TypeBinding::Unbound(id, type_var_kind) => { + TypeBinding::Unbound(_id, type_var_kind) => { other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings, type_var_kind.kind()) + other.try_bind_to(var, bindings, type_var_kind.clone()) }) } } } - // TODO: remove after testing - // (TypeVariable(var, Kind::IntegerOrField), other) - // | (other, TypeVariable(var, Kind::IntegerOrField)) => { - // other.try_unify_to_type_variable(var, bindings, |bindings| { - // let only_integer = false; - // other.try_bind_to_polymorphic_int(var, bindings, only_integer) - // }) - // } - // - // (TypeVariable(var, Kind::Integer), other) - // | (other, TypeVariable(var, Kind::Integer)) => { - // other.try_unify_to_type_variable(var, bindings, |bindings| { - // let only_integer = true; - // other.try_bind_to_polymorphic_int(var, bindings, only_integer) - // }) - // } - // - // (TypeVariable(var, type_var_kind), other) | (other, TypeVariable(var, type_var_kind)) => { - // other.try_unify_to_type_variable(var, bindings, |bindings| { - // other.try_bind_to(var, bindings, type_var_kind.kind()) - // }) - // } - - (Array(len_a, elem_a), Array(len_b, elem_b)) => { len_a.try_unify(len_b, bindings)?; elem_a.try_unify(elem_b, bindings) @@ -1678,7 +1591,7 @@ impl Type { } } - (NamedGeneric(binding, _, _), other) | (other, NamedGeneric(binding, _, _)) + (NamedGeneric(binding, _), other) | (other, NamedGeneric(binding, _)) if !binding.borrow().is_unbound() => { if let TypeBinding::Bound(link) = &*binding.borrow() { @@ -1688,13 +1601,13 @@ impl Type { } } - (NamedGeneric(binding_a, name_a, kind_a), NamedGeneric(binding_b, name_b, kind_b)) => { + (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); if name_a == name_b { - kind_a.unify(kind_b) + binding_a.kind().unify(&binding_b.kind()) } else { Err(UnificationError) } @@ -1783,7 +1696,7 @@ impl Type { bindings: &mut TypeBindings, // Bind the type variable to a type. This is factored out since depending on the - // TypeVariableKind, there are different methods to check whether the variable can + // Kind, there are different methods to check whether the variable can // bind to the given type or not. bind_variable: impl FnOnce(&mut TypeBindings) -> Result<(), UnificationError>, ) -> Result<(), UnificationError> { @@ -2044,7 +1957,7 @@ impl Type { fn type_variable_id(&self) -> Option { match self { - Type::TypeVariable(variable) | Type::NamedGeneric(variable, _, _) => { + Type::TypeVariable(variable) | Type::NamedGeneric(variable, _) => { Some(variable.0) } _ => None, @@ -2149,7 +2062,7 @@ impl Type { let fields = fields.substitute_helper(type_bindings, substitute_bound_typevars); Type::FmtString(Box::new(size), Box::new(fields)) } - Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding) => { + Type::NamedGeneric(binding, _) | Type::TypeVariable(binding) => { substitute_binding(binding) } // Do not substitute_helper fields, it can lead to infinite recursion @@ -2238,7 +2151,7 @@ impl Type { || args.named.iter().any(|arg| arg.typ.occurs(target_id)) } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), - Type::NamedGeneric(type_var, _, _) | Type::TypeVariable(type_var) => { + Type::NamedGeneric(type_var, _) | Type::TypeVariable(type_var) => { match &*type_var.borrow() { TypeBinding::Bound(binding) => { type_var.id() == target_id || binding.occurs(target_id) @@ -2296,7 +2209,7 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var) | NamedGeneric(var, _, _) => { + TypeVariable(var) | NamedGeneric(var, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -2380,7 +2293,7 @@ impl Type { Type::TypeVariable(var) => { let var = var.borrow(); if let TypeBinding::Bound(binding) = &*var { - let mut binding = binding.clone(); + let binding = binding.clone(); drop(var); *self = binding; } @@ -2393,7 +2306,7 @@ impl Type { generic.typ.replace_named_generics_with_type_variables(); } } - Type::NamedGeneric(var, _, kind) => { + Type::NamedGeneric(var, _) => { let type_binding = var.borrow(); if let TypeBinding::Bound(binding) = &*type_binding { let mut binding = binding.clone(); @@ -2491,19 +2404,6 @@ impl BinaryTypeOperator { } } -impl TypeVariableKind { - /// Returns the default type this type variable should be bound to if it is still unbound - /// during monomorphization. - pub(crate) fn default_type(&self) -> Option { - match self { - TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), - TypeVariableKind::Integer => Some(Type::default_int_type()), - TypeVariableKind::Normal => None, - TypeVariableKind::Numeric(typ) => Some(*typ.clone()), - } - } -} - impl From for PrintableType { fn from(value: Type) -> Self { Self::from(&value) @@ -2533,9 +2433,10 @@ impl From<&Type> for PrintableType { }, Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_, TypeVariableKind::Integer) => Type::default_int_type().into(), - TypeBinding::Unbound(_, TypeVariableKind::IntegerOrField) => Type::default_int_or_field_type().into(), - TypeBinding::Unbound(_, TypeVariableKind::Numeric(typ)) => (**typ).into(), + TypeBinding::Unbound(_, Kind::Integer) => Type::default_int_type().into(), + TypeBinding::Unbound(_, Kind::IntegerOrField) => Type::default_int_or_field_type().into(), + TypeBinding::Unbound(_, Kind::Numeric(typ)) => (*typ.clone()).into(), + TypeBinding::Unbound(_, Kind::Normal) => unreachable!(), }, Type::Bool => PrintableType::Boolean, Type::String(size) => { @@ -2589,13 +2490,13 @@ impl std::fmt::Debug for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var) => { - let binding = var.1; + let binding = &var.1; if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { match type_var_kind { - TypeVariableKind::Normal => write!(f, "{:?}", var), - TypeVariableKind::IntegerOrField => write!(f, "IntOrField{:?}", binding), - TypeVariableKind::Integer => write!(f, "Int{:?}", binding), - TypeVariableKind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), + Kind::Normal => write!(f, "{:?}", var), + Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), + Kind::Integer => write!(f, "Int{:?}", binding), + Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), } } else { write!(f, "{}", binding.borrow()) @@ -2629,8 +2530,8 @@ impl std::fmt::Debug for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, kind) => match kind { - Kind::Normal => { + Type::NamedGeneric(binding, name) => match binding.kind() { + Kind::Normal | Kind::Integer | Kind::IntegerOrField => { write!(f, "{}{:?}", name, binding) } Kind::Numeric(typ) => { @@ -2822,8 +2723,8 @@ impl PartialEq for Type { // still want them to be equal for canonicalization checks in arithmetic generics. // Without this we'd fail the `serialize` test. ( - NamedGeneric(lhs_var, _, _) | TypeVariable(lhs_var), - NamedGeneric(rhs_var, _, _) | TypeVariable(rhs_var), + NamedGeneric(lhs_var, _) | TypeVariable(lhs_var), + NamedGeneric(rhs_var, _) | TypeVariable(rhs_var), ) => lhs_var.id() == rhs_var.id(), _ => false, } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index c8c12f32d06..9c5c3b35493 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -973,24 +973,24 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _, kind) => { - if let TypeBinding::Bound(binding) = &*binding.borrow() { + HirType::NamedGeneric(binding, _) => { + if let TypeBinding::Bound(ref binding) = &*binding.borrow() { return Self::convert_type(binding, location); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_or_field_type(), kind); + binding.bind(HirType::default_int_or_field_type(), &binding.kind()); ast::Type::Field } - HirType::TypeVariable(binding) => { + HirType::TypeVariable(ref binding) => { let type_var_kind = match &*binding.borrow() { TypeBinding::Bound(ref binding) => { return Self::convert_type(binding, location); } - TypeBinding::Unbound(_, type_var_kind) => type_var_kind, + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1002,7 +1002,7 @@ impl<'interner> Monomorphizer<'interner> { }; let monomorphized_default = Self::convert_type(&default, location)?; - binding.bind(default, &type_var_kind.kind()); + binding.bind(default, &type_var_kind); monomorphized_default } @@ -1094,7 +1094,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), HirType::Slice(element) => Self::check_type(element.as_ref(), location), - HirType::NamedGeneric(binding, _, _) => { + HirType::NamedGeneric(binding, _) => { if let TypeBinding::Bound(ref binding) = &*binding.borrow() { return Self::check_type(binding, location); } @@ -1102,12 +1102,12 @@ impl<'interner> Monomorphizer<'interner> { Ok(()) } - HirType::TypeVariable(binding) => { + HirType::TypeVariable(ref binding) => { let type_var_kind = match &*binding.borrow() { TypeBinding::Bound(binding) => { return Self::check_type(binding, location); } - TypeBinding::Unbound(_, type_var_kind) => type_var_kind, + TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b3acaef2c62..accdbd9fcb7 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -48,7 +48,7 @@ use crate::locations::LocationIndices; use crate::token::{Attributes, SecondaryAttribute}; use crate::GenericTypeVars; use crate::Generics; -use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind}; +use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId}; /// An arbitrary number to limit the recursion depth when searching for trait impls. /// This is needed to stop recursing for cases such as `impl Foo for T where T: Eq` @@ -727,7 +727,7 @@ impl NodeInterner { crate_id: unresolved_trait.crate_id, location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), generics, - self_type_typevar: TypeVariable::unbound(self.next_type_variable_id()), + self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), methods: Vec::new(), method_ids: unresolved_trait.method_ids.clone(), associated_types, @@ -2222,11 +2222,11 @@ impl NodeInterner { let trait_generics = the_trait.generics.clone(); let self_type_var = the_trait.self_type_typevar.clone(); - bindings.insert(self_type_var.id(), (self_type_var, Kind::Normal, impl_self_type)); + bindings.insert(self_type_var.id(), (self_type_var.clone(), self_type_var.kind(), impl_self_type)); for (trait_generic, trait_impl_generic) in trait_generics.iter().zip(trait_impl_generics) { let type_var = trait_generic.type_var.clone(); - bindings.insert(type_var.id(), (type_var, trait_generic.kind.clone(), trait_impl_generic.clone())); + bindings.insert(type_var.id(), (type_var, trait_generic.kind(), trait_impl_generic.clone())); } // Now that the normal bindings are added, we still need to bind the associated types @@ -2235,7 +2235,7 @@ impl NodeInterner { for (trait_type, impl_type) in trait_associated_types.iter().zip(impl_associated_types) { let type_variable = trait_type.type_var.clone(); - bindings.insert(type_variable.id(), (type_variable, trait_type.kind.clone(), impl_type.typ.clone())); + bindings.insert(type_variable.id(), (type_variable, trait_type.kind(), impl_type.typ.clone())); } bindings @@ -2356,22 +2356,26 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Array(_, _) => Some(Array), Type::Slice(_) => Some(Slice), Type::Integer(_, _) => Some(FieldOrInt), - Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), - Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt), + Type::TypeVariable(var) => { + if var.is_integer() || var.is_integer_or_field() { + Some(FieldOrInt) + } else { + None + } + } Type::Bool => Some(Bool), Type::String(_) => Some(String), Type::FmtString(_, _) => Some(FmtString), Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _, _) => Some(Function), - Type::NamedGeneric(_, _, _) => Some(Generic), + Type::NamedGeneric(_, _) => Some(Generic), Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), // We do not support adding methods to these types - Type::TypeVariable(_, _) - | Type::Forall(_, _) + Type::Forall(_, _) | Type::Constant(..) | Type::Error | Type::Struct(_, _) From 9cb1aab94286ad953d3bddbd2972a46b8faccae3 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 25 Sep 2024 16:45:25 -0400 Subject: [PATCH 12/56] wip debugging and add Kind::Normal --- .../noirc_frontend/src/elaborator/types.rs | 28 +++--- .../src/hir/comptime/interpreter.rs | 6 +- compiler/noirc_frontend/src/hir_def/types.rs | 85 ++++++++++--------- .../src/monomorphization/mod.rs | 9 +- compiler/noirc_frontend/src/node_interner.rs | 6 ++ 5 files changed, 80 insertions(+), 54 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 23757e39292..d42b245fac5 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -59,17 +59,17 @@ impl<'context> Elaborator<'context> { /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { - // TODO: cleanup - let dbg_this = match &typ.typ { - UnresolvedTypeData::Integer(..) => true, - UnresolvedTypeData::Named(..) => true, - UnresolvedTypeData::FieldElement => true, - UnresolvedTypeData::Expression(..) => true, - _ => false, - }; - if dbg_this { - dbg!("resolve_type_inner", &typ, kind); - } + // // TODO: cleanup + // let dbg_this = match &typ.typ { + // UnresolvedTypeData::Integer(..) => true, + // UnresolvedTypeData::Named(..) => true, + // UnresolvedTypeData::FieldElement => true, + // UnresolvedTypeData::Expression(..) => true, + // _ => false, + // }; + // if dbg_this { + // dbg!("resolve_type_inner", &typ, kind); + // } use crate::ast::UnresolvedTypeData::*; @@ -243,7 +243,8 @@ impl<'context> Elaborator<'context> { // TODO: cleanup dbg!("resolve_named_type: WILDCARD_TYPE", &name); - return self.interner.next_type_variable(); + // return self.interner.next_type_variable(); + return self.interner.next_type_variable_with_kind(Kind::Any); } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { if !args.is_empty() { @@ -728,8 +729,9 @@ impl<'context> Elaborator<'context> { // match &typ.typ { UnresolvedTypeData::Unspecified => { + // // TODO: cleanup dbg!("resolve_inferred_type: unspecified", &typ); - self.interner.next_type_variable() + self.interner.next_type_variable_with_kind(Kind::Any) } _ => { // dbg!("resolve_inferred_type: specified", &typ); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index c53bb02d369..12b3c24f9ec 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -90,7 +90,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { // To match the monomorphizer, we need to call follow_bindings on each of // the instantiation bindings before we unbind the generics from the previous function. // This is because the instantiation bindings refer to variables from the call site. - for (_, _kind, binding) in instantiation_bindings.values_mut() { + for (_, kind, binding) in instantiation_bindings.values_mut() { + *kind = kind.follow_bindings(); *binding = binding.follow_bindings(); } @@ -99,7 +100,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut impl_bindings = perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - for (_, _kind, binding) in impl_bindings.values_mut() { + for (_, kind, binding) in impl_bindings.values_mut() { + *kind = kind.follow_bindings(); *binding = binding.follow_bindings(); } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a9802d616a0..42b9ab83632 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -134,6 +134,9 @@ pub enum Type { /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { + /// Can bind to any type + Any, + /// Can bind to any type, except Type::Constant and Type::InfixExpr Normal, @@ -168,10 +171,30 @@ impl Kind { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } + pub(crate) fn follow_bindings(&self) -> Self { + match self { + Self::Any => Self::Any, + Self::Normal => Self::Normal, + Self::Integer => Self::Integer, + Self::IntegerOrField => Self::IntegerOrField, + Self::Numeric(typ) => Self::Numeric(Box::new(typ.follow_bindings())) + } + } + /// Unifies this kind with the other. Returns true on success pub(crate) fn unifies(&self, other: &Kind) -> bool { match (self, other) { - (Kind::Normal, Kind::Normal) => true, + // Kind::Any unifies with everything + (Kind::Any, _) + | (_, Kind::Any) + + // Kind::Normal unifies with Kind::Integer and Kind::IntegerOrField + | (Kind::Normal, Kind::Integer | Kind::IntegerOrField) + | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) + + // Kind::Integer unifies with Kind::IntegerOrField + | (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, + (Kind::Numeric(lhs), Kind::Numeric(rhs)) => { let mut bindings = TypeBindings::new(); let unifies = lhs.try_unify(rhs, &mut bindings).is_ok(); @@ -180,7 +203,7 @@ impl Kind { } unifies } - _ => false, + (lhs, rhs) => lhs == rhs, } } @@ -196,6 +219,7 @@ impl Kind { /// during monomorphization. pub(crate) fn default_type(&self) -> Option { match self { + Kind::Any => None, Kind::IntegerOrField => Some(Type::default_int_or_field_type()), Kind::Integer => Some(Type::default_int_type()), Kind::Normal => None, @@ -208,6 +232,7 @@ impl Kind { impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Kind::Any => write!(f, "any"), Kind::Normal => write!(f, "normal"), Kind::Integer => write!(f, "int"), Kind::IntegerOrField => write!(f, "intOrField"), @@ -407,7 +432,7 @@ impl StructType { /// Instantiate this struct type, returning a Vec of the new generic args (in /// the same order as self.generics) pub fn instantiate(&self, interner: &mut NodeInterner) -> Vec { - vecmap(&self.generics, |_| interner.next_type_variable()) + vecmap(&self.generics, |generic| interner.next_type_variable_with_kind(generic.kind())) } } @@ -594,13 +619,13 @@ impl TypeVariable { } pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { - // TODO: remove before review - assert!( - binding.kind().unifies(kind), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - binding.kind(), - kind - ); + if !binding.kind().unifies(kind) { + return Err(TypeCheckError::TypeKindMismatch { + expected_kind: format!("{}", kind), + expr_kind: format!("{}", binding.kind()), + expr_span: span, + }); + } let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { @@ -622,10 +647,11 @@ impl TypeVariable { self.1.borrow() } - /// Borrows this TypeVariable to (e.g.) manually match on the inner TypeBinding. - pub fn borrow_mut(&self) -> std::cell::RefMut { - self.1.borrow_mut() - } + // TODO: remove since it's error-prone and unused + // /// Borrows this TypeVariable to (e.g.) manually match on the inner TypeBinding. + // pub fn borrow_mut(&self) -> std::cell::RefMut { + // self.1.borrow_mut() + // } /// Unbind this type variable, setting it to Unbound(id). /// @@ -677,20 +703,6 @@ impl TypeVariable { TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), } } - - /// Set the Kind if unbound, - /// else unify the Kind::kind() with the expected Kind - fn set_type_variable_kind(&self, new_type_var_kind: Kind) -> Result<(), UnificationError> { - match &mut*self.borrow_mut() { - TypeBinding::Bound(binding) => { - binding.kind().unify(&new_type_var_kind) - } - TypeBinding::Unbound(_, ref mut type_var_kind) => { - *type_var_kind = new_type_var_kind; - Ok(()) - } - } - } } /// TypeBindings are the mutable insides of a TypeVariable. @@ -732,13 +744,13 @@ impl std::fmt::Display for Type { match &*binding.borrow() { TypeBinding::Unbound(_, type_var_kind) => { match type_var_kind { - Kind::Normal => write!(f, "{}", var.borrow()), + Kind::Any | Kind::Normal => write!(f, "{}", var.borrow()), Kind::Integer => write!(f, "{}", Type::default_int_type()), Kind::IntegerOrField => write!(f, "Field"), // TODO: after debugging // Kind::Numeric(typ) => write!(f, "_"), - Kind::Numeric(typ) => write!(f, "_!({:?})", typ), + Kind::Numeric(typ) => write!(f, "_#!({:?})", typ), } } @@ -1389,7 +1401,6 @@ impl Type { if only_integer { let var_clone = var.clone(); Kind::Integer.unify(&type_var_kind)?; - var.set_type_variable_kind(Kind::Integer)?; // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var_clone); @@ -1405,8 +1416,7 @@ impl Type { bindings.insert(target_id, (var.clone(), type_var_kind, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, Kind::Normal) => { - let type_var_kind = Kind::Normal; + TypeBinding::Unbound(new_target_id, type_var_kind) => { let var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = if only_integer { @@ -1415,7 +1425,6 @@ impl Type { Kind::IntegerOrField }; clone_kind.unify(&type_var_kind)?; - var_clone.set_type_variable_kind(clone_kind.clone())?; let clone = Type::TypeVariable(var_clone); bindings.insert(*new_target_id, (self_var.clone(), clone_kind, clone)); Ok(()) @@ -1891,7 +1900,7 @@ impl Type { for var in typevars { bindings .entry(var.id()) - .or_insert_with(|| (var.clone(), Kind::Normal, interner.next_type_variable())); + .or_insert_with(|| (var.clone(), Kind::Normal, interner.next_type_variable_with_kind(var.kind()))); } let instantiated = typ.force_substitute(&bindings); @@ -2436,7 +2445,7 @@ impl From<&Type> for PrintableType { TypeBinding::Unbound(_, Kind::Integer) => Type::default_int_type().into(), TypeBinding::Unbound(_, Kind::IntegerOrField) => Type::default_int_or_field_type().into(), TypeBinding::Unbound(_, Kind::Numeric(typ)) => (*typ.clone()).into(), - TypeBinding::Unbound(_, Kind::Normal) => unreachable!(), + TypeBinding::Unbound(_, Kind::Any | Kind::Normal) => unreachable!(), }, Type::Bool => PrintableType::Boolean, Type::String(size) => { @@ -2493,7 +2502,7 @@ impl std::fmt::Debug for Type { let binding = &var.1; if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { match type_var_kind { - Kind::Normal => write!(f, "{:?}", var), + Kind::Any | Kind::Normal => write!(f, "{:?}", var), Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), Kind::Integer => write!(f, "Int{:?}", binding), Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), @@ -2531,7 +2540,7 @@ impl std::fmt::Debug for Type { Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), Type::NamedGeneric(binding, name) => match binding.kind() { - Kind::Normal | Kind::Integer | Kind::IntegerOrField => { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { write!(f, "{}{:?}", name, binding) } Kind::Numeric(typ) => { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9c5c3b35493..0464239c1a1 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -857,7 +857,14 @@ impl<'interner> Monomorphizer<'interner> { // Ensure all instantiation bindings are bound. // This ensures even unused type variables like `fn foo() {}` have concrete types if let Some(bindings) = self.interner.try_get_instantiation_bindings(expr_id) { - for (_, _kind, binding) in bindings.values() { + for (_, kind, binding) in bindings.values() { + match kind { + Kind::Any => (), + Kind::Normal => (), + Kind::Integer => (), + Kind::IntegerOrField => (), + Kind::Numeric(typ) => Self::check_type(typ, ident.location)?, + } Self::check_type(binding, ident.location)?; } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index accdbd9fcb7..ba9fcca38e3 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1330,6 +1330,12 @@ impl NodeInterner { Type::type_variable(self.next_type_variable_id()) } + // TODO: make closer to self.next_type_variable() + pub fn next_type_variable_with_kind(&self, kind: Kind) -> Type { + let var = TypeVariable::unbound(self.next_type_variable_id(), kind); + Type::TypeVariable(var) + } + pub fn store_instantiation_bindings( &mut self, expr_id: ExprId, From 1ba8e44877bfb18d72ed4e60916ccf75d13c4728 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 26 Sep 2024 14:58:24 -0400 Subject: [PATCH 13/56] wip debugging failing tests, updating kinds in noirc_driver and lsp, propagate more kinds instead of using Kind::Normal --- compiler/noirc_driver/src/abi_gen.rs | 21 +- .../noirc_frontend/src/elaborator/types.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 23 +- compiler/noirc_frontend/src/tests.rs | 6851 +++++++++-------- tooling/lsp/src/requests/completion.rs | 10 +- tooling/lsp/src/requests/hover.rs | 4 +- tooling/lsp/src/requests/inlay_hint.rs | 30 +- .../src/trait_impl_method_stub_generator.rs | 14 +- 8 files changed, 3480 insertions(+), 3475 deletions(-) diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index be89b24fee5..fdde00eb0f6 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -13,7 +13,7 @@ use noirc_frontend::{ macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, }; -use noirc_frontend::{TypeBinding, TypeVariableKind}; +use noirc_frontend::TypeBinding; /// Arranges a function signature and a generated circuit's return witnesses into a /// `noirc_abi::Abi`. @@ -68,13 +68,18 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { AbiType::Integer { sign, width: (*bit_width).into() } } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { - TypeBinding::Bound(typ) => abi_type_from_hir_type(context, typ), - TypeBinding::Unbound(_) => { - abi_type_from_hir_type(context, &Type::default_int_or_field_type()) + Type::TypeVariable(binding) => { + if binding.is_integer() || binding.is_integer_or_field() { + match &*binding.borrow() { + TypeBinding::Bound(typ) => abi_type_from_hir_type(context, typ), + TypeBinding::Unbound(_id, _kind) => { + abi_type_from_hir_type(context, &Type::default_int_or_field_type()) + } + } + } else { + unreachable!("{typ} cannot be used in the abi") } - }, + } Type::Bool => AbiType::Boolean, Type::String(size) => { let size = size @@ -102,7 +107,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::Constant(..) | Type::InfixExpr(..) | Type::TraitAsType(..) - | Type::TypeVariable(_, _) + | Type::TypeVariable(_) | Type::NamedGeneric(..) | Type::Forall(..) | Type::Quoted(_) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d42b245fac5..d91bbd22065 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -730,7 +730,7 @@ impl<'context> Elaborator<'context> { match &typ.typ { UnresolvedTypeData::Unspecified => { // // TODO: cleanup - dbg!("resolve_inferred_type: unspecified", &typ); + dbg!("resolve_inferred_type: unspecified"); self.interner.next_type_variable_with_kind(Kind::Any) } _ => { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 42b9ab83632..f9480f10d28 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -194,7 +194,8 @@ impl Kind { // Kind::Integer unifies with Kind::IntegerOrField | (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, - + + // Kind::Numeric unifies along its Type argument (Kind::Numeric(lhs), Kind::Numeric(rhs)) => { let mut bindings = TypeBindings::new(); let unifies = lhs.try_unify(rhs, &mut bindings).is_ok(); @@ -203,6 +204,8 @@ impl Kind { } unifies } + + // everything unifies with itself (lhs, rhs) => lhs == rhs, } } @@ -285,7 +288,7 @@ pub struct StructType { /// Corresponds to generic lists such as `` in the source program. /// Used mainly for resolved types which no longer need information such -/// as names or kinds (i.e. they have Kind::Normal). +/// as names or kinds pub type GenericTypeVars = Vec; /// Corresponds to generic lists such as `` with additional @@ -305,7 +308,7 @@ impl ResolvedGeneric { Type::NamedGeneric(self.type_var, self.name) } - pub(crate) fn kind(&self) -> Kind { + pub fn kind(&self) -> Kind { self.type_var.kind() } @@ -679,7 +682,7 @@ impl TypeVariable { } } - pub(crate) fn kind(&self) -> Kind { + pub fn kind(&self) -> Kind { match &*self.borrow() { TypeBinding::Bound(binding) => binding.kind(), TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), @@ -688,7 +691,7 @@ impl TypeVariable { /// Check that if bound, it's an integer /// and if unbound, that it's a Kind::Integer - pub(crate) fn is_integer(&self) -> bool { + pub fn is_integer(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::Integer), @@ -697,7 +700,7 @@ impl TypeVariable { /// Check that if bound, it's an integer or field /// and if unbound, that it's a Kind::IntegerOrField - pub(crate) fn is_integer_or_field(&self) -> bool { + pub fn is_integer_or_field(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..) | Type::FieldElement), TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), @@ -1474,7 +1477,7 @@ impl Type { if this.occurs(target_id) { Err(UnificationError) } else { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + bindings.insert(target_id, (var.clone(), this.kind(), this.clone())); Ok(()) } } @@ -1900,7 +1903,7 @@ impl Type { for var in typevars { bindings .entry(var.id()) - .or_insert_with(|| (var.clone(), Kind::Normal, interner.next_type_variable_with_kind(var.kind()))); + .or_insert_with(|| (var.clone(), var.kind(), interner.next_type_variable_with_kind(var.kind()))); } let instantiated = typ.force_substitute(&bindings); @@ -1920,7 +1923,7 @@ impl Type { .iter() .map(|var| { let new = interner.next_type_variable(); - (var.id(), (var.clone(), Kind::Normal, new)) + (var.id(), (var.clone(), var.kind(), new)) }) .collect(); @@ -1954,7 +1957,7 @@ impl Type { let replacements = typevars .iter() .zip(bindings) - .map(|(var, binding)| (var.id(), (var.clone(), Kind::Normal, binding))) + .map(|(var, binding)| (var.id(), (var.clone(), var.kind(), binding))) .collect(); let instantiated = typ.substitute(&replacements); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 8947487c4a0..d93869812cb 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -125,3457 +125,3469 @@ fn assert_no_errors(src: &str) { } } -// TODO: re-enable! -// #[test] -// fn check_trait_implemented_for_all_t() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait IsDefault { -// fn is_default(self) -> bool; -// } -// -// impl IsDefault for T where T: Default + Eq { -// fn is_default(self) -> bool { -// self.eq(T::default()) -// } -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Default for u64 { -// fn default() -> Self { -// 0 -// } -// } -// -// impl Default for Foo { -// fn default() -> Self { -// Foo { a: Default::default() } -// } -// } -// -// fn main(a: Foo) -> pub bool { -// a.is_default() -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_implementation_duplicate_method() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// y + 2 * x -// } -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// x + 2 * y -// } -// } -// -// fn main() {}"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); -// assert_eq!(first_def, "default"); -// assert_eq!(second_def, "default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// struct Foo { -// } -// -// impl Default for Foo { -// fn default() -> Field { -// 0 -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type2() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, _y: Field) -> Field { -// x -// } -// } -// -// fn main() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_missing_implementation() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// -// fn method2(x: Field) -> Field; -// -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { -// trait_name, -// method_name, -// trait_impl_span: _, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(method_name, "method2"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_not_in_scope() { -// let src = " -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// // Default trait does not exist -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// } -// -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { -// trait_path, -// }) => { -// assert_eq!(trait_path.as_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_name() { -// let src = " -// trait Default { -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// // wrong trait name method should not compile -// impl Default for Foo { -// fn does_not_exist(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// }"; -// let compilation_errors = get_program_errors(src); -// assert!(!has_parser_error(&compilation_errors)); -// assert!( -// compilation_errors.len() == 1, -// "Expected 1 compilation error, got: {:?}", -// compilation_errors -// ); -// -// for (err, _file_id) in compilation_errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { -// trait_name, -// impl_method, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(impl_method, "does_not_exist"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter() { -// let src = " -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// struct Foo { -// bar: u32, -// } -// -// impl Default for Foo { -// fn default(x: u32) -> Self { -// Foo {bar: x} -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "u32"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter2() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field, y: Foo) -> Self { -// Self { bar: x, array: [x, y.bar] } -// } -// } -// -// fn main() { -// }"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "Foo"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter_type() { -// let src = " -// pub trait Default { -// fn default(x: Field, y: NotAType) -> Field; -// } -// -// fn main(x: Field, y: Field) { -// assert(y == x); -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// -// // This is a duplicate error in the name resolver & type checker. -// // In the elaborator there is no duplicate and only 1 error is issued -// assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Unresolved(ident), -// )) => { -// assert_eq!(ident, "NotAType"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameters_count() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field) -> Self { -// Self { bar: x, array: [x, x] } -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { -// actual_num_parameters, -// expected_num_parameters, -// trait_name, -// method_name, -// .. -// }) => { -// assert_eq!(actual_num_parameters, &1_usize); -// assert_eq!(expected_num_parameters, &2_usize); -// assert_eq!(method_name, "default"); -// assert_eq!(trait_name, "Default"); -// } -// _ => { -// panic!("No other errors are expected in this test case! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_impl_for_non_type() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// impl Default for main { -// fn default(x: Field, y: Field) -> Field { -// x + y -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { -// assert_eq!(expected, "type"); -// assert_eq!(got, "function"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_impl_struct_not_trait() { -// let src = " -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// struct Default { -// x: Field, -// z: Field, -// } -// -// // Default is a struct not a trait -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { -// not_a_trait_name, -// }) => { -// assert_eq!(not_a_trait_name.to_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_declaration() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Self; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// fn default(x: Field,y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// fn main() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::Trait); -// assert_eq!(first_def, "Default"); -// assert_eq!(second_def, "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation() { -// let src = " -// trait Default { -// } -// struct Foo { -// bar: Field, -// } -// -// impl Default for Foo { -// } -// impl Default for Foo { -// } -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation_with_alias() { -// let src = " -// trait Default { -// } -// -// struct MyStruct { -// } -// -// type MyType = MyStruct; -// -// impl Default for MyStruct { -// } -// -// impl Default for MyType { -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn test_impl_self_within_default_def() { -// let src = " -// trait Bar { -// fn ok(self) -> Self; -// -// fn ref_ok(self) -> Self { -// self.ok() -// } -// } -// -// impl Bar for (T, T) where T: Bar { -// fn ok(self) -> Self { -// self -// } -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_fn_parameter() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// fn test_eq(x: impl Eq) -> bool { -// x.eq(x) -// } -// -// fn main(a: Foo) -> pub bool { -// test_eq(a) -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_two_fn_parameters() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait Test { -// fn test(self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Test for u64 { -// fn test(self) -> bool { self == self } -// } -// -// fn test_eq(x: impl Eq, y: impl Test) -> bool { -// x.eq(x) == y.test() -// } -// -// fn main(a: Foo, b: u64) -> pub bool { -// test_eq(a, b) -// }"; -// assert_no_errors(src); -// } - -fn get_program_captures(src: &str) -> Vec> { - let (program, context, _errors) = get_program(src); - let interner = context.def_interner; - let mut all_captures: Vec> = Vec::new(); - for func in program.into_sorted().functions { - let func_id = interner.find_function(func.item.name()).unwrap(); - let hir_func = interner.function(&func_id); - // Iterate over function statements and apply filtering function - find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); - } - all_captures -} - -fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { - for stmt_id in stmts.iter() { - let hir_stmt = interner.statement(stmt_id); - let expr_id = match hir_stmt { - HirStatement::Expression(expr_id) => expr_id, - HirStatement::Let(let_stmt) => let_stmt.expression, - HirStatement::Assign(assign_stmt) => assign_stmt.expression, - HirStatement::Constrain(constr_stmt) => constr_stmt.0, - HirStatement::Semi(semi_expr) => semi_expr, - HirStatement::For(for_loop) => for_loop.block, - HirStatement::Error => panic!("Invalid HirStatement!"), - HirStatement::Break => panic!("Unexpected break"), - HirStatement::Continue => panic!("Unexpected continue"), - HirStatement::Comptime(_) => panic!("Unexpected comptime"), - }; - let expr = interner.expression(&expr_id); - - get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter - } -} - -fn get_lambda_captures( - expr: HirExpression, - interner: &NodeInterner, - result: &mut Vec>, -) { - if let HirExpression::Lambda(lambda_expr) = expr { - let mut cur_capture = Vec::new(); - - for capture in lambda_expr.captures.iter() { - cur_capture.push(interner.definition(capture.ident.id).name.clone()); - } - result.push(cur_capture); - - // Check for other captures recursively within the lambda body - let hir_body_expr = interner.expression(&lambda_expr.body); - if let HirExpression::Block(block_expr) = hir_body_expr { - find_lambda_captures(block_expr.statements(), interner, result); - } - } -} - -// #[test] -// fn resolve_empty_function() { -// let src = " -// fn main() { -// -// } -// "; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_basic_function() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_unused_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(x == x); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unused variable -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { -// assert_eq!(&ident.0.contents, "y"); -// } -// _ => unreachable!("we should only have an unused var error"), -// } -// } -// -// #[test] -// fn resolve_unresolved_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == z); -// } -// "#; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { -// assert_eq!(name, "z"); -// } -// _ => unimplemented!("we should only have an unresolved variable"), -// } -// } -// -// #[test] -// fn unresolved_path() { -// let src = " -// fn main(x : Field) { -// let _z = some::path::to::a::func(x); -// } -// "; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "some"); -// } -// _ => unimplemented!("we should only have an unresolved function"), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_literal_expr() { -// let src = r#" -// fn main(x : Field) { -// let y = 5; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn multiple_resolution_errors() { -// let src = r#" -// fn main(x : Field) { -// let y = foo::bar(x); -// let z = y + a; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); -// -// // Errors are: -// // `a` is undeclared -// // `z` is unused -// // `foo::bar` does not exist -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::UnusedVariable { ident } => { -// assert_eq!(&ident.0.contents, "z"); -// } -// ResolverError::VariableNotDeclared { name, .. } => { -// assert_eq!(name, "a"); -// } -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "foo"); -// } -// _ => unimplemented!(), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_prefix_expr() { -// let src = r#" -// fn main(x : Field) { -// let _y = -x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_for_expr() { -// let src = r#" -// fn main(x : u64) { -// for i in 1..20 { -// let _z = x + i; -// }; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_call_expr() { -// let src = r#" -// fn main(x : Field) { -// let _z = foo(x); -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_shadowing() { -// let src = r#" -// fn main(x : Field) { -// let x = foo(x); -// let x = x; -// let (x, x) = (x, x); -// let _ = x; -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_basic_closure() { -// let src = r#" -// fn main(x : Field) -> pub Field { -// let closure = |y| y + x; -// closure(x) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_simplified_closure() { -// // based on bug https://github.com/noir-lang/noir/issues/1088 -// -// let src = r#"fn do_closure(x: Field) -> Field { -// let y = x; -// let ret_capture = || { -// y -// }; -// ret_capture() -// } -// -// fn main(x: Field) { -// assert(do_closure(x) == 100); -// } -// -// "#; -// let parsed_captures = get_program_captures(src); -// let expected_captures = vec![vec!["y".to_string()]]; -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_complex_closures() { -// let src = r#" -// fn main(x: Field) -> pub Field { -// let closure_without_captures = |x: Field| -> Field { x + x }; -// let a = closure_without_captures(1); -// -// let closure_capturing_a_param = |y: Field| -> Field { y + x }; -// let b = closure_capturing_a_param(2); -// -// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; -// let c = closure_capturing_a_local_var(3); -// -// let closure_with_transitive_captures = |y: Field| -> Field { -// let d = 5; -// let nested_closure = |z: Field| -> Field { -// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; -// a + z + y + d + x + doubly_nested_closure(4) + x + y -// }; -// let res = nested_closure(5); -// res -// }; -// -// a + b + c + closure_with_transitive_captures(6) -// } -// "#; -// assert_no_errors(src); -// -// let expected_captures = vec![ -// vec![], -// vec!["x".to_string()], -// vec!["b".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], -// vec!["x".to_string(), "b".to_string()], -// ]; -// -// let parsed_captures = get_program_captures(src); -// -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_fmt_strings() { -// let src = r#" -// fn main() { -// let string = f"this is i: {i}"; -// println(string); -// -// println(f"I want to print {0}"); -// -// let new_val = 10; -// println(f"random_string{new_val}{new_val}"); -// } -// fn println(x : T) -> T { -// x -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { -// name, .. -// }) => { -// assert_eq!(name, "i"); -// } -// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { -// name, -// .. -// }) => { -// assert_eq!(name, "0"); -// } -// CompilationError::TypeError(TypeCheckError::UnusedResultError { -// expr_type: _, -// expr_span, -// }) => { -// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); -// assert!( -// a == "println(string)" -// || a == "println(f\"I want to print {0}\")" -// || a == "println(f\"random_string{new_val}{new_val}\")" -// ); -// } -// _ => unimplemented!(), -// }; -// } -// } - -fn check_rewrite(src: &str, expected: &str) { - let (_program, mut context, _errors) = get_program(src); - let main_func_id = context.def_interner.find_function("main").unwrap(); - let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); - assert!(format!("{}", program) == expected); -} - -// TODO: re-enable! -// #[test] -// fn simple_closure_with_no_captured_variables() { -// let src = r#" -// fn main() -> pub Field { -// let x = 1; -// let closure = || x; -// closure() -// } -// "#; -// -// let expected_rewrite = r#"fn main$f0() -> Field { -// let x$0 = 1; -// let closure$3 = { -// let closure_variable$2 = { -// let env$1 = (x$l0); -// (env$l1, lambda$f1) -// }; -// closure_variable$l2 -// }; -// { -// let tmp$4 = closure$l3; -// tmp$l4.1(tmp$l4.0) -// } -// } -// fn lambda$f1(mut env$l1: (Field)) -> Field { -// env$l1.0 -// } -// "#; -// check_rewrite(src, expected_rewrite); -// } -// -// #[test] -// fn deny_cyclic_globals() { -// let src = r#" -// global A = B; -// global B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_cyclic_type_aliases() { -// let src = r#" -// type A = B; -// type B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn ensure_nested_type_aliases_type_check() { -// let src = r#" -// type A = B; -// type B = u8; -// fn main() { -// let _a: A = 0 as u16; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn type_aliases_in_entry_point() { -// let src = r#" -// type Foo = u8; -// fn main(_x: Foo) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn operators_in_global_used_in_type() { -// let src = r#" -// global ONE: u32 = 1; -// global COUNT: u32 = ONE + 2; -// fn main() { -// let _array: [Field; COUNT] = [1, 2, 3]; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn break_and_continue_in_constrained_fn() { -// let src = r#" -// fn main() { -// for i in 0 .. 10 { -// if i == 2 { -// continue; -// } -// if i == 5 { -// break; -// } -// } -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } -// -// #[test] -// fn break_and_continue_outside_loop() { -// let src = r#" -// unconstrained fn main() { -// continue; -// break; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } - -// TODO: failing -// Regression for #2540 -#[test] -fn for_loop_over_array() { - let src = r#" - fn hello(_array: [u1; N]) { - for _ in 0..N {} - } - - fn main() { - let array: [u1; 2] = [0, 1]; - hello(array); - } - "#; - let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 0); -} - -// TODO: re-enable! -// // Regression for #4545 -// #[test] -// fn type_aliases_in_main() { -// let src = r#" -// type Outer = [u8; N]; -// fn main(_arg: Outer<1>) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn ban_mutable_globals() { -// // Mutable globals are only allowed in a comptime context -// let src = r#" -// mut global FOO: Field = 0; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_inline_attribute_on_unconstrained() { -// let src = r#" -// #[no_predicates] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn deny_fold_attribute_on_unconstrained() { -// let src = r#" -// #[fold] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn specify_function_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn specify_method_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn incorrect_turbofish_count_function_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn incorrect_turbofish_count_method_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_function() { -// let src = r#" -// struct Foo { -// inner: u64 -// } -// -// pub fn bar() { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_struct() { -// let src = r#" -// pub struct Foo { -// inner: u64 -// } -// -// pub struct Bar { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } -// ), -// )); -// } -// -// #[test] -// fn bool_numeric_generic() { -// let src = r#" -// pub fn read() -> Field { -// if N { -// 0 -// } else { -// 1 -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_binary_operation_type_mismatch() { -// let src = r#" -// pub fn foo() -> bool { -// let mut check: bool = true; -// check = N; -// check -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), -// )); -// } -// -// #[test] -// fn bool_generic_as_loop_bound() { -// let src = r#" -// pub fn read() { // error here -// let mut fields = [0; N]; // error here -// for i in 0..N { // error here -// fields[i] = i + 1; -// } -// assert(fields[0] == 1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, expr_typ, .. -// }) = &errors[2].0 -// else { -// panic!("Got an error other than a type mismatch"); -// }; -// -// assert_eq!(expected_typ, "Field"); -// assert_eq!(expr_typ, "bool"); -// } -// -// #[test] -// fn numeric_generic_in_function_signature() { -// let src = r#" -// pub fn foo(arr: [Field; N]) -> [Field; N] { arr } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_as_struct_field_type_fails() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: N, -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_as_array_length() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: [Field; N], -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_as_param_type() { -// let src = r#" -// pub fn foo(x: I) -> I { -// let _q: I = 5; -// x -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// -// // Error from the parameter type -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the let statement annotated type -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the return type -// assert!(matches!( -// errors[2].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_fails() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: Bar, -// } -// struct Bar { -// inner: N -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_used_in_nested_array_length_fail() { -// let src = r#" -// pub struct Foo { -// a: Field, -// b: Bar, -// } -// pub struct Bar { -// inner: [Field; N] -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_pass() { -// // The order of these structs should not be changed to make sure -// // that we are accurately resolving all struct generics before struct fields -// let src = r#" -// pub struct NestedNumeric { -// a: Field, -// b: InnerNumeric -// } -// pub struct InnerNumeric { -// inner: [u64; N], -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_trait() { -// // We want to make sure that `N` in `impl Deserialize` does -// // not trigger `expected type, found numeric generic parameter N` as the trait -// // does in fact expect a numeric generic. -// let src = r#" -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// impl Deserialize for MyType { -// fn deserialize(fields: [Field; N], other: T) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: other } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N], other: T) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_in_trait_impl_with_extra_impl_generics() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. -// // `N` is used first in the trait impl generics (`Deserialize for MyType`). -// // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind -// // while `T` has a normal kind. -// impl Deserialize for MyType where T: Default { -// fn deserialize(fields: [Field; N]) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_where_clause() { -// let src = r#" -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// -// pub fn read() -> T where T: Deserialize { -// let mut fields: [Field; N] = [0; N]; -// for i in 0..N { -// fields[i] = i as Field + 1; -// } -// T::deserialize(fields) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_turbofish() { -// let src = r#" -// pub fn double() -> u32 { -// // Used as an expression -// N * 2 -// } -// -// pub fn double_numeric_generics_test() { -// // Example usage of a numeric generic arguments. -// assert(double::<9>() == 18); -// assert(double::<7 + 8>() == 30); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn constant_used_with_numeric_generic() { -// let src = r#" -// struct ValueNote { -// value: Field, -// } -// -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// impl Serialize<1> for ValueNote { -// fn serialize(self) -> [Field; 1] { -// [self.value] -// } -// } -// "#; -// assert_no_errors(src); -// } - -// TODO: failing -#[test] -fn normal_generic_used_when_numeric_expected_in_where_clause() { - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - T::deserialize([0, 1]) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); -} - -// TODO: re-enable! -// #[test] -// fn numeric_generics_type_kind_mismatch() { -// let src = r#" -// fn foo() -> u16 { -// N as u16 -// } -// -// global J: u16 = 10; -// -// fn bar() -> u16 { -// foo::() -// } -// -// global M: u16 = 3; -// -// fn main() { -// let _ = bar::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generics_value_kind_mismatch_u32_u64() { -// let src = r#" -// struct BoundedVec { -// storage: [T; MaxLen], -// // can't be compared to MaxLen: u32 -// // can't be used to index self.storage -// len: u64, -// } -// -// impl BoundedVec { -// pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { -// // We do this to avoid an unused variable warning on `self` -// let _ = self.len; -// for _ in 0..Len { } -// } -// -// pub fn push(&mut self, elem: T) { -// assert(self.len < MaxLen, "push out of bounds"); -// self.storage[self.len] = elem; -// self.len += 1; -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IntegerBitWidth { -// bit_width_x: IntegerBitSize::SixtyFour, -// bit_width_y: IntegerBitSize::ThirtyTwo, -// .. -// }), -// )); -// } -// -// #[test] -// fn quote_code_fragments() { -// // This test ensures we can quote (and unquote/splice) code fragments -// // which by themselves are not valid code. They only need to be valid -// // by the time they are unquoted into the macro's call site. -// let src = r#" -// fn main() { -// comptime { -// concat!(quote { assert( }, quote { false); }); -// } -// } -// -// comptime fn concat(a: Quoted, b: Quoted) -> Quoted { -// quote { $a $b } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use InterpreterError::FailingConstraint; -// assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); -// } - -// TODO: failing -#[test] -fn impl_stricter_than_trait_no_trait_method_constraints() { - // This test ensures that the error we get from the where clause on the trait impl method - // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. - let src = r#" - trait Serialize { - // We want to make sure we trigger the error when override a trait method - // which itself has no trait constraints. - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } - - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() - } - - struct MyType { - a: T, - b: T, - } - - impl Serialize<2> for MyType { - fn serialize(self) -> [Field; 2] where T: ToField { - [ self.a.to_field(), self.b.to_field() ] - } - } - - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } - } - "#; - - let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); -} - -// TODO: re-enable! -// #[test] -// fn impl_stricter_than_trait_different_generics() { -// let src = r#" -// trait Default { } -// -// // Object type of the trait constraint differs -// trait Foo { -// fn foo_good() where T: Default; -// -// fn foo_bad() where T: Default; -// } -// -// impl Foo for () { -// fn foo_good() where A: Default {} -// -// fn foo_bad() where B: Default {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_object_generics() { -// let src = r#" -// trait MyTrait { } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// struct OtherOption { -// inner: Option, -// } -// -// trait Bar { -// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn array_good() where [T; 8]: MyTrait; -// -// fn array_bad() where [T; 8]: MyTrait; -// -// fn tuple_good() where (Option, Option): MyTrait; -// -// fn tuple_bad() where (Option, Option): MyTrait; -// } -// -// impl Bar for () { -// fn bar_good() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn bar_bad() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn array_good() where [A; 8]: MyTrait { } -// -// fn array_bad() where [B; 8]: MyTrait { } -// -// fn tuple_good() where (Option, Option): MyTrait { } -// -// fn tuple_bad() where (Option, Option): MyTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[1].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[2].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait() { -// let src = r#" -// trait Default { } -// -// trait OtherDefault { } -// -// struct Option { -// inner: T -// } -// -// trait Bar { -// fn bar() where Option: Default; -// } -// -// impl Bar for () { -// // Trait constraint differs due to the trait even though the constraint -// // types are the same. -// fn bar() where Option: OtherDefault {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "OtherDefault")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn trait_impl_where_clause_stricter_pass() { -// let src = r#" -// trait MyTrait { -// fn good_foo() where H: OtherTrait; -// -// fn bad_foo() where H: OtherTrait; -// } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// impl MyTrait for [T] where Option: MyTrait { -// fn good_foo() where B: OtherTrait { } -// -// fn bad_foo() where A: OtherTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "OtherTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait_generics() { -// let src = r#" -// trait Foo { -// fn foo() where T: T2; -// } -// -// impl Foo for () { -// // Should be A: T2 -// fn foo() where A: T2 {} -// } -// -// trait T2 {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// constraint_generics, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "T2")); -// assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_not_found_for_inner_impl() { -// // We want to guarantee that we get a no impl found error -// let src = r#" -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// trait ToField { -// fn to_field(self) -> Field; -// } -// -// fn process_array(array: [Field; N]) -> Field { -// array[0] -// } -// -// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { -// thing.serialize() -// } -// -// struct MyType { -// a: T, -// b: T, -// } -// -// impl Serialize<2> for MyType where T: ToField { -// fn serialize(self) -> [Field; 2] { -// [ self.a.to_field(), self.b.to_field() ] -// } -// } -// -// impl MyType { -// fn do_thing_with_serialization_with_extra_steps(self) -> Field { -// process_array(serialize_thing(self)) -// } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) -// )); -// } -// -// // Regression for #5388 -// #[test] -// fn comptime_let() { -// let src = r#"fn main() { -// comptime let my_var = 2; -// assert_eq(my_var, 2); -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn overflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = 256; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁸` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = -1; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-1` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn overflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = 128; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = -129; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-129` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } +#[test] +fn check_trait_implemented_for_all_t() { + let src = " + trait Default { + fn default() -> Self; + } + + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait IsDefault { + fn is_default(self) -> bool; + } + + impl IsDefault for T where T: Default + Eq { + fn is_default(self) -> bool { + self.eq(T::default()) + } + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Default for u64 { + fn default() -> Self { + 0 + } + } + + impl Default for Foo { + fn default() -> Self { + Foo { a: Default::default() } + } + } + + fn main(a: Foo) -> pub bool { + a.is_default() + }"; + assert_no_errors(src); +} -// TODO: failing #[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls - let src = r#" - fn foo() -> [u8; N] { - [0; N] +fn check_trait_implementation_duplicate_method() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + y + 2 * x + } + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + x + 2 * y + } + } + + fn main() {}"; - fn bar() -> [u8; N] { - foo::() + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); + assert_eq!(first_def, "default"); + assert_eq!(second_def, "default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; } +} - global M: u32 = 3; +#[test] +fn check_trait_wrong_method_return_type() { + let src = " + trait Default { + fn default() -> Self; + } + + struct Foo { + } + + impl Default for Foo { + fn default() -> Field { + 0 + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} +#[test] +fn check_trait_wrong_method_return_type2() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, _y: Field) -> Field { + x + } + } + fn main() { - let _ = bar::(); + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; } - "#; - assert_no_errors(src); +} - // Check for turbofish numeric generics used with method calls - let src = r#" - struct Foo { - a: T +#[test] +fn check_trait_missing_implementation() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field; + + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() { } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { + trait_name, + method_name, + trait_impl_span: _, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(method_name, "method2"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} - impl Foo { - fn static_method() -> [u8; N] { - [0; N] +#[test] +fn check_trait_not_in_scope() { + let src = " + struct Foo { + bar: Field, + array: [Field; 2], + } + + // Default trait does not exist + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } } + } + + fn main() { + } + + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { + trait_path, + }) => { + assert_eq!(trait_path.as_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} - fn impl_method(self) -> [T; N] { - [self.a; N] +#[test] +fn check_trait_wrong_method_name() { + let src = " + trait Default { + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + // wrong trait name method should not compile + impl Default for Foo { + fn does_not_exist(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } } } + + fn main() { + }"; + let compilation_errors = get_program_errors(src); + assert!(!has_parser_error(&compilation_errors)); + assert!( + compilation_errors.len() == 1, + "Expected 1 compilation error, got: {:?}", + compilation_errors + ); + + for (err, _file_id) in compilation_errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { + trait_name, + impl_method, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(impl_method, "does_not_exist"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() +#[test] +fn check_trait_wrong_parameter() { + let src = " + trait Default { + fn default(x: Field) -> Self; + } + + struct Foo { + bar: u32, + } + + impl Default for Foo { + fn default(x: u32) -> Self { + Foo {bar: x} + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "u32"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; } +} - global M: u32 = 3; +#[test] +fn check_trait_wrong_parameter2() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field, y: Foo) -> Self { + Self { bar: x, array: [x, y.bar] } + } + } + + fn main() { + }"; + + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "Foo"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter_type() { + let src = " + pub trait Default { + fn default(x: Field, y: NotAType) -> Field; + } + + fn main(x: Field, y: Field) { + assert(y == x); + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + + // This is a duplicate error in the name resolver & type checker. + // In the elaborator there is no duplicate and only 1 error is issued + assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Unresolved(ident), + )) => { + assert_eq!(ident, "NotAType"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} +#[test] +fn check_trait_wrong_parameters_count() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field) -> Self { + Self { bar: x, array: [x, x] } + } + } + fn main() { - let _ = bar::(); } - "#; - assert_no_errors(src); + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { + actual_num_parameters, + expected_num_parameters, + trait_name, + method_name, + .. + }) => { + assert_eq!(actual_num_parameters, &1_usize); + assert_eq!(expected_num_parameters, &2_usize); + assert_eq!(method_name, "default"); + assert_eq!(trait_name, "Default"); + } + _ => { + panic!("No other errors are expected in this test case! Found = {:?}", err); + } + }; + } } -// TODO: re-enable! -// #[test] -// fn use_super() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// use super::some_func; -// -// pub fn bar() { -// some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn use_super_in_path() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// pub fn func() { -// super::some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn no_super() { -// let src = "use super::some_func;"; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( -// PathResolutionError::NoSuper(span), -// )) = &errors[0].0 -// else { -// panic!("Expected a 'no super' error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(span.start(), 4); -// assert_eq!(span.end(), 9); -// } -// -// #[test] -// fn cannot_call_unconstrained_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// foo(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// let func = foo; -// // Warning should trigger here -// func(); -// inner(func); -// } -// -// fn inner(x: unconstrained fn() -> ()) { -// // Warning should trigger here -// x(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); -// -// for error in &errors { -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// } +#[test] +fn check_trait_impl_for_non_type() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; + } + + impl Default for main { + fn default(x: Field, y: Field) -> Field { + x + y + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { + assert_eq!(expected, "type"); + assert_eq!(got, "function"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_impl_struct_not_trait() { + let src = " + struct Foo { + bar: Field, + array: [Field; 2], + } + + struct Default { + x: Field, + z: Field, + } + + // Default is a struct not a trait + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { + not_a_trait_name, + }) => { + assert_eq!(not_a_trait_name.to_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} -// TODO: failing #[test] -fn missing_unsafe_block_when_needing_type_annotations() { - // This test is a regression check that even when an unsafe block is missing - // that we still appropriately continue type checking and infer type annotations. - let src = r#" +fn check_trait_duplicate_declaration() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + + trait Default { + fn default(x: Field) -> Self; + } + fn main() { - let z = BigNum { limbs: [2, 0, 0] }; - assert(z.__is_zero() == false); + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::Trait); + assert_eq!(first_def, "Default"); + assert_eq!(second_def, "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; } +} - struct BigNum { - limbs: [u64; N], +#[test] +fn check_trait_duplicate_implementation() { + let src = " + trait Default { + } + struct Foo { + bar: Field, + } + + impl Default for Foo { } + impl Default for Foo { + } + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} - impl BigNum { - unconstrained fn __is_zero_impl(self) -> bool { - let mut result: bool = true; - for i in 0..N { - result = result & (self.limbs[i] == 0); +#[test] +fn check_trait_duplicate_implementation_with_alias() { + let src = " + trait Default { + } + + struct MyStruct { + } + + type MyType = MyStruct; + + impl Default for MyStruct { + } + + impl Default for MyType { + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); } - result - } + }; } +} - trait BigNumTrait { - fn __is_zero(self) -> bool; +#[test] +fn test_impl_self_within_default_def() { + let src = " + trait Bar { + fn ok(self) -> Self; + + fn ref_ok(self) -> Self { + self.ok() + } } - impl BigNumTrait for BigNum { - fn __is_zero(self) -> bool { - self.__is_zero_impl() + impl Bar for (T, T) where T: Bar { + fn ok(self) -> Self { + self } + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_fn_parameter() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + fn test_eq(x: impl Eq) -> bool { + x.eq(x) + } + + fn main(a: Foo) -> pub bool { + test_eq(a) + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_two_fn_parameters() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait Test { + fn test(self) -> bool; + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Test for u64 { + fn test(self) -> bool { self == self } + } + + fn test_eq(x: impl Eq, y: impl Test) -> bool { + x.eq(x) == y.test() + } + + fn main(a: Foo, b: u64) -> pub bool { + test_eq(a, b) + }"; + assert_no_errors(src); +} + +fn get_program_captures(src: &str) -> Vec> { + let (program, context, _errors) = get_program(src); + let interner = context.def_interner; + let mut all_captures: Vec> = Vec::new(); + for func in program.into_sorted().functions { + let func_id = interner.find_function(func.item.name()).unwrap(); + let hir_func = interner.function(&func_id); + // Iterate over function statements and apply filtering function + find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); + } + all_captures +} + +fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { + for stmt_id in stmts.iter() { + let hir_stmt = interner.statement(stmt_id); + let expr_id = match hir_stmt { + HirStatement::Expression(expr_id) => expr_id, + HirStatement::Let(let_stmt) => let_stmt.expression, + HirStatement::Assign(assign_stmt) => assign_stmt.expression, + HirStatement::Constrain(constr_stmt) => constr_stmt.0, + HirStatement::Semi(semi_expr) => semi_expr, + HirStatement::For(for_loop) => for_loop.block, + HirStatement::Error => panic!("Invalid HirStatement!"), + HirStatement::Break => panic!("Unexpected break"), + HirStatement::Continue => panic!("Unexpected continue"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), + }; + let expr = interner.expression(&expr_id); + + get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter + } +} + +fn get_lambda_captures( + expr: HirExpression, + interner: &NodeInterner, + result: &mut Vec>, +) { + if let HirExpression::Lambda(lambda_expr) = expr { + let mut cur_capture = Vec::new(); + + for capture in lambda_expr.captures.iter() { + cur_capture.push(interner.definition(capture.ident.id).name.clone()); + } + result.push(cur_capture); + + // Check for other captures recursively within the lambda body + let hir_body_expr = interner.expression(&lambda_expr.body); + if let HirExpression::Block(block_expr) = hir_body_expr { + find_lambda_captures(block_expr.statements(), interner, result); + } + } } -// TODO: re-enable! -// #[test] -// fn cannot_pass_unconstrained_function_to_regular_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) { -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_assign_unconstrained_and_regular_fn_to_variable() { -// let src = r#" -// fn main() { -// let _func = if true { foo } else { bar }; -// } -// -// fn foo() {} -// unconstrained fn bar() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { -// panic!("Expected a context error, got {:?}", errors[0].0); -// }; -// -// if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { -// assert_eq!(expected_typ, "fn() -> ()"); -// assert_eq!(expr_typ, "unconstrained fn() -> ()"); -// } else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_pass_regular_function_to_unconstrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_unconstrained(func); -// } -// -// fn foo() {} -// -// fn expect_unconstrained(_func: unconstrained fn() -> ()) {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn cannot_pass_unconstrained_function_to_constrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { -// let src = r#" -// fn main() { -// let _func: unconstrained fn() -> () = foo; -// } -// -// fn foo() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { -// let src = r#" -// fn main() { -// let _ = Foo { func: foo }; -// } -// -// fn foo() {} -// -// struct Foo { -// func: unconstrained fn() -> (), -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn trait_impl_generics_count_mismatch() { -// let src = r#" -// trait Foo {} -// -// impl Foo<()> for Field {} -// -// fn main() {}"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "Foo"); -// assert_eq!(*expected, 0); -// assert_eq!(*found, 1); -// } -// -// #[test] -// fn bit_not_on_untyped_integer() { -// let src = r#" -// fn main() { -// let _: u32 = 3 & !1; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn duplicate_struct_field() { -// let src = r#" -// pub struct Foo { -// x: i32, -// x: i32, -// } -// -// fn main() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { -// first_def, -// second_def, -// }) = &errors[0].0 -// else { -// panic!("Expected a duplicate field error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(first_def.to_string(), "x"); -// assert_eq!(second_def.to_string(), "x"); -// -// assert_eq!(first_def.span().start(), 30); -// assert_eq!(second_def.span().start(), 46); -// } -// -// #[test] -// fn trait_constraint_on_tuple_type() { -// let src = r#" -// trait Foo { -// fn foo(self, x: A) -> bool; -// } -// -// pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { -// x.foo(y) -// } -// -// fn main() {}"#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_constructor_generics_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let _ = Foo:: { x: 1 }; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// 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#" -// struct Foo { -// x: T -// } -// -// impl Foo { -// fn new(x: T) -> Self { -// Foo { x } -// } -// } -// -// fn main() { -// let _ = Foo::::new(1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), -// )); -// } -// -// #[test] -// fn turbofish_in_struct_pattern() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_struct_pattern_errors_if_type_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 -// else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn turbofish_in_struct_pattern_generic_count_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "struct Foo"); -// assert_eq!(*expected, 1); -// assert_eq!(*found, 2); -// } -// -// #[test] -// fn incorrect_generic_count_on_struct_impl() { -// let src = r#" -// struct Foo {} -// impl Foo {} -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// -// #[test] -// fn incorrect_generic_count_on_type_alias() { -// let src = r#" -// struct Foo {} -// type Bar = Foo; -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// // #[test] -// fn uses_self_type_for_struct_function_call() { -// let src = r#" -// struct S { } -// -// impl S { -// fn one() -> Field { -// 1 -// } +// fn resolve_empty_function() { +// let src = " +// fn main() { // -// fn two() -> Field { -// Self::one() + Self::one() // } -// } -// -// fn main() {} -// "#; +// "; // assert_no_errors(src); // } -// // #[test] -// fn uses_self_type_inside_trait() { +// fn resolve_basic_function() { // let src = r#" -// trait Foo { -// fn foo() -> Self { -// Self::bar() -// } -// -// fn bar() -> Self; -// } -// -// impl Foo for Field { -// fn bar() -> Self { -// 1 +// fn main(x : Field) { +// let y = x + x; +// assert(y == x); // } -// } -// -// fn main() { -// let _: Field = Foo::foo(); -// } // "#; // assert_no_errors(src); // } -// // #[test] -// fn uses_self_type_in_trait_where_clause() { +// fn resolve_unused_var() { // let src = r#" -// pub trait Trait { -// fn trait_func() -> bool; -// } -// -// pub trait Foo where Self: Trait { -// fn foo(self) -> bool { -// self.trait_func() +// fn main(x : Field) { +// let y = x + x; +// assert(x == x); // } -// } -// -// struct Bar { -// -// } -// -// impl Foo for Bar { -// -// } -// -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected an unresolved method call error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(method_name, "trait_func"); -// } -// -// #[test] -// fn do_not_eagerly_error_on_cast_on_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x: u8 = 1; -// let _: Field = foo(x, |x| x as Field); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn error_on_cast_over_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x = "a"; -// let _: Field = foo(x, |x| x as Field); -// } // "#; // // let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) -// )); -// } -// -// #[test] -// fn trait_impl_for_a_type_that_implements_another_trait() { -// let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// self -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unused variable +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { +// assert_eq!(&ident.0.contents, "y"); // } +// _ => unreachable!("we should only have an unused var error"), // } -// -// pub fn use_it(t: T) -> i32 where T: Two { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); // } // // #[test] -// fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { +// fn resolve_unresolved_var() { // let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// let _ = self; -// 1 -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 -// } -// } -// -// impl Two for u32 { -// fn two(self) -> i32 { -// let _ = self; -// 0 +// fn main(x : Field) { +// let y = x + x; +// assert(y == z); // } -// } -// -// pub fn use_it(t: u32) -> i32 { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn impl_missing_associated_type() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// impl Foo for () {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) -// )); -// } -// -// #[test] -// fn as_trait_path_syntax_resolves_outside_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// // AsTraitPath syntax is a bit silly when associated types -// // are explicitly specified -// let _: i64 = 1 as >::Assoc; -// } // "#; -// // let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// use TypeCheckError::TypeMismatch; -// let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { -// panic!("Expected TypeMismatch error, found {:?}", errors[0].0); -// }; -// -// assert_eq!(expected_typ, "i64".to_string()); -// assert_eq!(expr_typ, "i32".to_string()); -// } -// -// #[test] -// fn as_trait_path_syntax_no_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// let _: i64 = 1 as >::Assoc; +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { +// assert_eq!(name, "z"); +// } +// _ => unimplemented!("we should only have an unresolved variable"), // } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); // } // // #[test] -// fn errors_on_unused_private_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// pub trait Foo { +// fn unresolved_path() { +// let src = " +// fn main(x : Field) { +// let _z = some::path::to::a::func(x); // } -// } -// -// use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// +// "; // let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); -// } -// -// #[test] -// fn errors_on_unused_pub_crate_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// pub trait Foo { +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "some"); +// } +// _ => unimplemented!("we should only have an unresolved function"), +// }; +// } +// _ => unimplemented!(), // } // } -// -// pub(crate) use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); // } // // #[test] -// fn warns_on_use_of_private_exported_item() { +// fn resolve_literal_expr() { // let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} +// fn main(x : Field) { +// let y = 5; +// assert(y == x); // } +// "#; +// assert_no_errors(src); +// } // -// use bar::baz; -// -// pub fn qux() { -// baz(); +// #[test] +// fn multiple_resolution_errors() { +// let src = r#" +// fn main(x : Field) { +// let y = foo::bar(x); +// let z = y + a; // } -// } -// -// fn main() { -// foo::baz(); -// } // "#; // // let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated +// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); // -// assert!(matches!( -// &errors[0].0, -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Private(..), -// )) -// )); +// // Errors are: +// // `a` is undeclared +// // `z` is unused +// // `foo::bar` does not exist +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::UnusedVariable { ident } => { +// assert_eq!(&ident.0.contents, "z"); +// } +// ResolverError::VariableNotDeclared { name, .. } => { +// assert_eq!(name, "a"); +// } +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "foo"); +// } +// _ => unimplemented!(), +// }; +// } +// _ => unimplemented!(), +// } +// } // } // // #[test] -// fn can_use_pub_use_item() { +// fn resolve_prefix_expr() { // let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} +// fn main(x : Field) { +// let _y = -x; // } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } // "#; // assert_no_errors(src); // } // // #[test] -// fn warns_on_re_export_of_item_with_less_visibility() { +// fn resolve_for_expr() { // let src = r#" -// mod foo { -// mod bar { -// pub(crate) fn baz() {} +// fn main(x : u64) { +// for i in 1..20 { +// let _z = x + i; +// }; // } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } // "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } -// ) -// )); +// assert_no_errors(src); // } // // #[test] -// fn unquoted_integer_as_integer_token() { +// fn resolve_call_expr() { // let src = r#" -// trait Serialize { -// fn serialize() {} -// } -// -// #[attr] -// pub fn foobar() {} -// -// comptime fn attr(_f: FunctionDefinition) -> Quoted { -// let serialized_len = 1; -// // We are testing that when we unquote $serialized_len, it's unquoted -// // as the token `1` and not as something else that later won't be parsed correctly -// // in the context of a generic argument. -// quote { -// impl Serialize<$serialized_len> for Field { -// fn serialize() { } -// } +// fn main(x : Field) { +// let _z = foo(x); // } -// } // -// fn main() {} +// fn foo(x : Field) -> Field { +// x +// } // "#; -// // assert_no_errors(src); // } // // #[test] -// fn errors_on_unused_function() { +// fn resolve_shadowing() { // let src = r#" -// contract some_contract { -// // This function is unused, but it's a contract entrypoint -// // so it should not produce a warning -// fn foo() -> pub Field { -// 1 +// fn main(x : Field) { +// let x = foo(x); +// let x = x; +// let (x, x) = (x, x); +// let _ = x; // } -// } // -// -// fn foo() { -// bar(); -// } -// -// fn bar() {} +// fn foo(x : Field) -> Field { +// x +// } // "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "foo"); -// assert_eq!(*item_type, "function"); +// assert_no_errors(src); // } // // #[test] -// fn errors_on_unused_struct() { +// fn resolve_basic_closure() { // let src = r#" -// struct Foo {} -// struct Bar {} -// -// fn main() { -// let _ = Bar {}; -// } +// fn main(x : Field) -> pub Field { +// let closure = |y| y + x; +// closure(x) +// } // "#; +// assert_no_errors(src); +// } // -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); +// #[test] +// fn resolve_simplified_closure() { +// // based on bug https://github.com/noir-lang/noir/issues/1088 +// +// let src = r#"fn do_closure(x: Field) -> Field { +// let y = x; +// let ret_capture = || { +// y +// }; +// ret_capture() +// } // -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; +// fn main(x: Field) { +// assert(do_closure(x) == 100); +// } // -// assert_eq!(ident.to_string(), "Foo"); -// assert_eq!(*item_type, "struct"); +// "#; +// let parsed_captures = get_program_captures(src); +// let expected_captures = vec![vec!["y".to_string()]]; +// assert_eq!(expected_captures, parsed_captures); // } // // #[test] -// fn errors_on_unused_trait() { +// fn resolve_complex_closures() { // let src = r#" -// trait Foo {} -// trait Bar {} +// fn main(x: Field) -> pub Field { +// let closure_without_captures = |x: Field| -> Field { x + x }; +// let a = closure_without_captures(1); // -// pub struct Baz { -// } +// let closure_capturing_a_param = |y: Field| -> Field { y + x }; +// let b = closure_capturing_a_param(2); +// +// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; +// let c = closure_capturing_a_local_var(3); // -// impl Bar for Baz {} +// let closure_with_transitive_captures = |y: Field| -> Field { +// let d = 5; +// let nested_closure = |z: Field| -> Field { +// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; +// a + z + y + d + x + doubly_nested_closure(4) + x + y +// }; +// let res = nested_closure(5); +// res +// }; // -// fn main() { -// } +// a + b + c + closure_with_transitive_captures(6) +// } // "#; +// assert_no_errors(src); // -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); +// let expected_captures = vec![ +// vec![], +// vec!["x".to_string()], +// vec!["b".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], +// vec!["x".to_string(), "b".to_string()], +// ]; // -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; +// let parsed_captures = get_program_captures(src); // -// assert_eq!(ident.to_string(), "Foo"); -// assert_eq!(*item_type, "trait"); +// assert_eq!(expected_captures, parsed_captures); // } // // #[test] -// fn constrained_reference_to_unconstrained() { +// fn resolve_fmt_strings() { // let src = r#" -// fn main(mut x: u32, y: pub u32) { -// let x_ref = &mut x; -// if x == 5 { -// unsafe { -// mut_ref_input(x_ref, y); -// } -// } +// fn main() { +// let string = f"this is i: {i}"; +// println(string); // -// assert(x == 10); -// } +// println(f"I want to print {0}"); // -// unconstrained fn mut_ref_input(x: &mut u32, y: u32) { -// *x = y; -// } +// let new_val = 10; +// println(f"random_string{new_val}{new_val}"); +// } +// fn println(x : T) -> T { +// x +// } // "#; // // let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = -// &errors[0].0 -// else { -// panic!("Expected an error about passing a constrained reference to unconstrained"); -// }; -// } +// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); // -// #[test] -// fn comptime_type_in_runtime_code() { -// let source = "pub fn foo(_f: FunctionDefinition) {}"; -// let errors = get_program_errors(source); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) -// )); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { +// name, .. +// }) => { +// assert_eq!(name, "i"); +// } +// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { +// name, +// .. +// }) => { +// assert_eq!(name, "0"); +// } +// CompilationError::TypeError(TypeCheckError::UnusedResultError { +// expr_type: _, +// expr_span, +// }) => { +// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); +// assert!( +// a == "println(string)" +// || a == "println(f\"I want to print {0}\")" +// || a == "println(f\"random_string{new_val}{new_val}\")" +// ); +// } +// _ => unimplemented!(), +// }; +// } // } -// TODO: failing +fn check_rewrite(src: &str, expected: &str) { + let (_program, mut context, _errors) = get_program(src); + let main_func_id = context.def_interner.find_function("main").unwrap(); + let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); + assert!(format!("{}", program) == expected); +} + +#[test] +fn simple_closure_with_no_captured_variables() { + let src = r#" + fn main() -> pub Field { + let x = 1; + let closure = || x; + closure() + } + "#; + + let expected_rewrite = r#"fn main$f0() -> Field { + let x$0 = 1; + let closure$3 = { + let closure_variable$2 = { + let env$1 = (x$l0); + (env$l1, lambda$f1) + }; + closure_variable$l2 + }; + { + let tmp$4 = closure$l3; + tmp$l4.1(tmp$l4.0) + } +} +fn lambda$f1(mut env$l1: (Field)) -> Field { + env$l1.0 +} +"#; + check_rewrite(src, expected_rewrite); +} + +#[test] +fn deny_cyclic_globals() { + let src = r#" + global A = B; + global B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_cyclic_type_aliases() { + let src = r#" + type A = B; + type B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn ensure_nested_type_aliases_type_check() { + let src = r#" + type A = B; + type B = u8; + fn main() { + let _a: A = 0 as u16; + } + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn type_aliases_in_entry_point() { + let src = r#" + type Foo = u8; + fn main(_x: Foo) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn operators_in_global_used_in_type() { + let src = r#" + global ONE: u32 = 1; + global COUNT: u32 = ONE + 2; + fn main() { + let _array: [Field; COUNT] = [1, 2, 3]; + } + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn break_and_continue_in_constrained_fn() { + let src = r#" + fn main() { + for i in 0 .. 10 { + if i == 2 { + continue; + } + if i == 5 { + break; + } + } + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} + +#[test] +fn break_and_continue_outside_loop() { + let src = r#" + unconstrained fn main() { + continue; + break; + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} + +// Regression for #2540 +#[test] +fn for_loop_over_array() { + let src = r#" + fn hello(_array: [u1; N]) { + for _ in 0..N {} + } + + fn main() { + let array: [u1; 2] = [0, 1]; + hello(array); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +// Regression for #4545 +#[test] +fn type_aliases_in_main() { + let src = r#" + type Outer = [u8; N]; + fn main(_arg: Outer<1>) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn ban_mutable_globals() { + // Mutable globals are only allowed in a comptime context + let src = r#" + mut global FOO: Field = 0; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_inline_attribute_on_unconstrained() { + let src = r#" + #[no_predicates] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn deny_fold_attribute_on_unconstrained() { + let src = r#" + #[fold] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn specify_function_types_with_turbofish() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn specify_method_types_with_turbofish() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn incorrect_turbofish_count_function_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn incorrect_turbofish_count_method_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +// TODO failing +#[test] +fn struct_numeric_generic_in_function() { + let src = r#" + struct Foo { + inner: u64 + } + + pub fn bar() { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +// TODO failing +#[test] +fn struct_numeric_generic_in_struct() { + let src = r#" + pub struct Foo { + inner: u64 + } + + pub struct Bar { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::UnsupportedNumericGenericType { .. } + ), + )); +} + +#[test] +fn bool_numeric_generic() { + let src = r#" + pub fn read() -> Field { + if N { + 0 + } else { + 1 + } + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn numeric_generic_binary_operation_type_mismatch() { + let src = r#" + pub fn foo() -> bool { + let mut check: bool = true; + check = N; + check + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), + )); +} + +// TODO failing +#[test] +fn bool_generic_as_loop_bound() { + let src = r#" + pub fn read() { // error here + let mut fields = [0; N]; // error here + for i in 0..N { // error here + fields[i] = i + 1; + } + assert(fields[0] == 1); + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); + + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[2].0 + else { + panic!("Got an error other than a type mismatch"); + }; + + assert_eq!(expected_typ, "Field"); + assert_eq!(expr_typ, "bool"); +} + +#[test] +fn numeric_generic_in_function_signature() { + let src = r#" + pub fn foo(arr: [Field; N]) -> [Field; N] { arr } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_as_struct_field_type_fails() { + let src = r#" + pub struct Foo { + a: Field, + b: N, + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_as_array_length() { + let src = r#" + pub struct Foo { + a: Field, + b: [Field; N], + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_as_param_type() { + let src = r#" + pub fn foo(x: I) -> I { + let _q: I = 5; + x + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + + // Error from the parameter type + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the let statement annotated type + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the return type + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_fails() { + let src = r#" + pub struct Foo { + a: Field, + b: Bar, + } + struct Bar { + inner: N + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_used_in_nested_array_length_fail() { + let src = r#" + pub struct Foo { + a: Field, + b: Bar, + } + pub struct Bar { + inner: [Field; N] + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_pass() { + // The order of these structs should not be changed to make sure + // that we are accurately resolving all struct generics before struct fields + let src = r#" + pub struct NestedNumeric { + a: Field, + b: InnerNumeric + } + pub struct InnerNumeric { + inner: [u64; N], + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_trait() { + // We want to make sure that `N` in `impl Deserialize` does + // not trigger `expected type, found numeric generic parameter N` as the trait + // does in fact expect a numeric generic. + let src = r#" + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + impl Deserialize for MyType { + fn deserialize(fields: [Field; N], other: T) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: other } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N], other: T) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_in_trait_impl_with_extra_impl_generics() { + let src = r#" + trait Default { + fn default() -> Self; + } + + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. + // `N` is used first in the trait impl generics (`Deserialize for MyType`). + // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind + // while `T` has a normal kind. + impl Deserialize for MyType where T: Default { + fn deserialize(fields: [Field; N]) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_turbofish() { + let src = r#" + pub fn double() -> u32 { + // Used as an expression + N * 2 + } + + pub fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); + } + "#; + assert_no_errors(src); +} + +#[test] +fn constant_used_with_numeric_generic() { + let src = r#" + struct ValueNote { + value: Field, + } + + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + impl Serialize<1> for ValueNote { + fn serialize(self) -> [Field; 1] { + [self.value] + } + } + "#; + assert_no_errors(src); +} + +// TODO: failing +#[test] +fn normal_generic_used_when_numeric_expected_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + T::deserialize([0, 1]) + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 4); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // N + assert!(matches!( + errors[3].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + )); +} + +#[test] +fn numeric_generics_type_kind_mismatch() { + let src = r#" + fn foo() -> u16 { + N as u16 + } + + global J: u16 = 10; + + fn bar() -> u16 { + foo::() + } + + global M: u16 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generics_value_kind_mismatch_u32_u64() { + let src = r#" + struct BoundedVec { + storage: [T; MaxLen], + // can't be compared to MaxLen: u32 + // can't be used to index self.storage + len: u64, + } + + impl BoundedVec { + pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { + // We do this to avoid an unused variable warning on `self` + let _ = self.len; + for _ in 0..Len { } + } + + pub fn push(&mut self, elem: T) { + assert(self.len < MaxLen, "push out of bounds"); + self.storage[self.len] = elem; + self.len += 1; + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IntegerBitWidth { + bit_width_x: IntegerBitSize::SixtyFour, + bit_width_y: IntegerBitSize::ThirtyTwo, + .. + }), + )); +} + +#[test] +fn quote_code_fragments() { + // This test ensures we can quote (and unquote/splice) code fragments + // which by themselves are not valid code. They only need to be valid + // by the time they are unquoted into the macro's call site. + let src = r#" + fn main() { + comptime { + concat!(quote { assert( }, quote { false); }); + } + } + + comptime fn concat(a: Quoted, b: Quoted) -> Quoted { + quote { $a $b } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use InterpreterError::FailingConstraint; + assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +} + +// TODO: failing +#[test] +fn impl_stricter_than_trait_no_trait_method_constraints() { + // This test ensures that the error we get from the where clause on the trait impl method + // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. + let src = r#" + trait Serialize { + // We want to make sure we trigger the error when override a trait method + // which itself has no trait constraints. + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType { + fn serialize(self) -> [Field; 2] where T: ToField { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) + )); +} + +#[test] +fn impl_stricter_than_trait_different_generics() { + let src = r#" + trait Default { } + + // Object type of the trait constraint differs + trait Foo { + fn foo_good() where T: Default; + + fn foo_bad() where T: Default; + } + + impl Foo for () { + fn foo_good() where A: Default {} + + fn foo_bad() where B: Default {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +// TODO failing +#[test] +fn impl_stricter_than_trait_different_object_generics() { + let src = r#" + trait MyTrait { } + + trait OtherTrait {} + + struct Option { + inner: T + } + + struct OtherOption { + inner: Option, + } + + trait Bar { + fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; + + fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + + fn array_good() where [T; 8]: MyTrait; + + fn array_bad() where [T; 8]: MyTrait; + + fn tuple_good() where (Option, Option): MyTrait; + + fn tuple_bad() where (Option, Option): MyTrait; + } + + impl Bar for () { + fn bar_good() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn bar_bad() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn array_good() where [A; 8]: MyTrait { } + + fn array_bad() where [B; 8]: MyTrait { } + + fn tuple_good() where (Option, Option): MyTrait { } + + fn tuple_bad() where (Option, Option): MyTrait { } + } + "#; + + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 3); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[1].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[2].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait() { + let src = r#" + trait Default { } + + trait OtherDefault { } + + struct Option { + inner: T + } + + trait Bar { + fn bar() where Option: Default; + } + + impl Bar for () { + // Trait constraint differs due to the trait even though the constraint + // types are the same. + fn bar() where Option: OtherDefault {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "OtherDefault")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn trait_impl_where_clause_stricter_pass() { + let src = r#" + trait MyTrait { + fn good_foo() where H: OtherTrait; + + fn bad_foo() where H: OtherTrait; + } + + trait OtherTrait {} + + struct Option { + inner: T + } + + impl MyTrait for [T] where Option: MyTrait { + fn good_foo() where B: OtherTrait { } + + fn bad_foo() where A: OtherTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "OtherTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait_generics() { + let src = r#" + trait Foo { + fn foo() where T: T2; + } + + impl Foo for () { + // Should be A: T2 + fn foo() where A: T2 {} + } + + trait T2 {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + constraint_generics, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "T2")); + assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_not_found_for_inner_impl() { + // We want to guarantee that we get a no impl found error + let src = r#" + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType where T: ToField { + fn serialize(self) -> [Field; 2] { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) + )); +} + +// Regression for #5388 +#[test] +fn comptime_let() { + let src = r#"fn main() { + comptime let my_var = 2; + assert_eq(my_var, 2); + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn overflowing_u8() { + let src = r#" + fn main() { + let _: u8 = 256; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁸` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_u8() { + let src = r#" + fn main() { + let _: u8 = -1; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-1` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn overflowing_i8() { + let src = r#" + fn main() { + let _: i8 = 128; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_i8() { + let src = r#" + fn main() { + let _: i8 = -129; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-129` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls + let src = r#" + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); + + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } + + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } + + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } + + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super() { + let src = r#" + fn some_func() {} + + mod foo { + use super::some_func; + + pub fn bar() { + some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super_in_path() { + let src = r#" + fn some_func() {} + + mod foo { + pub fn func() { + super::some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn no_super() { + let src = "use super::some_func;"; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( + PathResolutionError::NoSuper(span), + )) = &errors[0].0 + else { + panic!("Expected a 'no super' error, got {:?}", errors[0].0); + }; + + assert_eq!(span.start(), 4); + assert_eq!(span.end(), 9); +} + +#[test] +fn cannot_call_unconstrained_function_outside_of_unsafe() { + let src = r#" + fn main() { + foo(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { + let src = r#" + fn main() { + let func = foo; + // Warning should trigger here + func(); + inner(func); + } + + fn inner(x: unconstrained fn() -> ()) { + // Warning should trigger here + x(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + for error in &errors { + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; + } +} + +#[test] +fn missing_unsafe_block_when_needing_type_annotations() { + // This test is a regression check that even when an unsafe block is missing + // that we still appropriately continue type checking and infer type annotations. + let src = r#" + fn main() { + let z = BigNum { limbs: [2, 0, 0] }; + assert(z.__is_zero() == false); + } + + struct BigNum { + limbs: [u64; N], + } + + impl BigNum { + unconstrained fn __is_zero_impl(self) -> bool { + let mut result: bool = true; + for i in 0..N { + result = result & (self.limbs[i] == 0); + } + result + } + } + + trait BigNumTrait { + fn __is_zero(self) -> bool; + } + + impl BigNumTrait for BigNum { + fn __is_zero(self) -> bool { + self.__is_zero_impl() + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_pass_unconstrained_function_to_regular_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) { + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_assign_unconstrained_and_regular_fn_to_variable() { + let src = r#" + fn main() { + let _func = if true { foo } else { bar }; + } + + fn foo() {} + unconstrained fn bar() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { + panic!("Expected a context error, got {:?}", errors[0].0); + }; + + if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { + assert_eq!(expected_typ, "fn() -> ()"); + assert_eq!(expr_typ, "unconstrained fn() -> ()"); + } else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_pass_regular_function_to_unconstrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_unconstrained(func); + } + + fn foo() {} + + fn expect_unconstrained(_func: unconstrained fn() -> ()) {} + "#; + assert_no_errors(src); +} + +#[test] +fn cannot_pass_unconstrained_function_to_constrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { + let src = r#" + fn main() { + let _func: unconstrained fn() -> () = foo; + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { + let src = r#" + fn main() { + let _ = Foo { func: foo }; + } + + fn foo() {} + + struct Foo { + func: unconstrained fn() -> (), + } + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_generics_count_mismatch() { + let src = r#" + trait Foo {} + + impl Foo<()> for Field {} + + fn main() {}"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "Foo"); + assert_eq!(*expected, 0); + assert_eq!(*found, 1); +} + +#[test] +fn bit_not_on_untyped_integer() { + let src = r#" + fn main() { + let _: u32 = 3 & !1; + } + "#; + assert_no_errors(src); +} + +#[test] +fn duplicate_struct_field() { + let src = r#" + pub struct Foo { + x: i32, + x: i32, + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { + first_def, + second_def, + }) = &errors[0].0 + else { + panic!("Expected a duplicate field error, got {:?}", errors[0].0); + }; + + assert_eq!(first_def.to_string(), "x"); + assert_eq!(second_def.to_string(), "x"); + + assert_eq!(first_def.span().start(), 30); + assert_eq!(second_def.span().start(), 46); +} + +#[test] +fn trait_constraint_on_tuple_type() { + let src = r#" + trait Foo { + fn foo(self, x: A) -> bool; + } + + pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { + x.foo(y) + } + + fn main() {}"#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_constructor_generics_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let _ = Foo:: { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + 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#" + struct Foo { + x: T + } + + impl Foo { + fn new(x: T) -> Self { + Foo { x } + } + } + + fn main() { + let _ = Foo::::new(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + )); +} + +#[test] +fn turbofish_in_struct_pattern() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn turbofish_in_struct_pattern_generic_count_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "struct Foo"); + assert_eq!(*expected, 1); + assert_eq!(*found, 2); +} + +#[test] +fn incorrect_generic_count_on_struct_impl() { + let src = r#" + struct Foo {} + impl Foo {} + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn incorrect_generic_count_on_type_alias() { + let src = r#" + struct Foo {} + type Bar = Foo; + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn uses_self_type_for_struct_function_call() { + let src = r#" + struct S { } + + impl S { + fn one() -> Field { + 1 + } + + fn two() -> Field { + Self::one() + Self::one() + } + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_inside_trait() { + let src = r#" + trait Foo { + fn foo() -> Self { + Self::bar() + } + + fn bar() -> Self; + } + + impl Foo for Field { + fn bar() -> Self { + 1 + } + } + + fn main() { + let _: Field = Foo::foo(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_in_trait_where_clause() { + let src = r#" + pub trait Trait { + fn trait_func() -> bool; + } + + pub trait Foo where Self: Trait { + fn foo(self) -> bool { + self.trait_func() + } + } + + struct Bar { + + } + + impl Foo for Bar { + + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = + &errors[0].0 + else { + panic!("Expected an unresolved method call error, got {:?}", errors[0].0); + }; + + assert_eq!(method_name, "trait_func"); +} + +#[test] +fn do_not_eagerly_error_on_cast_on_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x: u8 = 1; + let _: Field = foo(x, |x| x as Field); + } + "#; + assert_no_errors(src); +} + +#[test] +fn error_on_cast_over_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x = "a"; + let _: Field = foo(x, |x| x as Field); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + self + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + pub fn use_it(t: T) -> i32 where T: Two { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + let _ = self; + 1 + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + impl Two for u32 { + fn two(self) -> i32 { + let _ = self; + 0 + } + } + + pub fn use_it(t: u32) -> i32 { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn impl_missing_associated_type() { + let src = r#" + trait Foo { + type Assoc; + } + + impl Foo for () {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) + )); +} + +#[test] +fn as_trait_path_syntax_resolves_outside_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + // AsTraitPath syntax is a bit silly when associated types + // are explicitly specified + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + use TypeCheckError::TypeMismatch; + let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { + panic!("Expected TypeMismatch error, found {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i64".to_string()); + assert_eq!(expr_typ, "i32".to_string()); +} + +#[test] +fn as_trait_path_syntax_no_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); +} + +#[test] +fn errors_on_unused_private_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn errors_on_unused_pub_crate_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + pub(crate) use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn warns_on_use_of_private_exported_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + use bar::baz; + + pub fn qux() { + baz(); + } + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated + + assert!(matches!( + &errors[0].0, + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(..), + )) + )); +} + +#[test] +fn can_use_pub_use_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn warns_on_re_export_of_item_with_less_visibility() { + let src = r#" + mod foo { + mod bar { + pub(crate) fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } + ) + )); +} + +#[test] +fn unquoted_integer_as_integer_token() { + let src = r#" + trait Serialize { + fn serialize() {} + } + + #[attr] + pub fn foobar() {} + + comptime fn attr(_f: FunctionDefinition) -> Quoted { + let serialized_len = 1; + // We are testing that when we unquote $serialized_len, it's unquoted + // as the token `1` and not as something else that later won't be parsed correctly + // in the context of a generic argument. + quote { + impl Serialize<$serialized_len> for Field { + fn serialize() { } + } + } + } + + fn main() {} + "#; + + assert_no_errors(src); +} + +#[test] +fn errors_on_unused_function() { + let src = r#" + contract some_contract { + // This function is unused, but it's a contract entrypoint + // so it should not produce a warning + fn foo() -> pub Field { + 1 + } + } + + + fn foo() { + bar(); + } + + fn bar() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "foo"); + assert_eq!(*item_type, "function"); +} + +#[test] +fn errors_on_unused_struct() { + let src = r#" + struct Foo {} + struct Bar {} + + fn main() { + let _ = Bar {}; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "struct"); +} + +#[test] +fn errors_on_unused_trait() { + let src = r#" + trait Foo {} + trait Bar {} + + pub struct Baz { + } + + impl Bar for Baz {} + + fn main() { + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "trait"); +} + +#[test] +fn constrained_reference_to_unconstrained() { + let src = r#" + fn main(mut x: u32, y: pub u32) { + let x_ref = &mut x; + if x == 5 { + unsafe { + mut_ref_input(x_ref, y); + } + } + + assert(x == 10); + } + + unconstrained fn mut_ref_input(x: &mut u32, y: u32) { + *x = y; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = + &errors[0].0 + else { + panic!("Expected an error about passing a constrained reference to unconstrained"); + }; +} + +#[test] +fn comptime_type_in_runtime_code() { + let source = "pub fn foo(_f: FunctionDefinition) {}"; + let errors = get_program_errors(source); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) + )); +} + #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { let source = r#" @@ -3595,121 +3607,119 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { assert_eq!(errors.len(), 0); } -// TODO: re-enable! -// #[test] -// fn cannot_mutate_immutable_variable() { -// let src = r#" -// fn main() { -// let array = [1]; -// mutate(&mut array); -// } -// -// fn mutate(_: &mut [Field; 1]) {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a CannotMutateImmutableVariable error"); -// }; -// -// assert_eq!(name, "array"); -// } -// -// #[test] -// fn cannot_mutate_immutable_variable_on_member_access() { -// let src = r#" -// struct Foo { -// x: Field -// } -// -// fn main() { -// let foo = Foo { x: 0 }; -// mutate(&mut foo.x); -// } -// -// fn mutate(foo: &mut Field) { -// *foo = 1; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a CannotMutateImmutableVariable error"); -// }; -// -// assert_eq!(name, "foo"); -// } -// -// #[test] -// fn does_not_crash_when_passing_mutable_undefined_variable() { -// let src = r#" -// fn main() { -// mutate(&mut undefined); -// } -// -// fn mutate(foo: &mut Field) { -// *foo = 1; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected a VariableNotDeclared error"); -// }; -// -// assert_eq!(name, "undefined"); -// } -// -// #[test] -// fn infer_globals_to_u32_from_type_use() { -// let src = r#" -// global ARRAY_LEN = 3; -// global STR_LEN = 2; -// global FMT_STR_LEN = 2; -// -// fn main() { -// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; -// let _b: str = "hi"; -// let _c: fmtstr = f"hi"; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn non_u32_in_array_length() { -// let src = r#" -// global ARRAY_LEN: u8 = 3; -// -// fn main() { -// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) -// )); -// } +#[test] +fn cannot_mutate_immutable_variable() { + let src = r#" + fn main() { + let array = [1]; + mutate(&mut array); + } + + fn mutate(_: &mut [Field; 1]) {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "array"); +} + +#[test] +fn cannot_mutate_immutable_variable_on_member_access() { + let src = r#" + struct Foo { + x: Field + } + + fn main() { + let foo = Foo { x: 0 }; + mutate(&mut foo.x); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "foo"); +} + +#[test] +fn does_not_crash_when_passing_mutable_undefined_variable() { + let src = r#" + fn main() { + mutate(&mut undefined); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = + &errors[0].0 + else { + panic!("Expected a VariableNotDeclared error"); + }; + + assert_eq!(name, "undefined"); +} + +#[test] +fn infer_globals_to_u32_from_type_use() { + let src = r#" + global ARRAY_LEN = 3; + global STR_LEN = 2; + global FMT_STR_LEN = 2; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + let _b: str = "hi"; + let _c: fmtstr = f"hi"; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn non_u32_in_array_length() { + let src = r#" + global ARRAY_LEN: u8 = 3; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) + )); +} -// TODO: failing #[test] fn use_non_u32_generic_in_struct() { let src = r#" @@ -3728,7 +3738,6 @@ fn use_non_u32_generic_in_struct() { assert_eq!(errors.len(), 0); } -// TODO: failing #[test] fn use_numeric_generic_in_trait_method() { let src = r#" diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 588f5b18f1b..c9d2225476f 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -581,8 +581,8 @@ impl<'a> NodeFinder<'a> { Type::Tuple(types) => { self.complete_tuple_fields(types, self_prefix); } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + if let TypeBinding::Bound(ref typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, prefix, @@ -1662,8 +1662,8 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { + if let TypeBinding::Bound(ref typ) = &*var.borrow() { get_field_type(typ, name) } else { None @@ -1680,7 +1680,7 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => { + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { get_array_element_type(typ.clone()) } else { diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 46d2a5cfc8f..63dac00c86c 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -500,7 +500,7 @@ impl<'a> TypeLinksGatherer<'a> { self.gather_type_links(generic); } } - Type::TypeVariable(var, _) => { + Type::TypeVariable(var) => { self.gather_type_variable_links(var); } Type::TraitAsType(trait_id, _, generics) => { @@ -513,7 +513,7 @@ impl<'a> TypeLinksGatherer<'a> { self.gather_type_links(&named_type.typ); } } - Type::NamedGeneric(var, _, _) => { + Type::NamedGeneric(var, _) => { self.gather_type_variable_links(var); } Type::Function(args, return_type, env, _) => { diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index a56e7029a79..8c7c085e01b 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -18,7 +18,7 @@ use noirc_frontend::{ macros_api::NodeInterner, node_interner::ReferenceId, parser::{Item, ParsedSubModule}, - Type, TypeBinding, TypeVariable, TypeVariableKind, + Kind, Type, TypeBinding, TypeVariable, }; use crate::{utils, LspState}; @@ -460,26 +460,14 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part("&mut ")); push_type_parts(typ, parts, files); } - Type::TypeVariable(var, TypeVariableKind::Normal) => { - push_type_variable_parts(var, parts, files); - } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - push_type_parts(&Type::default_int_type(), parts, files); - } else { - push_type_variable_parts(binding, parts, files); - } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - parts.push(string_part("Field")); - } else { - push_type_variable_parts(binding, parts, files); - } - } - Type::TypeVariable(binding, TypeVariableKind::Numeric(typ)) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - push_type_parts(&typ, parts, files); + Type::TypeVariable(binding) => { + if let TypeBinding::Unbound(_, kind) = &*binding.borrow() { + match kind { + Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), + Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), + Kind::IntegerOrField => parts.push(string_part("Field")), + Kind::Numeric(typ) => push_type_parts(&typ, parts, files), + } } else { push_type_variable_parts(binding, parts, files); } diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index ae12bac4c06..91dc1aa52e3 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -10,7 +10,7 @@ use noirc_frontend::{ hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, macros_api::{ModuleDefId, NodeInterner}, node_interner::{FunctionModifiers, ReferenceId}, - Kind, ResolvedGeneric, Type, TypeVariableKind, + Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; @@ -295,7 +295,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push_str(&trait_.name.0.contents); self.append_trait_generics(trait_generics); } - Type::TypeVariable(typevar, _) => { + Type::TypeVariable(typevar) => { if typevar.id() == self.trait_.self_type_typevar.id() { self.string.push_str("Self"); return; @@ -328,8 +328,8 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push_str("error"); } - Type::NamedGeneric(typevar, _name, _kind) => { - self.append_type(&Type::TypeVariable(typevar.clone(), TypeVariableKind::Normal)); + Type::NamedGeneric(typevar, _name) => { + self.append_type(&Type::TypeVariable(typevar.clone())); } Type::Function(args, ret, env, unconstrained) => { if *unconstrained { @@ -442,9 +442,9 @@ impl<'a> TraitImplMethodStubGenerator<'a> { } fn append_resolved_generic(&mut self, generic: &ResolvedGeneric) { - match &generic.kind { - Kind::Normal => self.string.push_str(&generic.name), - Kind::Numeric(typ) => { + match &generic.kind() { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => self.string.push_str(&generic.name), + Kind::Numeric(ref typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); self.string.push_str(": "); From 9e9fde0a6559232f45a41f7adc8f68c69f27192a Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 26 Sep 2024 15:08:55 -0400 Subject: [PATCH 14/56] wip debugging (most frontend tests passing or only failing with slightly different errors than expected) --- compiler/noirc_frontend/src/tests.rs | 395 ++++++++++++++------------- 1 file changed, 198 insertions(+), 197 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index d93869812cb..1dbef92c3e2 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1541,74 +1541,75 @@ fn incorrect_turbofish_count_method_call() { )); } -// TODO failing -#[test] -fn struct_numeric_generic_in_function() { - let src = r#" - struct Foo { - inner: u64 - } - - pub fn bar() { } - "#; - let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} - -// TODO failing -#[test] -fn struct_numeric_generic_in_struct() { - let src = r#" - pub struct Foo { - inner: u64 - } - - pub struct Bar { } - "#; - let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::UnsupportedNumericGenericType { .. } - ), - )); -} - -#[test] -fn bool_numeric_generic() { - let src = r#" - pub fn read() -> Field { - if N { - 0 - } else { - 1 - } - } - "#; - let errors = get_program_errors(src); +// // TODO failing (barely) +// #[test] +// fn struct_numeric_generic_in_function() { +// let src = r#" +// struct Foo { +// inner: u64 +// } +// +// pub fn bar() { } +// "#; +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } - // TODO cleanup - dbg!(&errors); +// // TODO failing (barely) +// #[test] +// fn struct_numeric_generic_in_struct() { +// let src = r#" +// pub struct Foo { +// inner: u64 +// } +// +// pub struct Bar { } +// "#; +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::DefinitionError( +// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } +// ), +// )); +// } - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} +// // TODO failing (barely) +// #[test] +// fn bool_numeric_generic() { +// let src = r#" +// pub fn read() -> Field { +// if N { +// 0 +// } else { +// 1 +// } +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } #[test] fn numeric_generic_binary_operation_type_mismatch() { @@ -1627,44 +1628,44 @@ fn numeric_generic_binary_operation_type_mismatch() { )); } -// TODO failing -#[test] -fn bool_generic_as_loop_bound() { - let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here - fields[i] = i + 1; - } - assert(fields[0] == 1); - } - "#; - let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 3); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2].0 - else { - panic!("Got an error other than a type mismatch"); - }; - - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); -} +// // TODO failing (barely) +// #[test] +// fn bool_generic_as_loop_bound() { +// let src = r#" +// pub fn read() { // error here +// let mut fields = [0; N]; // error here +// for i in 0..N { // error here +// fields[i] = i + 1; +// } +// assert(fields[0] == 1); +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 3); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, expr_typ, .. +// }) = &errors[2].0 +// else { +// panic!("Got an error other than a type mismatch"); +// }; +// +// assert_eq!(expected_typ, "Field"); +// assert_eq!(expr_typ, "bool"); +// } #[test] fn numeric_generic_in_function_signature() { @@ -2129,99 +2130,99 @@ fn impl_stricter_than_trait_different_generics() { } } -// TODO failing -#[test] -fn impl_stricter_than_trait_different_object_generics() { - let src = r#" - trait MyTrait { } - - trait OtherTrait {} - - struct Option { - inner: T - } - - struct OtherOption { - inner: Option, - } - - trait Bar { - fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; - - fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; - - fn array_good() where [T; 8]: MyTrait; - - fn array_bad() where [T; 8]: MyTrait; - - fn tuple_good() where (Option, Option): MyTrait; - - fn tuple_bad() where (Option, Option): MyTrait; - } - - impl Bar for () { - fn bar_good() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn bar_bad() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn array_good() where [A; 8]: MyTrait { } - - fn array_bad() where [B; 8]: MyTrait { } - - fn tuple_good() where (Option, Option): MyTrait { } - - fn tuple_bad() where (Option, Option): MyTrait { } - } - "#; - - let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} +// // TODO failing (barely) +// #[test] +// fn impl_stricter_than_trait_different_object_generics() { +// let src = r#" +// trait MyTrait { } +// +// trait OtherTrait {} +// +// struct Option { +// inner: T +// } +// +// struct OtherOption { +// inner: Option, +// } +// +// trait Bar { +// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn array_good() where [T; 8]: MyTrait; +// +// fn array_bad() where [T; 8]: MyTrait; +// +// fn tuple_good() where (Option, Option): MyTrait; +// +// fn tuple_bad() where (Option, Option): MyTrait; +// } +// +// impl Bar for () { +// fn bar_good() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn bar_bad() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn array_good() where [A; 8]: MyTrait { } +// +// fn array_bad() where [B; 8]: MyTrait { } +// +// fn tuple_good() where (Option, Option): MyTrait { } +// +// fn tuple_bad() where (Option, Option): MyTrait { } +// } +// "#; +// +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 3); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[1].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[2].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } #[test] fn impl_stricter_than_trait_different_trait() { From 3204c21d04788f2889a853754df0206cbd94d84e Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 26 Sep 2024 15:33:36 -0400 Subject: [PATCH 15/56] wip debugging: disable substitution kind check when overwiting bound type var, frontend tests passing --- compiler/noirc_frontend/src/hir_def/types.rs | 34 +- compiler/noirc_frontend/src/tests.rs | 612 +++++++++---------- 2 files changed, 328 insertions(+), 318 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index f9480f10d28..0e5dc76574b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -605,7 +605,7 @@ impl TypeVariable { // TODO: remove before review assert!( typ.kind().unifies(kind), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + "while binding: expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", typ.kind(), kind ); @@ -672,7 +672,7 @@ impl TypeVariable { // TODO: remove before review assert!( typ.kind().unifies(kind), - "expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", + "while force-binding: expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", kind, typ.kind() ); @@ -1384,11 +1384,11 @@ impl Type { let this = self.substitute(bindings).follow_bindings(); match &this { Type::Integer(..) => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this)); + bindings.insert(target_id, (var.clone(), Kind::Integer, this)); Ok(()) } Type::FieldElement if !only_integer => { - bindings.insert(target_id, (var.clone(), Kind::Normal, this)); + bindings.insert(target_id, (var.clone(), Kind::IntegerOrField, this)); Ok(()) } Type::TypeVariable(self_var) => { @@ -1409,7 +1409,7 @@ impl Type { let clone = Type::TypeVariable(var_clone); bindings.insert(*new_target_id, (self_var.clone(), type_var_kind, clone)); } else { - bindings.insert(target_id, (var.clone(), Kind::Normal, this.clone())); + bindings.insert(target_id, (var.clone(), Kind::IntegerOrField, this.clone())); } Ok(()) } @@ -2025,12 +2025,22 @@ impl Type { // This is needed for monomorphizing trait impl methods. match type_bindings.get(&binding.0) { Some((_, kind, replacement)) if substitute_bound_typevars => { - assert!( - kind.unifies(&replacement.kind()), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - replacement.kind() - ); + // TODO: don't need to check kind since we're overwriting w/ replacement? + // assert!( + // kind.unifies(&replacement.kind()), + // "while substituting (substitute_bound_typevars): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + // kind, + // replacement.kind() + // ); + + // TODO: cleanup debugging + if !kind.unifies(&replacement.kind()) { + dbg!( + "while substituting (substitute_bound_typevars): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + kind, + replacement.kind() + ); + } recur_on_binding(binding.0, replacement) } @@ -2042,7 +2052,7 @@ impl Type { Some((_, kind, replacement)) => { assert!( kind.unifies(&replacement.kind()), - "expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", + "while substituting (unbound): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", kind, replacement.kind() ); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1dbef92c3e2..5340561d6c0 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -908,312 +908,312 @@ fn get_lambda_captures( } } -// #[test] -// fn resolve_empty_function() { -// let src = " -// fn main() { -// -// } -// "; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_basic_function() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_unused_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(x == x); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unused variable -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { -// assert_eq!(&ident.0.contents, "y"); -// } -// _ => unreachable!("we should only have an unused var error"), -// } -// } -// -// #[test] -// fn resolve_unresolved_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == z); -// } -// "#; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { -// assert_eq!(name, "z"); -// } -// _ => unimplemented!("we should only have an unresolved variable"), -// } -// } -// -// #[test] -// fn unresolved_path() { -// let src = " -// fn main(x : Field) { -// let _z = some::path::to::a::func(x); -// } -// "; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "some"); -// } -// _ => unimplemented!("we should only have an unresolved function"), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_literal_expr() { -// let src = r#" -// fn main(x : Field) { -// let y = 5; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn multiple_resolution_errors() { -// let src = r#" -// fn main(x : Field) { -// let y = foo::bar(x); -// let z = y + a; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); -// -// // Errors are: -// // `a` is undeclared -// // `z` is unused -// // `foo::bar` does not exist -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::UnusedVariable { ident } => { -// assert_eq!(&ident.0.contents, "z"); -// } -// ResolverError::VariableNotDeclared { name, .. } => { -// assert_eq!(name, "a"); -// } -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "foo"); -// } -// _ => unimplemented!(), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_prefix_expr() { -// let src = r#" -// fn main(x : Field) { -// let _y = -x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_for_expr() { -// let src = r#" -// fn main(x : u64) { -// for i in 1..20 { -// let _z = x + i; -// }; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_call_expr() { -// let src = r#" -// fn main(x : Field) { -// let _z = foo(x); -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_shadowing() { -// let src = r#" -// fn main(x : Field) { -// let x = foo(x); -// let x = x; -// let (x, x) = (x, x); -// let _ = x; -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_basic_closure() { -// let src = r#" -// fn main(x : Field) -> pub Field { -// let closure = |y| y + x; -// closure(x) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_simplified_closure() { -// // based on bug https://github.com/noir-lang/noir/issues/1088 -// -// let src = r#"fn do_closure(x: Field) -> Field { -// let y = x; -// let ret_capture = || { -// y -// }; -// ret_capture() -// } -// -// fn main(x: Field) { -// assert(do_closure(x) == 100); -// } -// -// "#; -// let parsed_captures = get_program_captures(src); -// let expected_captures = vec![vec!["y".to_string()]]; -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_complex_closures() { -// let src = r#" -// fn main(x: Field) -> pub Field { -// let closure_without_captures = |x: Field| -> Field { x + x }; -// let a = closure_without_captures(1); -// -// let closure_capturing_a_param = |y: Field| -> Field { y + x }; -// let b = closure_capturing_a_param(2); -// -// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; -// let c = closure_capturing_a_local_var(3); -// -// let closure_with_transitive_captures = |y: Field| -> Field { -// let d = 5; -// let nested_closure = |z: Field| -> Field { -// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; -// a + z + y + d + x + doubly_nested_closure(4) + x + y -// }; -// let res = nested_closure(5); -// res -// }; -// -// a + b + c + closure_with_transitive_captures(6) -// } -// "#; -// assert_no_errors(src); -// -// let expected_captures = vec![ -// vec![], -// vec!["x".to_string()], -// vec!["b".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], -// vec!["x".to_string(), "b".to_string()], -// ]; -// -// let parsed_captures = get_program_captures(src); -// -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_fmt_strings() { -// let src = r#" -// fn main() { -// let string = f"this is i: {i}"; -// println(string); -// -// println(f"I want to print {0}"); -// -// let new_val = 10; -// println(f"random_string{new_val}{new_val}"); -// } -// fn println(x : T) -> T { -// x -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { -// name, .. -// }) => { -// assert_eq!(name, "i"); -// } -// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { -// name, -// .. -// }) => { -// assert_eq!(name, "0"); -// } -// CompilationError::TypeError(TypeCheckError::UnusedResultError { -// expr_type: _, -// expr_span, -// }) => { -// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); -// assert!( -// a == "println(string)" -// || a == "println(f\"I want to print {0}\")" -// || a == "println(f\"random_string{new_val}{new_val}\")" -// ); -// } -// _ => unimplemented!(), -// }; -// } -// } +#[test] +fn resolve_empty_function() { + let src = " + fn main() { + + } + "; + assert_no_errors(src); +} +#[test] +fn resolve_basic_function() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == x); + } + "#; + assert_no_errors(src); +} +#[test] +fn resolve_unused_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(x == x); + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unused variable + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { + assert_eq!(&ident.0.contents, "y"); + } + _ => unreachable!("we should only have an unused var error"), + } +} + +#[test] +fn resolve_unresolved_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == z); + } + "#; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { + assert_eq!(name, "z"); + } + _ => unimplemented!("we should only have an unresolved variable"), + } +} + +#[test] +fn unresolved_path() { + let src = " + fn main(x : Field) { + let _z = some::path::to::a::func(x); + } + "; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "some"); + } + _ => unimplemented!("we should only have an unresolved function"), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_literal_expr() { + let src = r#" + fn main(x : Field) { + let y = 5; + assert(y == x); + } + "#; + assert_no_errors(src); +} + +#[test] +fn multiple_resolution_errors() { + let src = r#" + fn main(x : Field) { + let y = foo::bar(x); + let z = y + a; + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); + + // Errors are: + // `a` is undeclared + // `z` is unused + // `foo::bar` does not exist + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::UnusedVariable { ident } => { + assert_eq!(&ident.0.contents, "z"); + } + ResolverError::VariableNotDeclared { name, .. } => { + assert_eq!(name, "a"); + } + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "foo"); + } + _ => unimplemented!(), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_prefix_expr() { + let src = r#" + fn main(x : Field) { + let _y = -x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_for_expr() { + let src = r#" + fn main(x : u64) { + for i in 1..20 { + let _z = x + i; + }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_call_expr() { + let src = r#" + fn main(x : Field) { + let _z = foo(x); + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_shadowing() { + let src = r#" + fn main(x : Field) { + let x = foo(x); + let x = x; + let (x, x) = (x, x); + let _ = x; + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_basic_closure() { + let src = r#" + fn main(x : Field) -> pub Field { + let closure = |y| y + x; + closure(x) + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_simplified_closure() { + // based on bug https://github.com/noir-lang/noir/issues/1088 + + let src = r#"fn do_closure(x: Field) -> Field { + let y = x; + let ret_capture = || { + y + }; + ret_capture() + } + + fn main(x: Field) { + assert(do_closure(x) == 100); + } + + "#; + let parsed_captures = get_program_captures(src); + let expected_captures = vec![vec!["y".to_string()]]; + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_complex_closures() { + let src = r#" + fn main(x: Field) -> pub Field { + let closure_without_captures = |x: Field| -> Field { x + x }; + let a = closure_without_captures(1); + + let closure_capturing_a_param = |y: Field| -> Field { y + x }; + let b = closure_capturing_a_param(2); + + let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; + let c = closure_capturing_a_local_var(3); + + let closure_with_transitive_captures = |y: Field| -> Field { + let d = 5; + let nested_closure = |z: Field| -> Field { + let doubly_nested_closure = |w: Field| -> Field { w + x + b }; + a + z + y + d + x + doubly_nested_closure(4) + x + y + }; + let res = nested_closure(5); + res + }; + + a + b + c + closure_with_transitive_captures(6) + } + "#; + assert_no_errors(src); + + let expected_captures = vec![ + vec![], + vec!["x".to_string()], + vec!["b".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], + vec!["x".to_string(), "b".to_string()], + ]; + + let parsed_captures = get_program_captures(src); + + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_fmt_strings() { + let src = r#" + fn main() { + let string = f"this is i: {i}"; + println(string); + + println(f"I want to print {0}"); + + let new_val = 10; + println(f"random_string{new_val}{new_val}"); + } + fn println(x : T) -> T { + x + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { + name, .. + }) => { + assert_eq!(name, "i"); + } + CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { + name, + .. + }) => { + assert_eq!(name, "0"); + } + CompilationError::TypeError(TypeCheckError::UnusedResultError { + expr_type: _, + expr_span, + }) => { + let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); + assert!( + a == "println(string)" + || a == "println(f\"I want to print {0}\")" + || a == "println(f\"random_string{new_val}{new_val}\")" + ); + } + _ => unimplemented!(), + }; + } +} fn check_rewrite(src: &str, expected: &str) { let (_program, mut context, _errors) = get_program(src); From ad7ed34d1532d97690bde6df9fa8d8ba8d14eb38 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 27 Sep 2024 15:19:58 -0400 Subject: [PATCH 16/56] all tests passing! use resolve_generic_kind when possible, use Kind::Any when kind is unknown, update expected errors for some expected-failure tests that now fail earlier, wip cleanup, cargo clippy/fmt --- compiler/noirc_frontend/src/ast/expression.rs | 2 +- compiler/noirc_frontend/src/elaborator/mod.rs | 32 +- .../src/elaborator/trait_impls.rs | 5 +- .../noirc_frontend/src/elaborator/traits.rs | 5 +- .../noirc_frontend/src/elaborator/types.rs | 67 +- .../src/hir/comptime/interpreter/builtin.rs | 2 +- .../src/hir/def_collector/dc_mod.rs | 5 +- compiler/noirc_frontend/src/hir/mod.rs | 5 +- .../src/hir/resolution/errors.rs | 7 +- compiler/noirc_frontend/src/hir_def/traits.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 209 ++--- .../src/monomorphization/mod.rs | 15 +- compiler/noirc_frontend/src/node_interner.rs | 27 +- compiler/noirc_frontend/src/tests.rs | 423 +++++---- .../src/tests/name_shadowing.rs | 831 +++++++++--------- 15 files changed, 794 insertions(+), 843 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 5a2d7c22786..ad8ab1a9302 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -80,7 +80,7 @@ pub enum UnresolvedGeneric { #[error("The only supported types of numeric generics are integers, fields, and booleans")] pub struct UnsupportedNumericGenericType { pub ident: Ident, - pub typ: UnresolvedTypeData + pub typ: UnresolvedTypeData, } impl UnresolvedGeneric { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index fec4984fcf4..655eec03ddb 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -511,6 +511,7 @@ impl<'context> Elaborator<'context> { trait_constraints: &mut Vec, ) -> Type { let new_generic_id = self.interner.next_type_variable_id(); + let new_generic = TypeVariable::unbound(new_generic_id, Kind::Normal); generics.push(new_generic.clone()); @@ -536,13 +537,7 @@ impl<'context> Elaborator<'context> { self.push_err(error); is_error = true; let id = self.interner.next_type_variable_id(); - let kind = match generic.kind() { - Ok(kind) => kind, - Err(error) => { - self.push_err(error); - Kind::Normal - } - }; + let kind = self.resolve_generic_kind(generic); (TypeVariable::unbound(id, kind), Rc::new("(error)".into())) } }; @@ -579,7 +574,8 @@ impl<'context> Elaborator<'context> { match generic { UnresolvedGeneric::Variable(_) | UnresolvedGeneric::Numeric { .. } => { let id = self.interner.next_type_variable_id(); - let typevar = TypeVariable::unbound(id, generic.kind()?); + let kind = self.resolve_generic_kind(generic); + let typevar = TypeVariable::unbound(id, kind); let ident = generic.ident(); let name = Rc::new(ident.0.contents.clone()); Ok((typevar, name)) @@ -588,9 +584,7 @@ impl<'context> Elaborator<'context> { // previous macro call being inserted into a generics list. UnresolvedGeneric::Resolved(id, span) => { match self.interner.get_quoted_type(*id).follow_bindings() { - Type::NamedGeneric(type_variable, name) => { - Ok((type_variable.clone(), name)) - } + Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { span: *span, typ: other.clone(), @@ -607,15 +601,19 @@ impl<'context> Elaborator<'context> { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let unresolved_typ = typ.clone(); let typ = if unresolved_typ.is_type_expression() { - self.resolve_type_inner(unresolved_typ.clone(), &Kind::Numeric(Box::new(Type::default_int_type()))) + self.resolve_type_inner( + unresolved_typ.clone(), + &Kind::Numeric(Box::new(Type::default_int_type())), + ) } else { self.resolve_type(unresolved_typ.clone()) }; if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = ResolverError::UnsupportedNumericGenericType(UnsupportedNumericGenericType { - ident: ident.clone(), - typ: unresolved_typ.typ.clone(), - }); + let unsupported_typ_err = + ResolverError::UnsupportedNumericGenericType(UnsupportedNumericGenericType { + ident: ident.clone(), + typ: unresolved_typ.typ.clone(), + }); self.push_err(unsupported_typ_err); } Kind::Numeric(Box::new(typ)) @@ -750,7 +748,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } - _ => self.resolve_type_inner(typ, &Kind::Normal), + _ => self.resolve_type_inner(typ, &Kind::Any), }; self.check_if_type_is_valid_for_program_input( diff --git a/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/compiler/noirc_frontend/src/elaborator/trait_impls.rs index 9c223a556a0..2ab867413c3 100644 --- a/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -159,7 +159,10 @@ impl<'context> Elaborator<'context> { { let trait_fn_kind = trait_fn_generic.kind(); let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); + bindings.insert( + trait_fn_generic.id(), + (trait_fn_generic.clone(), trait_fn_kind.clone(), arg), + ); } let mut substituted_method_ids = HashSet::default(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 7aaa0040f81..68de9f33928 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -82,8 +82,7 @@ impl<'context> Elaborator<'context> { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); - let self_type = - Type::TypeVariable(self_typevar.clone()); + let self_type = Type::TypeVariable(self_typevar.clone()); let name_span = the_trait.name.span(); this.add_existing_generic( @@ -292,7 +291,7 @@ pub(crate) fn check_trait_impl_method_matches_declaration( { let trait_fn_kind = trait_fn_generic.kind(); let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); - bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind.clone(), arg)); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), trait_fn_kind, arg)); } let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d91bbd22065..6458926d9a6 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -59,18 +59,6 @@ impl<'context> Elaborator<'context> { /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { - // // TODO: cleanup - // let dbg_this = match &typ.typ { - // UnresolvedTypeData::Integer(..) => true, - // UnresolvedTypeData::Named(..) => true, - // UnresolvedTypeData::FieldElement => true, - // UnresolvedTypeData::Expression(..) => true, - // _ => false, - // }; - // if dbg_this { - // dbg!("resolve_type_inner", &typ, kind); - // } - use crate::ast::UnresolvedTypeData::*; let span = typ.span; @@ -184,9 +172,7 @@ impl<'context> Elaborator<'context> { 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(), + expr_kind: resolved_type.kind().to_string(), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -240,10 +226,6 @@ impl<'context> Elaborator<'context> { return self_type; } } else if name == WILDCARD_TYPE { - // TODO: cleanup - dbg!("resolve_named_type: WILDCARD_TYPE", &name); - - // return self.interner.next_type_variable(); return self.interner.next_type_variable_with_kind(Kind::Any); } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { @@ -720,23 +702,11 @@ impl<'context> Elaborator<'context> { /// Translates a (possibly Unspecified) UnresolvedType to a Type. /// Any UnresolvedType::Unspecified encountered are replaced with fresh type variables. pub(super) fn resolve_inferred_type(&mut self, typ: UnresolvedType) -> Type { - // TODO: cleanup - // - // match &typ.typ { - // UnresolvedTypeData::Unspecified => self.interner.next_type_variable(), - // _ => self.resolve_type(typ), - // } - // match &typ.typ { UnresolvedTypeData::Unspecified => { - // // TODO: cleanup - dbg!("resolve_inferred_type: unspecified"); self.interner.next_type_variable_with_kind(Kind::Any) } - _ => { - // dbg!("resolve_inferred_type: specified", &typ); - self.resolve_type(typ) - } + _ => self.resolve_type(typ), } } @@ -829,15 +799,14 @@ impl<'context> Elaborator<'context> { return self.bind_function_type(typ.clone(), args, span); } - // TODO: cleanup - dbg!("bind_function_type", &binding); let ret = self.interner.next_type_variable(); let args = vecmap(args, |(arg, _, _)| arg); let env_type = self.interner.next_type_variable(); let expected = Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); - if let Err(error) = binding.try_bind(expected, &Kind::Normal, span) { + let expected_kind = expected.kind(); + if let Err(error) = binding.try_bind(expected, &expected_kind, span) { self.push_err(error); } ret @@ -857,9 +826,7 @@ impl<'context> Elaborator<'context> { pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { match from.follow_bindings() { - Type::Integer(..) - | Type::FieldElement - | Type::Bool => (), + Type::Integer(..) | Type::FieldElement | Type::Bool => (), Type::TypeVariable(var) if var.is_integer() || var.is_integer_or_field() => (), @@ -1223,7 +1190,11 @@ impl<'context> Elaborator<'context> { let object_type = object_type.substitute(&bindings); bindings.insert( the_trait.self_type_typevar.id(), - (the_trait.self_type_typevar.clone(), Kind::Normal, object_type.clone()), + ( + the_trait.self_type_typevar.clone(), + the_trait.self_type_typevar.kind(), + object_type.clone(), + ), ); self.interner.select_impl_for_expression( expr_id, @@ -1601,8 +1572,14 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); } ImplSearchErrorKind::Nested(constraints) => { - if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) + // TODO cleanup + // if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) + if let Some(error) = + NoMatchingImplFoundError::new(self.interner, constraints.clone(), span) { + // TODO cleanup + dbg!("ImplSearchErrorKind::Nested", &constraints); + self.push_err(TypeCheckError::NoMatchingImplFound(error)); } } @@ -1788,7 +1765,10 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) { // Avoid binding t = t if !arg.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.clone())); + bindings.insert( + param.type_var.id(), + (param.type_var.clone(), param.kind(), arg.clone()), + ); } } @@ -1807,7 +1787,10 @@ impl<'context> Elaborator<'context> { // Avoid binding t = t if !arg.typ.occurs(param.type_var.id()) { - bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.typ.clone())); + bindings.insert( + param.type_var.id(), + (param.type_var.clone(), param.kind(), arg.typ.clone()), + ); } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 5ac7b04b0dd..41ec98e5c41 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2083,7 +2083,7 @@ fn fmtstr_quoted_contents( // fn fresh_type_variable() -> Type fn fresh_type_variable(interner: &NodeInterner) -> IResult { - Ok(Value::Type(interner.next_type_variable())) + Ok(Value::Type(interner.next_type_variable_with_kind(Kind::Any))) } // fn add_attribute(self, attribute: str) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index d75a7371244..f9f3d29695d 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -524,7 +524,10 @@ impl<'a> ModCollector<'a> { associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), - type_var: TypeVariable::unbound(type_variable_id, Kind::Numeric(Box::new(typ))), + type_var: TypeVariable::unbound( + type_variable_id, + Kind::Numeric(Box::new(typ)), + ), span: name.span(), }); } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 506177e859d..015b7deb6e0 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -11,7 +11,7 @@ use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::ParserError; -use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, Type, TypeVariable}; +use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, TypeVariable}; use def_collector::dc_crate::CompilationError; use def_map::{Contract, CrateDefMap}; use fm::{FileId, FileManager}; @@ -283,7 +283,8 @@ impl Context<'_, '_> { let type_var_kind = generic.kind().unwrap_or_else(|err| { errors.push((err.into(), file_id)); - Kind::Numeric(Box::new(Type::Error)) + // When there's an error, unify with any other kinds + Kind::Any }); let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 108cc250778..bfe1b7641a7 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -2,7 +2,12 @@ pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::{ast::{Ident, UnsupportedNumericGenericType}, hir::comptime::InterpreterError, parser::ParserError, Type}; +use crate::{ + ast::{Ident, UnsupportedNumericGenericType}, + hir::comptime::InterpreterError, + parser::ParserError, + Type, +}; use super::import::PathResolutionError; diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index 32240e0c987..3859db26e39 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -3,12 +3,12 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::{Ident, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; +use crate::ResolvedGeneric; use crate::{ graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, Generics, Type, TypeBindings, TypeVariable, }; -use crate::ResolvedGeneric; use fm::FileId; use noirc_errors::{Location, Span}; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 0e5dc76574b..3a9b9abe76d 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -177,7 +177,7 @@ impl Kind { Self::Normal => Self::Normal, Self::Integer => Self::Integer, Self::IntegerOrField => Self::IntegerOrField, - Self::Numeric(typ) => Self::Numeric(Box::new(typ.follow_bindings())) + Self::Numeric(typ) => Self::Numeric(Box::new(typ.follow_bindings())), } } @@ -229,7 +229,6 @@ impl Kind { Kind::Numeric(typ) => Some(*typ.clone()), } } - } impl std::fmt::Display for Kind { @@ -381,7 +380,12 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) + .map(|(old, new)| { + ( + old.type_var.id(), + (old.type_var.clone(), old.type_var.kind(), new.clone()), + ) + }) .collect(); (typ.substitute(&substitutions), i) @@ -397,7 +401,9 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) + .map(|(old, new)| { + (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone())) + }) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -509,7 +515,9 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone()))) + .map(|(old, new)| { + (old.type_var.id(), (old.type_var.clone(), old.type_var.kind(), new.clone())) + }) .collect(); self.typ.substitute(&substitutions) @@ -602,14 +610,6 @@ impl TypeVariable { /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. pub fn bind(&self, typ: Type, kind: &Kind) { - // TODO: remove before review - assert!( - typ.kind().unifies(kind), - "while binding: expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - typ.kind(), - kind - ); - let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) @@ -650,12 +650,6 @@ impl TypeVariable { self.1.borrow() } - // TODO: remove since it's error-prone and unused - // /// Borrows this TypeVariable to (e.g.) manually match on the inner TypeBinding. - // pub fn borrow_mut(&self) -> std::cell::RefMut { - // self.1.borrow_mut() - // } - /// Unbind this type variable, setting it to Unbound(id). /// /// This is generally a logic error to use outside of monomorphization. @@ -667,16 +661,8 @@ impl TypeVariable { /// variable is already bound to a different type. This generally /// a logic error to use outside of monomorphization. /// - /// Asserts that the given type is compatible with the given Kind + /// Asserts that the given type is compatible with the given Kind pub fn force_bind(&self, typ: Type, kind: &Kind) { - // TODO: remove before review - assert!( - typ.kind().unifies(kind), - "while force-binding: expected kind of TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - typ.kind() - ); - if !typ.occurs(self.id()) { *self.1.borrow_mut() = TypeBinding::Bound(typ); } @@ -702,7 +688,9 @@ impl TypeVariable { /// and if unbound, that it's a Kind::IntegerOrField pub fn is_integer_or_field(&self) -> bool { match &*self.borrow() { - TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..) | Type::FieldElement), + TypeBinding::Bound(binding) => { + matches!(binding, Type::Integer(..) | Type::FieldElement) + } TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), } } @@ -745,18 +733,12 @@ impl std::fmt::Display for Type { Type::TypeVariable(var) => { let binding = &var.1; match &*binding.borrow() { - TypeBinding::Unbound(_, type_var_kind) => { - match type_var_kind { - Kind::Any | Kind::Normal => write!(f, "{}", var.borrow()), - Kind::Integer => write!(f, "{}", Type::default_int_type()), - Kind::IntegerOrField => write!(f, "Field"), - - // TODO: after debugging - // Kind::Numeric(typ) => write!(f, "_"), - Kind::Numeric(typ) => write!(f, "_#!({:?})", typ), - - } - } + TypeBinding::Unbound(_, type_var_kind) => match type_var_kind { + Kind::Any | Kind::Normal => write!(f, "{}", var.borrow()), + Kind::Integer => write!(f, "{}", Type::default_int_type()), + Kind::IntegerOrField => write!(f, "Field"), + Kind::Numeric(_typ) => write!(f, "_"), + }, TypeBinding::Bound(binding) => { write!(f, "{}", binding) } @@ -794,15 +776,10 @@ impl std::fmt::Display for Type { Type::Error => write!(f, "error"), Type::NamedGeneric(binding, name) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), - // TODO: revert after debugging - // TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_", kind), - TypeBinding::Unbound(_, kind) if name.is_empty() => write!(f, "_:! {:?}", kind), + TypeBinding::Unbound(_, _) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_, _) => write!(f, "{name}"), }, - - // TODO: revert after debugging - // Type::Constant(x, _kind) => write!(f, "{x}"), - Type::Constant(x, kind) => write!(f, "{x}:! {kind}"), + Type::Constant(x, _kind) => write!(f, "{x}"), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) @@ -853,9 +830,7 @@ impl std::fmt::Display for BinaryTypeOperator { impl std::fmt::Display for TypeVariableId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // TODO: revert after debugging - // TODO remove '!' - write!(f, "_!") + write!(f, "_") } } @@ -888,6 +863,7 @@ impl std::fmt::Display for QuotedType { } } +#[derive(Debug)] pub struct UnificationError; impl Type { @@ -958,18 +934,18 @@ impl Type { /// While Kind::is_numeric refers to numeric _types_, /// this method checks for numeric _values_ pub fn is_numeric_value(&self) -> bool { - use Type::*; use Kind as K; + use Type::*; match self.follow_bindings() { FieldElement => true, Integer(..) => true, Bool => true, - TypeVariable(var) => { - match &*var.borrow() { - TypeBinding::Bound(typ) => typ.is_numeric_value(), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, K::Integer | K::IntegerOrField), + TypeVariable(var) => match &*var.borrow() { + TypeBinding::Bound(typ) => typ.is_numeric_value(), + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind, K::Integer | K::IntegerOrField) } - } + }, _ => false, } } @@ -1378,7 +1354,7 @@ impl Type { }; if !self.kind().unifies(&Kind::IntegerOrField) { - return Err(UnificationError) + return Err(UnificationError); } let this = self.substitute(bindings).follow_bindings(); @@ -1407,34 +1383,31 @@ impl Type { // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. let clone = Type::TypeVariable(var_clone); - bindings.insert(*new_target_id, (self_var.clone(), type_var_kind, clone)); + bindings + .insert(*new_target_id, (self_var.clone(), type_var_kind, clone)); } else { - bindings.insert(target_id, (var.clone(), Kind::IntegerOrField, this.clone())); + bindings.insert( + target_id, + (var.clone(), Kind::IntegerOrField, this.clone()), + ); } Ok(()) } TypeBinding::Unbound(_new_target_id, Kind::Integer) => { - let type_var_kind = Kind::Integer; - Kind::Integer.unify(&type_var_kind)?; - bindings.insert(target_id, (var.clone(), type_var_kind, this.clone())); + Kind::Integer.unify(&Kind::Integer)?; + bindings.insert(target_id, (var.clone(), Kind::Integer, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, type_var_kind) => { + TypeBinding::Unbound(new_target_id, ref type_var_kind) => { let var_clone = var.clone(); // Bind to the most specific type variable kind - let clone_kind = if only_integer { - Kind::Integer - } else { - Kind::IntegerOrField - }; - clone_kind.unify(&type_var_kind)?; + let clone_kind = + if only_integer { Kind::Integer } else { Kind::IntegerOrField }; + clone_kind.unify(type_var_kind)?; let clone = Type::TypeVariable(var_clone); bindings.insert(*new_target_id, (self_var.clone(), clone_kind, clone)); Ok(()) } - TypeBinding::Unbound(_new_target_id, Kind::Numeric(_typ)) => { - Err(UnificationError) - } } } _ => Err(UnificationError), @@ -1459,7 +1432,7 @@ impl Type { }; if !self.kind().unifies(&kind) { - return Err(UnificationError) + return Err(UnificationError); } let this = self.substitute(bindings).follow_bindings(); @@ -1530,39 +1503,36 @@ impl Type { alias.try_unify(other, bindings) } - (TypeVariable(var), other) | (other, TypeVariable(var)) => { - match &*var.borrow() { - TypeBinding::Bound(typ) => { - if typ.is_numeric_value() { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = matches!(typ, Type::Integer(..)); - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) - } else { - other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings, typ.kind()) - }) - } - } - TypeBinding::Unbound(_id, Kind::IntegerOrField) => { + (TypeVariable(var), other) | (other, TypeVariable(var)) => match &*var.borrow() { + TypeBinding::Bound(typ) => { + if typ.is_numeric_value() { other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = false; + let only_integer = matches!(typ, Type::Integer(..)); other.try_bind_to_polymorphic_int(var, bindings, only_integer) }) - } - TypeBinding::Unbound(_id, Kind::Integer) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { - let only_integer = true; - other.try_bind_to_polymorphic_int(var, bindings, only_integer) - }) - } - TypeBinding::Unbound(_id, type_var_kind) => { + } else { other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to(var, bindings, type_var_kind.clone()) + other.try_bind_to(var, bindings, typ.kind()) }) } } - } + TypeBinding::Unbound(_id, Kind::IntegerOrField) => other + .try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = false; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }), + TypeBinding::Unbound(_id, Kind::Integer) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = true; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } + TypeBinding::Unbound(_id, type_var_kind) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + other.try_bind_to(var, bindings, type_var_kind.clone()) + }) + } + }, (Array(len_a, elem_a), Array(len_b, elem_b)) => { len_a.try_unify(len_b, bindings)?; @@ -1665,7 +1635,7 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - // TODO: replace evaluate_to_u32 + // TODO(https://github.com/noir-lang/noir/pull/6137): replace evaluate_to_u32 if let Some(other_value) = other.evaluate_to_u32() { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) @@ -1901,9 +1871,9 @@ impl Type { match self { Type::Forall(typevars, typ) => { for var in typevars { - bindings - .entry(var.id()) - .or_insert_with(|| (var.clone(), var.kind(), interner.next_type_variable_with_kind(var.kind()))); + bindings.entry(var.id()).or_insert_with(|| { + (var.clone(), var.kind(), interner.next_type_variable_with_kind(var.kind())) + }); } let instantiated = typ.force_substitute(&bindings); @@ -1922,7 +1892,7 @@ impl Type { let replacements = typevars .iter() .map(|var| { - let new = interner.next_type_variable(); + let new = interner.next_type_variable_with_kind(var.kind()); (var.id(), (var.clone(), var.kind(), new)) }) .collect(); @@ -1969,9 +1939,7 @@ impl Type { fn type_variable_id(&self) -> Option { match self { - Type::TypeVariable(variable) | Type::NamedGeneric(variable, _) => { - Some(variable.0) - } + Type::TypeVariable(variable) | Type::NamedGeneric(variable, _) => Some(variable.0), _ => None, } } @@ -2024,24 +1992,7 @@ impl Type { // type variables that have already been bound over. // This is needed for monomorphizing trait impl methods. match type_bindings.get(&binding.0) { - Some((_, kind, replacement)) if substitute_bound_typevars => { - // TODO: don't need to check kind since we're overwriting w/ replacement? - // assert!( - // kind.unifies(&replacement.kind()), - // "while substituting (substitute_bound_typevars): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - // kind, - // replacement.kind() - // ); - - // TODO: cleanup debugging - if !kind.unifies(&replacement.kind()) { - dbg!( - "while substituting (substitute_bound_typevars): expected kind of unbound TypeVariable ({:?}) to match the kind of its binding ({:?})", - kind, - replacement.kind() - ); - } - + Some((_, _kind, replacement)) if substitute_bound_typevars => { recur_on_binding(binding.0, replacement) } _ => match &*binding.borrow() { @@ -2058,7 +2009,7 @@ impl Type { ); recur_on_binding(binding.0, replacement) - }, + } None => self.clone(), }, }, @@ -2456,7 +2407,9 @@ impl From<&Type> for PrintableType { Type::TypeVariable(binding) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), TypeBinding::Unbound(_, Kind::Integer) => Type::default_int_type().into(), - TypeBinding::Unbound(_, Kind::IntegerOrField) => Type::default_int_or_field_type().into(), + TypeBinding::Unbound(_, Kind::IntegerOrField) => { + Type::default_int_or_field_type().into() + } TypeBinding::Unbound(_, Kind::Numeric(typ)) => (*typ.clone()).into(), TypeBinding::Unbound(_, Kind::Any | Kind::Normal) => unreachable!(), }, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 0464239c1a1..f7aa7be1702 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -934,15 +934,21 @@ impl<'interner> Monomorphizer<'interner> { }; let location = self.interner.id_location(expr_id); - // TODO: check that numeric_typ matches typ // TODO: possible to remove clones? - if !Kind::Numeric(numeric_typ.clone()).unifies(&Kind::Numeric(Box::new(typ.clone()))) { + if !Kind::Numeric(numeric_typ.clone()) + .unifies(&Kind::Numeric(Box::new(typ.clone()))) + { let message = "ICE: Generic's kind does not match expected type"; return Err(MonomorphizationError::InternalError { location, message }); } let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer((value as u128).into(), false, typ, location)) + ast::Expression::Literal(ast::Literal::Integer( + (value as u128).into(), + false, + typ, + location, + )) } }; @@ -1788,8 +1794,7 @@ impl<'interner> Monomorphizer<'interner> { let lambda_name = "zeroed_lambda"; let parameters = vecmap(parameter_types, |parameter_type| { - // TODO remove "??" - (self.next_local_id(), false, "_??".into(), parameter_type.clone()) + (self.next_local_id(), false, "_".into(), parameter_type.clone()) }); let body = self.zeroed_value_of_type(ret_type, location); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index ba9fcca38e3..106c126424e 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1737,7 +1737,17 @@ impl NodeInterner { // Replace each generic with a fresh type variable let substitutions = impl_generics .into_iter() - .map(|typevar| (typevar.id(), (typevar, Kind::Normal, self.next_type_variable()))) + .map(|typevar| { + let typevar_kind = typevar.kind(); + ( + typevar.id(), + ( + typevar, + typevar_kind.clone(), + self.next_type_variable_with_kind(typevar_kind), + ), + ) + }) .collect(); let instantiated_object_type = object_type.substitute(&substitutions); @@ -2228,11 +2238,17 @@ impl NodeInterner { let trait_generics = the_trait.generics.clone(); let self_type_var = the_trait.self_type_typevar.clone(); - bindings.insert(self_type_var.id(), (self_type_var.clone(), self_type_var.kind(), impl_self_type)); + bindings.insert( + self_type_var.id(), + (self_type_var.clone(), self_type_var.kind(), impl_self_type), + ); for (trait_generic, trait_impl_generic) in trait_generics.iter().zip(trait_impl_generics) { let type_var = trait_generic.type_var.clone(); - bindings.insert(type_var.id(), (type_var, trait_generic.kind(), trait_impl_generic.clone())); + bindings.insert( + type_var.id(), + (type_var, trait_generic.kind(), trait_impl_generic.clone()), + ); } // Now that the normal bindings are added, we still need to bind the associated types @@ -2241,7 +2257,10 @@ impl NodeInterner { for (trait_type, impl_type) in trait_associated_types.iter().zip(impl_associated_types) { let type_variable = trait_type.type_var.clone(); - bindings.insert(type_variable.id(), (type_variable, trait_type.kind(), impl_type.typ.clone())); + bindings.insert( + type_variable.id(), + (type_variable, trait_type.kind(), impl_type.typ.clone()), + ); } bindings diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 5340561d6c0..96d98ffa81b 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1541,75 +1541,58 @@ fn incorrect_turbofish_count_method_call() { )); } -// // TODO failing (barely) -// #[test] -// fn struct_numeric_generic_in_function() { -// let src = r#" -// struct Foo { -// inner: u64 -// } -// -// pub fn bar() { } -// "#; -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } - -// // TODO failing (barely) -// #[test] -// fn struct_numeric_generic_in_struct() { -// let src = r#" -// pub struct Foo { -// inner: u64 -// } -// -// pub struct Bar { } -// "#; -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } -// ), -// )); -// } - -// // TODO failing (barely) -// #[test] -// fn bool_numeric_generic() { -// let src = r#" -// pub fn read() -> Field { -// if N { -// 0 -// } else { -// 1 -// } -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } +#[test] +fn struct_numeric_generic_in_function() { + let src = r#" + struct Foo { + inner: u64 + } + + pub fn bar() { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn struct_numeric_generic_in_struct() { + let src = r#" + pub struct Foo { + inner: u64 + } + + pub struct Bar { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), + )); +} + +#[test] +fn bool_numeric_generic() { + let src = r#" + pub fn read() -> Field { + if N { + 0 + } else { + 1 + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} #[test] fn numeric_generic_binary_operation_type_mismatch() { @@ -1628,44 +1611,39 @@ fn numeric_generic_binary_operation_type_mismatch() { )); } -// // TODO failing (barely) -// #[test] -// fn bool_generic_as_loop_bound() { -// let src = r#" -// pub fn read() { // error here -// let mut fields = [0; N]; // error here -// for i in 0..N { // error here -// fields[i] = i + 1; -// } -// assert(fields[0] == 1); -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 3); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, expr_typ, .. -// }) = &errors[2].0 -// else { -// panic!("Got an error other than a type mismatch"); -// }; -// -// assert_eq!(expected_typ, "Field"); -// assert_eq!(expr_typ, "bool"); -// } +#[test] +fn bool_generic_as_loop_bound() { + let src = r#" + pub fn read() { // error here + let mut fields = [0; N]; // error here + for i in 0..N { // error here + fields[i] = i + 1; + } + assert(fields[0] == 1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); + + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[2].0 + else { + panic!("Got an error other than a type mismatch"); + }; + + assert_eq!(expected_typ, "Field"); + assert_eq!(expr_typ, "bool"); +} #[test] fn numeric_generic_in_function_signature() { @@ -1716,7 +1694,7 @@ fn numeric_generic_as_param_type() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); + assert_eq!(errors.len(), 2); // Error from the parameter type assert!(matches!( @@ -1728,11 +1706,6 @@ fn numeric_generic_as_param_type() { errors[1].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); } #[test] @@ -1901,7 +1874,6 @@ fn constant_used_with_numeric_generic() { assert_no_errors(src); } -// TODO: failing #[test] fn normal_generic_used_when_numeric_expected_in_where_clause() { let src = r#" @@ -1914,10 +1886,6 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -2044,7 +2012,6 @@ fn quote_code_fragments() { assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); } -// TODO: failing #[test] fn impl_stricter_than_trait_no_trait_method_constraints() { // This test ensures that the error we get from the where clause on the trait impl method @@ -2087,10 +2054,6 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - assert_eq!(errors.len(), 1); assert!(matches!( &errors[0].0, @@ -2130,99 +2093,94 @@ fn impl_stricter_than_trait_different_generics() { } } -// // TODO failing (barely) -// #[test] -// fn impl_stricter_than_trait_different_object_generics() { -// let src = r#" -// trait MyTrait { } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// struct OtherOption { -// inner: Option, -// } -// -// trait Bar { -// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn array_good() where [T; 8]: MyTrait; -// -// fn array_bad() where [T; 8]: MyTrait; -// -// fn tuple_good() where (Option, Option): MyTrait; -// -// fn tuple_bad() where (Option, Option): MyTrait; -// } -// -// impl Bar for () { -// fn bar_good() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn bar_bad() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn array_good() where [A; 8]: MyTrait { } -// -// fn array_bad() where [B; 8]: MyTrait { } -// -// fn tuple_good() where (Option, Option): MyTrait { } -// -// fn tuple_bad() where (Option, Option): MyTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 3); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[1].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[2].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } +#[test] +fn impl_stricter_than_trait_different_object_generics() { + let src = r#" + trait MyTrait { } + + trait OtherTrait {} + + struct Option { + inner: T + } + + struct OtherOption { + inner: Option, + } + + trait Bar { + fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; + + fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + + fn array_good() where [T; 8]: MyTrait; + + fn array_bad() where [T; 8]: MyTrait; + + fn tuple_good() where (Option, Option): MyTrait; + + fn tuple_bad() where (Option, Option): MyTrait; + } + + impl Bar for () { + fn bar_good() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn bar_bad() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn array_good() where [A; 8]: MyTrait { } + + fn array_bad() where [B; 8]: MyTrait { } + + fn tuple_good() where (Option, Option): MyTrait { } + + fn tuple_bad() where (Option, Option): MyTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[1].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[2].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} #[test] fn impl_stricter_than_trait_different_trait() { @@ -2376,6 +2334,35 @@ fn impl_not_found_for_inner_impl() { )); } +#[test] +fn struct_array_len() { + let src = r#" + struct Array { + inner: [T; N], + } + + impl Array { + pub fn len(self) -> u32 { + N as u32 + } + } + + fn main(xs: [Field; 2]) { + let ys = Array { + inner: xs, + }; + assert(ys.len() == 2); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) + )); +} + // Regression for #5388 #[test] fn comptime_let() { @@ -3732,10 +3719,6 @@ fn use_non_u32_generic_in_struct() { "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } diff --git a/compiler/noirc_frontend/src/tests/name_shadowing.rs b/compiler/noirc_frontend/src/tests/name_shadowing.rs index d67cd519103..b0d83510039 100644 --- a/compiler/noirc_frontend/src/tests/name_shadowing.rs +++ b/compiler/noirc_frontend/src/tests/name_shadowing.rs @@ -2,419 +2,418 @@ use super::get_program_errors; use std::collections::HashSet; -// TODO: re-enable! -// #[test] -// fn test_name_shadowing() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for bool { -// fn default() -> bool { -// false -// } -// } -// -// impl Default for Field { -// fn default() -> Field { -// 0 -// } -// } -// -// impl Default for [T; N] where T: Default { -// fn default() -> [T; N] { -// [Default::default(); N] -// } -// } -// -// impl Default for (T, U) where T: Default, U: Default { -// fn default() -> (T, U) { -// (Default::default(), Default::default()) -// } -// } -// -// fn drop_var(_x: T, y: U) -> U { y } -// -// mod local_module { -// use crate::{Default, drop_var}; -// -// global LOCAL_GLOBAL_N: Field = 0; -// -// global LOCAL_GLOBAL_M: Field = 1; -// -// struct LocalStruct { -// field1: A, -// field2: B, -// field3: [A; N], -// field4: ([A; N], [B; M]), -// field5: &mut A, -// } -// -// impl Default for LocalStruct where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// field1: Default::default(), -// field2: Default::default(), -// field3: Default::default(), -// field4: Default::default(), -// field5: mut_field, -// } -// } -// } -// -// trait DefinedInLocalModule1 { -// fn trait_fn1(self, x: A); -// fn trait_fn2(self, y: B); -// fn trait_fn3(&mut self, x: A, y: B); -// fn trait_fn4(self, x: [A; 0], y: [B]); -// fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl DefinedInLocalModule1 for LocalStruct { -// fn trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// let x: Field = 0; -// assert(x == 0); -// let x: Field = 1; -// assert(x == 1); -// [] -// } -// } -// -// mod library { -// use crate::{Default, drop_var}; -// -// mod library2 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_2: Field = 4; -// -// global IMPORT_GLOBAL_M_2: Field = 5; -// -// // When we re-export this type from another library and then use it in -// // main, we get a panic -// struct ReExportMeFromAnotherLib1 { -// x : Field, -// } -// -// struct PubLibLocalStruct3 { -// pub_field1: A, -// pub_field2: B, -// pub_field3: [A; N], -// pub_field4: ([A; N], [B; M]), -// pub_field5: &mut A, -// } -// -// impl Default for PubLibLocalStruct3 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// pub_field1: Default::default(), -// pub_field2: Default::default(), -// pub_field3: Default::default(), -// pub_field4: Default::default(), -// pub_field5: mut_field, -// } -// } -// } -// -// trait PubLibDefinedInLocalModule3 { -// fn pub_trait_fn1(self, x: A); -// fn pub_trait_fn2(self, y: B); -// fn pub_trait_fn3(&mut self, x: A, y: B); -// fn pub_trait_fn4(self, x: [A; 0], y: [B]); -// fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { -// fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// [] -// } -// } -// -// // Re-export -// use library2::ReExportMeFromAnotherLib1; -// -// global IMPORT_GLOBAL_N_1: Field = 2; -// -// global IMPORT_GLOBAL_M_1: Field = 3; -// -// struct LibLocalStruct1 { -// lib_field1: A, -// lib_field2: B, -// lib_field3: [A; N], -// lib_field4: ([A; N], [B; M]), -// lib_field5: &mut A, -// } -// -// impl Default for LibLocalStruct1 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// lib_field1: Default::default(), -// lib_field2: Default::default(), -// lib_field3: Default::default(), -// lib_field4: Default::default(), -// lib_field5: mut_field, -// } -// } -// } -// -// trait LibDefinedInLocalModule1 { -// fn lib_trait_fn1(self, x: A); -// fn lib_trait_fn2(self, y: B); -// fn lib_trait_fn3(&mut self, x: A, y: B); -// fn lib_trait_fn4(self, x: [A; 0], y: [B]); -// fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl LibDefinedInLocalModule1 for LibLocalStruct1 { -// fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// [] -// } -// } -// -// mod library3 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_3: Field = 6; -// -// global IMPORT_GLOBAL_M_3: Field = 7; -// -// struct ReExportMeFromAnotherLib2 { -// x : Field, -// } -// -// struct PubCrateLibLocalStruct2 { -// crate_field1: A, -// crate_field2: B, -// crate_field3: [A; N], -// crate_field4: ([A; N], [B; M]), -// crate_field5: &mut A, -// } -// -// impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// crate_field1: Default::default(), -// crate_field2: Default::default(), -// crate_field3: Default::default(), -// crate_field4: Default::default(), -// crate_field5: mut_field, -// } -// } -// } -// -// trait PubCrateLibDefinedInLocalModule2 { -// fn crate_trait_fn1(self, x: A); -// fn crate_trait_fn2(self, y: B); -// fn crate_trait_fn3(&mut self, x: A, y: B); -// fn crate_trait_fn4(self, x: [A; 0], y: [B]); -// fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { -// fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// } -// -// pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// [] -// } -// } -// -// -// use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; -// -// use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; -// -// // overlapping -// // use library::library2::ReExportMeFromAnotherLib1; -// use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; -// -// use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; -// -// -// fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// -// let x: LocalStruct = Default::default(); -// assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// -// let x: LibLocalStruct1 = Default::default(); -// assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// -// let x: PubLibLocalStruct3 = Default::default(); -// assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// -// let x: PubCrateLibLocalStruct2 = Default::default(); -// assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// -// assert(drop_var(local_fn2((0, 1), [], []), true)); -// assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); -// assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); -// assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); -// }"; -// -// // NOTE: these names must be "replacement-unique", i.e. -// // replacing one in a discinct name should do nothing -// let names_to_collapse = [ -// "DefinedInLocalModule1", -// "IMPORT_GLOBAL_M_1", -// "IMPORT_GLOBAL_M_2", -// "IMPORT_GLOBAL_M_3", -// "IMPORT_GLOBAL_N_1", -// "IMPORT_GLOBAL_N_2", -// "IMPORT_GLOBAL_N_3", -// "LOCAL_GLOBAL_M", -// "LOCAL_GLOBAL_N", -// "LibDefinedInLocalModule1", -// "LibLocalStruct1", -// "Liblocal_fn1", -// "LocalStruct", -// "PubCrateLibDefinedInLocalModule2", -// "PubCrateLibLocalStruct2", -// "PubCrateLiblocal_fn2", -// "PubLibDefinedInLocalModule3", -// "PubLibLocalStruct3", -// "PubLiblocal_fn3", -// "ReExportMeFromAnotherLib1", -// "ReExportMeFromAnotherLib2", -// "local_fn4", -// "crate_field1", -// "crate_field2", -// "crate_field3", -// "crate_field4", -// "crate_field5", -// "crate_trait_fn1", -// "crate_trait_fn2", -// "crate_trait_fn3", -// "crate_trait_fn4", -// "crate_trait_fn5", -// "crate_trait_fn6", -// "crate_trait_fn7", -// "field1", -// "field2", -// "field3", -// "field4", -// "field5", -// "lib_field1", -// "lib_field2", -// "lib_field3", -// "lib_field4", -// "lib_field5", -// "lib_trait_fn1", -// "lib_trait_fn2", -// "lib_trait_fn3", -// "lib_trait_fn4", -// "lib_trait_fn5", -// "lib_trait_fn6", -// "lib_trait_fn7", -// "pub_field1", -// "pub_field2", -// "pub_field3", -// "pub_field4", -// "pub_field5", -// "pub_trait_fn1", -// "pub_trait_fn2", -// "pub_trait_fn3", -// "pub_trait_fn4", -// "pub_trait_fn5", -// "pub_trait_fn6", -// "pub_trait_fn7", -// "trait_fn1", -// "trait_fn2", -// "trait_fn3", -// "trait_fn4", -// "trait_fn5", -// "trait_fn6", -// "trait_fn7", -// ]; -// -// // TODO(https://github.com/noir-lang/noir/issues/4973): -// // Name resolution panic from name shadowing test -// let cases_to_skip = [ -// (1, 21), -// (2, 11), -// (2, 21), -// (3, 11), -// (3, 18), -// (3, 21), -// (4, 21), -// (5, 11), -// (5, 21), -// (6, 11), -// (6, 18), -// (6, 21), -// ]; -// let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); -// -// for (i, x) in names_to_collapse.iter().enumerate() { -// for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { -// if !cases_to_skip.contains(&(i, j)) { -// dbg!((i, j)); -// -// let modified_src = src.replace(x, y); -// let errors = get_program_errors(&modified_src); -// assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); -// } -// } -// } -// } +#[test] +fn test_name_shadowing() { + let src = " + trait Default { + fn default() -> Self; + } + + impl Default for bool { + fn default() -> bool { + false + } + } + + impl Default for Field { + fn default() -> Field { + 0 + } + } + + impl Default for [T; N] where T: Default { + fn default() -> [T; N] { + [Default::default(); N] + } + } + + impl Default for (T, U) where T: Default, U: Default { + fn default() -> (T, U) { + (Default::default(), Default::default()) + } + } + + fn drop_var(_x: T, y: U) -> U { y } + + mod local_module { + use crate::{Default, drop_var}; + + global LOCAL_GLOBAL_N: Field = 0; + + global LOCAL_GLOBAL_M: Field = 1; + + struct LocalStruct { + field1: A, + field2: B, + field3: [A; N], + field4: ([A; N], [B; M]), + field5: &mut A, + } + + impl Default for LocalStruct where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + field1: Default::default(), + field2: Default::default(), + field3: Default::default(), + field4: Default::default(), + field5: mut_field, + } + } + } + + trait DefinedInLocalModule1 { + fn trait_fn1(self, x: A); + fn trait_fn2(self, y: B); + fn trait_fn3(&mut self, x: A, y: B); + fn trait_fn4(self, x: [A; 0], y: [B]); + fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl DefinedInLocalModule1 for LocalStruct { + fn trait_fn1(self, _x: A) { drop_var(self, ()) } + fn trait_fn2(self, _y: B) { drop_var(self, ()) } + fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + let x: Field = 0; + assert(x == 0); + let x: Field = 1; + assert(x == 1); + [] + } + } + + mod library { + use crate::{Default, drop_var}; + + mod library2 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_2: Field = 4; + + global IMPORT_GLOBAL_M_2: Field = 5; + + // When we re-export this type from another library and then use it in + // main, we get a panic + struct ReExportMeFromAnotherLib1 { + x : Field, + } + + struct PubLibLocalStruct3 { + pub_field1: A, + pub_field2: B, + pub_field3: [A; N], + pub_field4: ([A; N], [B; M]), + pub_field5: &mut A, + } + + impl Default for PubLibLocalStruct3 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + pub_field1: Default::default(), + pub_field2: Default::default(), + pub_field3: Default::default(), + pub_field4: Default::default(), + pub_field5: mut_field, + } + } + } + + trait PubLibDefinedInLocalModule3 { + fn pub_trait_fn1(self, x: A); + fn pub_trait_fn2(self, y: B); + fn pub_trait_fn3(&mut self, x: A, y: B); + fn pub_trait_fn4(self, x: [A; 0], y: [B]); + fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { + fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + [] + } + } + + // Re-export + use library2::ReExportMeFromAnotherLib1; + + global IMPORT_GLOBAL_N_1: Field = 2; + + global IMPORT_GLOBAL_M_1: Field = 3; + + struct LibLocalStruct1 { + lib_field1: A, + lib_field2: B, + lib_field3: [A; N], + lib_field4: ([A; N], [B; M]), + lib_field5: &mut A, + } + + impl Default for LibLocalStruct1 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + lib_field1: Default::default(), + lib_field2: Default::default(), + lib_field3: Default::default(), + lib_field4: Default::default(), + lib_field5: mut_field, + } + } + } + + trait LibDefinedInLocalModule1 { + fn lib_trait_fn1(self, x: A); + fn lib_trait_fn2(self, y: B); + fn lib_trait_fn3(&mut self, x: A, y: B); + fn lib_trait_fn4(self, x: [A; 0], y: [B]); + fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl LibDefinedInLocalModule1 for LibLocalStruct1 { + fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + [] + } + } + + mod library3 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_3: Field = 6; + + global IMPORT_GLOBAL_M_3: Field = 7; + + struct ReExportMeFromAnotherLib2 { + x : Field, + } + + struct PubCrateLibLocalStruct2 { + crate_field1: A, + crate_field2: B, + crate_field3: [A; N], + crate_field4: ([A; N], [B; M]), + crate_field5: &mut A, + } + + impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + crate_field1: Default::default(), + crate_field2: Default::default(), + crate_field3: Default::default(), + crate_field4: Default::default(), + crate_field5: mut_field, + } + } + } + + trait PubCrateLibDefinedInLocalModule2 { + fn crate_trait_fn1(self, x: A); + fn crate_trait_fn2(self, y: B); + fn crate_trait_fn3(&mut self, x: A, y: B); + fn crate_trait_fn4(self, x: [A; 0], y: [B]); + fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { + fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + } + + pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + [] + } + } + + + use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; + + use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; + + // overlapping + // use library::library2::ReExportMeFromAnotherLib1; + use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; + + use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; + + + fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + + let x: LocalStruct = Default::default(); + assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + + let x: LibLocalStruct1 = Default::default(); + assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + + let x: PubLibLocalStruct3 = Default::default(); + assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + + let x: PubCrateLibLocalStruct2 = Default::default(); + assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + + assert(drop_var(local_fn2((0, 1), [], []), true)); + assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); + assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); + assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); + }"; + + // NOTE: these names must be "replacement-unique", i.e. + // replacing one in a discinct name should do nothing + let names_to_collapse = [ + "DefinedInLocalModule1", + "IMPORT_GLOBAL_M_1", + "IMPORT_GLOBAL_M_2", + "IMPORT_GLOBAL_M_3", + "IMPORT_GLOBAL_N_1", + "IMPORT_GLOBAL_N_2", + "IMPORT_GLOBAL_N_3", + "LOCAL_GLOBAL_M", + "LOCAL_GLOBAL_N", + "LibDefinedInLocalModule1", + "LibLocalStruct1", + "Liblocal_fn1", + "LocalStruct", + "PubCrateLibDefinedInLocalModule2", + "PubCrateLibLocalStruct2", + "PubCrateLiblocal_fn2", + "PubLibDefinedInLocalModule3", + "PubLibLocalStruct3", + "PubLiblocal_fn3", + "ReExportMeFromAnotherLib1", + "ReExportMeFromAnotherLib2", + "local_fn4", + "crate_field1", + "crate_field2", + "crate_field3", + "crate_field4", + "crate_field5", + "crate_trait_fn1", + "crate_trait_fn2", + "crate_trait_fn3", + "crate_trait_fn4", + "crate_trait_fn5", + "crate_trait_fn6", + "crate_trait_fn7", + "field1", + "field2", + "field3", + "field4", + "field5", + "lib_field1", + "lib_field2", + "lib_field3", + "lib_field4", + "lib_field5", + "lib_trait_fn1", + "lib_trait_fn2", + "lib_trait_fn3", + "lib_trait_fn4", + "lib_trait_fn5", + "lib_trait_fn6", + "lib_trait_fn7", + "pub_field1", + "pub_field2", + "pub_field3", + "pub_field4", + "pub_field5", + "pub_trait_fn1", + "pub_trait_fn2", + "pub_trait_fn3", + "pub_trait_fn4", + "pub_trait_fn5", + "pub_trait_fn6", + "pub_trait_fn7", + "trait_fn1", + "trait_fn2", + "trait_fn3", + "trait_fn4", + "trait_fn5", + "trait_fn6", + "trait_fn7", + ]; + + // TODO(https://github.com/noir-lang/noir/issues/4973): + // Name resolution panic from name shadowing test + let cases_to_skip = [ + (1, 21), + (2, 11), + (2, 21), + (3, 11), + (3, 18), + (3, 21), + (4, 21), + (5, 11), + (5, 21), + (6, 11), + (6, 18), + (6, 21), + ]; + let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); + + for (i, x) in names_to_collapse.iter().enumerate() { + for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { + if !cases_to_skip.contains(&(i, j)) { + dbg!((i, j)); + + let modified_src = src.replace(x, y); + let errors = get_program_errors(&modified_src); + assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); + } + } + } +} From b81611b8e7ebe6b57b5d05cd03de438ed7e95695 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 27 Sep 2024 15:30:02 -0400 Subject: [PATCH 17/56] cleanup, remove unused kind arguments from (force_)bind, simplify next_type_variable_with_kind --- compiler/noirc_frontend/src/elaborator/mod.rs | 2 +- .../noirc_frontend/src/hir/comptime/interpreter.rs | 4 ++-- compiler/noirc_frontend/src/hir_def/types.rs | 14 +++++++------- .../noirc_frontend/src/monomorphization/mod.rs | 11 +++++------ compiler/noirc_frontend/src/node_interner.rs | 4 +--- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 655eec03ddb..b0ac1c1f736 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -473,7 +473,7 @@ impl<'context> Elaborator<'context> { for typ in context.type_variables { if let Type::TypeVariable(variable) = typ.follow_bindings() { let msg = "TypeChecker should only track defaultable type vars"; - variable.bind(variable.kind().default_type().expect(msg), &variable.kind()); + variable.bind(variable.kind().default_type().expect(msg)); } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 12b3c24f9ec..f055bef24a5 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -351,8 +351,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.bound_generics.pop(); if let Some(bindings) = self.bound_generics.last() { - for (var, (binding, kind)) in bindings { - var.force_bind(binding.clone(), kind); + for (var, (binding, _kind)) in bindings { + var.force_bind(binding.clone()); } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 3a9b9abe76d..b0dbd468b1e 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -609,7 +609,7 @@ impl TypeVariable { /// Panics if this TypeVariable is already Bound. /// Also Panics if the ID of this TypeVariable occurs within the given /// binding, as that would cause an infinitely recursive type. - pub fn bind(&self, typ: Type, kind: &Kind) { + pub fn bind(&self, typ: Type) { let id = match &*self.1.borrow() { TypeBinding::Bound(binding) => { unreachable!("TypeVariable::bind, cannot bind bound var {} to {}", binding, typ) @@ -662,7 +662,7 @@ impl TypeVariable { /// a logic error to use outside of monomorphization. /// /// Asserts that the given type is compatible with the given Kind - pub fn force_bind(&self, typ: Type, kind: &Kind) { + pub fn force_bind(&self, typ: Type) { if !typ.occurs(self.id()) { *self.1.borrow_mut() = TypeBinding::Bound(typ); } @@ -875,7 +875,7 @@ impl Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) } - pub fn type_variable_with_kind(interner: &mut NodeInterner, type_var_kind: Kind) -> Type { + pub fn type_variable_with_kind(interner: &NodeInterner, type_var_kind: Kind) -> Type { let id = interner.next_type_variable_id(); let var = TypeVariable::unbound(id, type_var_kind); Type::TypeVariable(var) @@ -886,12 +886,12 @@ impl Type { Type::TypeVariable(var) } - pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { + pub fn polymorphic_integer_or_field(interner: &NodeInterner) -> Type { let type_var_kind = Kind::IntegerOrField; Self::type_variable_with_kind(interner, type_var_kind) } - pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { + pub fn polymorphic_integer(interner: &NodeInterner) -> Type { let type_var_kind = Kind::Integer; Self::type_variable_with_kind(interner, type_var_kind) } @@ -1797,8 +1797,8 @@ impl Type { /// Apply the given type bindings, making them permanently visible for each /// clone of each type variable bound. pub fn apply_type_bindings(bindings: TypeBindings) { - for (type_variable, kind, binding) in bindings.values() { - type_variable.bind(binding.clone(), kind); + for (type_variable, _kind, binding) in bindings.values() { + type_variable.bind(binding.clone()); } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index f7aa7be1702..1cc476ba119 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -301,7 +301,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { if let Some((self_type, trait_id)) = self.interner.get_function_trait(&f) { let the_trait = self.interner.get_trait(trait_id); - the_trait.self_type_typevar.force_bind(self_type, &types::Kind::Normal); + the_trait.self_type_typevar.force_bind(self_type); } let meta = self.interner.function_meta(&f).clone(); @@ -934,7 +934,6 @@ impl<'interner> Monomorphizer<'interner> { }; let location = self.interner.id_location(expr_id); - // TODO: possible to remove clones? if !Kind::Numeric(numeric_typ.clone()) .unifies(&Kind::Numeric(Box::new(typ.clone()))) { @@ -994,7 +993,7 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_or_field_type(), &binding.kind()); + binding.bind(HirType::default_int_or_field_type()); ast::Type::Field } @@ -1015,7 +1014,7 @@ impl<'interner> Monomorphizer<'interner> { }; let monomorphized_default = Self::convert_type(&default, location)?; - binding.bind(default, &type_var_kind); + binding.bind(default); monomorphized_default } @@ -1935,8 +1934,8 @@ fn unwrap_struct_type( } pub fn perform_instantiation_bindings(bindings: &TypeBindings) { - for (var, kind, binding) in bindings.values() { - var.force_bind(binding.clone(), kind); + for (var, _kind, binding) in bindings.values() { + var.force_bind(binding.clone()); } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 106c126424e..b41b332c46f 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1330,10 +1330,8 @@ impl NodeInterner { Type::type_variable(self.next_type_variable_id()) } - // TODO: make closer to self.next_type_variable() pub fn next_type_variable_with_kind(&self, kind: Kind) -> Type { - let var = TypeVariable::unbound(self.next_type_variable_id(), kind); - Type::TypeVariable(var) + Type::type_variable_with_kind(self, kind) } pub fn store_instantiation_bindings( From 306a5d7dbdbf05e4a49bce9257ebe77840f7eaf1 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 27 Sep 2024 15:57:32 -0400 Subject: [PATCH 18/56] cargo clippy / fmt --- compiler/noirc_driver/src/abi_gen.rs | 3 +-- compiler/noirc_frontend/src/elaborator/traits.rs | 3 +-- tooling/lsp/src/requests/inlay_hint.rs | 2 +- tooling/lsp/src/trait_impl_method_stub_generator.rs | 4 +++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index fdde00eb0f6..772f02d89d6 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -7,13 +7,13 @@ use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; use noirc_frontend::ast::{Signedness, Visibility}; +use noirc_frontend::TypeBinding; use noirc_frontend::{ hir::Context, hir_def::{expr::HirArrayLiteral, function::Param, stmt::HirPattern, types::Type}, macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, }; -use noirc_frontend::TypeBinding; /// Arranges a function signature and a generated circuit's return witnesses into a /// `noirc_abi::Abi`. @@ -107,7 +107,6 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::Constant(..) | Type::InfixExpr(..) | Type::TraitAsType(..) - | Type::TypeVariable(_) | Type::NamedGeneric(..) | Type::Forall(..) | Type::Quoted(_) diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index b18226929de..b46444d8c8c 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -14,8 +14,7 @@ use crate::{ NodeInterner, NoirFunction, UnresolvedType, }, node_interner::{FuncId, ReferenceId, TraitId}, - token::Attributes, - Kind, ResolvedGeneric, Type, TypeBindings, + ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index 8c7c085e01b..bd20a06d2da 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -466,7 +466,7 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), Kind::IntegerOrField => parts.push(string_part("Field")), - Kind::Numeric(typ) => push_type_parts(&typ, parts, files), + Kind::Numeric(ref typ) => push_type_parts(typ, parts, files), } } else { push_type_variable_parts(binding, parts, files); diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index c5f9c5ba775..c60a81776ec 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -438,7 +438,9 @@ impl<'a> TraitImplMethodStubGenerator<'a> { fn append_resolved_generic(&mut self, generic: &ResolvedGeneric) { match &generic.kind() { - Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => self.string.push_str(&generic.name), + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { + self.string.push_str(&generic.name); + } Kind::Numeric(ref typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); From 2581427b41ffd6fabcc3d2c09b7dad7ccf4768d5 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 30 Sep 2024 11:20:10 -0400 Subject: [PATCH 19/56] Update compiler/noirc_frontend/src/node_interner.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_frontend/src/node_interner.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index a9b8d86f903..5ea2c1276e2 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1743,14 +1743,8 @@ impl NodeInterner { .into_iter() .map(|typevar| { let typevar_kind = typevar.kind(); - ( - typevar.id(), - ( - typevar, - typevar_kind.clone(), - self.next_type_variable_with_kind(typevar_kind), - ), - ) + let substitution = (typevar, typevar_kind.clone(), self.next_type_variable_with_kind(typevar_kind)); + (typevar.id(), substitution) }) .collect(); From d8381736367a85990402b75da1ba0484d4dd57a9 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 30 Sep 2024 11:20:23 -0400 Subject: [PATCH 20/56] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_frontend/src/hir_def/types.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index b0dbd468b1e..ea1bd47030f 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -185,15 +185,14 @@ impl Kind { pub(crate) fn unifies(&self, other: &Kind) -> bool { match (self, other) { // Kind::Any unifies with everything - (Kind::Any, _) - | (_, Kind::Any) + (Kind::Any, _) | (_, Kind::Any) => true // Kind::Normal unifies with Kind::Integer and Kind::IntegerOrField - | (Kind::Normal, Kind::Integer | Kind::IntegerOrField) - | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) + (Kind::Normal, Kind::Integer | Kind::IntegerOrField) + | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) => true // Kind::Integer unifies with Kind::IntegerOrField - | (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, + (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, // Kind::Numeric unifies along its Type argument (Kind::Numeric(lhs), Kind::Numeric(rhs)) => { From ceac4b0711eadffbdb470d2073e3cbf2300a523f Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 30 Sep 2024 13:49:09 -0400 Subject: [PATCH 21/56] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/hir_def/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ea1bd47030f..a873d7f4ce8 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -862,7 +862,6 @@ impl std::fmt::Display for QuotedType { } } -#[derive(Debug)] pub struct UnificationError; impl Type { From 07925664131fec7958f14d39195ff584985708e3 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 30 Sep 2024 13:49:27 -0400 Subject: [PATCH 22/56] Update compiler/noirc_frontend/src/hir/def_collector/errors.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/hir/def_collector/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index ed72fe6930d..c31a5aa3bc3 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -106,7 +106,7 @@ impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { let typ = &error.typ; Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), + format!("{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`."), "Unsupported numeric generic type".to_string(), error.ident.0.span(), ) From a70a97bb108c890fd6d230484bb4e92b99bbca24 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Oct 2024 10:04:39 -0400 Subject: [PATCH 23/56] wip debugging parameter type regression, cleanup, note issue for kind size checks, add distinct tests for different positions of numeric generic in numeric_generic_as_param_type, move some tests into individual folders --- .../noirc_frontend/src/elaborator/types.rs | 3 - compiler/noirc_frontend/src/hir_def/types.rs | 7 +- compiler/noirc_frontend/src/node_interner.rs | 3 +- compiler/noirc_frontend/src/tests.rs | 223 +++++------------- 4 files changed, 61 insertions(+), 175 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6458926d9a6..c3551e12d2b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1577,9 +1577,6 @@ impl<'context> Elaborator<'context> { if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints.clone(), span) { - // TODO cleanup - dbg!("ImplSearchErrorKind::Nested", &constraints); - self.push_err(TypeCheckError::NoMatchingImplFound(error)); } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a873d7f4ce8..8dfb88fc82e 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -185,11 +185,11 @@ impl Kind { pub(crate) fn unifies(&self, other: &Kind) -> bool { match (self, other) { // Kind::Any unifies with everything - (Kind::Any, _) | (_, Kind::Any) => true + (Kind::Any, _) | (_, Kind::Any) => true, // Kind::Normal unifies with Kind::Integer and Kind::IntegerOrField (Kind::Normal, Kind::Integer | Kind::IntegerOrField) - | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) => true + | (Kind::Integer | Kind::IntegerOrField, Kind::Normal) => true, // Kind::Integer unifies with Kind::IntegerOrField (Kind::Integer | Kind::IntegerOrField, Kind::Integer | Kind::IntegerOrField) => true, @@ -2348,7 +2348,8 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - // TODO: the Kind is included since it'll be needed for size checks + // TODO(https://github.com/noir-lang/noir/pull/6137): the Kind is included + // since it'll be needed for size checks pub fn function(self, a: u32, b: u32, _kind: &Kind) -> Option { match self { BinaryTypeOperator::Addition => a.checked_add(b), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5ea2c1276e2..83695069110 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1743,8 +1743,9 @@ impl NodeInterner { .into_iter() .map(|typevar| { let typevar_kind = typevar.kind(); + let typevar_id = typevar.id(); let substitution = (typevar, typevar_kind.clone(), self.next_type_variable_with_kind(typevar_kind)); - (typevar.id(), substitution) + (typevar_id, substitution) }) .collect(); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7b30b89a94e..0938fe0ba43 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -66,6 +66,10 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation let root_crate_id = context.crate_graph.add_crate_root(root_file_id); let (program, parser_errors) = parse_program(src); + + // TODO cleanup + dbg!(&program); + let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); @@ -1703,7 +1707,7 @@ fn normal_generic_as_array_length() { #[test] fn numeric_generic_as_param_type() { let src = r#" - pub fn foo(x: I) -> I { + pub fn foo(x: I) -> I { let _q: I = 5; x } @@ -1711,18 +1715,64 @@ fn numeric_generic_as_param_type() { let errors = get_program_errors(src); assert_eq!(errors.len(), 2); - // Error from the parameter type + // Error from the let statement annotated type assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); - // Error from the let statement annotated type + // Error from the return type assert!(matches!( errors[1].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } +#[test] +fn numeric_generic_as_unused_param_type() { + let src = r#" + pub fn foo(x: I) { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_as_return_type() { + let src = r#" + // std::mem::zeroed() without stdlib + trait Zeroed { + fn zeroed(self) -> T; + } + + fn foo(x: T) -> I where T: Zeroed { + x.zeroed() + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + // Error from the return type + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // foo is unused + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnusedItem { .. }), + )); +} + #[test] fn numeric_generic_used_in_nested_type_fails() { let src = r#" @@ -2407,7 +2457,7 @@ fn struct_array_len() { )); } -// TODO moved? +// TODO move to tests/metaprogramming // Regression for #5388 #[test] fn comptime_let() { @@ -2419,170 +2469,7 @@ fn comptime_let() { assert_eq!(errors.len(), 0); } -// TODO moved? -#[test] -fn overflowing_u8() { - let src = r#" - fn main() { - let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁸` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -// TODO moved? -#[test] -fn underflowing_u8() { - let src = r#" - fn main() { - let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -// TODO moved? -#[test] -fn overflowing_i8() { - let src = r#" - fn main() { - let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -// TODO moved? -#[test] -fn underflowing_i8() { - let src = r#" - fn main() { - let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -// TODO moved? -#[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls - let src = r#" - fn foo() -> [u8; N] { - [0; N] - } - - fn bar() -> [u8; N] { - foo::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); - - // Check for turbofish numeric generics used with method calls - let src = r#" - struct Foo { - a: T - } - - impl Foo { - fn static_method() -> [u8; N] { - [0; N] - } - - fn impl_method(self) -> [T; N] { - [self.a; N] - } - } - - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); -} - -// TODO moved? -#[test] -fn use_super() { - let src = r#" - fn some_func() {} - - mod foo { - use super::some_func; - - pub fn bar() { - some_func(); - } - } - "#; - assert_no_errors(src); -} - -// TODO moved? -#[test] -fn use_super_in_path() { - let src = r#" - fn some_func() {} - - mod foo { - pub fn func() { - super::some_func(); - } - } - "#; - assert_no_errors(src); -} - +// TODO move to imports #[test] fn no_super() { let src = "use super::some_func;"; From 029f1c35e99d9a31ef87d5507afbfd5817b5ca39 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Oct 2024 10:54:22 -0400 Subject: [PATCH 24/56] fixed numeric generic as param type --- compiler/noirc_frontend/src/elaborator/mod.rs | 3 +- compiler/noirc_frontend/src/tests.rs | 41 ++++++++++++++----- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 7fbde190178..0e145f834ab 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -752,7 +752,8 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } - _ => self.resolve_type_inner(typ, &Kind::Any), + // Function parameters have Kind::Normal + _ => self.resolve_type_inner(typ, &Kind::Normal), }; self.check_if_type_is_valid_for_program_input( diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 0938fe0ba43..7b127fd8d45 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -66,10 +66,6 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation let root_crate_id = context.crate_graph.add_crate_root(root_file_id); let (program, parser_errors) = parse_program(src); - - // TODO cleanup - dbg!(&program); - let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); @@ -1713,13 +1709,18 @@ fn numeric_generic_as_param_type() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); + assert_eq!(errors.len(), 3); - // Error from the let statement annotated type + // Error from the parameter type assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); + // Error from the let statement annotated type + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); // Error from the return type assert!(matches!( errors[1].0, @@ -1730,13 +1731,9 @@ fn numeric_generic_as_param_type() { #[test] fn numeric_generic_as_unused_param_type() { let src = r#" - pub fn foo(x: I) { } + pub fn foo(_x: I) { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1744,6 +1741,28 @@ fn numeric_generic_as_unused_param_type() { )); } +#[test] +fn numeric_generic_as_unused_trait_fn_param_type() { + let src = r#" + trait Foo { + fn foo(_x: I) { } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Foo is unused + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnusedItem { .. }), + )); +} + + + #[test] fn numeric_generic_as_return_type() { let src = r#" From 0831c61b7a1c77afc72280f2ac691e4b541bce30 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Oct 2024 11:21:48 -0400 Subject: [PATCH 25/56] cargo clippy / fmt --- compiler/noirc_frontend/src/node_interner.rs | 6 +++++- compiler/noirc_frontend/src/tests.rs | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 83695069110..2eb0e956dc2 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1744,7 +1744,11 @@ impl NodeInterner { .map(|typevar| { let typevar_kind = typevar.kind(); let typevar_id = typevar.id(); - let substitution = (typevar, typevar_kind.clone(), self.next_type_variable_with_kind(typevar_kind)); + let substitution = ( + typevar, + typevar_kind.clone(), + self.next_type_variable_with_kind(typevar_kind), + ); (typevar_id, substitution) }) .collect(); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7b127fd8d45..2acf8bed55b 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1761,8 +1761,6 @@ fn numeric_generic_as_unused_trait_fn_param_type() { )); } - - #[test] fn numeric_generic_as_return_type() { let src = r#" From 334dd1dd0be95c0f833dd989d7438bcc77ff13d0 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 1 Oct 2024 12:01:46 -0400 Subject: [PATCH 26/56] move tests to test sub-folders --- compiler/noirc_frontend/src/tests.rs | 89 ++++++------------- compiler/noirc_frontend/src/tests/imports.rs | 17 ++++ .../src/tests/metaprogramming.rs | 11 +++ 3 files changed, 57 insertions(+), 60 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 2acf8bed55b..e0be8df0693 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2444,66 +2444,6 @@ fn impl_not_found_for_inner_impl() { )); } -// TODO move? -#[test] -fn struct_array_len() { - let src = r#" - struct Array { - inner: [T; N], - } - - impl Array { - pub fn len(self) -> u32 { - N as u32 - } - } - - fn main(xs: [Field; 2]) { - let ys = Array { - inner: xs, - }; - assert(ys.len() == 2); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) - )); -} - -// TODO move to tests/metaprogramming -// Regression for #5388 -#[test] -fn comptime_let() { - let src = r#"fn main() { - comptime let my_var = 2; - assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -// TODO move to imports -#[test] -fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); -} - #[test] fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" @@ -3137,6 +3077,35 @@ fn infer_globals_to_u32_from_type_use() { assert_eq!(errors.len(), 0); } +#[test] +fn struct_array_len() { + let src = r#" + struct Array { + inner: [T; N], + } + + impl Array { + pub fn len(self) -> u32 { + N as u32 + } + } + + fn main(xs: [Field; 2]) { + let ys = Array { + inner: xs, + }; + assert(ys.len() == 2); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) + )); +} + #[test] fn non_u32_in_array_length() { let src = r#" diff --git a/compiler/noirc_frontend/src/tests/imports.rs b/compiler/noirc_frontend/src/tests/imports.rs index dfdc60e15e4..5ebc5b3bdbd 100644 --- a/compiler/noirc_frontend/src/tests/imports.rs +++ b/compiler/noirc_frontend/src/tests/imports.rs @@ -21,6 +21,23 @@ fn use_super() { assert_no_errors(src); } +#[test] +fn no_super() { + let src = "use super::some_func;"; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( + PathResolutionError::NoSuper(span), + )) = &errors[0].0 + else { + panic!("Expected a 'no super' error, got {:?}", errors[0].0); + }; + + assert_eq!(span.start(), 4); + assert_eq!(span.end(), 9); +} + #[test] fn use_super_in_path() { let src = r#" diff --git a/compiler/noirc_frontend/src/tests/metaprogramming.rs b/compiler/noirc_frontend/src/tests/metaprogramming.rs index d980cba5cfd..ec52310b3d6 100644 --- a/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -2,6 +2,17 @@ use crate::hir::def_collector::dc_crate::CompilationError; use super::get_program_errors; +// Regression for #5388 +#[test] +fn comptime_let() { + let src = r#"fn main() { + comptime let my_var = 2; + assert_eq(my_var, 2); + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + #[test] fn comptime_type_in_runtime_code() { let source = "pub fn foo(_f: FunctionDefinition) {}"; From 67f60589030548beede66fb8133e2c416fb232c1 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 1 Oct 2024 13:17:18 -0400 Subject: [PATCH 27/56] note issue to follow up on Kind::Any --- compiler/noirc_frontend/src/hir_def/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 8dfb88fc82e..8f20fe1c685 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -135,6 +135,7 @@ pub enum Type { #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { /// Can bind to any type + // TODO(https://github.com/noir-lang/noir/issues/6194): evaluate need for and usage of Any, /// Can bind to any type, except Type::Constant and Type::InfixExpr From 00dd2fc9063dfb6b984ea199dddd48838783f173 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Oct 2024 14:08:34 -0400 Subject: [PATCH 28/56] patching after merge --- compiler/noirc_frontend/src/ast/expression.rs | 1 - compiler/noirc_frontend/src/elaborator/mod.rs | 13 +++---------- compiler/noirc_frontend/src/elaborator/patterns.rs | 2 +- compiler/noirc_frontend/src/elaborator/traits.rs | 8 ++------ tooling/lsp/src/trait_impl_method_stub_generator.rs | 2 +- 5 files changed, 7 insertions(+), 19 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 996c38fe2b9..362f94171d3 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -7,7 +7,6 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::node_interner::{ ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId, StructId, }; diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 729cba6e4f1..bc1976febd4 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -8,8 +8,7 @@ use crate::{ ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedTypeData, - UnsupportedNumericGenericType, + UnresolvedTraitConstraint, UnresolvedTypeData, UnsupportedNumericGenericType, }, graph::CrateId, hir::{ @@ -40,13 +39,6 @@ use crate::{ token::{CustomAttribute, SecondaryAttribute}, Shared, Type, TypeVariable, }; -use crate::{ - hir::{ - def_collector::{dc_crate::CollectedItems, errors::DefCollectorErrorKind}, - def_map::{LocalModuleId, ModuleDefId, ModuleId, MAIN_FUNCTION}, - resolution::import::PathResolution, - }, -}; mod comptime; mod expressions; @@ -354,7 +346,8 @@ impl<'context> Elaborator<'context> { // Introduce all numeric generics into scope for generic in &all_generics { if let Kind::Numeric(typ) = &generic.kind() { - let definition = DefinitionKind::GenericType(generic.type_var.clone(), typ.clone()); + let definition = + DefinitionKind::NumericGeneric(generic.type_var.clone(), typ.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = self.add_variable_decl( ident, false, // mutable diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index ba97d041ee3..132d1988b78 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -558,7 +558,7 @@ impl<'context> Elaborator<'context> { self.interner.add_global_reference(global_id, hir_ident.location); } - DefinitionKind::GenericType(_, ref numeric_typ) => { + DefinitionKind::NumericGeneric(_, ref numeric_typ) => { // Initialize numeric generics to a polymorphic integer type in case // they're used in expressions. We must do this here since type_check_variable // does not check definition kinds and otherwise expects parameters to diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 76f05d61762..b4042bd3e31 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -11,12 +11,8 @@ use crate::{ }, hir::{def_collector::dc_crate::UnresolvedTrait, type_check::TypeCheckError}, hir_def::{function::Parameters, traits::TraitFunction}, - macros_api::{ - BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, - NodeInterner, NoirFunction, UnresolvedType, - }, - node_interner::{FuncId, ReferenceId, TraitId}, - Kind, ResolvedGeneric, Type, TypeBindings, + node_interner::{FuncId, NodeInterner, ReferenceId, TraitId}, + ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index a328df1ce99..14b40858bb1 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -10,7 +10,7 @@ use noirc_frontend::{ }, hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, node_interner::{FunctionModifiers, NodeInterner, ReferenceId}, - Kind, ResolvedGeneric, Type, TypeVariableKind, + Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; From 3a82c99d2e0e283dc154320180dbb779591a82aa Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Oct 2024 15:15:38 -0400 Subject: [PATCH 29/56] patch noirc_frontend/tests.rs diff from merging michaeljklein/sync-type-var-kinds --- compiler/noirc_frontend/src/tests.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9517b33d43e..e0be8df0693 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -368,6 +368,7 @@ fn check_trait_not_in_scope() { fn main() { } + "; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); @@ -2912,6 +2913,8 @@ fn trait_impl_for_a_type_that_implements_another_trait() { pub fn use_it(t: T) -> i32 where T: Two { Two::two(t) } + + fn main() {} "#; assert_no_errors(src); } @@ -2928,8 +2931,6 @@ fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() let _ = self; 1 } - - pub use bar::baz; } trait Two { @@ -3154,7 +3155,8 @@ fn use_numeric_generic_in_trait_method() { } fn main() { - let _ = Bar{}.foo([1,2,3]); + let bytes: [u8; 3] = [1,2,3]; + let _ = Bar{}.foo(bytes); } "#; From 172f4a4bde53783f75bf81555b1161e3aaa24ecc Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 3 Oct 2024 11:20:42 -0400 Subject: [PATCH 30/56] wip migrating to TypeVariableKind=Kind and fixing after merge --- .../src/hir/comptime/interpreter.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 21 +++++++++---------- compiler/noirc_frontend/src/node_interner.rs | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 584dcb570cd..01659ce3362 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -35,7 +35,7 @@ use crate::{ }, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, - Shared, Type, TypeBinding, TypeBindings, + Kind, Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ce56eae7574..1cee9315981 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -5,6 +5,8 @@ use std::{ rc::Rc, }; +use acvm::FieldElement; + use crate::{ ast::IntegerBitSize, hir::type_check::{generics::TraitGenerics, TypeCheckError}, @@ -1469,7 +1471,7 @@ impl Type { } } - fn get_inner_type_variable(&self) -> Option> { + fn get_inner_type_variable(&self) -> Option> { match self { Type::TypeVariable(var) => Some((var.1.clone(), var.kind())), Type::NamedGeneric(var, _) => Some((var.1.clone(), var.kind())), @@ -2341,19 +2343,16 @@ impl Type { Some(((1u128 << max_bit_size) - 1).into()) } Type::Bool => Some(FieldElement::from(1u128)), - - Type::TypeVariable(_binding, TypeVariableKind::Normal) => None, - Type::TypeVariable(binding, TypeVariableKind::Integer | TypeVariableKind::IntegerOrField) => { + Type::TypeVariable(var) => { + let binding = &var.1; match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_) => None, + TypeBinding::Unbound(_, type_var_kind) => match type_var_kind { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => None, + Kind::Numeric(typ) => typ.integral_maximum_size(), + }, + TypeBinding::Bound(binding) => typ.integral_maximum_size(), } } - - // TODO: after sync-TypeVariableKind - // TODO: include a similar assertion as with Type::NamedGeneric - // Type::TypeVariable(_var, TypeVariableKind::Numeric(typ)) => typ.integral_maximum_size(), - Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), Type::NamedGeneric(binding, _name, kind) => { let binding_maximum_size = match &*binding.borrow() { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 495a201120d..6a7096c10c2 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -34,7 +34,7 @@ use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; use crate::hir_def::traits::{Trait, TraitConstraint}; -use crate::hir_def::types::{StructType, Type}; +use crate::hir_def::types::{Kind, StructType, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, From 6b28ede37ee450bf142967f2f7263bc3bc98c64c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 4 Oct 2024 11:06:39 -0400 Subject: [PATCH 31/56] wip debugging convert_type size checks --- .../noirc_frontend/src/elaborator/types.rs | 24 +++++++++++-- .../src/hir/comptime/interpreter.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 35 +++++++++++-------- .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 15 ++++---- 5 files changed, 53 insertions(+), 25 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b6b273ec2b5..03a609a737a 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -824,10 +824,11 @@ impl<'context> Elaborator<'context> { } pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { - match from.follow_bindings() { + let from_follow_bindings = from.follow_bindings(); + match from_follow_bindings { Type::Integer(..) | Type::FieldElement | Type::Bool => (), - Type::TypeVariable(var) if var.is_integer() || var.is_integer_or_field() => (), + Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => (), Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity @@ -845,6 +846,25 @@ impl<'context> Elaborator<'context> { } } + // TODO does this resolve https://github.com/noir-lang/noir/issues/6219 ? + // TODO also check minimum size when 'from' is negative + // (casting to a smaller value?) + match (from_follow_bindings.integral_maximum_size(), to.integral_maximum_size()) { + (_, None) => (), + (None, Some(_)) => { + let from = from.clone(); + self.push_err(TypeCheckError::InvalidCast { from, span }); + return Type::Error; + } + (Some(from_maximum_size), Some(to_maximum_size)) => { + if from_maximum_size > to_maximum_size { + let from = from.clone(); + self.push_err(TypeCheckError::InvalidCast { from, span }); + return Type::Error; + } + } + } + match to { Type::Integer(sign, bits) => Type::Integer(*sign, *bits), Type::FieldElement => Type::FieldElement, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 560f2e73a2b..2b881e1be8d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -36,7 +36,7 @@ use crate::{ types::Kind, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, - Kind, Shared, Type, TypeBinding, TypeBindings, + Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index b04a0ef42cd..39090815e04 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -238,6 +238,21 @@ impl Kind { Self::Numeric(typ) => typ.integral_maximum_size(), } } + + /// Ensure the given value fits in self.integral_maximum_size() + pub(crate) fn ensure_value_fits>(&self, value: T) -> Option { + match self.integral_maximum_size() { + None => Some(value), + Some(maximum_size) => { + // TODO possible to avoid clone, e.g. get by ref and return a bool? + if value.clone().into() <= maximum_size { + Some(value) + } else { + None + } + } + } + } } impl std::fmt::Display for Kind { @@ -2329,7 +2344,7 @@ impl Type { } } - fn integral_maximum_size(&self) -> Option { + pub(crate) fn integral_maximum_size(&self) -> Option { match self { Type::FieldElement => None, Type::Integer(sign, num_bits) => { @@ -2347,23 +2362,15 @@ impl Type { Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => None, Kind::Numeric(typ) => typ.integral_maximum_size(), }, - TypeBinding::Bound(binding) => typ.integral_maximum_size(), + TypeBinding::Bound(typ) => typ.integral_maximum_size(), } } Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), - Type::NamedGeneric(binding, _name, kind) => { - let binding_maximum_size = match &*binding.borrow() { + Type::NamedGeneric(binding, _name) => { + match &*binding.borrow() { TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_) => None, - }; - let kind_maximum_size = kind.integral_maximum_size(); - assert!( - binding_maximum_size.is_none() || kind_maximum_size.is_none() || binding_maximum_size == kind_maximum_size, - "NamedGeneric binding has incompatible maximum size ({:?}) with its kind ({:?})", - binding_maximum_size, - kind_maximum_size - ); - binding_maximum_size.or(kind_maximum_size) + TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), + } }, Type::MutableReference(typ) => typ.integral_maximum_size(), Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs).integral_maximum_size(), diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 43190532772..35650b25b9e 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -944,7 +944,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = Self::convert_type(&typ, ident.location)?; ast::Expression::Literal(ast::Literal::Integer( - (value as u128).into(), + value, false, typ, location, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c71837b819d..1882a9165a4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2028,13 +2028,13 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { #[test] fn numeric_generics_type_kind_mismatch() { let src = r#" - fn foo() -> u16 { - N as u16 + fn foo() -> u64 { + N as u64 } global J: u16 = 10; - fn bar() -> u16 { + fn bar() -> u64 { foo::() } @@ -2991,6 +2991,7 @@ fn impl_missing_associated_type() { )); } +// TODO currently failing because "1" is implicitly a Field and is casted to an i32 #[test] fn as_trait_path_syntax_resolves_outside_impl() { let src = r#" @@ -3191,14 +3192,14 @@ fn trait_unconstrained_methods_typechecked_correctly() { unconstrained fn foo(self) -> u64; } - impl Foo for Field { - unconstrained fn foo(self) -> u64 { - self as u64 + impl Foo for u64 { + unconstrained fn foo(self) -> Field { + self as Field } } unconstrained fn main() { - assert_eq(2.foo() as Field, 2.identity()); + assert_eq(2.foo(), 2.identity() as Field); } "#; From 087abe06d484c716093c8cf142268fd5437dcff1 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 4 Oct 2024 15:12:28 -0400 Subject: [PATCH 32/56] wip debugging new size checks, adding value size checks to check_cast and evaluate_infix, add reason to InvalidCast error, follow_bindings in kinds, comment out some tests to focus on others --- .../src/elaborator/expressions.rs | 2 +- .../noirc_frontend/src/elaborator/types.rs | 128 +++++++++-- .../src/hir/comptime/interpreter.rs | 208 ++++++++++-------- .../src/hir/type_check/errors.rs | 9 +- compiler/noirc_frontend/src/hir_def/types.rs | 18 +- compiler/noirc_frontend/src/tests.rs | 180 ++++++++++++++- 6 files changed, 421 insertions(+), 124 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index cbd72788c85..611b612ac44 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -665,7 +665,7 @@ impl<'context> Elaborator<'context> { fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { let (lhs, lhs_type) = self.elaborate_expression(cast.lhs); let r#type = self.resolve_type(cast.r#type); - let result = self.check_cast(&lhs_type, &r#type, span); + let result = self.check_cast(&lhs, &lhs_type, &r#type, span); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 03a609a737a..4180a43d36d 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -619,7 +619,16 @@ impl<'context> Elaborator<'context> { match result.map(|length| length.try_into()) { Ok(Ok(length_value)) => return length_value, - Ok(Err(_cast_err)) => self.push_err(ResolverError::IntegerTooLarge { span }), + + // TODO cleanup + // Ok(Err(_cast_err)) => { + Ok(Err(cast_err)) => { + + // TODO cleanup + dbg!("eval_global_as_array_length (IntegerTooLarge):", self.interner.expression(&length), &cast_err); + + self.push_err(ResolverError::IntegerTooLarge { span }) + } Err(Some(error)) => self.push_err(error), Err(None) => (), } @@ -823,12 +832,42 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { + pub(super) fn check_cast(&mut self, from_expr_id: &ExprId, from: &Type, to: &Type, span: Span) -> Type { let from_follow_bindings = from.follow_bindings(); - match from_follow_bindings { - Type::Integer(..) | Type::FieldElement | Type::Bool => (), - Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => (), + // TODO cleanup + dbg!(&from_follow_bindings, &to, &self.interner.expression(&from_expr_id)); + + // TODO: factor out size checks from type checks? + let from_value_opt = match self.interner.expression(&from_expr_id) { + HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), + + // TODO + other => { + dbg!("from_value_opt: other", other); + None + } + }; + + // TODO type + let (_from_value_size_opt, from_is_polymorphic): (Option, bool) = match from_follow_bindings { + Type::Integer(..) | Type::FieldElement | Type::Bool => (None, false), + + Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => { + + // TODO cleanup/fix + match &*var.borrow() { + TypeBinding::Bound(typ) => { + dbg!("bound: {:?} ;;; {}", &typ, &typ); + } + TypeBinding::Unbound(_id, kind) => { + dbg!("unbound: (kind) {:?} ;;; {}", &kind, &kind); + } + } + + // TODO None? + (None, true) + }, Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity @@ -837,32 +876,69 @@ impl<'context> Elaborator<'context> { self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), span, + reason: "casting from a non-integral type is unsupported".into(), }); + (None, false) } Type::Error => return Type::Error, from => { - self.push_err(TypeCheckError::InvalidCast { from, span }); + let reason = "casting from this type is unsupported".into(); + self.push_err(TypeCheckError::InvalidCast { from, span, reason }); return Type::Error; } - } + }; + + // TODO cleanup + dbg!(&from_value_opt, &from_is_polymorphic); + + // If we have the value itself, that's the size, + // otherwise use its type's maximum size + let from_size = from_value_opt.or(from_follow_bindings.integral_maximum_size()); - // TODO does this resolve https://github.com/noir-lang/noir/issues/6219 ? // TODO also check minimum size when 'from' is negative // (casting to a smaller value?) - match (from_follow_bindings.integral_maximum_size(), to.integral_maximum_size()) { - (_, None) => (), - (None, Some(_)) => { - let from = from.clone(); - self.push_err(TypeCheckError::InvalidCast { from, span }); - return Type::Error; - } - (Some(from_maximum_size), Some(to_maximum_size)) => { + // TODO get is_polymorphic out of match? + match (from_is_polymorphic, from_size, to.integral_maximum_size()) { + // allow casting from unsized polymorphic variables + (true, None, _) => (), + + // allow casting from sized to unsized types + (_, Some(_), None) => (), + + // disallow casting specific unsized types to sized types + (false, None, Some(_)) => { + // TODO: lots of usage of this in the stdlib, e.g. (some_field_element as u64) + // + // let from = from.clone(); + // let reason = "casting from a max-size type to one with a smaller type is unsupported".into(); + // self.push_err(TypeCheckError::InvalidCast { from, span, reason }); + // return Type::Error; + () + } + + // if both types are specific, check their sizes + (_, Some(from_maximum_size), Some(to_maximum_size)) => { if from_maximum_size > to_maximum_size { - let from = from.clone(); - self.push_err(TypeCheckError::InvalidCast { from, span }); - return Type::Error; + // TODO: lots of usage of this in the stdlib + // e.g. + // let high = if crate::field::modulus_num_bits() as u32 > 196 { + // --------------------------------------- casting from size 18446744073709551615 to a smaller size (4294967295) is unsupported + // + // let from = from.clone(); + // let reason = format!("casting from size {} to a smaller size ({}) is unsupported", from_maximum_size, to_maximum_size); + // self.push_err(TypeCheckError::InvalidCast { from, span, reason }); + // return Type::Error; + () } } + + // allow casting from non-polymorphic unsized types + (false, None, None) => (), + } + + // TODO cleanup + if matches!(to, Type::Integer(..) | Type::FieldElement | Type::Bool) { + dbg!("check_cast appears successful"); } match to { @@ -870,7 +946,13 @@ impl<'context> Elaborator<'context> { Type::FieldElement => Type::FieldElement, Type::Bool => Type::Bool, Type::Error => Type::Error, - _ => { + + // TODO cleanup + // _ => { + other => { + // TODO cleanup + dbg!("check_cast failed type check", other); + self.push_err(TypeCheckError::UnsupportedCast { span }); Type::Error } @@ -1845,6 +1927,9 @@ fn try_eval_array_length_id_with_fuel( match interner.expression(&rhs) { HirExpression::Literal(HirLiteral::Integer(int, false)) => { + // TODO cleanup + dbg!("try_eval_array_length_id_with_fuel: try_into_u128", int, int.try_into_u128()); + int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span })) } HirExpression::Ident(ident, _) => { @@ -1895,6 +1980,9 @@ fn try_eval_array_length_id_with_fuel( Interpreter::evaluate_cast_one_step(&cast, rhs, lhs_value, interner) .map_err(|error| Some(ResolverError::ArrayLengthInterpreter { error }))?; + // TODO cleanup + dbg!("try_eval_array_length_id_with_fuel: Cast", &evaluated_value); + evaluated_value .to_u128() .ok_or_else(|| Some(ResolverError::InvalidArrayLengthExpr { span })) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 2b881e1be8d..9d1290fde70 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -895,72 +895,90 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } + fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { - let lhs = self.evaluate(infix.lhs)?; - let rhs = self.evaluate(infix.rhs)?; + let location = self.elaborator.interner.expr_location(&id); + + let lhs_value = self.evaluate(infix.lhs)?; + let rhs_value = self.evaluate(infix.rhs)?; if self.elaborator.interner.get_selected_impl_for_expression(id).is_some() { - return self.evaluate_overloaded_infix(infix, lhs, rhs, id); + // TODO cleanup + dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); + + return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } - let make_error = |this: &mut Self, lhs: Value, rhs: Value, operator| { - let location = this.elaborator.interner.expr_location(&id); + fn make_error_value(lhs: &Value, rhs: &Value, location: Location, operator: &'static str) -> InterpreterError { let lhs = lhs.get_type().into_owned(); let rhs = rhs.get_type().into_owned(); - Err(InvalidValuesForBinary { lhs, rhs, location, operator }) + InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } + } + + // make_error_value if the result is None + fn or_err(lhs_rhs_location: (&Value, &Value, Location), operator: &'static str, result: Option) -> IResult { + let (lhs, rhs, location) = lhs_rhs_location; + result.ok_or(make_error_value(lhs, rhs, location, operator)) + } + + let make_error = |lhs: Value, rhs: Value, operator| -> IResult { + Err(make_error_value(&lhs, &rhs, location, operator)) }; + // TODO possible to remove clones? + let error_ctx = (&lhs_value, &rhs_value, location); + use InterpreterError::InvalidValuesForBinary; match infix.operator.kind { - BinaryOpKind::Add => match (lhs, rhs) { + BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs + rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs + rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs + rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs + rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "+"), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (lhs, rhs) => make_error(lhs, rhs, "+"), }, - BinaryOpKind::Subtract => match (lhs, rhs) { + BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs - rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs - rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs - rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs - rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "-"), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (lhs, rhs) => make_error(lhs, rhs, "-"), }, - BinaryOpKind::Multiply => match (lhs, rhs) { + BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs * rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs * rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs * rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs * rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "*"), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (lhs, rhs) => make_error(lhs, rhs, "*"), }, - BinaryOpKind::Divide => match (lhs, rhs) { + BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs / rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs / rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs / rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs / rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "/"), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (lhs, rhs) => make_error(lhs, rhs, "/"), }, - BinaryOpKind::Equal => match (lhs, rhs) { + BinaryOpKind::Equal => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs == rhs)), @@ -971,9 +989,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "=="), + (lhs, rhs) => make_error(lhs, rhs, "=="), }, - BinaryOpKind::NotEqual => match (lhs, rhs) { + BinaryOpKind::NotEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs != rhs)), @@ -984,9 +1002,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "!="), + (lhs, rhs) => make_error(lhs, rhs, "!="), }, - BinaryOpKind::Less => match (lhs, rhs) { + BinaryOpKind::Less => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs < rhs)), @@ -996,9 +1014,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "<"), + (lhs, rhs) => make_error(lhs, rhs, "<"), }, - BinaryOpKind::LessEqual => match (lhs, rhs) { + BinaryOpKind::LessEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs <= rhs)), @@ -1008,9 +1026,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "<="), + (lhs, rhs) => make_error(lhs, rhs, "<="), }, - BinaryOpKind::Greater => match (lhs, rhs) { + BinaryOpKind::Greater => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs > rhs)), @@ -1020,9 +1038,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, ">"), + (lhs, rhs) => make_error(lhs, rhs, ">"), }, - BinaryOpKind::GreaterEqual => match (lhs, rhs) { + BinaryOpKind::GreaterEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::Bool(lhs >= rhs)), @@ -1032,9 +1050,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, ">="), + (lhs, rhs) => make_error(lhs, rhs, ">="), }, - BinaryOpKind::And => match (lhs, rhs) { + BinaryOpKind::And => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs & rhs)), @@ -1044,9 +1062,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs & rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "&"), + (lhs, rhs) => make_error(lhs, rhs, "&"), }, - BinaryOpKind::Or => match (lhs, rhs) { + BinaryOpKind::Or => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs | rhs)), @@ -1056,9 +1074,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs | rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "|"), + (lhs, rhs) => make_error(lhs, rhs, "|"), }, - BinaryOpKind::Xor => match (lhs, rhs) { + BinaryOpKind::Xor => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs ^ rhs)), @@ -1068,40 +1086,42 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs ^ rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "^"), + (lhs, rhs) => make_error(lhs, rhs, "^"), }, - BinaryOpKind::ShiftRight => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs >> rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs >> rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs >> rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs >> rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, ">>"), + + // TODO: refactor try_into part? + BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, ">>", lhs.checked_shr(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (lhs, rhs) => make_error(lhs, rhs, ">>"), }, - BinaryOpKind::ShiftLeft => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs << rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs << rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs << rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs << rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "<<"), + BinaryOpKind::ShiftLeft => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "<<", lhs.checked_shl(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (lhs, rhs) => make_error(lhs, rhs, "<<"), }, - BinaryOpKind::Modulo => match (lhs, rhs) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs % rhs)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs % rhs)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs % rhs)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs % rhs)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), - (lhs, rhs) => make_error(self, lhs, rhs, "%"), + BinaryOpKind::Modulo => match (lhs_value.clone(), rhs_value.clone()) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (lhs, rhs) => make_error(lhs, rhs, "%"), }, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 54699792901..88f4bd39dbe 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -53,7 +53,7 @@ pub enum TypeCheckError { #[error("Return type in a function cannot be public")] PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] - InvalidCast { from: Type, span: Span }, + InvalidCast { from: Type, span: Span, reason: String }, #[error("Expected a function, but found a(n) {found}")] ExpectedFunction { found: Type, span: Span }, #[error("Type {lhs_type} has no member named {field_name}")] @@ -284,8 +284,11 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { }; Diagnostic::simple_error(msg, String::new(), *span) } - TypeCheckError::InvalidCast { span, .. } - | TypeCheckError::ExpectedFunction { span, .. } + TypeCheckError::InvalidCast { span, reason, .. } => { + Diagnostic::simple_error(error.to_string(), reason.clone(), *span) + } + + TypeCheckError::ExpectedFunction { span, .. } | TypeCheckError::AccessUnknownMember { span, .. } | TypeCheckError::UnsupportedCast { span } | TypeCheckError::TupleIndexOutOfBounds { span, .. } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 39090815e04..65a655c398b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -2212,11 +2212,19 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var) | NamedGeneric(var, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { - return typ.follow_bindings(); - } - self.clone() + TypeVariable(var) => { + let (id, kind) = match &*var.borrow() { + TypeBinding::Bound(typ) => return typ.follow_bindings(), + TypeBinding::Unbound(id, kind) => (*id, kind.follow_bindings()), + }; + TypeVariable(crate::TypeVariable::unbound(id, kind)) + } + NamedGeneric(var, name) => { + let (id, kind) = match &*var.borrow() { + TypeBinding::Bound(typ) => return typ.follow_bindings(), + TypeBinding::Unbound(id, kind) => (*id, kind.follow_bindings()), + }; + NamedGeneric(crate::TypeVariable::unbound(id, kind), name.clone()) } Function(args, ret, env, unconstrained) => { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1882a9165a4..2b8dfa95b25 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1949,6 +1949,184 @@ fn numeric_generic_used_in_turbofish() { assert_no_errors(src); } +// TODO this doesn't involve check_cast +// +// #[test] +// fn numeric_generic_u16_array_size() { +// let src = r#" +// fn len(_arr: [Field; N]) -> u32 { +// N +// } +// +// pub fn foo() -> u32 { +// let fields: [Field; N] = [0; N]; +// len(fields) +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO: cleanup +// dbg!(&errors); +// println!(); +// println!(); +// println!(); +// +// // TODO +// assert_eq!(errors.len(), 2); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } + +// TODO might want to hold off on this one because +// it involves array size evaluation outside of array size usage +// +// // https://github.com/noir-lang/noir/issues/6125 +// #[test] +// fn numeric_generic_field_larger_than_u32() { +// let src = r#" +// global A: Field = 4294967297; +// +// fn foo() { } +// +// fn main() { +// let _ = foo::(); +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO: cleanup +// dbg!(&errors); +// println!(); +// println!(); +// println!(); +// +// // TODO +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } + +// https://github.com/noir-lang/noir/issues/6126 +#[test] +fn numeric_generic_field_arithmetic_larger_than_u32() { + let src = r#" + struct Foo {} + + impl Foo { + fn size(self) -> Field { + F + } + } + + // 2^32 - 1 + global A: Field = 4294967295; + + fn foo() -> Foo { + Foo {} + } + + fn main() { + let _ = foo::().size(); + } + "#; + let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + println!(); + println!(); + println!(); + + // TODO + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) + )); +} + +// https://github.com/noir-lang/noir/issues/6219 +#[test] +fn cast_256_to_u8_size_checks() { + let src = r#" + fn main() { + assert(256 as u8 == 0); + } + "#; + let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + println!(); + println!(); + println!(); + + // TODO + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::InvalidCast { .. }), + )); +} + +// TODO add negative integer literal checks +// +// // https://github.com/noir-lang/noir/issues/6219 +// #[test] +// fn cast_negative_one_to_u8_size_checks() { +// let src = r#" +// fn main() { +// assert((-1) as u8 != 0); +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO: cleanup +// dbg!(&errors); +// println!(); +// println!(); +// println!(); +// +// // TODO +// assert_eq!(errors.len(), 4); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } + +// https://github.com/noir-lang/noir/issues/6219 +#[test] +fn cast_negative_one_infix_to_u8_size_checks() { + let src = r#" + fn main() { + assert((0 - 1) as u8 != 0); + } + "#; + let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + println!(); + println!(); + println!(); + + // TODO + assert_eq!(errors.len(), 4); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + #[test] fn constant_used_with_numeric_generic() { let src = r#" @@ -3189,7 +3367,7 @@ fn trait_unconstrained_methods_typechecked_correctly() { self } - unconstrained fn foo(self) -> u64; + unconstrained fn foo(self) -> Field; } impl Foo for u64 { From 7db0f14d8c1bf9b85737dbfe1fe034d3cf178317 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Oct 2024 07:49:20 -0400 Subject: [PATCH 33/56] wip --- .../noirc_frontend/src/elaborator/types.rs | 66 ++++--------- .../src/hir/comptime/interpreter.rs | 50 +++++----- .../src/hir/type_check/errors.rs | 5 + compiler/noirc_frontend/src/hir_def/types.rs | 98 +++++++++++++------ .../src/hir_def/types/arithmetic.rs | 33 ++++--- 5 files changed, 136 insertions(+), 116 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 4180a43d36d..0a385752eb6 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -849,26 +849,12 @@ impl<'context> Elaborator<'context> { } }; - // TODO type - let (_from_value_size_opt, from_is_polymorphic): (Option, bool) = match from_follow_bindings { - Type::Integer(..) | Type::FieldElement | Type::Bool => (None, false), - - Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => { - - // TODO cleanup/fix - match &*var.borrow() { - TypeBinding::Bound(typ) => { - dbg!("bound: {:?} ;;; {}", &typ, &typ); - } - TypeBinding::Unbound(_id, kind) => { - dbg!("unbound: (kind) {:?} ;;; {}", &kind, &kind); - } - } - - // TODO None? - (None, true) - }, + // TODO disable type downsizing checks + // TODO (Option.., ..) type + let from_is_polymorphic = match from_follow_bindings { + Type::Integer(..) | Type::FieldElement | Type::Bool => false, + Type::TypeVariable(ref var) if var.is_integer() || var.is_integer_or_field() => true, Type::TypeVariable(_) => { // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. @@ -878,7 +864,7 @@ impl<'context> Elaborator<'context> { span, reason: "casting from a non-integral type is unsupported".into(), }); - (None, false) + false } Type::Error => return Type::Error, from => { @@ -891,47 +877,33 @@ impl<'context> Elaborator<'context> { // TODO cleanup dbg!(&from_value_opt, &from_is_polymorphic); - // If we have the value itself, that's the size, - // otherwise use its type's maximum size - let from_size = from_value_opt.or(from_follow_bindings.integral_maximum_size()); - // TODO also check minimum size when 'from' is negative // (casting to a smaller value?) // TODO get is_polymorphic out of match? - match (from_is_polymorphic, from_size, to.integral_maximum_size()) { + match (from_is_polymorphic, from_value_opt, to.integral_maximum_size()) { // allow casting from unsized polymorphic variables (true, None, _) => (), // allow casting from sized to unsized types (_, Some(_), None) => (), - // disallow casting specific unsized types to sized types - (false, None, Some(_)) => { - // TODO: lots of usage of this in the stdlib, e.g. (some_field_element as u64) - // - // let from = from.clone(); - // let reason = "casting from a max-size type to one with a smaller type is unsupported".into(); - // self.push_err(TypeCheckError::InvalidCast { from, span, reason }); - // return Type::Error; - () - } + // allow casting specific unsized types to sized types + (false, None, Some(_)) => (), - // if both types are specific, check their sizes - (_, Some(from_maximum_size), Some(to_maximum_size)) => { + // when casting a polymorphic value to a specifically sized type, + // check that it fits or throw a warning + (true, Some(from_maximum_size), Some(to_maximum_size)) => { if from_maximum_size > to_maximum_size { - // TODO: lots of usage of this in the stdlib - // e.g. - // let high = if crate::field::modulus_num_bits() as u32 > 196 { - // --------------------------------------- casting from size 18446744073709551615 to a smaller size (4294967295) is unsupported - // - // let from = from.clone(); - // let reason = format!("casting from size {} to a smaller size ({}) is unsupported", from_maximum_size, to_maximum_size); - // self.push_err(TypeCheckError::InvalidCast { from, span, reason }); - // return Type::Error; - () + let from = from.clone(); + let reason = format!("casting untyped value ({}) to a type with a maximum size ({}) that's smaller than it", from_maximum_size, to_maximum_size); + self.push_err(TypeCheckError::DownsizingCast { from, span, reason }); + return Type::Error; } } + // allow casting typed values to other sizes + (false, Some(_), Some(_)) => (), + // allow casting from non-polymorphic unsized types (false, None, None) => (), } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 9d1290fde70..1bfeecc2980 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -932,38 +932,38 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { match infix.operator.kind { BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_add(rhs))), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_add(rhs))), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_add(rhs))), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_add(rhs))), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_add(rhs))), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_add(rhs))), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_add(rhs))), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_add(rhs))), (lhs, rhs) => make_error(lhs, rhs, "+"), }, BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_sub(rhs))), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_sub(rhs))), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_sub(rhs))), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_sub(rhs))), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_sub(rhs))), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_sub(rhs))), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_sub(rhs))), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_sub(rhs))), (lhs, rhs) => make_error(lhs, rhs, "-"), }, BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_mul(rhs))), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_mul(rhs))), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_mul(rhs))), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_mul(rhs))), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_mul(rhs))), + (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_mul(rhs))), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_mul(rhs))), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_mul(rhs))), (lhs, rhs) => make_error(lhs, rhs, "*"), }, BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { @@ -1088,8 +1088,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), (lhs, rhs) => make_error(lhs, rhs, "^"), }, - - // TODO: refactor try_into part? BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 88f4bd39dbe..b1b9ad2aa4b 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -54,6 +54,8 @@ pub enum TypeCheckError { PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] InvalidCast { from: Type, span: Span, reason: String }, + #[error("Casting value with {from} to a smaller type")] + DownsizingCast { from: Type, span: Span, reason: String }, #[error("Expected a function, but found a(n) {found}")] ExpectedFunction { found: Type, span: Span }, #[error("Type {lhs_type} has no member named {field_name}")] @@ -287,6 +289,9 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { TypeCheckError::InvalidCast { span, reason, .. } => { Diagnostic::simple_error(error.to_string(), reason.clone(), *span) } + TypeCheckError::DownsizingCast { span, reason, .. } => { + Diagnostic::simple_warning(error.to_string(), reason.clone(), *span) + } TypeCheckError::ExpectedFunction { span, .. } | TypeCheckError::AccessUnknownMember { span, .. } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 65a655c398b..56c142820f5 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -5,7 +5,7 @@ use std::{ rc::Rc, }; -use acvm::FieldElement; +use acvm::{AcirField, FieldElement}; use crate::{ ast::IntegerBitSize, @@ -112,9 +112,11 @@ pub enum Type { /// will be and thus needs the full TypeVariable link. Forall(GenericTypeVars, Box), - /// A type-level integer. Included to let an Array's size type variable - /// bind to an integer without special checks to bind it to a non-type. - Constant(u32, Kind), + /// A type-level integer. Included to let + /// 1. an Array's size type variable + /// bind to an integer without special checks to bind it to a non-type. + /// 2. values be used at the type level + Constant(FieldElement, Kind), /// The type of quoted code in macros. This is always a comptime-only type Quoted(QuotedType), @@ -1658,8 +1660,7 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - // TODO(https://github.com/noir-lang/noir/pull/6137): replace evaluate_to_u32 - if let Some(other_value) = other.evaluate_to_u32() { + if let Some(other_value) = other.evaluate_to_field_element(kind) { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -1829,28 +1830,38 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - if let Some((binding, _kind)) = self.get_inner_type_variable() { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - return binding.evaluate_to_u32(); - } - } - - match self.canonicalize() { - Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x, _kind) => Some(x), - Type::InfixExpr(lhs, op, rhs) => { - let lhs_u32 = lhs.evaluate_to_u32()?; - let rhs_u32 = rhs.evaluate_to_u32()?; - op.function(lhs_u32, rhs_u32, &lhs.infix_kind(&rhs)) - } - _ => None, - } + self.evaluate_to_field_element(&Kind::u32()).and_then(|field_element| field_element.try_to_u32()) } // TODO: implement! pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { - self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) - + todo!() + // // self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) + // + // if let Some((binding, _kind)) = self.get_inner_type_variable() { + // if let TypeBinding::Bound(binding) = &*binding.borrow() { + // return binding.evaluate_to_field_element(); + // } + // } + // + // match self.canonicalize() { + // // TODO: unused case? (was a patch to use evaluate_to_u32 for array.len()) + // // Type::Array(len, _elem) => len.evaluate_to_field_element(), + // + // Type::Constant(x, constant_kind) => { + // if kind.unifies(constant_kind) { + // kind.ensure_value_fits(x) + // } else { + // None + // } + // } + // Type::InfixExpr(lhs, op, rhs) => { + // let lhs_value = lhs.evaluate_to_field_element()?; + // let rhs_value = rhs.evaluate_to_field_element()?; + // op.function(lhs_value, rhs_value, &lhs.infix_kind(&rhs)) + // } + // _ => None, + // } } /// Iterate over the fields of this type. @@ -2437,13 +2448,38 @@ impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator // TODO(https://github.com/noir-lang/noir/pull/6137): the Kind is included // since it'll be needed for size checks - pub fn function(self, a: u32, b: u32, _kind: &Kind) -> Option { - match self { - BinaryTypeOperator::Addition => a.checked_add(b), - BinaryTypeOperator::Subtraction => a.checked_sub(b), - BinaryTypeOperator::Multiplication => a.checked_mul(b), - BinaryTypeOperator::Division => a.checked_div(b), - BinaryTypeOperator::Modulo => a.checked_rem(b), + pub fn function(self, a: FieldElement, b: FieldElement, kind: &Kind) -> Option { + match kind.follow_bindings().integral_maximum_size() { + None => { + match self { + BinaryTypeOperator::Addition => Some(a + b), + BinaryTypeOperator::Subtraction => Some(a - b), + BinaryTypeOperator::Multiplication => Some(a * b), + BinaryTypeOperator::Division => { + if b == FieldElement::zero() { + None + } else { + Some(a / b) + } + } + BinaryTypeOperator::Modulo => None, + } + } + Some(maximum_size) => { + let maximum_size = maximum_size.to_i128(); + let a = a.to_i128(); + let b = b.to_i128(); + + let result = match self { + BinaryTypeOperator::Addition => (a + b) % maximum_size, + BinaryTypeOperator::Subtraction => (a - b) % maximum_size, + BinaryTypeOperator::Multiplication => (a * b) % maximum_size, + BinaryTypeOperator::Division => (a.checked_div(b)?) % maximum_size, + BinaryTypeOperator::Modulo => a.checked_rem(b)?, + }; + + Some(FieldElement::from(result)) + } } } diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index af94ef27535..4fca5399dbe 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -1,5 +1,7 @@ use std::collections::BTreeMap; +use acvm::{AcirField, FieldElement}; + use crate::{BinaryTypeOperator, Type, TypeBindings, UnificationError}; impl Type { @@ -15,10 +17,11 @@ impl Type { pub fn canonicalize(&self) -> Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs) => { - // evaluate_to_u32 also calls canonicalize so if we just called - // `self.evaluate_to_u32()` we'd get infinite recursion. + let kind = lhs.infix_kind(&rhs); + // evaluate_to_field_element also calls canonicalize so if we just called + // `self.evaluate_to_field_element(..)` we'd get infinite recursion. if let (Some(lhs_u32), Some(rhs_u32)) = - (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) + (lhs.evaluate_to_field_element(&kind), rhs.evaluate_to_field_element(&kind)) { let kind = lhs.infix_kind(&rhs); if let Some(result) = op.function(lhs_u32, rhs_u32, &kind) { @@ -58,7 +61,7 @@ impl Type { // Maps each term to the number of times that term was used. let mut sorted = BTreeMap::new(); - let zero_value = if op == BinaryTypeOperator::Addition { 0 } else { 1 }; + let zero_value = if op == BinaryTypeOperator::Addition { FieldElement::zero() } else { FieldElement::one() }; let mut constant = zero_value; // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. @@ -175,14 +178,15 @@ impl Type { fn parse_partial_constant_expr( lhs: &Type, rhs: &Type, - ) -> Option<(Box, BinaryTypeOperator, u32, u32)> { - let rhs = rhs.evaluate_to_u32()?; + ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { + let kind = lhs.infix_kind(rhs); + let rhs = rhs.evaluate_to_field_element(&kind)?; let Type::InfixExpr(l_type, l_op, l_rhs) = lhs.follow_bindings() else { return None; }; - let l_rhs = l_rhs.evaluate_to_u32()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind)?; Some((l_type, l_op, l_rhs, rhs)) } @@ -215,8 +219,11 @@ impl Type { if l_op == Division { op = op.inverse()?; } + + let divides_evenly = !lhs.infix_kind(rhs).is_field_element() && l_const.to_i128() % r_const != 0 + // If op is a division we need to ensure it divides evenly - if op == Division && (r_const == 0 || l_const % r_const != 0) { + if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { None } else { let result = op.function(l_const, r_const, &lhs.infix_kind(rhs))?; @@ -237,8 +244,9 @@ impl Type { ) -> Result<(), UnificationError> { if let Type::InfixExpr(lhs_a, op_a, rhs_a) = self { if let Some(inverse) = op_a.inverse() { - if let Some(rhs_a_u32) = rhs_a.evaluate_to_u32() { - let rhs_a = Box::new(Type::Constant(rhs_a_u32, lhs_a.infix_kind(rhs_a))); + let kind = lhs_a.infix_kind(rhs_a); + if let Some(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind) { + let rhs_a = Box::new(Type::Constant(rhs_a_value, lhs_a.infix_kind(rhs_a))); let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); let mut tmp_bindings = bindings.clone(); @@ -252,8 +260,9 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b) = other { if let Some(inverse) = op_b.inverse() { - if let Some(rhs_b_u32) = rhs_b.evaluate_to_u32() { - let rhs_b = Box::new(Type::Constant(rhs_b_u32, lhs_b.infix_kind(rhs_b))); + let kind = lhs_b.infix_kind(rhs_b); + if let Some(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind) { + let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b); let mut tmp_bindings = bindings.clone(); From a9c9a2021a7f4f753080a9d88d6e4e92f31eaf78 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Oct 2024 09:53:12 -0400 Subject: [PATCH 34/56] wip debugging: Constant value u32 -> FieldElement, ensure eval_global_as_array_length only runs on u32's, add is_field_element, implemented the rest of evaluate_to_field_element --- compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../src/elaborator/expressions.rs | 9 +- .../noirc_frontend/src/elaborator/types.rs | 13 ++- .../src/hir/comptime/hir_to_display_ast.rs | 2 +- .../src/hir/comptime/interpreter/builtin.rs | 12 ++- .../noirc_frontend/src/hir/comptime/value.rs | 2 +- .../src/hir/resolution/errors.rs | 3 +- compiler/noirc_frontend/src/hir_def/types.rs | 88 +++++++++++++------ .../src/hir_def/types/arithmetic.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 9 +- 10 files changed, 94 insertions(+), 53 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 94e81b19582..bc42d10f212 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -20,6 +20,7 @@ pub use expression::*; pub use function::*; pub use docs::*; +use acvm::FieldElement; use noirc_errors::Span; use serde::{Deserialize, Serialize}; pub use statement::*; @@ -219,7 +220,7 @@ pub struct UnaryRhsMethodCall { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { Variable(Path), - Constant(u32, Span), + Constant(FieldElement, Span), BinaryOperation( Box, BinaryTypeOperator, @@ -421,12 +422,12 @@ impl UnresolvedTypeExpression { fn from_expr_helper(expr: Expression) -> Result { match expr.kind { ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { - Some(int) => Ok(UnresolvedTypeExpression::Constant(int, expr.span)), + Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.span)), None => Err(expr), }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span)); + let lhs = Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 611b612ac44..30249a10ed5 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1,3 +1,4 @@ +use acvm::{AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Location, Span}; use regex::Regex; @@ -161,7 +162,7 @@ impl<'context> Elaborator<'context> { (Lit(int), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { - let len = Type::Constant(str.len() as u32, Kind::u32()); + let len = Type::Constant(str.len().into(), Kind::u32()); (Lit(HirLiteral::Str(str)), Type::String(Box::new(len))) } Literal::FmtStr(str) => self.elaborate_fmt_string(str, span), @@ -203,7 +204,7 @@ impl<'context> Elaborator<'context> { elem_id }); - let length = Type::Constant(elements.len() as u32, Kind::u32()); + let length = Type::Constant(elements.len().into(), Kind::u32()); (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { @@ -211,7 +212,7 @@ impl<'context> Elaborator<'context> { let length = UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { self.push_err(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(0, span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), span) }); let length = self.convert_expression_type(length, &Kind::u32(), span); @@ -267,7 +268,7 @@ impl<'context> Elaborator<'context> { } } - let len = Type::Constant(str.len() as u32, Kind::u32()); + let len = Type::Constant(str.len().into(), Kind::u32()); let typ = Type::FmtString(Box::new(len), Box::new(Type::Tuple(capture_types))); (HirExpression::Literal(HirLiteral::FmtStr(str, fmt_str_idents)), typ) } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 0a385752eb6..1ed528f481e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -412,7 +412,18 @@ impl<'context> Elaborator<'context> { .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) .unwrap_or(Kind::u32()); - Some(Type::Constant(self.eval_global_as_array_length(id, path), kind)) + // TODO: support non-u32 generics + if kind.unifies(&Kind::u32()) { + Some(Type::Constant(self.eval_global_as_array_length(id, path).into(), kind)) + } else { + let error = TypeCheckError::TypeKindMismatch { + expected_kind: Kind::u32().to_string(), + expr_kind: kind.to_string(), + expr_span: path.span(), + }; + self.push_err(error); + None + } } _ => None, } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 3542c724b30..97d90b905d4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -417,7 +417,7 @@ impl HirArrayLiteral { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { Type::Constant(length, _kind) => { - let literal = Literal::Integer((*length as u128).into(), false); + let literal = Literal::Integer(*length, false); let expr_kind = ExpressionKind::Literal(literal); Box::new(Expression::new(expr_kind, span)) } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 0b028217c46..c550e4b6b53 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -305,7 +305,7 @@ fn str_as_bytes( let bytes: im::Vector = string.bytes().map(Value::U8).collect(); let byte_array_type = Type::Array( - Box::new(Type::Constant(bytes.len() as u32, Kind::u32())), + Box::new(Type::Constant(bytes.len().into(), Kind::u32())), Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), ); Ok(Value::Array(bytes, byte_array_type)) @@ -780,8 +780,12 @@ fn to_le_radix( let value = get_field(value)?; let radix = get_u32(radix)?; let limb_count = if let Type::Array(length, _) = return_type { - if let Type::Constant(limb_count, _kind) = *length { - limb_count + if let Type::Constant(limb_count, kind) = *length { + if kind.unifies(&Kind::u32()) { + limb_count + } else { + return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); + } } else { return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); } @@ -791,7 +795,7 @@ fn to_le_radix( // Decompose the integer into its radix digits in little endian form. let decomposed_integer = compute_to_radix_le(value, radix); - let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) { + let decomposed_integer = vecmap(0..limb_count.to_u128() as usize, |i| match decomposed_integer.get(i) { Some(digit) => Value::U8(*digit), None => Value::U8(0), }); diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 4c968234f04..97b219b7fc0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -121,7 +121,7 @@ impl Value { Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), Value::String(value) => { - let length = Type::Constant(value.len() as u32, Kind::u32()); + let length = Type::Constant(value.len().into(), Kind::u32()); Type::String(Box::new(length)) } Value::FormatString(_, typ) => return Cow::Borrowed(typ), diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 4e9520ad761..ac0d1078696 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; @@ -125,7 +126,7 @@ pub enum ResolverError { #[error("Associated constants may only be a field or integer type")] AssociatedConstantsMustBeNumeric { span: Span }, #[error("Overflow in `{lhs} {op} {rhs}`")] - OverflowInType { lhs: u32, op: crate::BinaryTypeOperator, rhs: u32, span: Span }, + OverflowInType { lhs: FieldElement, op: crate::BinaryTypeOperator, rhs: FieldElement, span: Span }, #[error("`quote` cannot be used in runtime code")] QuoteInRuntimeCode { span: Span }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 56c142820f5..f5ae7e0aad7 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -172,6 +172,14 @@ impl Kind { matches!(self, Self::Numeric { .. }) } + pub(crate) fn is_field_element(&self) -> bool { + match self { + Kind::Numeric(typ) => typ.is_field_element(), + Kind::IntegerOrField => true, + _ => false, + } + } + pub(crate) fn u32() -> Self { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } @@ -720,6 +728,13 @@ impl TypeVariable { TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), } } + + fn is_field_element(&self) -> bool { + match &*self.borrow() { + TypeBinding::Bound(binding) => binding.is_field_element(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.is_field_element(), + } + } } /// TypeBindings are the mutable insides of a TypeVariable. @@ -948,6 +963,15 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(_, _)) } + fn is_field_element(&self) -> bool { + match self.follow_bindings() { + Type::FieldElement => true, + Type::TypeVariable(var) => var.is_field_element(), + Type::Constant(_, kind) => kind.is_field_element(), + _ => false, + } + } + pub fn is_signed(&self) -> bool { matches!(self.follow_bindings(), Type::Integer(Signedness::Signed, _)) } @@ -1835,33 +1859,39 @@ impl Type { // TODO: implement! pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { - todo!() - // // self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) - // - // if let Some((binding, _kind)) = self.get_inner_type_variable() { - // if let TypeBinding::Bound(binding) = &*binding.borrow() { - // return binding.evaluate_to_field_element(); - // } - // } - // - // match self.canonicalize() { - // // TODO: unused case? (was a patch to use evaluate_to_u32 for array.len()) - // // Type::Array(len, _elem) => len.evaluate_to_field_element(), - // - // Type::Constant(x, constant_kind) => { - // if kind.unifies(constant_kind) { - // kind.ensure_value_fits(x) - // } else { - // None - // } - // } - // Type::InfixExpr(lhs, op, rhs) => { - // let lhs_value = lhs.evaluate_to_field_element()?; - // let rhs_value = rhs.evaluate_to_field_element()?; - // op.function(lhs_value, rhs_value, &lhs.infix_kind(&rhs)) - // } - // _ => None, - // } + // self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) + + if let Some((binding, binding_kind)) = self.get_inner_type_variable() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { + if kind.unifies(&binding_kind) { + return binding.evaluate_to_field_element(&binding_kind); + } + } + } + + match self.canonicalize() { + // TODO: unused case? (was a patch to use evaluate_to_u32 for array.len()) + // Type::Array(len, _elem) => len.evaluate_to_field_element(), + + Type::Constant(x, constant_kind) => { + if kind.unifies(&constant_kind) { + kind.ensure_value_fits(x) + } else { + None + } + } + Type::InfixExpr(lhs, op, rhs) => { + let infix_kind = lhs.infix_kind(&rhs); + if kind.unifies(&infix_kind) { + let lhs_value = lhs.evaluate_to_field_element(&infix_kind)?; + let rhs_value = rhs.evaluate_to_field_element(&infix_kind)?; + op.function(lhs_value, rhs_value, &infix_kind) + } else { + None + } + } + _ => None, + } } /// Iterate over the fields of this type. @@ -2373,7 +2403,7 @@ impl Type { } Some(((1u128 << max_bit_size) - 1).into()) } - Type::Bool => Some(FieldElement::from(1u128)), + Type::Bool => Some(FieldElement::one()), Type::TypeVariable(var) => { let binding = &var.1; match &*binding.borrow() { @@ -2478,7 +2508,7 @@ impl BinaryTypeOperator { BinaryTypeOperator::Modulo => a.checked_rem(b)?, }; - Some(FieldElement::from(result)) + Some(result.into()) } } } diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 4fca5399dbe..6c358452f6a 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -220,7 +220,7 @@ impl Type { op = op.inverse()?; } - let divides_evenly = !lhs.infix_kind(rhs).is_field_element() && l_const.to_i128() % r_const != 0 + let divides_evenly = !lhs.infix_kind(rhs).is_field_element() && l_const.to_i128() % r_const.to_i128() != 0; // If op is a division we need to ensure it divides evenly if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 2b8dfa95b25..49680e0f397 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2063,17 +2063,10 @@ fn cast_256_to_u8_size_checks() { "#; let errors = get_program_errors(src); - // TODO: cleanup - dbg!(&errors); - println!(); - println!(); - println!(); - - // TODO assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::InvalidCast { .. }), + CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), )); } From d859bc0ba2a852ea3158411377e514cd68420095 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Oct 2024 12:15:36 -0400 Subject: [PATCH 35/56] wip debugging, add tests for intepreter: return type checking and let statement type checking --- compiler/noirc_frontend/src/elaborator/mod.rs | 3 + .../noirc_frontend/src/elaborator/types.rs | 17 ++++-- .../src/hir/comptime/interpreter.rs | 33 +++++++++-- .../noirc_frontend/src/hir/comptime/tests.rs | 19 ++++++- compiler/noirc_frontend/src/hir_def/types.rs | 4 +- compiler/noirc_frontend/src/tests.rs | 55 +++++++++---------- 6 files changed, 90 insertions(+), 41 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index bd78febc1d0..a571c18b1ae 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -363,6 +363,9 @@ impl<'context> Elaborator<'context> { } pub(crate) fn elaborate_function(&mut self, id: FuncId) { + // TODO + dbg!("elaborate_function"); + let func_meta = self.interner.func_meta.get_mut(&id); let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 9d6e4875611..1ef7b6dea96 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -419,6 +419,9 @@ impl<'context> Elaborator<'context> { // TODO: support non-u32 generics if kind.unifies(&Kind::u32()) { + // TODO cleanup + dbg!("lookup_generic_or_global_type", &kind); + Some(Type::Constant(self.eval_global_as_array_length(id, path).into(), kind)) } else { let error = TypeCheckError::TypeKindMismatch { @@ -917,8 +920,8 @@ impl<'context> Elaborator<'context> { if from_maximum_size > to_maximum_size { let from = from.clone(); let reason = format!("casting untyped value ({}) to a type with a maximum size ({}) that's smaller than it", from_maximum_size, to_maximum_size); + // we warn that the 'to' type is too small for the value self.push_err(TypeCheckError::DownsizingCast { from, span, reason }); - return Type::Error; } } @@ -929,11 +932,6 @@ impl<'context> Elaborator<'context> { (false, None, None) => (), } - // TODO cleanup - if matches!(to, Type::Integer(..) | Type::FieldElement | Type::Bool) { - dbg!("check_cast appears successful"); - } - match to { Type::Integer(sign, bits) => Type::Integer(*sign, *bits), Type::FieldElement => Type::FieldElement, @@ -1576,6 +1574,9 @@ impl<'context> Elaborator<'context> { let (expr_span, empty_function) = self.function_info(body_id); let declared_return_type = meta.return_type(); + // TODO + dbg!("type_check_function_body", &body_type, &declared_return_type); + let func_span = self.interner.expr_span(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet if let Type::TraitAsType(trait_id, _, generics) = declared_return_type { if self @@ -1611,6 +1612,10 @@ impl<'context> Elaborator<'context> { } error }); + + // TODO + dbg!("after unify_with_coercions", &body_type, &declared_return_type); + } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index edaaec0b65c..259ebcfb959 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -675,6 +675,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let typ = self.elaborator.interner.id_type(id).follow_bindings(); let location = self.elaborator.interner.expr_location(&id); + // TODO + dbg!("evaluate_integer", &value, &typ); + if let Type::FieldElement = &typ { Ok(Value::Field(value)) } else if let Type::Integer(sign, bit_size) = &typ { @@ -902,9 +905,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let lhs_value = self.evaluate(infix.lhs)?; let rhs_value = self.evaluate(infix.rhs)?; + // TODO cleanup + dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); + if self.elaborator.interner.get_selected_impl_for_expression(id).is_some() { - // TODO cleanup - dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); + // // TODO cleanup + // dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } @@ -1389,6 +1395,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn evaluate_cast(&mut self, cast: &HirCastExpression, id: ExprId) -> IResult { + + // TODO cleanup + dbg!("evaluate_cast", cast); + let evaluated_lhs = self.evaluate(cast.lhs)?; Self::evaluate_cast_one_step(cast, id, evaluated_lhs, self.elaborator.interner) } @@ -1588,6 +1598,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn evaluate_assign(&mut self, assign: HirAssignStatement) -> IResult { + // TODO + dbg!("evaluate_assign", &assign); + let rhs = self.evaluate(assign.expression)?; self.store_lvalue(assign.lvalue, rhs)?; Ok(Value::Unit) @@ -1596,7 +1609,13 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { match lvalue { HirLValue::Ident(ident, typ) => self.mutate(ident.id, rhs, ident.location), - HirLValue::Dereference { lvalue, element_type: _, location } => { + + // TODO revert + // HirLValue::Dereference { lvalue, element_type: _, location } => { + HirLValue::Dereference { lvalue, element_type, location } => { + // TODO cleanup + dbg!("store_lvalue", element_type); + match self.evaluate_lvalue(&lvalue)? { Value::Pointer(value, _) => { *value.borrow_mut() = rhs; @@ -1657,7 +1676,13 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Pointer(elem, true) => Ok(elem.borrow().clone()), other => Ok(other), }, - HirLValue::Dereference { lvalue, element_type: _, location } => { + + // TODO revert + // HirLValue::Dereference { lvalue, element_type: _, location } => { + HirLValue::Dereference { lvalue, element_type, location } => { + // TODO cleanup + dbg!("evaluate_lvalue", element_type); + match self.evaluate_lvalue(lvalue)? { Value::Pointer(value, _) => Ok(value.borrow().clone()), value => { diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index 310c4a78414..1f3fb5b7a1e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -78,6 +78,23 @@ fn interpreter_works() { assert_eq!(result, Value::Field(3u128.into())); } +#[test] +fn interpreter_type_checking_works() { + let program = "comptime fn main() -> pub u8 { 3 }"; + let result = interpret(program); + assert_eq!(result, Value::U8(3u8)); +} + +#[test] +fn let_statement_works() { + let program = "comptime fn main() -> pub i8 { + let x = 4; + x + }"; + let result = interpret(program); + assert_eq!(result, Value::I8(4)); +} + #[test] fn mutation_works() { let program = "comptime fn main() -> pub i8 { @@ -277,5 +294,5 @@ fn generic_functions() { } "; let result = interpret(program); - assert!(matches!(result, Value::U8(2))); + assert_eq!(result, Value::U8(2)); } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index f5ae7e0aad7..6030f22fd6a 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1850,7 +1850,6 @@ impl Type { } } - // TODO: replace usages outside of array/slice/str with evaluate_to_field_element /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { @@ -1859,7 +1858,8 @@ impl Type { // TODO: implement! pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { - // self.evaluate_to_u32().map(|result| (result as u128).into()).and_then(|result| kind.ensure_value_fits(result)) + // TODO + dbg!("evaluate_to_field_element", self); if let Some((binding, binding_kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 49680e0f397..23feafe06fc 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1986,33 +1986,33 @@ fn numeric_generic_used_in_turbofish() { // TODO might want to hold off on this one because // it involves array size evaluation outside of array size usage // -// // https://github.com/noir-lang/noir/issues/6125 -// #[test] -// fn numeric_generic_field_larger_than_u32() { -// let src = r#" -// global A: Field = 4294967297; -// -// fn foo() { } -// -// fn main() { -// let _ = foo::(); -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO: cleanup -// dbg!(&errors); -// println!(); -// println!(); -// println!(); -// -// // TODO -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } +// https://github.com/noir-lang/noir/issues/6125 +#[test] +fn numeric_generic_field_larger_than_u32() { + let src = r#" + global A: Field = 4294967297; + + fn foo() { } + + fn main() { + let _ = foo::(); + } + "#; + let errors = get_program_errors(src); + + // TODO: cleanup + dbg!(&errors); + println!(); + println!(); + println!(); + + // TODO + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} // https://github.com/noir-lang/noir/issues/6126 #[test] @@ -3162,7 +3162,6 @@ fn impl_missing_associated_type() { )); } -// TODO currently failing because "1" is implicitly a Field and is casted to an i32 #[test] fn as_trait_path_syntax_resolves_outside_impl() { let src = r#" From 5a8ff677dcea10fc3ff1ec8b92f0f529e4f8aa41 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Oct 2024 14:15:32 -0400 Subject: [PATCH 36/56] fix follow_bindings error, follow_bindings on kind when matching, move TypeKindMismatch error to EvaluatedGlobalPartialSizeChecks and comment that it's incomplete, some cleanup --- compiler/noirc_frontend/src/elaborator/mod.rs | 3 - .../noirc_frontend/src/elaborator/types.rs | 20 +-- .../src/hir/comptime/interpreter.rs | 24 +-- .../src/hir/type_check/errors.rs | 10 ++ compiler/noirc_frontend/src/hir_def/types.rs | 42 ++---- compiler/noirc_frontend/src/tests.rs | 138 +++++++++++------- 6 files changed, 118 insertions(+), 119 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index a571c18b1ae..bd78febc1d0 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -363,9 +363,6 @@ impl<'context> Elaborator<'context> { } pub(crate) fn elaborate_function(&mut self, id: FuncId) { - // TODO - dbg!("elaborate_function"); - let func_meta = self.interner.func_meta.get_mut(&id); let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 1ef7b6dea96..19a00545bda 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -417,21 +417,16 @@ impl<'context> Elaborator<'context> { .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) .unwrap_or(Kind::u32()); - // TODO: support non-u32 generics - if kind.unifies(&Kind::u32()) { - // TODO cleanup - dbg!("lookup_generic_or_global_type", &kind); - - Some(Type::Constant(self.eval_global_as_array_length(id, path).into(), kind)) - } else { - let error = TypeCheckError::TypeKindMismatch { + // TODO: make issue to support non-u32 generics here + if !kind.unifies(&Kind::u32()) { + let error = TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind: Kind::u32().to_string(), expr_kind: kind.to_string(), expr_span: path.span(), }; self.push_err(error); - None } + Some(Type::Constant(self.eval_global_as_array_length(id, path).into(), kind)) } _ => None, } @@ -1574,9 +1569,6 @@ impl<'context> Elaborator<'context> { let (expr_span, empty_function) = self.function_info(body_id); let declared_return_type = meta.return_type(); - // TODO - dbg!("type_check_function_body", &body_type, &declared_return_type); - let func_span = self.interner.expr_span(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet if let Type::TraitAsType(trait_id, _, generics) = declared_return_type { if self @@ -1612,10 +1604,6 @@ impl<'context> Elaborator<'context> { } error }); - - // TODO - dbg!("after unify_with_coercions", &body_type, &declared_return_type); - } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 259ebcfb959..f5bfbf50481 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -675,9 +675,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let typ = self.elaborator.interner.id_type(id).follow_bindings(); let location = self.elaborator.interner.expr_location(&id); - // TODO - dbg!("evaluate_integer", &value, &typ); - if let Type::FieldElement = &typ { Ok(Value::Field(value)) } else if let Type::Integer(sign, bit_size) = &typ { @@ -1395,10 +1392,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn evaluate_cast(&mut self, cast: &HirCastExpression, id: ExprId) -> IResult { - - // TODO cleanup - dbg!("evaluate_cast", cast); - let evaluated_lhs = self.evaluate(cast.lhs)?; Self::evaluate_cast_one_step(cast, id, evaluated_lhs, self.elaborator.interner) } @@ -1598,9 +1591,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn evaluate_assign(&mut self, assign: HirAssignStatement) -> IResult { - // TODO - dbg!("evaluate_assign", &assign); - let rhs = self.evaluate(assign.expression)?; self.store_lvalue(assign.lvalue, rhs)?; Ok(Value::Unit) @@ -1609,13 +1599,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { match lvalue { HirLValue::Ident(ident, typ) => self.mutate(ident.id, rhs, ident.location), - - // TODO revert - // HirLValue::Dereference { lvalue, element_type: _, location } => { - HirLValue::Dereference { lvalue, element_type, location } => { - // TODO cleanup - dbg!("store_lvalue", element_type); - + HirLValue::Dereference { lvalue, element_type: _, location } => { match self.evaluate_lvalue(&lvalue)? { Value::Pointer(value, _) => { *value.borrow_mut() = rhs; @@ -1676,13 +1660,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Pointer(elem, true) => Ok(elem.borrow().clone()), other => Ok(other), }, - - // TODO revert - // HirLValue::Dereference { lvalue, element_type: _, location } => { HirLValue::Dereference { lvalue, element_type, location } => { - // TODO cleanup - dbg!("evaluate_lvalue", element_type); - match self.evaluate_lvalue(lvalue)? { Value::Pointer(value, _) => Ok(value.borrow().clone()), value => { diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index b1b9ad2aa4b..f1fe1859d97 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -48,6 +48,9 @@ pub enum TypeCheckError { TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, + // TODO reference issue + #[error("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case")] + EvaluatedGlobalPartialSizeChecks { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span }, #[error("Return type in a function cannot be public")] @@ -229,6 +232,13 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { *expr_span, ) } + TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind, expr_kind, expr_span } => { + Diagnostic::simple_warning( + format!("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case"), + String::new(), + *expr_span, + ) + } TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 6030f22fd6a..ca007351650 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -115,7 +115,7 @@ pub enum Type { /// A type-level integer. Included to let /// 1. an Array's size type variable /// bind to an integer without special checks to bind it to a non-type. - /// 2. values be used at the type level + /// 2. values to be used at the type level Constant(FieldElement, Kind), /// The type of quoted code in macros. This is always a comptime-only type @@ -162,18 +162,18 @@ pub enum Kind { impl Kind { pub(crate) fn is_error(&self) -> bool { - match self { - Self::Numeric(typ) => **typ == Type::Error, + match self.follow_bindings() { + Self::Numeric(typ) => *typ == Type::Error, _ => false, } } pub(crate) fn is_numeric(&self) -> bool { - matches!(self, Self::Numeric { .. }) + matches!(self.follow_bindings(), Self::Numeric { .. }) } pub(crate) fn is_field_element(&self) -> bool { - match self { + match self.follow_bindings() { Kind::Numeric(typ) => typ.is_field_element(), Kind::IntegerOrField => true, _ => false, @@ -243,7 +243,7 @@ impl Kind { } fn integral_maximum_size(&self) -> Option { - match self { + match self.follow_bindings() { Kind::Any | Kind::IntegerOrField | Kind::Integer | Kind::Normal => None, Self::Numeric(typ) => typ.integral_maximum_size(), } @@ -713,8 +713,8 @@ impl TypeVariable { /// and if unbound, that it's a Kind::Integer pub fn is_integer(&self) -> bool { match &*self.borrow() { - TypeBinding::Bound(binding) => matches!(binding, Type::Integer(..)), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::Integer), + TypeBinding::Bound(binding) => matches!(binding.follow_bindings(), Type::Integer(..)), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind.follow_bindings(), Kind::Integer), } } @@ -723,9 +723,9 @@ impl TypeVariable { pub fn is_integer_or_field(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => { - matches!(binding, Type::Integer(..) | Type::FieldElement) + matches!(binding.follow_bindings(), Type::Integer(..) | Type::FieldElement) } - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind, Kind::IntegerOrField), + TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind.follow_bindings(), Kind::IntegerOrField), } } @@ -1856,7 +1856,6 @@ impl Type { self.evaluate_to_field_element(&Kind::u32()).and_then(|field_element| field_element.try_to_u32()) } - // TODO: implement! pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { // TODO dbg!("evaluate_to_field_element", self); @@ -2253,21 +2252,12 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var) => { - let (id, kind) = match &*var.borrow() { - TypeBinding::Bound(typ) => return typ.follow_bindings(), - TypeBinding::Unbound(id, kind) => (*id, kind.follow_bindings()), - }; - TypeVariable(crate::TypeVariable::unbound(id, kind)) - } - NamedGeneric(var, name) => { - let (id, kind) = match &*var.borrow() { - TypeBinding::Bound(typ) => return typ.follow_bindings(), - TypeBinding::Unbound(id, kind) => (*id, kind.follow_bindings()), - }; - NamedGeneric(crate::TypeVariable::unbound(id, kind), name.clone()) + TypeVariable(var) | NamedGeneric(var, _) => { + if let TypeBinding::Bound(typ) = &*var.borrow() { + return typ.follow_bindings(); + } + self.clone() } - Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| arg.follow_bindings()); let ret = Box::new(ret.follow_bindings()); @@ -2476,8 +2466,6 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - // TODO(https://github.com/noir-lang/noir/pull/6137): the Kind is included - // since it'll be needed for size checks pub fn function(self, a: FieldElement, b: FieldElement, kind: &Kind) -> Option { match kind.follow_bindings().integral_maximum_size() { None => { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 23feafe06fc..2687170414c 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1983,10 +1983,8 @@ fn numeric_generic_used_in_turbofish() { // )); // } -// TODO might want to hold off on this one because -// it involves array size evaluation outside of array size usage -// -// https://github.com/noir-lang/noir/issues/6125 +// TODO reference issue w/ EvaluatedGlobalPartialSizeChecks +// (originally from https://github.com/noir-lang/noir/issues/6125) #[test] fn numeric_generic_field_larger_than_u32() { let src = r#" @@ -2006,11 +2004,14 @@ fn numeric_generic_field_larger_than_u32() { println!(); println!(); - // TODO - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::IntegerTooLarge { .. }) )); } @@ -2046,9 +2047,16 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { println!(); // TODO - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 2); + + // TODO to be removed + reference issue assert!(matches!( errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + )); + + assert!(matches!( + errors[1].0, CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) )); } @@ -2096,29 +2104,31 @@ fn cast_256_to_u8_size_checks() { // )); // } -// https://github.com/noir-lang/noir/issues/6219 -#[test] -fn cast_negative_one_infix_to_u8_size_checks() { - let src = r#" - fn main() { - assert((0 - 1) as u8 != 0); - } - "#; - let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - println!(); - println!(); - println!(); - - // TODO - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} +// TODO add negative integer literal checks +// +// // https://github.com/noir-lang/noir/issues/6219 +// #[test] +// fn cast_negative_one_infix_to_u8_size_checks() { +// let src = r#" +// fn main() { +// assert((0 - 1) as u8 != 0); +// } +// "#; +// let errors = get_program_errors(src); +// +// // TODO: cleanup +// dbg!(&errors); +// println!(); +// println!(); +// println!(); +// +// // TODO +// assert_eq!(errors.len(), 4); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } #[test] fn constant_used_with_numeric_generic() { @@ -2216,11 +2226,30 @@ fn numeric_generics_type_kind_mismatch() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 3); + + // TODO to be removed + reference issue assert!(matches!( errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + )); + + assert!(matches!( + errors[1].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); + + // TODO to be removed + reference issue + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + )); + + } #[test] @@ -3290,24 +3319,29 @@ fn struct_array_len() { )); } -#[test] -fn non_u32_in_array_length() { - let src = r#" - global ARRAY_LEN: u8 = 3; - - fn main() { - let _a: [u32; ARRAY_LEN] = [1, 2, 3]; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); -} +// TODO separate out feature +// #[test] +// fn non_u32_in_array_length() { +// let src = r#" +// global ARRAY_LEN: u8 = 3; +// +// fn main() { +// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; +// } +// "#; +// +// let errors = get_program_errors(src); +// +// // TODO cleanup +// dbg!(&errors); +// +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) +// )); +// } #[test] fn use_non_u32_generic_in_struct() { @@ -3320,6 +3354,10 @@ fn use_non_u32_generic_in_struct() { "#; let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + assert_eq!(errors.len(), 0); } From 45ace88e2052a87fc91413b45399529768b45ee2 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Oct 2024 06:33:42 -0400 Subject: [PATCH 37/56] add issue for EvaluatedGlobalPartialSizeChecks and some cleanup --- .../noirc_frontend/src/elaborator/types.rs | 3 +- .../src/hir/type_check/errors.rs | 4 ++- compiler/noirc_frontend/src/tests.rs | 36 +++++-------------- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 19a00545bda..6aae390d45c 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -417,7 +417,8 @@ impl<'context> Elaborator<'context> { .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) .unwrap_or(Kind::u32()); - // TODO: make issue to support non-u32 generics here + // TODO(https://github.com/noir-lang/noir/issues/6238): + // support non-u32 generics here if !kind.unifies(&Kind::u32()) { let error = TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind: Kind::u32().to_string(), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index f1fe1859d97..2abf0b6fe94 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -48,7 +48,7 @@ pub enum TypeCheckError { TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, - // TODO reference issue + // TODO(https://github.com/noir-lang/noir/issues/6238): implement handling for larger types #[error("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case")] EvaluatedGlobalPartialSizeChecks { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] @@ -232,6 +232,8 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { *expr_span, ) } + // TODO(https://github.com/noir-lang/noir/issues/6238): implement + // handling for larger types TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind, expr_kind, expr_span } => { Diagnostic::simple_warning( format!("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case"), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 2687170414c..83c50fbcea7 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1983,7 +1983,8 @@ fn numeric_generic_used_in_turbofish() { // )); // } -// TODO reference issue w/ EvaluatedGlobalPartialSizeChecks +// TODO(https://github.com/noir-lang/noir/issues/6238): +// The EvaluatedGlobalPartialSizeChecks warning is a stopgap // (originally from https://github.com/noir-lang/noir/issues/6125) #[test] fn numeric_generic_field_larger_than_u32() { @@ -1997,13 +1998,6 @@ fn numeric_generic_field_larger_than_u32() { } "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - println!(); - println!(); - println!(); - assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, @@ -2015,7 +2009,9 @@ fn numeric_generic_field_larger_than_u32() { )); } -// https://github.com/noir-lang/noir/issues/6126 +// TODO(https://github.com/noir-lang/noir/issues/6238): +// The EvaluatedGlobalPartialSizeChecks warning is a stopgap +// (originally from https://github.com/noir-lang/noir/issues/6126) #[test] fn numeric_generic_field_arithmetic_larger_than_u32() { let src = r#" @@ -2039,17 +2035,8 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { } "#; let errors = get_program_errors(src); - - // TODO: cleanup - dbg!(&errors); - println!(); - println!(); - println!(); - - // TODO assert_eq!(errors.len(), 2); - // TODO to be removed + reference issue assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), @@ -2226,13 +2213,10 @@ fn numeric_generics_type_kind_mismatch() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 3); - // TODO to be removed + reference issue + // TODO(https://github.com/noir-lang/noir/issues/6238): + // The EvaluatedGlobalPartialSizeChecks warning is a stopgap assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), @@ -2243,7 +2227,7 @@ fn numeric_generics_type_kind_mismatch() { CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); - // TODO to be removed + reference issue + // TODO(https://github.com/noir-lang/noir/issues/6238): see above assert!(matches!( errors[2].0, CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), @@ -3354,10 +3338,6 @@ fn use_non_u32_generic_in_struct() { "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } From 6b7bc1120436dd0373f7add104063a473172d7ed Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Oct 2024 10:39:33 -0400 Subject: [PATCH 38/56] add tests to check large (> 2^32) Field bytes, updating tests w/ follow up issues --- compiler/noirc_frontend/src/tests.rs | 169 +++++++----------- .../check_large_field_bits/Nargo.toml | 7 + .../check_large_field_bits/src/main.nr | 33 ++++ 3 files changed, 100 insertions(+), 109 deletions(-) create mode 100644 test_programs/execution_success/check_large_field_bits/Nargo.toml create mode 100644 test_programs/execution_success/check_large_field_bits/src/main.nr diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 83c50fbcea7..526207b611d 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1949,39 +1949,31 @@ fn numeric_generic_used_in_turbofish() { assert_no_errors(src); } -// TODO this doesn't involve check_cast -// -// #[test] -// fn numeric_generic_u16_array_size() { -// let src = r#" -// fn len(_arr: [Field; N]) -> u32 { -// N -// } -// -// pub fn foo() -> u32 { -// let fields: [Field; N] = [0; N]; -// len(fields) -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO: cleanup -// dbg!(&errors); -// println!(); -// println!(); -// println!(); -// -// // TODO -// assert_eq!(errors.len(), 2); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } +// TODO(https://github.com/noir-lang/noir/issues/6245): +// allow u16 to be used as an array size +#[test] +fn numeric_generic_u16_array_size() { + let src = r#" + fn len(_arr: [Field; N]) -> u32 { + N + } + + pub fn foo() -> u32 { + let fields: [Field; N] = [0; N]; + len(fields) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} // TODO(https://github.com/noir-lang/noir/issues/6238): // The EvaluatedGlobalPartialSizeChecks warning is a stopgap @@ -2048,7 +2040,6 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { )); } -// https://github.com/noir-lang/noir/issues/6219 #[test] fn cast_256_to_u8_size_checks() { let src = r#" @@ -2057,7 +2048,6 @@ fn cast_256_to_u8_size_checks() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -2065,57 +2055,18 @@ fn cast_256_to_u8_size_checks() { )); } -// TODO add negative integer literal checks -// -// // https://github.com/noir-lang/noir/issues/6219 -// #[test] -// fn cast_negative_one_to_u8_size_checks() { -// let src = r#" -// fn main() { -// assert((-1) as u8 != 0); -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO: cleanup -// dbg!(&errors); -// println!(); -// println!(); -// println!(); -// -// // TODO -// assert_eq!(errors.len(), 4); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } - -// TODO add negative integer literal checks -// -// // https://github.com/noir-lang/noir/issues/6219 -// #[test] -// fn cast_negative_one_infix_to_u8_size_checks() { -// let src = r#" -// fn main() { -// assert((0 - 1) as u8 != 0); -// } -// "#; -// let errors = get_program_errors(src); -// -// // TODO: cleanup -// dbg!(&errors); -// println!(); -// println!(); -// println!(); -// -// // TODO -// assert_eq!(errors.len(), 4); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } +// TODO(https://github.com/noir-lang/noir/issues/6247): +// add negative integer literal checks +#[test] +fn cast_negative_one_to_u8_size_checks() { + let src = r#" + fn main() { + assert((-1) as u8 != 0); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} #[test] fn constant_used_with_numeric_generic() { @@ -3303,29 +3254,29 @@ fn struct_array_len() { )); } -// TODO separate out feature -// #[test] -// fn non_u32_in_array_length() { -// let src = r#" -// global ARRAY_LEN: u8 = 3; -// -// fn main() { -// let _a: [u32; ARRAY_LEN] = [1, 2, 3]; -// } -// "#; -// -// let errors = get_program_errors(src); -// -// // TODO cleanup -// dbg!(&errors); -// -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) -// )); -// } +// TODO(https://github.com/noir-lang/noir/issues/6245): +// support u16 as an array size +#[test] +fn non_u32_as_array_length() { + let src = r#" + global ARRAY_LEN: u8 = 3; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }) + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) + )); +} #[test] fn use_non_u32_generic_in_struct() { diff --git a/test_programs/execution_success/check_large_field_bits/Nargo.toml b/test_programs/execution_success/check_large_field_bits/Nargo.toml new file mode 100644 index 00000000000..33d5dd66484 --- /dev/null +++ b/test_programs/execution_success/check_large_field_bits/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "check_large_field_bits" +type = "bin" +authors = [""] +compiler_version = ">=0.35.0" + +[dependencies] diff --git a/test_programs/execution_success/check_large_field_bits/src/main.nr b/test_programs/execution_success/check_large_field_bits/src/main.nr new file mode 100644 index 00000000000..c7e33a5a33d --- /dev/null +++ b/test_programs/execution_success/check_large_field_bits/src/main.nr @@ -0,0 +1,33 @@ +// 2^32 + 1 +global A: Field = 4294967297; +global B: Field = 4294967297; + +// 2^33 + 2 +global C: Field = A + B; + +fn main() { + // 2 * (2^32 + 1) == 2^33 + 2 + assert(C == 8589934594); + + let mut leading_zeroes = 0; + let mut stop = false; + let bits: [u1; 64] = C.to_be_bits(); + for i in 0..64 { + if (bits[i] == 0) & !stop { + leading_zeroes += 1; + } else { + stop = true; + } + } + let size = 64 - leading_zeroes; + + // 8589934594 has 34 bits + assert(size == 34); + C.assert_max_bit_size(34); + + assert(C.to_be_bits() == [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); + + // leading big-endian bits past 34 are 0's + assert(C.to_be_bits() == [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); + assert(C.to_be_bits() == [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); +} From f31c8f4c9e93b0e90ccb4ec374c47507ba9539a3 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Oct 2024 10:59:31 -0400 Subject: [PATCH 39/56] patch after merge --- .../src/parser/parser/type_expression.rs | 30 ++++++------------- .../noirc_frontend/src/parser/parser/types.rs | 10 ++++--- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index c3f27d9d49a..0561bcb70fa 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,14 +1,14 @@ use crate::{ ast::{ - Expression, ExpressionKind, GenericTypeArgs, Literal, UnresolvedType, UnresolvedTypeData, + GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }, - parser::{labels::ParsingRuleLabel, ParserError, ParserErrorReason}, + parser::{labels::ParsingRuleLabel, ParserError}, token::Token, BinaryTypeOperator, }; -use acvm::acir::AcirField; +use acvm::acir::{AcirField, FieldElement}; use noirc_errors::Span; use super::{parse_many::separated_by_comma_until_right_paren, Parser}; @@ -119,7 +119,7 @@ impl<'a> Parser<'a> { if self.eat(Token::Minus) { return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); let op = BinaryTypeOperator::Subtraction; let span = self.span_since(start_span); Some(UnresolvedTypeExpression::BinaryOperation( @@ -165,20 +165,6 @@ impl<'a> Parser<'a> { return None; }; - let int = if let Some(int) = int.try_to_u32() { - int - } else { - let err_expr = Expression { - kind: ExpressionKind::Literal(Literal::Integer(int, false)), - span: self.previous_token_span, - }; - self.push_error( - ParserErrorReason::InvalidTypeExpression(err_expr), - self.previous_token_span, - ); - 0 - }; - Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) } @@ -267,7 +253,7 @@ impl<'a> Parser<'a> { // If we ate '-' what follows must be a type expression, never a type return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(0, start_span); + let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); let op = BinaryTypeOperator::Subtraction; let span = self.span_since(start_span); let type_expr = UnresolvedTypeExpression::BinaryOperation( @@ -444,7 +430,8 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - assert_eq!(n, 42); + let fourty_two = 42_u32.into(); + assert_eq!(n, fourty_two); } #[test] @@ -496,7 +483,8 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - assert_eq!(n, 42); + let fourty_two = 42_u32.into(); + assert_eq!(n, fourty_two); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 6702704d32c..5b789772a99 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,3 +1,5 @@ +use acvm::{AcirField, FieldElement}; + use crate::{ ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{labels::ParsingRuleLabel, ParserErrorReason}, @@ -137,7 +139,7 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + let expr = UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); return Some(UnresolvedTypeData::String(expr)); } @@ -145,7 +147,7 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(0, self.current_token_span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) } }; @@ -161,7 +163,7 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(0, self.current_token_span); + let expr = UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } @@ -170,7 +172,7 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(0, self.current_token_span) + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) } }; From c47d7fdb1d0209b543576b259cc19057fe23ce6b Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Oct 2024 12:36:49 -0400 Subject: [PATCH 40/56] comment r.e. mul/div simplification to disable some cases --- .../arithmetic_generics/src/main.nr | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index 0a7d319485c..8507a8197c2 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -115,11 +115,17 @@ fn test_constant_folding() { // N - C1 + C2 = N - (C1 - C2) let _: W = W:: {}; - // N * C1 / C2 = N * (C1 / C2) - let _: W = W:: {}; - - // N / C1 * C2 = N / (C1 / C2) - let _: W = W:: {}; + // TODO see PR + // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) + // for more info + // // N * C1 / C2 = N * (C1 / C2) + // let _: W = W:: {}; + + // TODO see PR + // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) + // for more info + // // N / C1 * C2 = N / (C1 / C2) + // let _: W = W:: {}; } fn test_non_constant_folding() { From 9ef37384eb76f8e230dbef12486f276e766d9e78 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Oct 2024 13:40:44 -0400 Subject: [PATCH 41/56] wip cleanup --- .../noirc_frontend/src/elaborator/types.rs | 45 +++---------------- .../src/hir/comptime/interpreter.rs | 6 --- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6aae390d45c..a999b32be4b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -639,14 +639,7 @@ impl<'context> Elaborator<'context> { match result.map(|length| length.try_into()) { Ok(Ok(length_value)) => return length_value, - - // TODO cleanup - // Ok(Err(_cast_err)) => { - Ok(Err(cast_err)) => { - - // TODO cleanup - dbg!("eval_global_as_array_length (IntegerTooLarge):", self.interner.expression(&length), &cast_err); - + Ok(Err(_cast_err)) => { self.push_err(ResolverError::IntegerTooLarge { span }) } Err(Some(error)) => self.push_err(error), @@ -855,22 +848,17 @@ impl<'context> Elaborator<'context> { pub(super) fn check_cast(&mut self, from_expr_id: &ExprId, from: &Type, to: &Type, span: Span) -> Type { let from_follow_bindings = from.follow_bindings(); - // TODO cleanup - dbg!(&from_follow_bindings, &to, &self.interner.expression(&from_expr_id)); - - // TODO: factor out size checks from type checks? let from_value_opt = match self.interner.expression(&from_expr_id) { HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), - // TODO + // TODO(https://github.com/noir-lang/noir/issues/6247): + // handle negative literals other => { dbg!("from_value_opt: other", other); None } }; - // TODO disable type downsizing checks - // TODO (Option.., ..) type let from_is_polymorphic = match from_follow_bindings { Type::Integer(..) | Type::FieldElement | Type::Bool => false, @@ -894,12 +882,8 @@ impl<'context> Elaborator<'context> { } }; - // TODO cleanup - dbg!(&from_value_opt, &from_is_polymorphic); - - // TODO also check minimum size when 'from' is negative - // (casting to a smaller value?) - // TODO get is_polymorphic out of match? + // TODO(https://github.com/noir-lang/noir/issues/6247): + // handle negative literals match (from_is_polymorphic, from_value_opt, to.integral_maximum_size()) { // allow casting from unsized polymorphic variables (true, None, _) => (), @@ -933,13 +917,7 @@ impl<'context> Elaborator<'context> { Type::FieldElement => Type::FieldElement, Type::Bool => Type::Bool, Type::Error => Type::Error, - - // TODO cleanup - // _ => { - other => { - // TODO cleanup - dbg!("check_cast failed type check", other); - + _ => { self.push_err(TypeCheckError::UnsupportedCast { span }); Type::Error } @@ -1660,10 +1638,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); } ImplSearchErrorKind::Nested(constraints) => { - // TODO cleanup - // if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) - if let Some(error) = - NoMatchingImplFoundError::new(self.interner, constraints.clone(), span) + if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) { self.push_err(TypeCheckError::NoMatchingImplFound(error)); } @@ -1914,9 +1889,6 @@ fn try_eval_array_length_id_with_fuel( match interner.expression(&rhs) { HirExpression::Literal(HirLiteral::Integer(int, false)) => { - // TODO cleanup - dbg!("try_eval_array_length_id_with_fuel: try_into_u128", int, int.try_into_u128()); - int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span })) } HirExpression::Ident(ident, _) => { @@ -1967,9 +1939,6 @@ fn try_eval_array_length_id_with_fuel( Interpreter::evaluate_cast_one_step(&cast, rhs, lhs_value, interner) .map_err(|error| Some(ResolverError::ArrayLengthInterpreter { error }))?; - // TODO cleanup - dbg!("try_eval_array_length_id_with_fuel: Cast", &evaluated_value); - evaluated_value .to_u128() .ok_or_else(|| Some(ResolverError::InvalidArrayLengthExpr { span })) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index f5bfbf50481..2ecdc68a279 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -902,13 +902,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let lhs_value = self.evaluate(infix.lhs)?; let rhs_value = self.evaluate(infix.rhs)?; - // TODO cleanup - dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); - if self.elaborator.interner.get_selected_impl_for_expression(id).is_some() { - // // TODO cleanup - // dbg!("evaluate_infix", &infix, &lhs_value, &rhs_value); - return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } From c1c0250712806c5b020471d43ba28db507432b0a Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 06:49:23 -0400 Subject: [PATCH 42/56] cargo clippy / fmt, remaining TODO's, update wrapping_op's to checked_op's in interpreter as per expectations of using them --- compiler/noirc_frontend/src/ast/mod.rs | 5 +- .../noirc_frontend/src/elaborator/types.rs | 17 +- .../src/hir/comptime/interpreter.rs | 270 +++++++++++++----- .../src/hir/comptime/interpreter/builtin.rs | 9 +- .../src/hir/resolution/errors.rs | 7 +- compiler/noirc_frontend/src/hir_def/types.rs | 57 ++-- .../src/hir_def/types/arithmetic.rs | 9 +- .../src/monomorphization/mod.rs | 16 +- .../src/parser/parser/type_expression.rs | 5 +- .../noirc_frontend/src/parser/parser/types.rs | 6 +- compiler/noirc_frontend/src/tests.rs | 2 - 11 files changed, 273 insertions(+), 130 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index bc42d10f212..07f15f37c6e 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -19,8 +19,8 @@ pub use visitor::Visitor; pub use expression::*; pub use function::*; -pub use docs::*; use acvm::FieldElement; +pub use docs::*; use noirc_errors::Span; use serde::{Deserialize, Serialize}; pub use statement::*; @@ -427,7 +427,8 @@ impl UnresolvedTypeExpression { }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); + let lhs = + Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index a999b32be4b..2ae95d61858 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -640,7 +640,7 @@ impl<'context> Elaborator<'context> { match result.map(|length| length.try_into()) { Ok(Ok(length_value)) => return length_value, Ok(Err(_cast_err)) => { - self.push_err(ResolverError::IntegerTooLarge { span }) + self.push_err(ResolverError::IntegerTooLarge { span }); } Err(Some(error)) => self.push_err(error), Err(None) => (), @@ -845,18 +845,21 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn check_cast(&mut self, from_expr_id: &ExprId, from: &Type, to: &Type, span: Span) -> Type { + pub(super) fn check_cast( + &mut self, + from_expr_id: &ExprId, + from: &Type, + to: &Type, + span: Span, + ) -> Type { let from_follow_bindings = from.follow_bindings(); - let from_value_opt = match self.interner.expression(&from_expr_id) { + let from_value_opt = match self.interner.expression(from_expr_id) { HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), // TODO(https://github.com/noir-lang/noir/issues/6247): // handle negative literals - other => { - dbg!("from_value_opt: other", other); - None - } + _ => None, }; let from_is_polymorphic = match from_follow_bindings { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 2ecdc68a279..6ef700b2315 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -586,8 +586,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { DefinitionKind::NumericGeneric(type_variable, numeric_typ) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_, _) => None, - // TODO: remove clone if possible - TypeBinding::Bound(binding) => binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())), + TypeBinding::Bound(binding) => { + binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())) + } }; if let Some(value) = value { @@ -895,10 +896,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } - fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { - let location = self.elaborator.interner.expr_location(&id); - let lhs_value = self.evaluate(infix.lhs)?; let rhs_value = self.evaluate(infix.rhs)?; @@ -906,73 +904,145 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } - fn make_error_value(lhs: &Value, rhs: &Value, location: Location, operator: &'static str) -> InterpreterError { + fn make_error_value( + lhs: &Value, + rhs: &Value, + location: Location, + operator: &'static str, + ) -> InterpreterError { let lhs = lhs.get_type().into_owned(); let rhs = rhs.get_type().into_owned(); InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } } // make_error_value if the result is None - fn or_err(lhs_rhs_location: (&Value, &Value, Location), operator: &'static str, result: Option) -> IResult { + fn or_err( + lhs_rhs_location: (&Value, &Value, Location), + operator: &'static str, + result: Option, + ) -> IResult { let (lhs, rhs, location) = lhs_rhs_location; result.ok_or(make_error_value(lhs, rhs, location, operator)) } + let location = self.elaborator.interner.expr_location(&id); + let error_ctx = (&lhs_value, &rhs_value, location); let make_error = |lhs: Value, rhs: Value, operator| -> IResult { Err(make_error_value(&lhs, &rhs, location, operator)) }; - // TODO possible to remove clones? - let error_ctx = (&lhs_value, &rhs_value, location); - use InterpreterError::InvalidValuesForBinary; match infix.operator.kind { BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_add(rhs))), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_add(rhs))), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_add(rhs))), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_add(rhs))), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_add(rhs))), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_add(rhs))), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_add(rhs))), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_add(rhs))), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + } (lhs, rhs) => make_error(lhs, rhs, "+"), }, BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_sub(rhs))), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_sub(rhs))), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_sub(rhs))), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_sub(rhs))), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_sub(rhs))), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_sub(rhs))), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_sub(rhs))), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_sub(rhs))), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + } (lhs, rhs) => make_error(lhs, rhs, "-"), }, BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs.wrapping_mul(rhs))), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(lhs.wrapping_mul(rhs))), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs.wrapping_mul(rhs))), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs.wrapping_mul(rhs))), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs.wrapping_mul(rhs))), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs.wrapping_mul(rhs))), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs.wrapping_mul(rhs))), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs.wrapping_mul(rhs))), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + } (lhs, rhs) => make_error(lhs, rhs, "*"), }, BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + } (lhs, rhs) => make_error(lhs, rhs, "/"), }, BinaryOpKind::Equal => match (lhs_value.clone(), rhs_value.clone()) { @@ -1086,36 +1156,104 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (lhs, rhs) => make_error(lhs, rhs, "^"), }, BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, ">>", lhs.checked_shr(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, ">>", lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err( + error_ctx, + ">>", + lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err( + error_ctx, + ">>", + lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err( + error_ctx, + ">>", + lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err( + error_ctx, + ">>", + lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, ">>", lhs.checked_shr(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err( + error_ctx, + ">>", + lhs.checked_shr(or_err(error_ctx, ">>", rhs.try_into().ok())?), + )?)), (lhs, rhs) => make_error(lhs, rhs, ">>"), }, BinaryOpKind::ShiftLeft => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "<<", lhs.checked_shl(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "<<", lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?))?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err( + error_ctx, + "<<", + lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err( + error_ctx, + "<<", + lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err( + error_ctx, + "<<", + lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err( + error_ctx, + "<<", + lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "<<", lhs.checked_shl(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err( + error_ctx, + "<<", + lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), + )?)), (lhs, rhs) => make_error(lhs, rhs, "<<"), }, BinaryOpKind::Modulo => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)), + (Value::I8(lhs), Value::I8(rhs)) => { + Ok(Value::I8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::I16(lhs), Value::I16(rhs)) => { + Ok(Value::I16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::I32(lhs), Value::I32(rhs)) => { + Ok(Value::I32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::I64(lhs), Value::I64(rhs)) => { + Ok(Value::I64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::U8(lhs), Value::U8(rhs)) => { + Ok(Value::U8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::U16(lhs), Value::U16(rhs)) => { + Ok(Value::U16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::U32(lhs), Value::U32(rhs)) => { + Ok(Value::U32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => { + Ok(Value::U64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + } (lhs, rhs) => make_error(lhs, rhs, "%"), }, } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 524e8092d91..ec6b5eb0870 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -817,10 +817,11 @@ fn to_le_radix( // Decompose the integer into its radix digits in little endian form. let decomposed_integer = compute_to_radix_le(value, radix); - let decomposed_integer = vecmap(0..limb_count.to_u128() as usize, |i| match decomposed_integer.get(i) { - Some(digit) => Value::U8(*digit), - None => Value::U8(0), - }); + let decomposed_integer = + vecmap(0..limb_count.to_u128() as usize, |i| match decomposed_integer.get(i) { + Some(digit) => Value::U8(*digit), + None => Value::U8(0), + }); Ok(Value::Array( decomposed_integer.into(), Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 51a344f73e8..a6eb1864d13 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -126,7 +126,12 @@ pub enum ResolverError { #[error("Associated constants may only be a field or integer type")] AssociatedConstantsMustBeNumeric { span: Span }, #[error("Overflow in `{lhs} {op} {rhs}`")] - OverflowInType { lhs: FieldElement, op: crate::BinaryTypeOperator, rhs: FieldElement, span: Span }, + OverflowInType { + lhs: FieldElement, + op: crate::BinaryTypeOperator, + rhs: FieldElement, + span: Span, + }, #[error("`quote` cannot be used in runtime code")] QuoteInRuntimeCode { span: Span }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 28c3745964a..910204dd93d 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -250,12 +250,14 @@ impl Kind { } /// Ensure the given value fits in self.integral_maximum_size() - pub(crate) fn ensure_value_fits>(&self, value: T) -> Option { + pub(crate) fn ensure_value_fits(&self, value: T) -> Option + where + for<'a> &'a FieldElement: From<&'a T>, + { match self.integral_maximum_size() { None => Some(value), Some(maximum_size) => { - // TODO possible to avoid clone, e.g. get by ref and return a bool? - if value.clone().into() <= maximum_size { + if <&T as Into<&FieldElement>>::into(&value) <= &maximum_size { Some(value) } else { None @@ -715,7 +717,9 @@ impl TypeVariable { pub fn is_integer(&self) -> bool { match &*self.borrow() { TypeBinding::Bound(binding) => matches!(binding.follow_bindings(), Type::Integer(..)), - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind.follow_bindings(), Kind::Integer), + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind.follow_bindings(), Kind::Integer) + } } } @@ -726,7 +730,9 @@ impl TypeVariable { TypeBinding::Bound(binding) => { matches!(binding.follow_bindings(), Type::Integer(..) | Type::FieldElement) } - TypeBinding::Unbound(_, type_var_kind) => matches!(type_var_kind.follow_bindings(), Kind::IntegerOrField), + TypeBinding::Unbound(_, type_var_kind) => { + matches!(type_var_kind.follow_bindings(), Kind::IntegerOrField) + } } } @@ -1854,13 +1860,11 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self) -> Option { - self.evaluate_to_field_element(&Kind::u32()).and_then(|field_element| field_element.try_to_u32()) + self.evaluate_to_field_element(&Kind::u32()) + .and_then(|field_element| field_element.try_to_u32()) } pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { - // TODO - dbg!("evaluate_to_field_element", self); - if let Some((binding, binding_kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { if kind.unifies(&binding_kind) { @@ -1870,9 +1874,6 @@ impl Type { } match self.canonicalize() { - // TODO: unused case? (was a patch to use evaluate_to_u32 for array.len()) - // Type::Array(len, _elem) => len.evaluate_to_field_element(), - Type::Constant(x, constant_kind) => { if kind.unifies(&constant_kind) { kind.ensure_value_fits(x) @@ -2406,11 +2407,9 @@ impl Type { } } Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), - Type::NamedGeneric(binding, _name) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.integral_maximum_size(), - TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), - } + Type::NamedGeneric(binding, _name) => match &*binding.borrow() { + TypeBinding::Bound(typ) => typ.integral_maximum_size(), + TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), }, Type::MutableReference(typ) => typ.integral_maximum_size(), Type::InfixExpr(lhs, _op, rhs) => lhs.infix_kind(rhs).integral_maximum_size(), @@ -2469,21 +2468,19 @@ impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator pub fn function(self, a: FieldElement, b: FieldElement, kind: &Kind) -> Option { match kind.follow_bindings().integral_maximum_size() { - None => { - match self { - BinaryTypeOperator::Addition => Some(a + b), - BinaryTypeOperator::Subtraction => Some(a - b), - BinaryTypeOperator::Multiplication => Some(a * b), - BinaryTypeOperator::Division => { - if b == FieldElement::zero() { - None - } else { - Some(a / b) - } + None => match self { + BinaryTypeOperator::Addition => Some(a + b), + BinaryTypeOperator::Subtraction => Some(a - b), + BinaryTypeOperator::Multiplication => Some(a * b), + BinaryTypeOperator::Division => { + if b == FieldElement::zero() { + None + } else { + Some(a / b) } - BinaryTypeOperator::Modulo => None, } - } + BinaryTypeOperator::Modulo => None, + }, Some(maximum_size) => { let maximum_size = maximum_size.to_i128(); let a = a.to_i128(); diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 6c358452f6a..1e71c8d779d 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -61,7 +61,11 @@ impl Type { // Maps each term to the number of times that term was used. let mut sorted = BTreeMap::new(); - let zero_value = if op == BinaryTypeOperator::Addition { FieldElement::zero() } else { FieldElement::one() }; + let zero_value = if op == BinaryTypeOperator::Addition { + FieldElement::zero() + } else { + FieldElement::one() + }; let mut constant = zero_value; // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. @@ -220,7 +224,8 @@ impl Type { op = op.inverse()?; } - let divides_evenly = !lhs.infix_kind(rhs).is_field_element() && l_const.to_i128() % r_const.to_i128() != 0; + let divides_evenly = !lhs.infix_kind(rhs).is_field_element() + && l_const.to_i128() % r_const.to_i128() != 0; // If op is a division we need to ensure it divides evenly if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 32fd8f82a2b..beb0b8712b7 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -928,10 +928,11 @@ impl<'interner> Monomorphizer<'interner> { TypeBinding::Unbound(_, _) => { unreachable!("Unbound type variable used in expression") } - // TODO: remove clone if possible - TypeBinding::Bound(binding) => binding.evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())).unwrap_or_else(|| { - panic!("Non-numeric type variable used in expression expecting a value") - }), + TypeBinding::Bound(binding) => binding + .evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone())) + .unwrap_or_else(|| { + panic!("Non-numeric type variable used in expression expecting a value") + }), }; let location = self.interner.id_location(expr_id); @@ -943,12 +944,7 @@ impl<'interner> Monomorphizer<'interner> { } let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer( - value, - false, - typ, - location, - )) + ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) } }; diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 0561bcb70fa..d7c150cb774 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,8 +1,5 @@ use crate::{ - ast::{ - GenericTypeArgs, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, - }, + ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{labels::ParsingRuleLabel, ParserError}, token::Token, BinaryTypeOperator, diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 5b789772a99..42fae40f669 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -139,7 +139,8 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); + let expr = + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); return Some(UnresolvedTypeData::String(expr)); } @@ -163,7 +164,8 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); + let expr = + UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 0967578ad77..9156b849827 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2180,8 +2180,6 @@ fn numeric_generics_type_kind_mismatch() { errors[2].0, CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), )); - - } #[test] From b098ee5d2b3387a14572adf73e1b90109ac6e593 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 07:28:03 -0400 Subject: [PATCH 43/56] nargo fmt --- .../arithmetic_generics/src/main.nr | 2 -- .../check_large_field_bits/src/main.nr | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index 8507a8197c2..e64ef4c2f06 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -114,13 +114,11 @@ fn test_constant_folding() { // N - C1 + C2 = N - (C1 - C2) let _: W = W:: {}; - // TODO see PR // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) // for more info // // N * C1 / C2 = N * (C1 / C2) // let _: W = W:: {}; - // TODO see PR // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) // for more info diff --git a/test_programs/execution_success/check_large_field_bits/src/main.nr b/test_programs/execution_success/check_large_field_bits/src/main.nr index c7e33a5a33d..1d65b342966 100644 --- a/test_programs/execution_success/check_large_field_bits/src/main.nr +++ b/test_programs/execution_success/check_large_field_bits/src/main.nr @@ -25,9 +25,21 @@ fn main() { assert(size == 34); C.assert_max_bit_size(34); - assert(C.to_be_bits() == [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); + assert( + C.to_be_bits() == [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); // leading big-endian bits past 34 are 0's - assert(C.to_be_bits() == [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); - assert(C.to_be_bits() == [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]); + assert( + C.to_be_bits() == [ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); + assert( + C.to_be_bits() == [ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 + ] + ); } From dfdbc548b8c04ea69aa20b2303410eeb5796583e Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 08:41:55 -0400 Subject: [PATCH 44/56] Update compiler/noirc_frontend/src/elaborator/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/elaborator/types.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2ae95d61858..65577f98dbd 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -639,9 +639,7 @@ impl<'context> Elaborator<'context> { match result.map(|length| length.try_into()) { Ok(Ok(length_value)) => return length_value, - Ok(Err(_cast_err)) => { - self.push_err(ResolverError::IntegerTooLarge { span }); - } + Ok(Err(_cast_err)) => self.push_err(ResolverError::IntegerTooLarge { span }), Err(Some(error)) => self.push_err(error), Err(None) => (), } From e53e543a11fa825eee37c794346ed406483af196 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 08:44:31 -0400 Subject: [PATCH 45/56] Update compiler/noirc_frontend/src/hir_def/types/arithmetic.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types/arithmetic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 1e71c8d779d..b21b55b2c6b 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -251,7 +251,7 @@ impl Type { if let Some(inverse) = op_a.inverse() { let kind = lhs_a.infix_kind(rhs_a); if let Some(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind) { - let rhs_a = Box::new(Type::Constant(rhs_a_value, lhs_a.infix_kind(rhs_a))); + let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); let mut tmp_bindings = bindings.clone(); From 962dd87063bb0a37e18d181e7e7729cd4a7cbd4f Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 08:44:53 -0400 Subject: [PATCH 46/56] Update compiler/noirc_frontend/src/parser/parser/type_expression.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/type_expression.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index d7c150cb774..b3d016bcc7e 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -480,8 +480,7 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - let fourty_two = 42_u32.into(); - assert_eq!(n, fourty_two); + assert_eq!(n, 42_u32.into()); } #[test] From b063d4243e9411d55adaaac0c65cd481a73922e4 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 08:53:23 -0400 Subject: [PATCH 47/56] reverting unneeded changes to numeric_generics_type_kind_mismatch --- compiler/noirc_frontend/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9156b849827..7e703450c5a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2144,13 +2144,13 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { #[test] fn numeric_generics_type_kind_mismatch() { let src = r#" - fn foo() -> u64 { - N as u64 + fn foo() -> u16 { + N as u16 } global J: u16 = 10; - fn bar() -> u64 { + fn bar() -> u16 { foo::() } From 59bfb4c7c96f74f838ceac3b4da3e101eb5ec6cf Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 08:53:44 -0400 Subject: [PATCH 48/56] Update compiler/noirc_frontend/src/parser/parser/type_expression.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/type_expression.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index b3d016bcc7e..7dd59aedb45 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -427,8 +427,7 @@ mod tests { let UnresolvedTypeExpression::Constant(n, _) = expr else { panic!("Expected constant"); }; - let fourty_two = 42_u32.into(); - assert_eq!(n, fourty_two); + assert_eq!(n, 42_u32.into()); } #[test] From 17b3c7ed1ebfff8a648cc19e673170f072e837a7 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 09:24:00 -0400 Subject: [PATCH 49/56] unifying var and polymorphic int is polymorphic, simplify check_cast on values, revert commenting out arithmetic_generics test cases, cargo clippy / fmt --- .../noirc_frontend/src/elaborator/types.rs | 38 ++++++------------- .../src/hir/type_check/errors.rs | 4 +- .../src/hir_def/types/arithmetic.rs | 2 +- .../arithmetic_generics/src/main.nr | 16 +++----- 4 files changed, 21 insertions(+), 39 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 65577f98dbd..262a1f3b0fb 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -873,7 +873,7 @@ impl<'context> Elaborator<'context> { span, reason: "casting from a non-integral type is unsupported".into(), }); - false + true } Type::Error => return Type::Error, from => { @@ -885,32 +885,18 @@ impl<'context> Elaborator<'context> { // TODO(https://github.com/noir-lang/noir/issues/6247): // handle negative literals - match (from_is_polymorphic, from_value_opt, to.integral_maximum_size()) { - // allow casting from unsized polymorphic variables - (true, None, _) => (), - - // allow casting from sized to unsized types - (_, Some(_), None) => (), - - // allow casting specific unsized types to sized types - (false, None, Some(_)) => (), - - // when casting a polymorphic value to a specifically sized type, - // check that it fits or throw a warning - (true, Some(from_maximum_size), Some(to_maximum_size)) => { - if from_maximum_size > to_maximum_size { - let from = from.clone(); - let reason = format!("casting untyped value ({}) to a type with a maximum size ({}) that's smaller than it", from_maximum_size, to_maximum_size); - // we warn that the 'to' type is too small for the value - self.push_err(TypeCheckError::DownsizingCast { from, span, reason }); - } + // when casting a polymorphic value to a specifically sized type, + // check that it fits or throw a warning + if let (Some(from_value), Some(to_maximum_size)) = + (from_value_opt, to.integral_maximum_size()) + { + if from_is_polymorphic && from_value > to_maximum_size { + let from = from.clone(); + let to = to.clone(); + let reason = format!("casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it"); + // we warn that the 'to' type is too small for the value + self.push_err(TypeCheckError::DownsizingCast { from, to, span, reason }); } - - // allow casting typed values to other sizes - (false, Some(_), Some(_)) => (), - - // allow casting from non-polymorphic unsized types - (false, None, None) => (), } match to { diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 2abf0b6fe94..6d5fb4377d2 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -57,8 +57,8 @@ pub enum TypeCheckError { PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] InvalidCast { from: Type, span: Span, reason: String }, - #[error("Casting value with {from} to a smaller type")] - DownsizingCast { from: Type, span: Span, reason: String }, + #[error("Casting value with type {from} to a smaller type ({to})")] + DownsizingCast { from: Type, to: Type, span: Span, reason: String }, #[error("Expected a function, but found a(n) {found}")] ExpectedFunction { found: Type, span: Span }, #[error("Type {lhs_type} has no member named {field_name}")] diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index b21b55b2c6b..3bdf08bfc74 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -225,7 +225,7 @@ impl Type { } let divides_evenly = !lhs.infix_kind(rhs).is_field_element() - && l_const.to_i128() % r_const.to_i128() != 0; + && l_const.to_i128().checked_rem(r_const.to_i128()) == Some(0); // If op is a division we need to ensure it divides evenly if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index e64ef4c2f06..0a7d319485c 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -114,16 +114,12 @@ fn test_constant_folding() { // N - C1 + C2 = N - (C1 - C2) let _: W = W:: {}; - // TODO see PR - // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) - // for more info - // // N * C1 / C2 = N * (C1 / C2) - // let _: W = W:: {}; - // TODO see PR - // [Integer division is not the inverse of integer multiplication](https://github.com/noir-lang/noir/pull/6243) - // for more info - // // N / C1 * C2 = N / (C1 / C2) - // let _: W = W:: {}; + + // N * C1 / C2 = N * (C1 / C2) + let _: W = W:: {}; + + // N / C1 * C2 = N / (C1 / C2) + let _: W = W:: {}; } fn test_non_constant_folding() { From 0ff37311d69f86eea0b845728934f42ba277ef6a Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 10:46:41 -0400 Subject: [PATCH 50/56] disable warning check in compile_success_empty for brillig_cast and macros_in_comptime (which currently emit a warning as per https://github.com/noir-lang/noir/issues/6238) --- tooling/nargo_cli/build.rs | 21 ++++++++++++++++--- .../tests/stdlib-props.proptest-regressions | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 50f022f64ed..94f74a06149 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -59,6 +59,15 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ "is_unconstrained", ]; +/// Some tests are expected to have warnings +/// These should be fixed and removed from this list. +const TESTS_WITH_EXPECTED_WARNINGS: [&str; 2] = [ + // TODO(https://github.com/noir-lang/noir/issues/6238): remove from list once issue is closed + "brillig_cast", + // TODO(https://github.com/noir-lang/noir/issues/6238): remove from list once issue is closed + "macros_in_comptime", +]; + fn read_test_cases( test_data_dir: &Path, test_sub_dir: &str, @@ -234,15 +243,21 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa for (test_name, test_dir) in test_cases { let test_dir = test_dir.display(); - let assert_zero_opcodes = r#" + let mut assert_zero_opcodes = r#" let output = nargo.output().expect("Failed to execute command"); if !output.status.success() {{ panic!("`nargo info` failed with: {}", String::from_utf8(output.stderr).unwrap_or_default()); }} + "#.to_string(); + + if !TESTS_WITH_EXPECTED_WARNINGS.contains(&test_name.as_str()) { + assert_zero_opcodes += r#" + nargo.assert().success().stderr(predicate::str::contains("warning:").not()); + "#; + } - nargo.assert().success().stderr(predicate::str::contains("warning:").not()); - + assert_zero_opcodes += r#" // `compile_success_empty` tests should be able to compile down to an empty circuit. let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ panic!("JSON was not well-formatted {:?}\n\n{:?}", e, std::str::from_utf8(&output.stdout)) diff --git a/tooling/nargo_cli/tests/stdlib-props.proptest-regressions b/tooling/nargo_cli/tests/stdlib-props.proptest-regressions index ab88db8b6c2..d41d504e530 100644 --- a/tooling/nargo_cli/tests/stdlib-props.proptest-regressions +++ b/tooling/nargo_cli/tests/stdlib-props.proptest-regressions @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 88db0227d5547742f771c14b1679f6783570b46bf7cf9e6897ee1aca4bd5034d # shrinks to io = SnippetInputOutput { description: "force_brillig = false, max_len = 200", inputs: {"input": Vec([Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(1), Field(5), Field(20), Field(133), Field(233), Field(99), Field(2⁶), Field(196), Field(232), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0)]), "message_size": Field(2⁶)}, expected_output: Vec([Field(102), Field(26), Field(94), Field(212), Field(102), Field(1), Field(215), Field(217), Field(167), Field(175), Field(158), Field(18), Field(20), Field(244), Field(158), Field(200), Field(2⁷), Field(186), Field(251), Field(243), Field(20), Field(207), Field(22), Field(3), Field(139), Field(81), Field(207), Field(2⁴), Field(50), Field(167), Field(1), Field(163)]) } +cc 0f334fe0c29748e8d0964d63f0d1f3a4eee536afa665eabc838045d8e1c67792 # shrinks to io = SnippetInputOutput { description: "force_brillig = true, max_len = 135", inputs: {"input": Vec([Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(0), Field(29), Field(131), Field(217), Field(115), Field(221), Field(92), Field(23), Field(14), Field(58), Field(90), Field(232), Field(155), Field(59), Field(209), Field(2⁴×15), Field(137), Field(214), Field(129), Field(11), Field(140), Field(99), Field(131), Field(188), Field(159), Field(27), Field(206), Field(89), Field(137), Field(248), Field(30), Field(149), Field(194), Field(121), Field(127), Field(245), Field(202), Field(155), Field(203), Field(122), Field(2⁵), Field(209), Field(194), Field(214), Field(11), Field(82), Field(26), Field(244), Field(34), Field(30), Field(125), Field(83), Field(2⁴×13), Field(30), Field(2⁴×10), Field(85), Field(245), Field(220), Field(211), Field(190), Field(46), Field(159), Field(87), Field(74), Field(51), Field(42), Field(202), Field(230), Field(137), Field(127), Field(29), Field(126), Field(243), Field(106), Field(156), Field(2⁴×6), Field(154), Field(70), Field(100), Field(130)]), "message_size": Field(135)}, expected_output: Vec([Field(149), Field(114), Field(68), Field(219), Field(215), Field(147), Field(139), Field(34), Field(145), Field(204), Field(248), Field(145), Field(21), Field(119), Field(2⁵), Field(125), Field(181), Field(142), Field(106), Field(169), Field(202), Field(111), Field(110), Field(6), Field(210), Field(250), Field(2⁴), Field(110), Field(209), Field(2), Field(33), Field(104)]) } From 5601afabfd975f67d46cf7297ff0b2bc9d6d552f Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 10:52:06 -0400 Subject: [PATCH 51/56] fix error check in numeric_generic_as_param_type (passing) --- compiler/noirc_frontend/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7e703450c5a..14107302a75 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1733,7 +1733,7 @@ fn numeric_generic_as_param_type() { )); // Error from the return type assert!(matches!( - errors[1].0, + errors[2].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } From aacd45aae3ea8e596afd4aabad48a624e0862097 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 10:55:37 -0400 Subject: [PATCH 52/56] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 6390f8dcc65..3f406397d55 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -2511,13 +2511,7 @@ impl BinaryTypeOperator { BinaryTypeOperator::Addition => Some(a + b), BinaryTypeOperator::Subtraction => Some(a - b), BinaryTypeOperator::Multiplication => Some(a * b), - BinaryTypeOperator::Division => { - if b == FieldElement::zero() { - None - } else { - Some(a / b) - } - } + BinaryTypeOperator::Division => (b != FieldElement::zero()).then(|| a / b), BinaryTypeOperator::Modulo => None, }, Some(maximum_size) => { From eb429515101ff7aa15984639a6f8c086044dbd22 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 9 Oct 2024 10:56:33 -0400 Subject: [PATCH 53/56] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 3f406397d55..3caf423cef1 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -250,19 +250,10 @@ impl Kind { } /// Ensure the given value fits in self.integral_maximum_size() - pub(crate) fn ensure_value_fits(&self, value: T) -> Option - where - for<'a> &'a FieldElement: From<&'a T>, - { + fn ensure_value_fits(&self, value: FieldElement) -> Option { match self.integral_maximum_size() { None => Some(value), - Some(maximum_size) => { - if <&T as Into<&FieldElement>>::into(&value) <= &maximum_size { - Some(value) - } else { - None - } - } + Some(maximum_size) => (value <= maximum_size).then_some(value), } } } From f32b7bb8c942282e8b274d5f45258d018ca49603 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 12:06:01 -0400 Subject: [PATCH 54/56] implement Jake's suggestion (+ extra clone) for simplifying checked_op error handling in interpreter, replace warning message that 'size checks are being implemented for this case' with note that warning may become error, rephrase DownsizingCast message, split is_field_element into type-level and value-level versions (using type-level one for generic arithmetics), note #6260 on evaluate_to_field_element, use get_type for Type::Alias, used checked arithmetic in BinaryTypeOperator::function --- .../src/hir/comptime/interpreter.rs | 239 ++++++++---------- .../src/hir/type_check/errors.rs | 6 +- compiler/noirc_frontend/src/hir_def/types.rs | 48 ++-- .../src/hir_def/types/arithmetic.rs | 2 +- 4 files changed, 135 insertions(+), 160 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 6ef700b2315..ffb759e74a2 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -904,31 +904,14 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { return self.evaluate_overloaded_infix(infix, lhs_value, rhs_value, id); } - fn make_error_value( - lhs: &Value, - rhs: &Value, - location: Location, - operator: &'static str, - ) -> InterpreterError { - let lhs = lhs.get_type().into_owned(); - let rhs = rhs.get_type().into_owned(); - InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } - } - - // make_error_value if the result is None - fn or_err( - lhs_rhs_location: (&Value, &Value, Location), - operator: &'static str, - result: Option, - ) -> IResult { - let (lhs, rhs, location) = lhs_rhs_location; - result.ok_or(make_error_value(lhs, rhs, location, operator)) - } - + let lhs_type = lhs_value.get_type().into_owned(); + let rhs_type = rhs_value.get_type().into_owned(); let location = self.elaborator.interner.expr_location(&id); - let error_ctx = (&lhs_value, &rhs_value, location); - let make_error = |lhs: Value, rhs: Value, operator| -> IResult { - Err(make_error_value(&lhs, &rhs, location, operator)) + + let error = |operator| { + let lhs = lhs_type.clone(); + let rhs = rhs_type.clone(); + InterpreterError::InvalidValuesForBinary { lhs, rhs, location, operator } }; use InterpreterError::InvalidValuesForBinary; @@ -936,114 +919,114 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { BinaryOpKind::Add => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::I8(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::I16(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::I32(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::I64(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::U8(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::U16(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::U32(lhs.checked_add(rhs).ok_or(error("+"))?)) } (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(or_err(error_ctx, "+", lhs.checked_add(rhs))?)) + Ok(Value::U64(lhs.checked_add(rhs).ok_or(error("+"))?)) } - (lhs, rhs) => make_error(lhs, rhs, "+"), + (lhs, rhs) => Err(error("+")), }, BinaryOpKind::Subtract => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::I8(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::I16(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::I32(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::I64(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::U8(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::U16(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::U32(lhs.checked_sub(rhs).ok_or(error("-"))?)) } (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(or_err(error_ctx, "-", lhs.checked_sub(rhs))?)) + Ok(Value::U64(lhs.checked_sub(rhs).ok_or(error("-"))?)) } - (lhs, rhs) => make_error(lhs, rhs, "-"), + (lhs, rhs) => Err(error("-")), }, BinaryOpKind::Multiply => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::I8(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::I16(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::I32(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::I64(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::U8(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::U16(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::U32(lhs.checked_mul(rhs).ok_or(error("*"))?)) } (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(or_err(error_ctx, "*", lhs.checked_mul(rhs))?)) + Ok(Value::U64(lhs.checked_mul(rhs).ok_or(error("*"))?)) } - (lhs, rhs) => make_error(lhs, rhs, "*"), + (lhs, rhs) => Err(error("*")), }, BinaryOpKind::Divide => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::I8(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::I16(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::I32(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::I64(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::U8(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::U16(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::U32(lhs.checked_div(rhs).ok_or(error("/"))?)) } (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(or_err(error_ctx, "/", lhs.checked_div(rhs))?)) + Ok(Value::U64(lhs.checked_div(rhs).ok_or(error("/"))?)) } - (lhs, rhs) => make_error(lhs, rhs, "/"), + (lhs, rhs) => Err(error("/")), }, BinaryOpKind::Equal => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), @@ -1056,7 +1039,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), - (lhs, rhs) => make_error(lhs, rhs, "=="), + (lhs, rhs) => Err(error("==")), }, BinaryOpKind::NotEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), @@ -1069,7 +1052,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), - (lhs, rhs) => make_error(lhs, rhs, "!="), + (lhs, rhs) => Err(error("!=")), }, BinaryOpKind::Less => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), @@ -1081,7 +1064,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), - (lhs, rhs) => make_error(lhs, rhs, "<"), + (lhs, rhs) => Err(error("<")), }, BinaryOpKind::LessEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), @@ -1093,7 +1076,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), - (lhs, rhs) => make_error(lhs, rhs, "<="), + (lhs, rhs) => Err(error("<=")), }, BinaryOpKind::Greater => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), @@ -1105,7 +1088,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), - (lhs, rhs) => make_error(lhs, rhs, ">"), + (lhs, rhs) => Err(error(">")), }, BinaryOpKind::GreaterEqual => match (lhs_value.clone(), rhs_value.clone()) { (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), @@ -1117,7 +1100,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), - (lhs, rhs) => make_error(lhs, rhs, ">="), + (lhs, rhs) => Err(error(">=")), }, BinaryOpKind::And => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), @@ -1129,7 +1112,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs & rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), - (lhs, rhs) => make_error(lhs, rhs, "&"), + (lhs, rhs) => Err(error("&")), }, BinaryOpKind::Or => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), @@ -1141,7 +1124,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs | rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), - (lhs, rhs) => make_error(lhs, rhs, "|"), + (lhs, rhs) => Err(error("|")), }, BinaryOpKind::Xor => match (lhs_value.clone(), rhs_value.clone()) { (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), @@ -1153,108 +1136,88 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::U16(lhs ^ rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), - (lhs, rhs) => make_error(lhs, rhs, "^"), + (lhs, rhs) => Err(error("^")), }, BinaryOpKind::ShiftRight => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err( - error_ctx, - ">>", - lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err( - error_ctx, - ">>", - lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err( - error_ctx, - ">>", - lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err( - error_ctx, - ">>", - lhs.checked_shr(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)) + Ok(Value::U8(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, ">>", lhs.checked_shr(rhs.into()))?)) + Ok(Value::U16(lhs.checked_shr(rhs.into()).ok_or(error(">>"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, ">>", lhs.checked_shr(rhs))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err( - error_ctx, - ">>", - lhs.checked_shr(or_err(error_ctx, ">>", rhs.try_into().ok())?), - )?)), - (lhs, rhs) => make_error(lhs, rhs, ">>"), + Ok(Value::U32(lhs.checked_shr(rhs).ok_or(error(">>"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( + lhs.checked_shr(rhs.try_into().map_err(|_| error(">>"))?).ok_or(error(">>"))?, + )), + (lhs, rhs) => Err(error(">>")), }, BinaryOpKind::ShiftLeft => match (lhs_value.clone(), rhs_value.clone()) { - (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(or_err( - error_ctx, - "<<", - lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16(or_err( - error_ctx, - "<<", - lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(or_err( - error_ctx, - "<<", - lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(or_err( - error_ctx, - "<<", - lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I16(lhs), Value::I16(rhs)) => Ok(Value::I16( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)) + Ok(Value::U8(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "<<", lhs.checked_shl(rhs.into()))?)) + Ok(Value::U16(lhs.checked_shl(rhs.into()).ok_or(error("<<"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "<<", lhs.checked_shl(rhs))?)) - } - (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(or_err( - error_ctx, - "<<", - lhs.checked_shl(or_err(error_ctx, "<<", rhs.try_into().ok())?), - )?)), - (lhs, rhs) => make_error(lhs, rhs, "<<"), + Ok(Value::U32(lhs.checked_shl(rhs).ok_or(error("<<"))?)) + } + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64( + lhs.checked_shl(rhs.try_into().map_err(|_| error("<<"))?).ok_or(error("<<"))?, + )), + (lhs, rhs) => Err(error("<<")), }, BinaryOpKind::Modulo => match (lhs_value.clone(), rhs_value.clone()) { (Value::I8(lhs), Value::I8(rhs)) => { - Ok(Value::I8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::I8(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::I16(lhs), Value::I16(rhs)) => { - Ok(Value::I16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::I16(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::I32(lhs), Value::I32(rhs)) => { - Ok(Value::I32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::I32(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::I64(lhs), Value::I64(rhs)) => { - Ok(Value::I64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::I64(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::U8(lhs), Value::U8(rhs)) => { - Ok(Value::U8(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::U8(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::U16(lhs), Value::U16(rhs)) => { - Ok(Value::U16(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::U16(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::U32(lhs), Value::U32(rhs)) => { - Ok(Value::U32(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::U32(lhs.checked_rem(rhs).ok_or(error("%"))?)) } (Value::U64(lhs), Value::U64(rhs)) => { - Ok(Value::U64(or_err(error_ctx, "%", lhs.checked_rem(rhs))?)) + Ok(Value::U64(lhs.checked_rem(rhs).ok_or(error("%"))?)) } - (lhs, rhs) => make_error(lhs, rhs, "%"), + (lhs, rhs) => Err(error("%")), }, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 6d5fb4377d2..28b0c20f36e 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -49,7 +49,7 @@ pub enum TypeCheckError { #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, // TODO(https://github.com/noir-lang/noir/issues/6238): implement handling for larger types - #[error("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case")] + #[error("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?} (this warning may become an error in the future)")] EvaluatedGlobalPartialSizeChecks { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span }, @@ -57,7 +57,7 @@ pub enum TypeCheckError { PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] InvalidCast { from: Type, span: Span, reason: String }, - #[error("Casting value with type {from} to a smaller type ({to})")] + #[error("Casting value of type {from} to a smaller type ({to})")] DownsizingCast { from: Type, to: Type, span: Span, reason: String }, #[error("Expected a function, but found a(n) {found}")] ExpectedFunction { found: Type, span: Span }, @@ -236,7 +236,7 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { // handling for larger types TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind, expr_kind, expr_span } => { Diagnostic::simple_warning( - format!("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?}, size checks are being implemented for this case"), + format!("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?} (this warning may become an error in the future)"), String::new(), *expr_span, ) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 3caf423cef1..538b5553afd 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -172,10 +172,17 @@ impl Kind { matches!(self.follow_bindings(), Self::Numeric { .. }) } - pub(crate) fn is_field_element(&self) -> bool { + pub(crate) fn is_type_level_field_element(&self) -> bool { + let type_level = false; + self.is_field_element(type_level) + } + + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { match self.follow_bindings() { - Kind::Numeric(typ) => typ.is_field_element(), - Kind::IntegerOrField => true, + Kind::Numeric(typ) => typ.is_field_element(value_level), + Kind::IntegerOrField => value_level, _ => false, } } @@ -760,10 +767,12 @@ impl TypeVariable { } } - fn is_field_element(&self) -> bool { + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { match &*self.borrow() { - TypeBinding::Bound(binding) => binding.is_field_element(), - TypeBinding::Unbound(_, type_var_kind) => type_var_kind.is_field_element(), + TypeBinding::Bound(binding) => binding.is_field_element(value_level), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.is_field_element(value_level), } } } @@ -994,11 +1003,13 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(_, _)) } - fn is_field_element(&self) -> bool { + /// If value_level, only check for Type::FieldElement, + /// else only check for a type-level FieldElement + fn is_field_element(&self, value_level: bool) -> bool { match self.follow_bindings() { - Type::FieldElement => true, - Type::TypeVariable(var) => var.is_field_element(), - Type::Constant(_, kind) => kind.is_field_element(), + Type::FieldElement => value_level, + Type::TypeVariable(var) => var.is_field_element(value_level), + Type::Constant(_, kind) => !value_level && kind.is_field_element(true), _ => false, } } @@ -1888,6 +1899,8 @@ impl Type { .and_then(|field_element| field_element.try_to_u32()) } + // TODO(https://github.com/noir-lang/noir/issues/6260): remove + // the unifies checks once all kinds checks are implemented? pub(crate) fn evaluate_to_field_element(&self, kind: &Kind) -> Option { if let Some((binding, binding_kind)) = self.get_inner_type_variable() { if let TypeBinding::Bound(binding) = &*binding.borrow() { @@ -2421,7 +2434,7 @@ impl Type { Type::Integer(sign, num_bits) => { let mut max_bit_size = num_bits.bit_size(); if sign == &Signedness::Signed { - max_bit_size >>= 1; + max_bit_size -= 1; } Some(((1u128 << max_bit_size) - 1).into()) } @@ -2436,7 +2449,7 @@ impl Type { TypeBinding::Bound(typ) => typ.integral_maximum_size(), } } - Type::Alias(alias, _args) => alias.borrow().typ.integral_maximum_size(), + Type::Alias(alias, args) => alias.borrow().get_type(args).integral_maximum_size(), Type::NamedGeneric(binding, _name) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.integral_maximum_size(), TypeBinding::Unbound(_, kind) => kind.integral_maximum_size(), @@ -2505,16 +2518,15 @@ impl BinaryTypeOperator { BinaryTypeOperator::Division => (b != FieldElement::zero()).then(|| a / b), BinaryTypeOperator::Modulo => None, }, - Some(maximum_size) => { - let maximum_size = maximum_size.to_i128(); + Some(_maximum_size) => { let a = a.to_i128(); let b = b.to_i128(); let result = match self { - BinaryTypeOperator::Addition => (a + b) % maximum_size, - BinaryTypeOperator::Subtraction => (a - b) % maximum_size, - BinaryTypeOperator::Multiplication => (a * b) % maximum_size, - BinaryTypeOperator::Division => (a.checked_div(b)?) % maximum_size, + BinaryTypeOperator::Addition => a.checked_add(b)?, + BinaryTypeOperator::Subtraction => a.checked_sub(b)?, + BinaryTypeOperator::Multiplication => a.checked_mul(b)?, + BinaryTypeOperator::Division => a.checked_div(b)?, BinaryTypeOperator::Modulo => a.checked_rem(b)?, }; diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 3bdf08bfc74..0eee7dbf824 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -224,7 +224,7 @@ impl Type { op = op.inverse()?; } - let divides_evenly = !lhs.infix_kind(rhs).is_field_element() + let divides_evenly = !lhs.infix_kind(rhs).is_type_level_field_element() && l_const.to_i128().checked_rem(r_const.to_i128()) == Some(0); // If op is a division we need to ensure it divides evenly From 51f80eab3f2253a9d296c901d8d84c340eca1007 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 12:49:19 -0400 Subject: [PATCH 55/56] rename EvaluatedGlobalPartialSizeChecks -> EvaluatedGlobalIsntU32 and fixup error message --- compiler/noirc_frontend/src/elaborator/types.rs | 2 +- .../noirc_frontend/src/hir/type_check/errors.rs | 8 ++++---- compiler/noirc_frontend/src/tests.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 262a1f3b0fb..82d14743428 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -420,7 +420,7 @@ impl<'context> Elaborator<'context> { // TODO(https://github.com/noir-lang/noir/issues/6238): // support non-u32 generics here if !kind.unifies(&Kind::u32()) { - let error = TypeCheckError::EvaluatedGlobalPartialSizeChecks { + let error = TypeCheckError::EvaluatedGlobalIsntU32 { expected_kind: Kind::u32().to_string(), expr_kind: kind.to_string(), expr_span: path.span(), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 28b0c20f36e..bfa903f73cb 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -49,8 +49,8 @@ pub enum TypeCheckError { #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, // TODO(https://github.com/noir-lang/noir/issues/6238): implement handling for larger types - #[error("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?} (this warning may become an error in the future)")] - EvaluatedGlobalPartialSizeChecks { expected_kind: String, expr_kind: String, expr_span: Span }, + #[error("expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)")] + EvaluatedGlobalIsntU32 { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span }, #[error("Return type in a function cannot be public")] @@ -234,9 +234,9 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { } // TODO(https://github.com/noir-lang/noir/issues/6238): implement // handling for larger types - TypeCheckError::EvaluatedGlobalPartialSizeChecks { expected_kind, expr_kind, expr_span } => { + TypeCheckError::EvaluatedGlobalIsntU32 { expected_kind, expr_kind, expr_span } => { Diagnostic::simple_warning( - format!("Expected type {expected_kind:?} when evaluating globals: found {expr_kind:?} (this warning may become an error in the future)"), + format!("Expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)"), String::new(), *expr_span, ) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 14107302a75..f190ef38bab 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1973,7 +1973,7 @@ fn numeric_generic_u16_array_size() { } // TODO(https://github.com/noir-lang/noir/issues/6238): -// The EvaluatedGlobalPartialSizeChecks warning is a stopgap +// The EvaluatedGlobalIsntU32 warning is a stopgap // (originally from https://github.com/noir-lang/noir/issues/6125) #[test] fn numeric_generic_field_larger_than_u32() { @@ -1990,7 +1990,7 @@ fn numeric_generic_field_larger_than_u32() { assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), )); assert!(matches!( errors[1].0, @@ -1999,7 +1999,7 @@ fn numeric_generic_field_larger_than_u32() { } // TODO(https://github.com/noir-lang/noir/issues/6238): -// The EvaluatedGlobalPartialSizeChecks warning is a stopgap +// The EvaluatedGlobalIsntU32 warning is a stopgap // (originally from https://github.com/noir-lang/noir/issues/6126) #[test] fn numeric_generic_field_arithmetic_larger_than_u32() { @@ -2028,7 +2028,7 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), )); assert!(matches!( @@ -2164,10 +2164,10 @@ fn numeric_generics_type_kind_mismatch() { assert_eq!(errors.len(), 3); // TODO(https://github.com/noir-lang/noir/issues/6238): - // The EvaluatedGlobalPartialSizeChecks warning is a stopgap + // The EvaluatedGlobalIsntU32 warning is a stopgap assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), )); assert!(matches!( @@ -2178,7 +2178,7 @@ fn numeric_generics_type_kind_mismatch() { // TODO(https://github.com/noir-lang/noir/issues/6238): see above assert!(matches!( errors[2].0, - CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }), + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }), )); } @@ -3265,7 +3265,7 @@ fn non_u32_as_array_length() { assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::EvaluatedGlobalPartialSizeChecks { .. }) + CompilationError::TypeError(TypeCheckError::EvaluatedGlobalIsntU32 { .. }) )); assert!(matches!( errors[1].0, From 9f2647a0c63a0e3f162fffab413a186d9bf55c36 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Oct 2024 12:50:40 -0400 Subject: [PATCH 56/56] typo --- compiler/noirc_frontend/src/hir/type_check/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index bfa903f73cb..d8dae1f6549 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -49,7 +49,7 @@ pub enum TypeCheckError { #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, // TODO(https://github.com/noir-lang/noir/issues/6238): implement handling for larger types - #[error("expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)")] + #[error("Expected type {expected_kind} when evaluating globals, but found {expr_kind} (this warning may become an error in the future)")] EvaluatedGlobalIsntU32 { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span },