Skip to content

Commit 1048f81

Browse files
feat!: Restrict bit sizes (#4235)
# Description Restricts allowed bit sizes for integer types to 1,8,32 and 64. ## Problem\* Resolves #4162 ## Summary\* ## 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. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: kevaundray <[email protected]>
1 parent 78ef013 commit 1048f81

21 files changed

Lines changed: 114 additions & 78 deletions

File tree

aztec_macros/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMac
11291129
add_array_to_hasher(
11301130
&id,
11311131
&UnresolvedType {
1132-
typ: UnresolvedTypeData::Integer(Signedness::Unsigned, 32),
1132+
typ: UnresolvedTypeData::Integer(
1133+
Signedness::Unsigned,
1134+
noirc_frontend::IntegerBitSize::ThirtyTwo,
1135+
),
11331136
span: None,
11341137
},
11351138
)

compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ impl<'a> FunctionContext<'a> {
219219
let element_types = Self::convert_type(element).flatten();
220220
Type::Array(Rc::new(element_types), *len as usize)
221221
}
222-
ast::Type::Integer(Signedness::Signed, bits) => Type::signed(*bits),
223-
ast::Type::Integer(Signedness::Unsigned, bits) => Type::unsigned(*bits),
222+
ast::Type::Integer(Signedness::Signed, bits) => Type::signed((*bits).into()),
223+
ast::Type::Integer(Signedness::Unsigned, bits) => Type::unsigned((*bits).into()),
224224
ast::Type::Bool => Type::unsigned(1),
225225
ast::Type::String(len) => Type::Array(Rc::new(vec![Type::char()]), *len as usize),
226226
ast::Type::FmtString(_, _) => {

compiler/noirc_frontend/src/ast/mod.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,63 @@ use crate::{
2828
};
2929
use iter_extended::vecmap;
3030

31+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
32+
pub enum IntegerBitSize {
33+
One,
34+
Eight,
35+
ThirtyTwo,
36+
SixtyFour,
37+
}
38+
39+
impl IntegerBitSize {
40+
pub fn allowed_sizes() -> Vec<Self> {
41+
vec![Self::One, Self::Eight, Self::ThirtyTwo, Self::SixtyFour]
42+
}
43+
}
44+
45+
impl From<IntegerBitSize> for u32 {
46+
fn from(size: IntegerBitSize) -> u32 {
47+
use IntegerBitSize::*;
48+
match size {
49+
One => 1,
50+
Eight => 8,
51+
ThirtyTwo => 32,
52+
SixtyFour => 64,
53+
}
54+
}
55+
}
56+
57+
pub struct InvalidIntegerBitSizeError(pub u32);
58+
59+
impl TryFrom<u32> for IntegerBitSize {
60+
type Error = InvalidIntegerBitSizeError;
61+
62+
fn try_from(value: u32) -> Result<Self, Self::Error> {
63+
use IntegerBitSize::*;
64+
match value {
65+
1 => Ok(One),
66+
8 => Ok(Eight),
67+
32 => Ok(ThirtyTwo),
68+
64 => Ok(SixtyFour),
69+
_ => Err(InvalidIntegerBitSizeError(value)),
70+
}
71+
}
72+
}
73+
74+
impl core::fmt::Display for IntegerBitSize {
75+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76+
write!(f, "{}", u32::from(*self))
77+
}
78+
}
79+
3180
/// The parser parses types as 'UnresolvedType's which
3281
/// require name resolution to resolve any type names used
3382
/// for structs within, but are otherwise identical to Types.
3483
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
3584
pub enum UnresolvedTypeData {
3685
FieldElement,
3786
Array(Option<UnresolvedTypeExpression>, Box<UnresolvedType>), // [4]Witness = Array(4, Witness)
38-
Integer(Signedness, u32), // u32 = Integer(unsigned, 32)
87+
Integer(Signedness, IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo)
3988
Bool,
4089
Expression(UnresolvedTypeExpression),
4190
String(Option<UnresolvedTypeExpression>),
@@ -197,11 +246,17 @@ impl UnresolvedType {
197246
}
198247

199248
impl UnresolvedTypeData {
200-
pub fn from_int_token(token: IntType) -> UnresolvedTypeData {
249+
pub fn from_int_token(
250+
token: IntType,
251+
) -> Result<UnresolvedTypeData, InvalidIntegerBitSizeError> {
201252
use {IntType::*, UnresolvedTypeData::Integer};
202253
match token {
203-
Signed(num_bits) => Integer(Signedness::Signed, num_bits),
204-
Unsigned(num_bits) => Integer(Signedness::Unsigned, num_bits),
254+
Signed(num_bits) => {
255+
Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?))
256+
}
257+
Unsigned(num_bits) => {
258+
Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?))
259+
}
205260
}
206261
}
207262

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::hir_def::expr::HirBinaryOp;
88
use crate::hir_def::types::Type;
99
use crate::BinaryOpKind;
1010
use crate::FunctionReturnType;
11+
use crate::IntegerBitSize;
1112
use crate::Signedness;
1213

