Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support casting in globals #5164

Merged
merged 11 commits into from
Jun 4, 2024
Merged
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use noirc_errors::{CustomDiagnostic, Location};
use super::value::Value;

/// The possible errors that can halt the interpreter.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
pub enum InterpreterError {
ArgumentCountMismatch { expected: usize, actual: usize, location: Location },
TypeMismatch { expected: Type, value: Value, location: Location },
Expand Down
29 changes: 20 additions & 9 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ impl<'a> Interpreter<'a> {
HirExpression::MemberAccess(access) => self.evaluate_access(access, id),
HirExpression::Call(call) => self.evaluate_call(call, id),
HirExpression::MethodCall(call) => self.evaluate_method_call(call, id),
HirExpression::Cast(cast) => self.evaluate_cast(cast, id),
HirExpression::Cast(cast) => self.evaluate_cast(&cast, id),
HirExpression::If(if_) => self.evaluate_if(if_, id),
HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple),
HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id),
Expand Down Expand Up @@ -945,7 +945,18 @@ impl<'a> Interpreter<'a> {
}
}

fn evaluate_cast(&mut self, cast: HirCastExpression, id: ExprId) -> IResult<Value> {
fn evaluate_cast(&mut self, cast: &HirCastExpression, id: ExprId) -> IResult<Value> {
let evaluated_lhs = self.evaluate(cast.lhs)?;
Self::evaluate_cast_one_step(cast, id, evaluated_lhs, self.interner)
}

/// evaluate_cast without recursion
pub fn evaluate_cast_one_step(
cast: &HirCastExpression,
id: ExprId,
evaluated_lhs: Value,
interner: &NodeInterner,
) -> IResult<Value> {
macro_rules! signed_int_to_field {
($x:expr) => {{
// Need to convert the signed integer to an i128 before
Expand All @@ -959,7 +970,7 @@ impl<'a> Interpreter<'a> {
}};
}

let (mut lhs, lhs_is_negative) = match self.evaluate(cast.lhs)? {
let (mut lhs, lhs_is_negative) = match evaluated_lhs {
Value::Field(value) => (value, false),
Value::U8(value) => ((value as u128).into(), false),
Value::U16(value) => ((value as u128).into(), false),
Expand All @@ -973,7 +984,7 @@ impl<'a> Interpreter<'a> {
(if value { FieldElement::one() } else { FieldElement::zero() }, false)
}
value => {
let location = self.interner.expr_location(&id);
let location = interner.expr_location(&id);
return Err(InterpreterError::NonNumericCasted { value, location });
}
};
Expand All @@ -998,8 +1009,8 @@ impl<'a> Interpreter<'a> {
}
Type::Integer(sign, bit_size) => match (sign, bit_size) {
(Signedness::Unsigned, IntegerBitSize::One) => {
let location = self.interner.expr_location(&id);
Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location })
let location = interner.expr_location(&id);
Err(InterpreterError::TypeUnsupported { typ: cast.r#type.clone(), location })
}
(Signedness::Unsigned, IntegerBitSize::Eight) => cast_to_int!(lhs, to_u128, u8, U8),
(Signedness::Unsigned, IntegerBitSize::Sixteen) => {
Expand All @@ -1012,8 +1023,8 @@ impl<'a> Interpreter<'a> {
cast_to_int!(lhs, to_u128, u64, U64)
}
(Signedness::Signed, IntegerBitSize::One) => {
let location = self.interner.expr_location(&id);
Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location })
let location = interner.expr_location(&id);
Err(InterpreterError::TypeUnsupported { typ: cast.r#type.clone(), location })
}
(Signedness::Signed, IntegerBitSize::Eight) => cast_to_int!(lhs, to_i128, i8, I8),
(Signedness::Signed, IntegerBitSize::Sixteen) => {
Expand All @@ -1028,7 +1039,7 @@ impl<'a> Interpreter<'a> {
},
Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)),
typ => {
let location = self.interner.expr_location(&id);
let location = interner.expr_location(&id);
Err(InterpreterError::CastToNonNumericType { typ, location })
}
}
Expand Down
47 changes: 44 additions & 3 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, rc::Rc};

use acvm::FieldElement;
use acvm::{AcirField, FieldElement};
use im::Vector;
use iter_extended::{try_vecmap, vecmap};
use noirc_errors::Location;
Expand Down Expand Up @@ -155,12 +155,12 @@ impl Value {
}
Value::Array(elements, _) => {
let elements =
try_vecmap(elements, |elements| elements.into_expression(interner, location))?;
try_vecmap(elements, |element| element.into_expression(interner, location))?;
HirExpression::Literal(HirLiteral::Array(HirArrayLiteral::Standard(elements)))
}
Value::Slice(elements, _) => {
let elements =
try_vecmap(elements, |elements| elements.into_expression(interner, location))?;
try_vecmap(elements, |element| element.into_expression(interner, location))?;
HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements)))
}
Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)),
Expand All @@ -174,6 +174,47 @@ impl Value {
interner.push_expr_type(id, typ);
Ok(id)
}

