Skip to content

Commit

Permalink
Merge 57786b4 into 30d7035
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Sep 19, 2024
2 parents 30d7035 + 57786b4 commit 7818431
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 87 deletions.
166 changes: 83 additions & 83 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ impl<'context> Elaborator<'context> {

let length = stmt.expression;
let span = self.interner.expr_span(&length);
let result = self.try_eval_array_length_id(length, span);
let result = try_eval_array_length_id(self.interner, length, span);

match result.map(|length| length.try_into()) {
Ok(Ok(length_value)) => return length_value,
Expand All @@ -633,88 +633,6 @@ impl<'context> Elaborator<'context> {
0
}

fn try_eval_array_length_id(
&self,
rhs: ExprId,
span: Span,
) -> Result<u128, Option<ResolverError>> {
// Arbitrary amount of recursive calls to try before giving up
let fuel = 100;
self.try_eval_array_length_id_with_fuel(rhs, span, fuel)
}

fn try_eval_array_length_id_with_fuel(
&self,
rhs: ExprId,
span: Span,
fuel: u32,
) -> Result<u128, Option<ResolverError>> {
if fuel == 0 {
// If we reach here, it is likely from evaluating cyclic globals. We expect an error to
// be issued for them after name resolution so issue no error now.
return Err(None);
}

match self.interner.expression(&rhs) {
HirExpression::Literal(HirLiteral::Integer(int, false)) => {
int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span }))
}
HirExpression::Ident(ident, _) => {
if let Some(definition) = self.interner.try_definition(ident.id) {
match definition.kind {
DefinitionKind::Global(global_id) => {
let let_statement = self.interner.get_global_let_statement(global_id);
if let Some(let_statement) = let_statement {
let expression = let_statement.expression;
self.try_eval_array_length_id_with_fuel(expression, span, fuel - 1)
} else {
Err(Some(ResolverError::InvalidArrayLengthExpr { span }))
}
}
_ => Err(Some(ResolverError::InvalidArrayLengthExpr { span })),
}
} else {
Err(Some(ResolverError::InvalidArrayLengthExpr { span }))
}
}
HirExpression::Infix(infix) => {
let lhs = self.try_eval_array_length_id_with_fuel(infix.lhs, span, fuel - 1)?;
let rhs = self.try_eval_array_length_id_with_fuel(infix.rhs, span, fuel - 1)?;

match infix.operator.kind {
BinaryOpKind::Add => Ok(lhs + rhs),
BinaryOpKind::Subtract => Ok(lhs - rhs),
BinaryOpKind::Multiply => Ok(lhs * rhs),
BinaryOpKind::Divide => Ok(lhs / rhs),
BinaryOpKind::Equal => Ok((lhs == rhs) as u128),
BinaryOpKind::NotEqual => Ok((lhs != rhs) as u128),
BinaryOpKind::Less => Ok((lhs < rhs) as u128),
BinaryOpKind::LessEqual => Ok((lhs <= rhs) as u128),
BinaryOpKind::Greater => Ok((lhs > rhs) as u128),
BinaryOpKind::GreaterEqual => Ok((lhs >= rhs) as u128),
BinaryOpKind::And => Ok(lhs & rhs),
BinaryOpKind::Or => Ok(lhs | rhs),
BinaryOpKind::Xor => Ok(lhs ^ rhs),
BinaryOpKind::ShiftRight => Ok(lhs >> rhs),
BinaryOpKind::ShiftLeft => Ok(lhs << rhs),
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 }))?;

evaluated_value
.to_u128()
.ok_or_else(|| Some(ResolverError::InvalidArrayLengthExpr { span }))
}
_other => Err(Some(ResolverError::InvalidArrayLengthExpr { span })),
}
}

pub fn unify(
&mut self,
actual: &Type,
Expand Down Expand Up @@ -1851,6 +1769,88 @@ impl<'context> Elaborator<'context> {
}
}

pub fn try_eval_array_length_id(
interner: &NodeInterner,
rhs: ExprId,
span: Span,
) -> Result<u128, Option<ResolverError>> {
// Arbitrary amount of recursive calls to try before giving up
let fuel = 100;
try_eval_array_length_id_with_fuel(interner, rhs, span, fuel)
}

