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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres
},
HirLiteral::Bool(value) => AbiValue::Boolean { value },
HirLiteral::Str(value) => AbiValue::String { value },
HirLiteral::Integer(value) => {
AbiValue::Integer { value: value.field.to_hex(), sign: value.is_negative }
}
HirLiteral::Integer(value) => AbiValue::Integer {
value: value.absolute_value().to_hex(),
sign: value.is_negative(),
},
_ => unreachable!("Literal cannot be used in the abi"),
},
_ => unreachable!("Type cannot be used in the abi {:?}", expression),
Expand Down
12 changes: 8 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,20 @@ impl NumericType {
match self {
NumericType::Unsigned { bit_size } => {
let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 };
if value.is_negative {
if value.is_negative() {
return Some(format!("0..={}", max));
}
if value.field <= max.into() { None } else { Some(format!("0..={}", max)) }
if value.absolute_value() <= max.into() {
None
} else {
Some(format!("0..={}", max))
}
}
NumericType::Signed { bit_size } => {
let min = 2u128.pow(bit_size - 1);
let max = 2u128.pow(bit_size - 1) - 1;
let target_max = if value.is_negative { min } else { max };
if value.field <= target_max.into() {
let target_max = if value.is_negative() { min } else { max };
if value.absolute_value() <= target_max.into() {
None
} else {
Some(format!("-{}..={}", min, max))
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,17 @@ impl<'a> FunctionContext<'a> {
});
}

let value = if value.is_negative {
let value = if value.is_negative() {
match numeric_type {
NumericType::NativeField => -value.field,
NumericType::NativeField => -value.absolute_value(),
NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => {
assert!(bit_size < 128);
let base = 1_u128 << bit_size;
FieldElement::from(base) - value.field
FieldElement::from(base) - value.absolute_value()
}
}
} else {
value.field
value.absolute_value()
};

Ok(self.builder.numeric_constant(value, numeric_type))
Expand Down
10 changes: 6 additions & 4 deletions compiler/noirc_frontend/src/elaborator/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ pub(crate) fn overflowing_int(
Type::Integer(Signedness::Unsigned, bit_size) => {
let bit_size: u32 = (*bit_size).into();
let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 };
if value.field > max.into() || value.is_negative {
if value.absolute_value() > max.into() || value.is_negative() {
errors.push(TypeCheckError::OverflowingAssignment {
expr: value,
ty: annotated_type.clone(),
Expand All @@ -235,9 +235,11 @@ pub(crate) fn overflowing_int(
let bit_count: u32 = (*bit_count).into();
let min = 2u128.pow(bit_count - 1);
let max = 2u128.pow(bit_count - 1) - 1;
if (value.is_negative && value.field > min.into())
|| (!value.is_negative && value.field > max.into())
{

let is_negative = value.is_negative();
let abs = value.absolute_value();

if (is_negative && abs > min.into()) || (!is_negative && abs > max.into()) {
errors.push(TypeCheckError::OverflowingAssignment {
expr: value,
ty: annotated_type.clone(),
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ use crate::{
TraitMethodId,
},
shared::Signedness,
signed_field::SignedField,
token::SecondaryAttributeKind,
};

Expand Down Expand Up @@ -1141,7 +1140,9 @@ impl Elaborator<'_> {

use HirExpression::Literal;
let from_value_opt = match self.interner.expression(from_expr_id) {
Literal(HirLiteral::Integer(SignedField { field, is_negative: false })) => Some(field),
Literal(HirLiteral::Integer(field)) if !field.is_negative() => {
Some(field.absolute_value())
}

// TODO(https://github.com/noir-lang/noir/issues/6247):
// handle negative literals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1954,15 +1954,15 @@ fn expr_as_integer(
expr_as(interner, arguments, return_type.clone(), location, |expr| match expr {
ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field))) => {
Some(Value::Tuple(vec![
Value::Field(SignedField::positive(field.field)),
Value::Bool(field.is_negative),
Value::Field(SignedField::positive(field.absolute_value())),
Value::Bool(field.is_negative()),
]))
}
ExprValue::Expression(ExpressionKind::Resolved(id)) => {
if let HirExpression::Literal(HirLiteral::Integer(field)) = interner.expression(&id) {
Some(Value::Tuple(vec![
Value::Field(SignedField::positive(field.field)),
Value::Bool(field.is_negative),
Value::Field(SignedField::positive(field.absolute_value())),
Value::Bool(field.is_negative()),
]))
} else {
None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub(super) fn evaluate_cast_one_step(
}};
}
let (lhs, lhs_is_negative) = match evaluated_lhs {
Value::Field(value) => (value.field, value.is_negative),
Value::Field(value) => (value.absolute_value(), value.is_negative()),
Value::U1(value) => ((value as u128).into(), false),
Value::U8(value) => ((value as u128).into(), false),
Value::U16(value) => ((value as u128).into(), false),
Expand Down
10 changes: 5 additions & 5 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,10 +551,10 @@ impl Value {
}
}
Value::Field(value) => {
if value.is_negative {
vec![Token::Minus, Token::Int(value.field)]
if value.is_negative() {
vec![Token::Minus, Token::Int(value.absolute_value())]
} else {
vec![Token::Int(value.field)]
vec![Token::Int(value.absolute_value())]
}
}
other => vec![Token::UnquoteMarker(other.into_hir_expression(interner, location)?)],
Expand Down Expand Up @@ -590,10 +590,10 @@ impl Value {
pub(crate) fn to_field_element(&self) -> Option<FieldElement> {
match self {
Self::Field(value) => {
if value.is_negative {
if value.is_negative() {
None
} else {
Some(value.field)
Some(value.absolute_value())
}
}

Expand Down
10 changes: 5 additions & 5 deletions compiler/noirc_frontend/src/monomorphization/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl Monomorphizer<'_> {
// instantiate tracked variable for the value type and associate it with
// the ID used by the injected instrumentation code
let var_type = self.interner.id_type(call.arguments[DEBUG_VALUE_ARG_SLOT]);
let source_var_id = source_var_id.field.to_u128().into();
let source_var_id = source_var_id.absolute_value().to_u128().into();
// then update the ID used for tracking at runtime
let var_id = self.debug_type_tracker.insert_var(source_var_id, &var_type);
let interned_var_id = self.intern_var_id(var_id, &call.location);
Expand All @@ -98,7 +98,7 @@ impl Monomorphizer<'_> {
unreachable!("Missing source_var_id in __debug_var_drop call");
};
// update variable ID for tracked drops (ie. when the var goes out of scope)
let source_var_id = source_var_id.field.to_u128().into();
let source_var_id = source_var_id.absolute_value().to_u128().into();
let var_id = self
.debug_type_tracker
.get_var_id(source_var_id)
Expand Down Expand Up @@ -126,7 +126,7 @@ impl Monomorphizer<'_> {
unreachable!("Missing source_var_id in __debug_member_assign call");
};
// update variable member assignments
let source_var_id = source_var_id.field.to_u128().into();
let source_var_id = source_var_id.absolute_value().to_u128().into();

let var_type = self
.debug_type_tracker
Expand All @@ -138,8 +138,8 @@ impl Monomorphizer<'_> {
if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i))) =
hir_arguments.get(DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i)
{
let index = fe_i.field.to_i128().unsigned_abs();
if fe_i.is_negative {
let index = fe_i.absolute_value().to_i128().unsigned_abs();
if fe_i.is_negative() {
// We use negative indices at instrumentation time to indicate
// and reference member accesses by name which cannot be
// resolved until we have a type. This strategy is also used
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/parser/parser/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ mod tests {
panic!("Expected integer literal expression, got {:?}", let_statement.expression.kind);
};

assert!(value.is_negative);
assert_eq!(value.field, FieldElement::from(17u128));
assert!(value.is_negative());
assert_eq!(value.absolute_value(), FieldElement::from(17u128));
}
}
25 changes: 18 additions & 7 deletions compiler/noirc_frontend/src/signed_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use acvm::{AcirField, FieldElement};

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SignedField {
pub field: FieldElement,
pub is_negative: bool,
field: FieldElement,
is_negative: bool,
}

impl SignedField {
pub fn new(field: FieldElement, is_negative: bool) -> Self {
pub fn new(field: FieldElement, mut is_negative: bool) -> Self {
if field.is_zero() {
is_negative = false;
}
Self { field, is_negative }
}

Expand All @@ -16,7 +19,7 @@ impl SignedField {
}

pub fn negative(field: impl Into<FieldElement>) -> Self {
Self { field: field.into(), is_negative: true }
Self::new(field.into(), true)
}

pub fn zero() -> SignedField {
Expand All @@ -27,6 +30,15 @@ impl SignedField {
Self { field: FieldElement::one(), is_negative: false }
}

/// Returns the inner FieldElement which will always be positive
pub fn absolute_value(&self) -> FieldElement {
self.field
}

pub fn is_negative(&self) -> bool {
self.is_negative
}

/// Convert a signed integer to a SignedField, carefully handling
/// INT_MIN in the process. Note that to convert an unsigned integer
/// you can call `SignedField::positive`.
Expand Down Expand Up @@ -147,9 +159,8 @@ impl std::ops::Div for SignedField {
impl std::ops::Neg for SignedField {
type Output = Self;

fn neg(mut self) -> Self::Output {
self.is_negative = !self.is_negative;
self
fn neg(self) -> Self::Output {
Self::new(self.field, !self.is_negative)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "comptime_negative_zero"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn main() {
comptime {
assert_eq(-0, 0);
assert_eq(-1 + 1, 0);
assert_eq(-1 - -1, 0);
assert_eq(0 * -1, 0);
assert_eq(0 / -1, 0);
}
}
11 changes: 4 additions & 7 deletions tooling/ast_fuzzer/src/program/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ pub fn gen_literal(u: &mut Unstructured, typ: &Type) -> arbitrary::Result<Expres
Type::Unit => Expression::Literal(Literal::Unit),
Type::Bool => Expression::Literal(Literal::Bool(bool::arbitrary(u)?)),
Type::Field => {
let field = SignedField {
field: Field::from(u128::arbitrary(u)?),
is_negative: bool::arbitrary(u)?,
};
let field = SignedField::new(Field::from(u128::arbitrary(u)?), bool::arbitrary(u)?);
Expression::Literal(Literal::Integer(field, Type::Field, Location::dummy()))
}
Type::Integer(signedness, integer_bit_size) => {
Expand Down Expand Up @@ -64,7 +61,7 @@ pub fn gen_literal(u: &mut Unstructured, typ: &Type) -> arbitrary::Result<Expres
};

Expression::Literal(Literal::Integer(
SignedField { field, is_negative },
SignedField::new(field, is_negative),
Type::Integer(*signedness, *integer_bit_size),
Location::dummy(),
))
Expand Down Expand Up @@ -190,7 +187,7 @@ pub fn gen_range(

let to_lit = |(field, is_negative)| {
Expression::Literal(Literal::Integer(
SignedField { field, is_negative },
SignedField::new(field, is_negative),
Type::Integer(*signedness, *integer_bit_size),
Location::dummy(),
))
Expand Down Expand Up @@ -237,7 +234,7 @@ where
FieldElement: From<V>,
{
Expression::Literal(Literal::Integer(
SignedField { field: FieldElement::from(value), is_negative },
SignedField::new(value.into(), is_negative),
typ,
Location::dummy(),
))
Expand Down
4 changes: 2 additions & 2 deletions tooling/lsp/src/requests/hover/from_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ impl Visitor for HoverFinder<'_> {
}

fn format_integer(typ: &Type, value: SignedField) -> String {
let value_base_10 = value.field.to_string();
let value_base_10 = value.absolute_value().to_string();

// For simplicity we parse the value as a BigInt to convert it to hex
// because `FieldElement::to_hex` will include many leading zeros.
let value_big_int = BigInt::from_str(&value_base_10).unwrap();
let negative = if value.is_negative { "-" } else { "" };
let negative = if value.is_negative() { "-" } else { "" };

format!(
" {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading