Skip to content

Commit c956be8

Browse files
michaeljkleinTomAFrenchjfecher
authored
feat: TypeVariableKind for just Integers (#4118)
# Description Add a new `TypeVariableKind` specifically for Integers: until this PR, `IntegerOrField` is used. ## Problem\* May resolve #3639, #4290 ## Summary\* Adds the new `TypeVariableKind` - [x] Unify - [x] Test - [ ] Docs ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: jfecher <jake@aztecprotocol.com>
1 parent d202ee6 commit c956be8

5 files changed

Lines changed: 99 additions & 26 deletions

File tree

compiler/noirc_frontend/src/hir/type_check/expr.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ impl<'interner> TypeChecker<'interner> {
565565
Type::Integer(..)
566566
| Type::FieldElement
567567
| Type::TypeVariable(_, TypeVariableKind::IntegerOrField)
568+
| Type::TypeVariable(_, TypeVariableKind::Integer)
568569
| Type::Bool => (),
569570

570571
Type::TypeVariable(_, _) => {
@@ -805,7 +806,7 @@ impl<'interner> TypeChecker<'interner> {
805806

806807
// Matches on TypeVariable must be first to follow any type
807808
// bindings.
808-
(TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => {
809+
(TypeVariable(int, int_kind), other) | (other, TypeVariable(int, int_kind)) => {
809810
if let TypeBinding::Bound(binding) = &*int.borrow() {
810811
return self.comparator_operand_type_rules(other, binding, op, span);
811812
}
@@ -823,7 +824,13 @@ impl<'interner> TypeChecker<'interner> {
823824
}
824825

825826
let mut bindings = TypeBindings::new();
826-
if other.try_bind_to_polymorphic_int(int, &mut bindings).is_ok()
827+
if other
828+
.try_bind_to_polymorphic_int(
829+
int,
830+
&mut bindings,
831+
*int_kind == TypeVariableKind::Integer,
832+
)
833+
.is_ok()
827834
|| other == &Type::Error
828835
{
829836
Type::apply_type_bindings(bindings);
@@ -1081,7 +1088,7 @@ impl<'interner> TypeChecker<'interner> {
10811088

10821089
// Matches on TypeVariable must be first so that we follow any type
10831090
// bindings.
1084-
(TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => {
1091+
(TypeVariable(int, int_kind), other) | (other, TypeVariable(int, int_kind)) => {
10851092
if let TypeBinding::Bound(binding) = &*int.borrow() {
10861093
return self.infix_operand_type_rules(binding, op, other, span);
10871094
}
@@ -1114,7 +1121,13 @@ impl<'interner> TypeChecker<'interner> {
11141121
}
11151122

11161123
let mut bindings = TypeBindings::new();
1117-
if other.try_bind_to_polymorphic_int(int, &mut bindings).is_ok()
1124+
if other
1125+
.try_bind_to_polymorphic_int(
1126+
int,
1127+
&mut bindings,
1128+
*int_kind == TypeVariableKind::Integer,
1129+
)
1130+
.is_ok()
11181131
|| other == &Type::Error
11191132
{
11201133
Type::apply_type_bindings(bindings);

compiler/noirc_frontend/src/hir_def/types.rs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ pub enum TypeVariableKind {
442442
/// type annotations on each integer literal.
443443
IntegerOrField,
444444

445+
/// A generic integer type. This is a more specific kind of TypeVariable
446+
/// that can only be bound to Type::Integer, or other polymorphic integers.
447+
Integer,
448+
445449
/// A potentially constant array size. This will only bind to itself, Type::NotConstant, or
446450
/// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound
447451
/// during monomorphization.
@@ -747,6 +751,13 @@ impl std::fmt::Display for Type {
747751
Signedness::Unsigned => write!(f, "u{num_bits}"),
748752
},
749753
Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()),
754+
Type::TypeVariable(binding, TypeVariableKind::Integer) => {
755+
if let TypeBinding::Unbound(_) = &*binding.borrow() {
756+
write!(f, "{}", TypeVariableKind::Integer.default_type())
757+
} else {
758+
write!(f, "{}", binding.borrow())
759+
}
760+
}
750761
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => {
751762
if let TypeBinding::Unbound(_) = &*binding.borrow() {
752763
// Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is
@@ -911,6 +922,7 @@ impl Type {
911922
Ok(())
912923
}
913924
TypeVariableKind::IntegerOrField => Err(UnificationError),
925+
TypeVariableKind::Integer => Err(UnificationError),
914926
},
915927
}
916928
}
@@ -925,6 +937,7 @@ impl Type {
925937
&self,
926938
var: &TypeVariable,
927939
bindings: &mut TypeBindings,
940+
only_integer: bool,
928941
) -> Result<(), UnificationError> {
929942
let target_id = match &*var.borrow() {
930943
TypeBinding::Bound(_) => unreachable!(),
@@ -940,7 +953,30 @@ impl Type {
940953
Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => {
941954
let borrow = self_var.borrow();
942955
match &*borrow {
943-
TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic_int(var, bindings),
956+
TypeBinding::Bound(typ) => {
957+
typ.try_bind_to_polymorphic_int(var, bindings, only_integer)
958+
}
959+
// Avoid infinitely recursive bindings
960+
TypeBinding::Unbound(id) if *id == target_id => Ok(()),
961+
TypeBinding::Unbound(new_target_id) => {
962+
if only_integer {
963+
// Integer is more specific than IntegerOrField so we bind the type
964+
// variable to Integer instead.
965+
let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer);
966+
bindings.insert(*new_target_id, (self_var.clone(), clone));
967+
} else {
968+
bindings.insert(target_id, (var.clone(), this.clone()));
969+
}
970+
Ok(())
971+
}
972+
}
973+
}
974+
Type::TypeVariable(self_var, TypeVariableKind::Integer) => {
975+
let borrow = self_var.borrow();
976+
match &*borrow {
977+
TypeBinding::Bound(typ) => {
978+
typ.try_bind_to_polymorphic_int(var, bindings, only_integer)
979+
}
944980
// Avoid infinitely recursive bindings
945981
TypeBinding::Unbound(id) if *id == target_id => Ok(()),
946982
TypeBinding::Unbound(_) => {
@@ -949,18 +985,23 @@ impl Type {
949985
}
950986
}
951987
}
952-
Type::TypeVariable(binding, TypeVariableKind::Normal) => {
953-
let borrow = binding.borrow();
988+
Type::TypeVariable(self_var, TypeVariableKind::Normal) => {
989+
let borrow = self_var.borrow();
954990
match &*borrow {
955-
TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic_int(var, bindings),
991+
TypeBinding::Bound(typ) => {
992+
typ.try_bind_to_polymorphic_int(var, bindings, only_integer)
993+
}
956994
// Avoid infinitely recursive bindings
957995
TypeBinding::Unbound(id) if *id == target_id => Ok(()),
958996
TypeBinding::Unbound(new_target_id) => {
959-
// IntegerOrField is more specific than TypeVariable so we bind the type
960-
// variable to IntegerOrField instead.
961-
let clone =
962-
Type::TypeVariable(var.clone(), TypeVariableKind::IntegerOrField);
963-
bindings.insert(*new_target_id, (binding.clone(), clone));
997+
// Bind to the most specific type variable kind
998+
let clone_kind = if only_integer {
999+
TypeVariableKind::Integer
1000+
} else {
1001+
TypeVariableKind::IntegerOrField
1002+
};
1003+
let clone = Type::TypeVariable(var.clone(), clone_kind);
1004+
bindings.insert(*new_target_id, (self_var.clone(), clone));
9641005
Ok(())
9651006
}
9661007
}
@@ -1050,7 +1091,16 @@ impl Type {
10501091
(TypeVariable(var, Kind::IntegerOrField), other)
10511092
| (other, TypeVariable(var, Kind::IntegerOrField)) => {
10521093
other.try_unify_to_type_variable(var, bindings, |bindings| {
1053-
other.try_bind_to_polymorphic_int(var, bindings)
1094+
let only_integer = false;
1095+
other.try_bind_to_polymorphic_int(var, bindings, only_integer)
1096+
})
1097+
}
1098+
1099+
(TypeVariable(var, Kind::Integer), other)
1100+
| (other, TypeVariable(var, Kind::Integer)) => {
1101+
other.try_unify_to_type_variable(var, bindings, |bindings| {
1102+
let only_integer = true;
1103+
other.try_bind_to_polymorphic_int(var, bindings, only_integer)
10541104
})
10551105
}
10561106

@@ -1599,6 +1649,7 @@ impl TypeVariableKind {
15991649
pub(crate) fn default_type(&self) -> Type {
16001650
match self {
16011651
TypeVariableKind::IntegerOrField | TypeVariableKind::Normal => Type::default_int_type(),
1652+
TypeVariableKind::Integer => Type::default_range_loop_type(),
16021653
TypeVariableKind::Constant(length) => Type::Constant(*length),
16031654
}
16041655
}
@@ -1627,6 +1678,10 @@ impl From<&Type> for PrintableType {
16271678
}
16281679
Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() },
16291680
},
1681+
Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() {
1682+
TypeBinding::Bound(typ) => typ.into(),
1683+
TypeBinding::Unbound(_) => Type::default_range_loop_type().into(),
1684+
},
16301685
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => {
16311686
match &*binding.borrow() {
16321687
TypeBinding::Bound(typ) => typ.into(),
@@ -1685,6 +1740,9 @@ impl std::fmt::Debug for Type {
16851740
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => {
16861741
write!(f, "IntOrField{:?}", binding)
16871742
}
1743+
Type::TypeVariable(binding, TypeVariableKind::Integer) => {
1744+
write!(f, "Int{:?}", binding)
1745+
}
16881746
Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => {
16891747
write!(f, "{}{:?}", n, binding)
16901748
}

compiler/noirc_frontend/src/monomorphization/mod.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -803,12 +803,14 @@ impl<'interner> Monomorphizer<'interner> {
803803
// Default any remaining unbound type variables.
804804
// This should only happen if the variable in question is unused
805805
// and within a larger generic type.
806-
let default =
807-
if self.is_range_loop && matches!(kind, TypeVariableKind::IntegerOrField) {
808-
Type::default_range_loop_type()
809-
} else {
810-
kind.default_type()
811-
};
806+
let default = if self.is_range_loop
807+
&& (matches!(kind, TypeVariableKind::IntegerOrField)
808+
|| matches!(kind, TypeVariableKind::Integer))
809+
{
810+
Type::default_range_loop_type()
811+
} else {
812+
kind.default_type()
813+
};
812814

813815
let monomorphized_default = self.convert_type(&default);
814816
binding.bind(default);

compiler/noirc_frontend/src/node_interner.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,7 @@ fn get_type_method_key(typ: &Type) -> Option<TypeMethodKey> {
17001700
Type::Array(_, _) => Some(Array),
17011701
Type::Integer(_, _) => Some(FieldOrInt),
17021702
Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt),
1703+
Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt),
17031704
Type::Bool => Some(Bool),
17041705
Type::String(_) => Some(String),
17051706
Type::FmtString(_, _) => Some(FmtString),

tooling/noirc_abi/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,11 @@ impl AbiType {
144144

145145
Self::Integer { sign, width: (*bit_width).into() }
146146
}
147-
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => {
148-
match &*binding.borrow() {
149-
TypeBinding::Bound(typ) => Self::from_type(context, typ),
150-
TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()),
151-
}
152-
}
147+
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField)
148+
| Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() {
149+
TypeBinding::Bound(typ) => Self::from_type(context, typ),
150+
TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()),
151+
},
153152
Type::Bool => Self::Boolean,
154153
Type::String(size) => {
155154
let size = size

0 commit comments

Comments
 (0)