fn try_eval_array_length_id_with_fuel(
interner: &NodeInterner,
rhs: ExprId,
span: Span,
fuel: u32,
) -> Result<u128, Option<ResolverError>> {
if fuel == 0 {
// If we reach here, it is likely from evaluating cyclic globals. We expect an error to
// be issued for them after name resolution so issue no error now.
return Err(None);
}

match interner.expression(&rhs) {
HirExpression::Literal(HirLiteral::Integer(int, false)) => {
int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span }))
}
HirExpression::Ident(ident, _) => {
if let Some(definition) = interner.try_definition(ident.id) {
match definition.kind {
DefinitionKind::Global(global_id) => {
let let_statement = interner.get_global_let_statement(global_id);
if let Some(let_statement) = let_statement {
let expression = let_statement.expression;
try_eval_array_length_id_with_fuel(interner, expression, span, fuel - 1)
} else {
Err(Some(ResolverError::InvalidArrayLengthExpr { span }))
}
}
_ => Err(Some(ResolverError::InvalidArrayLengthExpr { span })),
}
} else {
Err(Some(ResolverError::InvalidArrayLengthExpr { span }))
}
}
HirExpression::Infix(infix) => {
let lhs = try_eval_array_length_id_with_fuel(interner, infix.lhs, span, fuel - 1)?;
let rhs = try_eval_array_length_id_with_fuel(interner, infix.rhs, span, fuel - 1)?;

match infix.operator.kind {
BinaryOpKind::Add => Ok(lhs + rhs),
BinaryOpKind::Subtract => Ok(lhs - rhs),
BinaryOpKind::Multiply => Ok(lhs * rhs),
BinaryOpKind::Divide => Ok(lhs / rhs),
BinaryOpKind::Equal => Ok((lhs == rhs) as u128),
BinaryOpKind::NotEqual => Ok((lhs != rhs) as u128),
BinaryOpKind::Less => Ok((lhs < rhs) as u128),
BinaryOpKind::LessEqual => Ok((lhs <= rhs) as u128),
BinaryOpKind::Greater => Ok((lhs > rhs) as u128),
BinaryOpKind::GreaterEqual => Ok((lhs >= rhs) as u128),
BinaryOpKind::And => Ok(lhs & rhs),
BinaryOpKind::Or => Ok(lhs | rhs),
BinaryOpKind::Xor => Ok(lhs ^ rhs),
BinaryOpKind::ShiftRight => Ok(lhs >> rhs),
BinaryOpKind::ShiftLeft => Ok(lhs << rhs),
BinaryOpKind::Modulo => Ok(lhs % rhs),
}
}
HirExpression::Cast(cast) => {
let lhs = try_eval_array_length_id_with_fuel(interner, cast.lhs, span, fuel - 1)?;
let lhs_value = Value::Field(lhs.into());
let evaluated_value =
Interpreter::evaluate_cast_one_step(&cast, rhs, lhs_value, interner)
.map_err(|error| Some(ResolverError::ArrayLengthInterpreter { error }))?;

evaluated_value
.to_u128()
.ok_or_else(|| Some(ResolverError::InvalidArrayLengthExpr { span }))
}
_other => Err(Some(ResolverError::InvalidArrayLengthExpr { span })),
}
}

