diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 6625eba5a0a..55c5f00c00c 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::{PolymorphicKind, TypeBinding, TypeVariableKind}; /// 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, + TypeVariableKind::Polymorphic(PolymorphicKind::IntegerOrField), + ) + | Type::TypeVariable(binding, TypeVariableKind::Polymorphic(PolymorphicKind::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::Bool => AbiType::Boolean, Type::String(size) => { let size = size diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ec144e8e134..47aa1030f62 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -783,8 +783,7 @@ impl<'context> Elaborator<'context> { match from.follow_bindings() { Type::Integer(..) | Type::FieldElement - | Type::TypeVariable(_, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(_, TypeVariableKind::Integer) + | Type::TypeVariable(_, TypeVariableKind::Polymorphic(..)) | Type::Bool => (), Type::TypeVariable(binding, TypeVariableKind::Normal) => { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index a3d37fd76fc..adaaca80db9 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -18,7 +18,6 @@ use crate::monomorphization::{ undo_instantiation_bindings, }; use crate::token::Tokens; -use crate::TypeVariable; use crate::{ hir_def::{ expr::{ @@ -36,6 +35,7 @@ use crate::{ node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; +use crate::{PolymorphicKind, TypeVariable}; use super::errors::{IResult, InterpreterError}; use super::value::{unwrap_rc, Value}; @@ -661,9 +661,17 @@ 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, + TypeVariableKind::Polymorphic(PolymorphicKind::IntegerOrField), + ) = &typ + { Ok(Value::Field(value)) - } else if let Type::TypeVariable(variable, TypeVariableKind::Integer) = &typ { + } else if let Type::TypeVariable( + variable, + TypeVariableKind::Polymorphic(PolymorphicKind::Integer), + ) = &typ + { let value: u64 = value .try_to_u64() .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 5d8e8d1b11e..7b4bb869381 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -439,21 +439,8 @@ pub enum TypeVariableKind { /// Can bind to any type Normal, - /// A generic integer or field or bool type. - /// This type is used in the `x as T` cast expression, where `x` is a type variable. - /// In this case, `x` must be an integer, field or bool for the cast to succeed. - IntegerOrFieldOrBool, - - /// 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, + /// A polymorphic type. + Polymorphic(PolymorphicKind), /// A potentially constant array size. This will only bind to itself or /// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound @@ -463,9 +450,31 @@ pub enum TypeVariableKind { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum PolymorphicKind { - IntegerOrFieldOrBool, - 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, + /// 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 or field or bool type. + /// This type is used in the `x as T` cast expression, where `x` is a type variable. + /// In this case, `x` must be an integer, field or bool for the cast to succeed. + IntegerOrFieldOrBool, +} + +impl PolymorphicKind { + /// Returns the default type this polymorphic kind should be bound to if it is still unbound + /// during monomorphization. + pub(crate) fn default_type(&self) -> Option { + match self { + PolymorphicKind::Integer => Some(Type::default_int_type()), + PolymorphicKind::IntegerOrField => Some(Type::default_int_or_field_type()), + PolymorphicKind::IntegerOrFieldOrBool => None, + } + } } /// A TypeVariable is a mutable reference that is either @@ -572,28 +581,9 @@ impl std::fmt::Display for Type { 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::IntegerOrFieldOrBool) => { + Type::TypeVariable(binding, TypeVariableKind::Polymorphic(polymorphic_kind)) => { if let TypeBinding::Unbound(_) = &*binding.borrow() { - // Show a pseudo-syntax for a union type, which might help users understand that the - // type must be one of these. - write!(f, "Integer | Field | bool") + polymorphic_kind.fmt(f) } else { write!(f, "{}", binding.borrow()) } @@ -669,6 +659,25 @@ impl std::fmt::Display for Type { } } +impl std::fmt::Display for PolymorphicKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PolymorphicKind::Integer => write!(f, "{}", Type::default_int_type()), + PolymorphicKind::IntegerOrField => { + // 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") + } + PolymorphicKind::IntegerOrFieldOrBool => { + // Show a pseudo-syntax for a union type, which might help users understand that the + // type must be one of these. + write!(f, "Integer | Field | bool") + } + } + } +} + impl std::fmt::Display for BinaryTypeOperator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -737,23 +746,21 @@ impl Type { Type::TypeVariable(var, kind) } + pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { + Type::polymorphic(interner, PolymorphicKind::Integer) + } + 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, kind) + Type::polymorphic(interner, PolymorphicKind::IntegerOrField) } pub fn polymorphic_integer_or_field_or_bool(interner: &mut NodeInterner) -> Type { - let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::IntegerOrFieldOrBool; - let var = TypeVariable::unbound(id); - Type::TypeVariable(var, kind) + Type::polymorphic(interner, PolymorphicKind::IntegerOrFieldOrBool) } - pub fn polymorphic_integer(interner: &mut NodeInterner) -> Type { + fn polymorphic(interner: &mut NodeInterner, kind: PolymorphicKind) -> Type { let id = interner.next_type_variable_id(); - let kind = TypeVariableKind::Integer; + let kind = TypeVariableKind::Polymorphic(kind); let var = TypeVariable::unbound(id); Type::TypeVariable(var, kind) } @@ -798,7 +805,14 @@ impl Type { use TypeVariableKind as K; matches!( self.follow_bindings(), - FieldElement | Integer(..) | Bool | TypeVariable(_, K::Integer | K::IntegerOrField) + FieldElement + | Integer(..) + | Bool + | TypeVariable( + _, + K::Polymorphic(PolymorphicKind::Integer) + | K::Polymorphic(PolymorphicKind::IntegerOrField) + ) ) } @@ -1277,9 +1291,7 @@ impl Type { } // *length != target_length TypeVariableKind::Constant(_) => Err(UnificationError), - TypeVariableKind::IntegerOrFieldOrBool => Err(UnificationError), - TypeVariableKind::IntegerOrField => Err(UnificationError), - TypeVariableKind::Integer => Err(UnificationError), + TypeVariableKind::Polymorphic(..) => Err(UnificationError), }, } } @@ -1315,8 +1327,7 @@ impl Type { bindings.insert(target_id, (var.clone(), this)); Ok(()) } - Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) - | Type::TypeVariable(self_var, TypeVariableKind::IntegerOrFieldOrBool) => { + Type::TypeVariable(self_var, TypeVariableKind::Polymorphic(..)) => { let borrow = self_var.borrow(); match &*borrow { TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic(var, bindings, kind), @@ -1326,7 +1337,10 @@ impl Type { if matches!(kind, PolymorphicKind::Integer) { // Integer is more specific than IntegerOrField so we bind the type // variable to Integer instead. - let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); + let clone = Type::TypeVariable( + var.clone(), + TypeVariableKind::Polymorphic(PolymorphicKind::Integer), + ); bindings.insert(*new_target_id, (self_var.clone(), clone)); } else { bindings.insert(target_id, (var.clone(), this.clone())); @@ -1335,18 +1349,6 @@ impl Type { } } } - Type::TypeVariable(self_var, TypeVariableKind::Integer) => { - let borrow = self_var.borrow(); - match &*borrow { - TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic(var, bindings, kind), - // Avoid infinitely recursive bindings - TypeBinding::Unbound(id) if *id == target_id => Ok(()), - TypeBinding::Unbound(_) => { - bindings.insert(target_id, (var.clone(), this.clone())); - Ok(()) - } - } - } Type::TypeVariable(self_var, TypeVariableKind::Normal) => { let borrow = self_var.borrow(); match &*borrow { @@ -1355,14 +1357,8 @@ impl Type { TypeBinding::Unbound(id) if *id == target_id => Ok(()), TypeBinding::Unbound(new_target_id) => { // Bind to the most specific type variable kind - let clone_kind = match kind { - PolymorphicKind::Integer => TypeVariableKind::Integer, - PolymorphicKind::IntegerOrField => TypeVariableKind::IntegerOrField, - PolymorphicKind::IntegerOrFieldOrBool => { - TypeVariableKind::IntegerOrFieldOrBool - } - }; - let clone = Type::TypeVariable(var.clone(), clone_kind); + let clone = + Type::TypeVariable(var.clone(), TypeVariableKind::Polymorphic(kind)); bindings.insert(*new_target_id, (self_var.clone(), clone)); Ok(()) } @@ -1454,28 +1450,32 @@ impl Type { alias.try_unify(other, bindings) } - (TypeVariable(var, Kind::IntegerOrFieldOrBool), other) - | (other, TypeVariable(var, Kind::IntegerOrFieldOrBool)) => other - .try_unify_to_type_variable(var, bindings, |bindings| { - let kind = PolymorphicKind::IntegerOrFieldOrBool; - other.try_bind_to_polymorphic(var, bindings, kind) - }), + ( + TypeVariable(var, Kind::Polymorphic(PolymorphicKind::IntegerOrFieldOrBool)), + other, + ) + | ( + other, + TypeVariable(var, Kind::Polymorphic(PolymorphicKind::IntegerOrFieldOrBool)), + ) => other.try_unify_to_type_variable(var, bindings, |bindings| { + let kind = PolymorphicKind::IntegerOrFieldOrBool; + other.try_bind_to_polymorphic(var, bindings, kind) + }), - (TypeVariable(var, Kind::IntegerOrField), other) - | (other, TypeVariable(var, Kind::IntegerOrField)) => { + (TypeVariable(var, Kind::Polymorphic(PolymorphicKind::IntegerOrField)), other) + | (other, TypeVariable(var, Kind::Polymorphic(PolymorphicKind::IntegerOrField))) => { other.try_unify_to_type_variable(var, bindings, |bindings| { let kind = PolymorphicKind::IntegerOrField; other.try_bind_to_polymorphic(var, bindings, kind) }) } - (TypeVariable(var, Kind::Integer), other) - | (other, TypeVariable(var, Kind::Integer)) => { - other.try_unify_to_type_variable(var, bindings, |bindings| { + (TypeVariable(var, Kind::Polymorphic(PolymorphicKind::Integer)), other) + | (other, TypeVariable(var, Kind::Polymorphic(PolymorphicKind::Integer))) => other + .try_unify_to_type_variable(var, bindings, |bindings| { let kind = PolymorphicKind::Integer; other.try_bind_to_polymorphic(var, bindings, kind) - }) - } + }), (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { @@ -2184,9 +2184,7 @@ impl TypeVariableKind { /// during monomorphization. pub(crate) fn default_type(&self) -> Option { match self { - TypeVariableKind::IntegerOrFieldOrBool => None, - TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), - TypeVariableKind::Integer => Some(Type::default_int_type()), + TypeVariableKind::Polymorphic(polymorphic_kind) => polymorphic_kind.default_type(), TypeVariableKind::Constant(length) => Some(Type::Constant(*length)), TypeVariableKind::Normal => None, } @@ -2220,16 +2218,20 @@ impl From<&Type> for PrintableType { } Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() }, }, - Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + Type::TypeVariable( + binding, + TypeVariableKind::Polymorphic(PolymorphicKind::Integer), + ) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), TypeBinding::Unbound(_) => Type::default_int_type().into(), }, - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_or_field_type().into(), - } - } + Type::TypeVariable( + binding, + TypeVariableKind::Polymorphic(PolymorphicKind::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"); @@ -2281,14 +2283,18 @@ impl std::fmt::Debug for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{:?}", var), - Type::TypeVariable(binding, TypeVariableKind::IntegerOrFieldOrBool) => { - write!(f, "IntOrFieldOrBool{:?}", binding) - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - write!(f, "IntOrField{:?}", binding) - } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - write!(f, "Int{:?}", binding) + Type::TypeVariable(binding, TypeVariableKind::Polymorphic(polymorphic_kind)) => { + match polymorphic_kind { + PolymorphicKind::Integer => { + write!(f, "Int{:?}", binding) + } + PolymorphicKind::IntegerOrField => { + write!(f, "IntOrField{:?}", binding) + } + PolymorphicKind::IntegerOrFieldOrBool => { + write!(f, "IntOrFieldOrBool{:?}", binding) + } + } } Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { write!(f, "{}{:?}", n, binding) diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 87ff45f8f1a..c7b80491898 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -20,6 +20,7 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::macros_api::UnaryOp; +use crate::PolymorphicKind; use crate::QuotedType; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; @@ -2048,8 +2049,12 @@ 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(_, TypeVariableKind::Polymorphic(PolymorphicKind::IntegerOrField)) => { + Some(FieldOrInt) + } + Type::TypeVariable(_, TypeVariableKind::Polymorphic(PolymorphicKind::Integer)) => { + Some(FieldOrInt) + } Type::Bool => Some(Bool), Type::String(_) => Some(String), Type::FmtString(_, _) => Some(FmtString), diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index bbf3e51fdb2..9d4189e111b 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, ItemKind}, - ParsedModule, Type, TypeBinding, TypeVariable, TypeVariableKind, + ParsedModule, PolymorphicKind, Type, TypeBinding, TypeVariable, TypeVariableKind, }; use crate::LspState; @@ -558,15 +558,21 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File Type::TypeVariable(var, TypeVariableKind::Normal) => { push_type_variable_parts(var, parts, files); } - Type::TypeVariable(binding, TypeVariableKind::Integer) => { + Type::TypeVariable(binding, TypeVariableKind::Polymorphic(PolymorphicKind::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) - | Type::TypeVariable(binding, TypeVariableKind::IntegerOrFieldOrBool) => { + Type::TypeVariable( + binding, + TypeVariableKind::Polymorphic(PolymorphicKind::IntegerOrField), + ) + | Type::TypeVariable( + binding, + TypeVariableKind::Polymorphic(PolymorphicKind::IntegerOrFieldOrBool), + ) => { if let TypeBinding::Unbound(_) = &*binding.borrow() { parts.push(string_part("Field")); } else {