Skip to content

chore: repro for very strange bug#8740

Closed
asterite wants to merge 1 commit intoab/no-cast-from-numeric-to-boolfrom
ab/repro-strange-bug
Closed

chore: repro for very strange bug#8740
asterite wants to merge 1 commit intoab/no-cast-from-numeric-to-boolfrom
ab/repro-strange-bug

Conversation

@asterite
Copy link
Collaborator

@asterite asterite commented May 30, 2025

Description

Problem

The compiler fails to compile a program if we add/remove unused variables.

Summary

If we compile the serialize_1 as it is in this PR, it fails to compile. But if we uncomment the let a1 = quote {}; variable, it compiles.

Note that if it's let a1 = 1; then it doesn't compile. For some reason if we use quote {} it compiles.

Then, if we keep uncomment variables the program will compile or not compile. It seems a bit random.

This makes me think there's a bug somewhere in the comptime code. We already observed that if some code is generated via macros instead of written by hand then it might lead to slower compilation, when it shouldn't, so maybe all of these things are related. I wonder if something is leaking from the comptime interpreter that's modifying something outside of it, some global state (or bindings, type vars, not sure).


Another way: instead of uncommenting any of those variables, add this to lib.nr for the standard library:

fn foo() {
    comptime {
        let _ = quote {};
    }
}

then the code compiles.

Additional Context

Documentation

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist*

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

@github-actions
Copy link
Contributor

Changes to Brillig bytecode sizes

Generated at commit: 57c0198351a2f15a72a0bb25e0e45300d6ea53e9, compared to commit: 2ae02a2824d89fae43d48c283610ab3e2bc2d727

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
wrapping_operations_inliner_max -16 ✅ -29.63%
wrapping_operations_inliner_min -16 ✅ -29.63%
wrapping_operations_inliner_zero -16 ✅ -29.63%

Full diff report 👇
Program Brillig opcodes (+/-) %
6_array_inliner_max 301 (-40) -11.73%
6_array_inliner_min 301 (-40) -11.73%
6_array_inliner_zero 301 (-40) -11.73%
signed_division_inliner_max 179 (-24) -11.82%
signed_division_inliner_min 179 (-24) -11.82%
signed_division_inliner_zero 179 (-24) -11.82%
5_over_inliner_max 35 (-7) -16.67%
5_over_inliner_min 35 (-7) -16.67%
5_over_inliner_zero 35 (-7) -16.67%
4_sub_inliner_max 31 (-7) -18.42%
4_sub_inliner_min 31 (-7) -18.42%
4_sub_inliner_zero 31 (-7) -18.42%
wrapping_operations_inliner_max 38 (-16) -29.63%
wrapping_operations_inliner_min 38 (-16) -29.63%
wrapping_operations_inliner_zero 38 (-16) -29.63%

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Test Suite Duration'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: 8530f89 Previous: cb4951d Ratio
test_report_AztecProtocol_aztec-packages_noir-projects_noir-protocol-circuits_crates_rollup-lib 267 s 192 s 1.39

This comment was automatically generated by workflow using github-action-benchmark.

CC: @TomAFrench

@asterite
Copy link
Collaborator Author

asterite commented Jun 3, 2025

I was able to reproduce this with a much smaller code.

For this program:

trait Serialize {
    let Size: u32;

    fn serialize(self) -> [Field; Self::Size];
}

impl<A, B> Serialize for (A, B)
where
    A: Serialize,
    B: Serialize,
{
    let Size: u32 = <A as Serialize>::Size + <B as Serialize>::Size;

    fn serialize(self: Self) -> [Field; Self::Size] {
        let _ = self.0.serialize();
        let _ = self.1.serialize();
        [0; Self::Size]
    }
}

impl<T, let N: u32> Serialize for [T; N]
where
    T: Serialize,
{
    let Size: u32 = <T as Serialize>::Size * N;

    fn serialize(self: Self) -> [Field; Self::Size] {
        [0; Self::Size]
    }
}

impl Serialize for Field {
    let Size: u32 = 1;

    fn serialize(self) -> [Field; Self::Size] {
        [self]
    }
}

fn main() {
    let x = (((1, [2, 3, 4]), [5, 6, 7, 8]), 9);
    let _ = x.serialize();
}

If we change noir_stdlib/src/lib.nr to be empty, the program above compiles.

If we put this in noir_stdlib/src/lib.nr then it stops compiling:

pub fn foo() {
    let _ = 1;
    let _ = 1;
}

Hopefully this will make finding the issue much easier 🤞

But also this means that it's likely unrelated to comptime stuff, as it happens with non-comptime stuff too (the foo above isn't comptime).

@asterite
Copy link
Collaborator Author

asterite commented Jun 3, 2025

It's also worth noting that this happens if foo above is in the standard library. If we move it from the standard library to the program, then it works fine.

@asterite
Copy link
Collaborator Author

asterite commented Jun 3, 2025

Oh, and it also seems to happen if we move foo to a dependency of the program. So it's not the standard library, it's any dependency.

@asterite
Copy link
Collaborator Author

asterite commented Jun 3, 2025

Closed in favor of this issue: #8780

@asterite asterite closed this Jun 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant