Skip to content

Commit

Permalink
feat: Sync from noir (#8643)
Browse files Browse the repository at this point in the history
Automated pull of development from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
fix: preserve generic kind on trait methods
(noir-lang/noir#6099)
feat: (LSP) show global value on hover
(noir-lang/noir#6097)
feat: (LSP) if in runtime code, always suggest functions that return
Quoted as macro calls (noir-lang/noir#6098)
chore: fix broken formatting on master
(noir-lang/noir#6096)
END_COMMIT_OVERRIDE

---------

Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
  • Loading branch information
AztecBot and TomAFrench authored Sep 19, 2024
1 parent 09e2f44 commit 7fb2a45
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2174ffb92b5d88e7e0926c91f42bc7f849e8ddc1
1df102a1ee0eb39dcbada50e10b226c7f7be0f26
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ pub(crate) fn check_trait_impl_method_matches_declaration(
// Substitute each generic on the trait function with the corresponding generic on the impl function
for (
ResolvedGeneric { type_var: trait_fn_generic, .. },
ResolvedGeneric { name, type_var: impl_fn_generic, .. },
ResolvedGeneric { name, type_var: impl_fn_generic, kind, .. },
) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics)
{
let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), Kind::Normal);
let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), kind.clone());
bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg));
}

Expand Down
166 changes: 83 additions & 83 deletions noir/noir-repo/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
24 changes: 24 additions & 0 deletions noir/noir-repo/compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3700,3 +3700,27 @@ fn use_non_u32_generic_in_struct() {
let errors = get_program_errors(src);
assert_eq!(errors.len(), 0);
}

#[test]
fn use_numeric_generic_in_trait_method() {
let src = r#"
trait Foo {
fn foo<let N: u32>(self, x: [u8; N]) -> Self;
}
struct Bar;
impl Foo for Bar {
fn foo<let N: u32>(self, _x: [u8; N]) -> Self {
self
}
}
fn main() {
let _ = Bar{}.foo([1,2,3]);
}
"#;

let errors = get_program_errors(src);
assert_eq!(errors.len(), 0);
}
14 changes: 14 additions & 0 deletions noir/noir-repo/tooling/lsp/src/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct NodeFinder<'a> {
/// The line where an auto_import must be inserted
auto_import_line: usize,
self_type: Option<Type>,
in_comptime: bool,
}

impl<'a> NodeFinder<'a> {
Expand Down Expand Up @@ -156,6 +157,7 @@ impl<'a> NodeFinder<'a> {
nesting: 0,
auto_import_line: 0,
self_type: None,
in_comptime: false,
}
}

Expand Down Expand Up @@ -1056,8 +1058,12 @@ impl<'a> Visitor for NodeFinder<'a> {
self.collect_local_variables(&param.pattern);
}

let old_in_comptime = self.in_comptime;
self.in_comptime = noir_function.def.is_comptime;

noir_function.def.body.accept(Some(span), self);

self.in_comptime = old_in_comptime;
self.type_parameters = old_type_parameters;
self.self_type = None;

Expand Down Expand Up @@ -1278,8 +1284,12 @@ impl<'a> Visitor for NodeFinder<'a> {
let old_local_variables = self.local_variables.clone();
self.local_variables.clear();

let old_in_comptime = self.in_comptime;
self.in_comptime = true;

statement.accept(self);

self.in_comptime = old_in_comptime;
self.local_variables = old_local_variables;

false
Expand Down Expand Up @@ -1424,8 +1434,12 @@ impl<'a> Visitor for NodeFinder<'a> {
let old_local_variables = self.local_variables.clone();
self.local_variables.clear();

let old_in_comptime = self.in_comptime;
self.in_comptime = true;

block_expression.accept(Some(span), self);

self.in_comptime = old_in_comptime;
self.local_variables = old_local_variables;

false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,13 @@ impl<'a> NodeFinder<'a> {
if modifiers.is_comptime
&& matches!(func_meta.return_type(), Type::Quoted(QuotedType::Quoted))
{
vec![make_completion_item(false), make_completion_item(true)]
if self.in_comptime {
vec![make_completion_item(false), make_completion_item(true)]
} else {
// If not in a comptime block we can't operate with comptime values so the only thing
// we can do is call a macro.
vec![make_completion_item(true)]
}
} else {
vec![make_completion_item(false)]
}
Expand Down
22 changes: 21 additions & 1 deletion noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2126,7 +2126,9 @@ mod completion_tests {
comptime fn foobar() -> Quoted {}
fn main() {
fooba>|<
comptime {
fooba>|<
}
}
"#;

Expand All @@ -2140,6 +2142,24 @@ mod completion_tests {
.await;
}

#[test]
async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime(
) {
let src = r#"
comptime fn foobar() -> Quoted {}
fn main() {
fooba>|<
}
"#;

assert_completion_excluding_auto_import(
src,
vec![function_completion_item("foobar!()", "foobar!()", "fn() -> Quoted")],
)
.await;
}

#[test]
async fn test_only_suggests_macro_call_for_unquote() {
let src = r#"
Expand Down
Loading

0 comments on commit 7fb2a45

Please sign in to comment.