/// Converts any unsigned `Value` into a `u128`.
/// Returns `None` for negative integers.
pub(crate) fn to_u128(&self) -> Option<u128> {
match self {
Self::Field(value) => Some(value.to_u128()),
Self::I8(value) => {
if value < &0 {
None
} else {
Some(*value as u128)
}
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
}
Self::I16(value) => {
if value < &0 {
None
} else {
Some(*value as u128)
}
}
Self::I32(value) => {
if value < &0 {
None
} else {
Some(*value as u128)
}
}
Self::I64(value) => {
if value < &0 {
None
} else {
Some(*value as u128)
}
}
Self::U8(value) => Some(*value as u128),
Self::U16(value) => Some(*value as u128),
Self::U32(value) => Some(*value as u128),
Self::U64(value) => Some(*value as u128),
_ => None,
}
}
}

/// Unwraps an Rc value without cloning the inner value if the reference count is 1. Clones otherwise.
Expand Down
9 changes: 8 additions & 1 deletion compiler/noirc_frontend/src/hir/resolution/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub use noirc_errors::Span;
use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic};
use thiserror::Error;

use crate::{ast::Ident, parser::ParserError, Type};
use crate::{ast::Ident, hir::comptime::InterpreterError, parser::ParserError, Type};

use super::import::PathResolutionError;

Expand Down Expand Up @@ -94,6 +94,8 @@ pub enum ResolverError {
NoPredicatesAttributeOnUnconstrained { ident: Ident },
#[error("#[fold] attribute is only allowed on constrained functions")]
FoldAttributeOnUnconstrained { ident: Ident },
#[error("Interpreting array-length failed")]
ArrayLengthInterpreter { error: InterpreterError, span: Span },
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
}

impl ResolverError {
Expand Down Expand Up @@ -386,6 +388,11 @@ 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::ArrayLengthInterpreter { error, span } => Diagnostic::simple_error(
"Interpreting array length failed".into(),
format!("with error: {}", Diagnostic::from(error)),
*span,
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
),
}
}
}
21 changes: 20 additions & 1 deletion compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ use crate::ast::{
};
use crate::graph::CrateId;
use crate::hir::def_map::{ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION};
use crate::hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver};
use crate::hir::{
comptime::{Interpreter, Value},
def_map::CrateDefMap,
resolution::path_resolver::PathResolver,
};
use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern};
use crate::node_interner::{
DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId,
Expand Down Expand Up @@ -2064,6 +2068,21 @@ impl<'a> Resolver<'a> {
BinaryOpKind::Modulo => Ok(lhs % rhs),
}
}
HirExpression::Cast(cast) => {
let lhs = self.try_eval_array_length_id_with_fuel(cast.lhs, span, fuel - 1)?;
let lhs_value = Value::Field(lhs.into());
let evaluated_value =
Interpreter::evaluate_cast_one_step(&cast, rhs, lhs_value, self.interner)
.map_err(|error| {
Some(ResolverError::ArrayLengthInterpreter { error, span })
})?;

if let Some(result) = evaluated_value.to_u128() {
Ok(result)
} else {
Err(Some(ResolverError::InvalidArrayLengthExpr { span }))
}
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
}
_other => Err(Some(ResolverError::InvalidArrayLengthExpr { span })),
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "cast_and_shift_global"
type = "bin"
authors = [""]
compiler_version = ">=0.30.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
global THREE: u64 = 3;
global EIGHT: u64 = 1 << THREE as u8;
global SEVEN: u64 = EIGHT - 1;

fn main() {
assert([0; EIGHT] == [0; 8]);
assert([0; SEVEN] == [0; 7]);
}
Loading