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: nargo expand to show code after macro expansions #7613

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from

Conversation

asterite
Copy link
Collaborator

@asterite asterite commented Mar 6, 2025

Description

Problem

Resolves #7552

Summary

Pending:

  • Manually show hir expressions instead of using to_display_ast()
  • Show imports
  • Show types according to imports (fully-qualify them if needed)
  • Show doc comments (this is easy)
  • Show comments (this is harder, but one idea is to get comments from the original code, indexed by file/line, then output those comments if we are outputting something that is on that file/line). This could also be done in a separate PR.
  • Don't output a string gigantic string (maybe this could be a follow-up PR because the tool as it is now is already useful)

The idea I have for this is:

  1. We type-check the code as usual
  2. All code, that which exists in source files and that which is created by macros, is stored in "def maps" and the node interner, so we can use those sources to rebuild the final code

The output right now goes to the console, and just for the top-level module, and just for functions, as a way to experiment with this and get feedback from you all. Eventually a directory with all the expanded code could be created, with one file per module to match the original source code.

The downside of this approach is that comments and formatting is lost, though we could at least run the formatter at the end for formatting. Comments could be done in a follow-up where they would also be stored in the node interner, and just for this tool (maybe!).

For example, if you run it for this code:

fn main() {
    let _ = std::as_witness(1);
    println("Hello world");
}

#[foo]
comptime fn foo(f: FunctionDefinition) -> Quoted {
    quote {
        pub fn bar(x: i32) -> i32  {  
            let y = x + 1;
            y + 2
        }
    }
}

#[mutate_add_one]
fn add_one() {}

comptime fn mutate_add_one(f: FunctionDefinition) {
    f.set_parameters(&[(quote { x }, quote { Field }.as_type())]);
    f.set_return_type(quote { Field }.as_type());
    f.set_body(quote { x + 1 }.as_expr().unwrap());
}

we get this output (the function bodies use HirExpression::to_display_ast which isn't faithful to the original code but we could try to improve it):

fn main() {
    let _: () = as_witness(1)
    println("Hello world");
}

comptime fn foo(f: FunctionDefinition) -> Quoted {
    quote { pub fn bar ( x : i32 ) -> i32 { let y = x + 1 ; y + 2 } }
}

fn add_one(x: Field) -> Field {
    (x + 1)
}

comptime fn mutate_add_one(f: FunctionDefinition) {
    set_parameters(f, &[(quote { x }, as_type(quote { Field }))]);
    set_return_type(f, as_type(quote { Field }));
    set_body(f, unwrap(as_expr(quote { x + 1 })));
}

pub fn bar(x: i32) -> i32 {
    let y: i32 = (x + 1)
    (y + 2)
}

Additional Context

Do you think this approach is good enough? What other ways we could tackle this?

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.

@jfecher
Copy link
Contributor

jfecher commented Mar 6, 2025

Eventually a directory with all the expanded code could be created, with one file per module to match the original source code.

How would we do this though? The reason we only have a CLI flag currently and only output the new code is that while we're elaborating we are losing information about the source code. We throw away all type and trait definitions for example. By monomorphization we're already left with only functions. How would we output any types/traits/imports/etc back into the program? I don't think just skipping past them is an option either since metaprogramming may modify a type definition such that it differs from the source.

@asterite
Copy link
Collaborator Author

asterite commented Mar 7, 2025

How would we do this though?

Oh, I just meant that if we have a function foo defined in src/main.nr we know that its location is in that file. Then we'd output it (after it has been potentially changed) in the same file, but a different directory. For code generated by macros... it would land on the file that has the macro attribute, I think.

We throw away all type and trait definitions for example. How would we output any types/traits/imports/etc back into the program?

They are in the NodeInterner, for example get_trait, get_type, etc. Imports are a bit more tricky but they are there in a module's scope (probably the ones that aren't in definitions). It's still tricky because if we need to output a type or a call to a function we might need to either fully-qualify it or use an existing import, but I think it's doable.

By monomorphization we're already left with only functions.

Right, we can do this before monomoprhization (or put another way: we don't monomorphize for this tool)

@asterite asterite changed the title Start working on a tool to show code after macro expansions feat: nargo expand to show code after macro expansions Mar 7, 2025
@asterite
Copy link
Collaborator Author

asterite commented Mar 7, 2025

Some progress: here's the output of running nargo expand on "ecdsa_k_account_contract": https://gist.github.com/asterite/0821b58e82e1a7adbdfce14fcecaeef5

I listed some pending stuff in the main description for this tool to generate almost valid Noir code, but I think the most difficult thing is already done (figuring out how to recreate impls and trait impls, though I think impls are missing where clauses and they show up instead in each function).

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.

Add a tool to show code after macro expansions
2 participants