/// Gives an error if a user tries to create a mutable reference
/// to an immutable variable.
fn verify_mutable_reference(interner: &NodeInterner, rhs: ExprId) -> Result<(), ResolverError> {
Expand Down
96 changes: 92 additions & 4 deletions tooling/lsp/src/requests/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use fm::FileMap;
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind};
use noirc_frontend::{
ast::Visibility,
elaborator::types::try_eval_array_length_id,
hir::def_map::ModuleId,
hir_def::{stmt::HirPattern, traits::Trait},
macros_api::{NodeInterner, StructId},
hir_def::{expr::HirArrayLiteral, stmt::HirPattern, traits::Trait},
macros_api::{HirExpression, HirLiteral, NodeInterner, StructId},
node_interner::{
DefinitionId, DefinitionKind, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId,
DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId,
},
Generics, Shared, StructType, Type, TypeAlias, TypeBinding, TypeVariable,
};
Expand Down Expand Up @@ -166,24 +167,107 @@ fn format_trait(id: TraitId, args: &ProcessRequestCallbackArgs) -> String {
fn format_global(id: GlobalId, args: &ProcessRequestCallbackArgs) -> String {
let global_info = args.interner.get_global(id);
let definition_id = global_info.definition_id;
let definition = args.interner.definition(definition_id);
let typ = args.interner.definition_type(definition_id);

let mut string = String::new();
if format_parent_module(ReferenceId::Global(id), args, &mut string) {
string.push('\n');
}

string.push_str(" ");
if definition.comptime {
string.push_str("comptime ");
}
if definition.mutable {
string.push_str("mut ");
}
string.push_str("global ");
string.push_str(&global_info.ident.0.contents);
string.push_str(": ");
string.push_str(&format!("{}", typ));

// See if we can figure out what's the global's value
if let Some(stmt) = args.interner.get_global_let_statement(id) {
if let Some(value) = get_global_value(args.interner, stmt.expression) {
string.push_str(" = ");
string.push_str(&value);
}
}

string.push_str(&go_to_type_links(&typ, args.interner, args.files));

append_doc_comments(args.interner, ReferenceId::Global(id), &mut string);

string
}

fn get_global_value(interner: &NodeInterner, expr: ExprId) -> Option<String> {
let span = interner.expr_span(&expr);

// Globals as array lengths are extremely common, so we try that first.
if let Ok(result) = try_eval_array_length_id(interner, expr, span) {
return Some(result.to_string());
}

match interner.expression(&expr) {
HirExpression::Literal(literal) => match literal {
HirLiteral::Array(hir_array_literal) => {
get_global_array_value(interner, hir_array_literal, false)
}
HirLiteral::Slice(hir_array_literal) => {
get_global_array_value(interner, hir_array_literal, true)
}
HirLiteral::Bool(value) => Some(value.to_string()),
HirLiteral::Integer(field_element, _) => Some(field_element.to_string()),
HirLiteral::Str(string) => Some(format!("{:?}", string)),
HirLiteral::FmtStr(..) => None,
HirLiteral::Unit => Some("()".to_string()),
},
HirExpression::Tuple(values) => {
get_exprs_global_value(interner, &values).map(|value| format!("({})", value))
}
_ => None,
}
}

fn get_global_array_value(
interner: &NodeInterner,
literal: HirArrayLiteral,
is_slice: bool,
) -> Option<String> {
match literal {
HirArrayLiteral::Standard(values) => {
get_exprs_global_value(interner, &values).map(|value| {
if is_slice {
format!("&[{}]", value)
} else {
format!("[{}]", value)
}
})
}
HirArrayLiteral::Repeated { repeated_element, length } => {
get_global_value(interner, repeated_element).map(|value| {
if is_slice {
format!("&[{}; {}]", value, length)
} else {
format!("[{}; {}]", value, length)
}
})
}
}
}

fn get_exprs_global_value(interner: &NodeInterner, exprs: &[ExprId]) -> Option<String> {
let strings: Vec<String> =
exprs.iter().filter_map(|value| get_global_value(interner, *value)).collect();
if strings.len() == exprs.len() {
Some(strings.join(", "))
} else {
None
}
}

fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String {
let func_meta = args.interner.function_meta(&id);
let func_name_definition_id = args.interner.definition(func_meta.name.id);
Expand Down Expand Up @@ -263,6 +347,10 @@ fn format_alias(id: TypeAliasId, args: &ProcessRequestCallbackArgs) -> String {

fn format_local(id: DefinitionId, args: &ProcessRequestCallbackArgs) -> String {
let definition_info = args.interner.definition(id);
if let DefinitionKind::Global(global_id) = &definition_info.kind {
return format_global(*global_id, args);
}

let DefinitionKind::Local(expr_id) = definition_info.kind else {
panic!("Expected a local reference to reference a local definition")
};
Expand Down Expand Up @@ -629,7 +717,7 @@ mod hover_tests {
"two/src/lib.nr",
Position { line: 15, character: 25 },
r#" one::subone
global some_global: Field"#,
global some_global: Field = 2"#,
)
.await;
}
Expand Down

0 comments on commit 7818431

Please sign in to comment.