1314
#[derive(Error, Debug, Clone, PartialEq, Eq)]
@@ -67,7 +68,7 @@ pub enum TypeCheckError {
6768
#[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")]
6869
IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span },
6970
#[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")]
70-
IntegerBitWidth { bit_width_x: u32, bit_width_y: u32, span: Span },
71+
IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, span: Span },
7172
#[error("{kind} cannot be used in an infix operation")]
7273
InvalidInfixOp { kind: &'static str, span: Span },
7374
#[error("{kind} cannot be used in a unary operation")]

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ impl<'interner> TypeChecker<'interner> {
351351
HirExpression::Literal(HirLiteral::Integer(value, false)) => {
352352
let v = value.to_u128();
353353
if let Type::Integer(_, bit_count) = annotated_type {
354+
let bit_count: u32 = (*bit_count).into();
354355
let max = 1 << bit_count;
355356
if v >= max {
356357
self.errors.push(TypeCheckError::OverflowingAssignment {

compiler/noirc_frontend/src/hir_def/types.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
use crate::{
99
hir::type_check::TypeCheckError,
1010
node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId},
11+
IntegerBitSize,
1112
};
1213
use iter_extended::vecmap;
1314
use noirc_errors::{Location, Span};
@@ -27,8 +28,8 @@ pub enum Type {
2728
Array(Box<Type>, Box<Type>),
2829

2930
/// A primitive integer type with the given sign and bit count.
30-
/// E.g. `u32` would be `Integer(Unsigned, 32)`
31-
Integer(Signedness, u32),
31+
/// E.g. `u32` would be `Integer(Unsigned, ThirtyTwo)`
32+
Integer(Signedness, IntegerBitSize),
3233

3334
/// The primitive `bool` type.
3435
Bool,
@@ -538,7 +539,7 @@ impl Type {
538539
}
539540

540541
pub fn default_range_loop_type() -> Type {
541-
Type::Integer(Signedness::Unsigned, 64)
542+
Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour)
542543
}
543544

544545
pub fn type_variable(id: TypeVariableId) -> Type {
@@ -1621,8 +1622,10 @@ impl From<&Type> for PrintableType {
16211622
PrintableType::Array { length, typ: Box::new(typ.into()) }
16221623
}
16231624
Type::Integer(sign, bit_width) => match sign {
1624-
Signedness::Unsigned => PrintableType::UnsignedInteger { width: *bit_width },
1625-
Signedness::Signed => PrintableType::SignedInteger { width: *bit_width },
1625+
Signedness::Unsigned => {
1626+
PrintableType::UnsignedInteger { width: (*bit_width).into() }
1627+
}
1628+
Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() },
16261629
},
16271630
Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => {
16281631
match &*binding.borrow() {

compiler/noirc_frontend/src/lexer/errors.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ pub enum LexerErrorKind {
1717
InvalidIntegerLiteral { span: Span, found: String },
1818
#[error("{:?} is not a valid attribute", found)]
1919
MalformedFuncAttribute { span: Span, found: String },
20-
#[error("Integer type is larger than the maximum supported size of u127")]
21-
TooManyBits { span: Span, max: u32, got: u32 },
2220
#[error("Logical and used instead of bitwise and")]
2321
LogicalAnd { span: Span },
2422
#[error("Unterminated block comment")]
@@ -45,7 +43,6 @@ impl LexerErrorKind {
4543
LexerErrorKind::NotADoubleChar { span, .. } => *span,
4644
LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span,
4745
LexerErrorKind::MalformedFuncAttribute { span, .. } => *span,
48-
LexerErrorKind::TooManyBits { span, .. } => *span,
4946
LexerErrorKind::LogicalAnd { span } => *span,
5047
LexerErrorKind::UnterminatedBlockComment { span } => *span,
5148
LexerErrorKind::UnterminatedStringLiteral { span } => *span,
@@ -85,13 +82,6 @@ impl LexerErrorKind {
8582
format!(" {found} is not a valid attribute"),
8683
*span,
8784
),
88-
LexerErrorKind::TooManyBits { span, max, got } => (
89-
"Integer literal too large".to_string(),
90-
format!(
91-
"The maximum number of bits needed to represent a field is {max}, This integer type needs {got} bits"
92-
),
93-
*span,
94-
),
9585
LexerErrorKind::LogicalAnd { span } => (
9686
"Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(),
9787
"Try `&` instead, or use `if` only if you require short-circuiting".to_string(),

compiler/noirc_frontend/src/lexer/lexer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ impl<'a> Lexer<'a> {
307307

308308
// Check if word an int type
309309
// if no error occurred, then it is either a valid integer type or it is not an int type
310-
let parsed_token = IntType::lookup_int_type(&word, Span::inclusive(start, end))?;
310+
let parsed_token = IntType::lookup_int_type(&word)?;
311311

312312
// Check if it is an int type
313313
if let Some(int_type_token) = parsed_token {

compiler/noirc_frontend/src/lexer/token.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ impl IntType {
306306
// XXX: Result<Option<Token, LexerErrorKind>
307307
// Is not the best API. We could split this into two functions. One that checks if the the
308308
// word is a integer, which only returns an Option
309-
pub(crate) fn lookup_int_type(word: &str, span: Span) -> Result<Option<Token>, LexerErrorKind> {
309+
pub(crate) fn lookup_int_type(word: &str) -> Result<Option<Token>, LexerErrorKind> {
310310
// Check if the first string is a 'u' or 'i'
311311

312312
let is_signed = if word.starts_with('i') {
@@ -324,12 +324,6 @@ impl IntType {
324324
Err(_) => return Ok(None),
325325
};
326326

327-
let max_bits = FieldElement::max_num_bits() / 2;
328-
329-
if str_as_u32 > max_bits {
330-
return Err(LexerErrorKind::TooManyBits { span, max: max_bits, got: str_as_u32 });
331-
}
332-
333327
if is_signed {
334328
Ok(Some(Token::IntType(IntType::Signed(str_as_u32))))
335329
} else {

compiler/noirc_frontend/src/monomorphization/ast.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use noirc_errors::{
66
};
77

88
use crate::{
9-
hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, Signedness, Visibility,
9+
hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, IntegerBitSize, Signedness,
10+
Visibility,
1011
};
1112

1213
/// The monomorphized AST is expression-based, all statements are also
@@ -217,8 +218,8 @@ pub struct Function {
217218
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
218219
pub enum Type {
219220
Field,
220-
Array(/*len:*/ u64, Box<Type>), // Array(4, Field) = [Field; 4]
221-
Integer(Signedness, /*bits:*/ u32), // u32 = Integer(unsigned, 32)
221+
Array(/*len:*/ u64, Box<Type>), // Array(4, Field) = [Field; 4]
222+
Integer(Signedness, /*bits:*/ IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo)
222223
Bool,
223224
String(/*len:*/ u64), // String(4) = str[4]
224225
FmtString(/*len:*/ u64, Box<Type>),

0 commit comments

Comments
 (0)