From: Arthur Cohen Date: Tue, 18 Apr 2023 14:22:22 +0000 (+0200) Subject: gccrs: enr: Accumulate nested escaped macros properly. X-Git-Tag: basepoints/gcc-15~2583 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=12dd77c19fbcf2446a895804848ffb498e0d3688;p=thirdparty%2Fgcc.git gccrs: enr: Accumulate nested escaped macros properly. gcc/rust/ChangeLog: * resolve/rust-early-name-resolver.cc (is_macro_use_module): New function. (EarlyNameResolver::accumulate_escaped_macros): New function. (EarlyNameResolver::go): Use `accumulate_escaped_macros`. (EarlyNameResolver::visit): Likewise. * resolve/rust-early-name-resolver.h: Declare `accumulate_escaped_macros`. gcc/testsuite/ChangeLog: * rust/compile/macro_use1.rs: New test. * rust/execute/torture/macro_use1.rs: New test. * rust/compile/nested_macro_use1.rs: New test. * rust/compile/nested_macro_use2.rs: New test. --- diff --git a/gcc/rust/resolve/rust-early-name-resolver.cc b/gcc/rust/resolve/rust-early-name-resolver.cc index 145b39311882..f3e4b198f09f 100644 --- a/gcc/rust/resolve/rust-early-name-resolver.cc +++ b/gcc/rust/resolve/rust-early-name-resolver.cc @@ -24,6 +24,55 @@ namespace Rust { namespace Resolver { +// Check if a module contains the `#[macro_use]` attribute +static bool +is_macro_use_module (const AST::Module &mod) +{ + for (const auto &attr : mod.get_outer_attrs ()) + if (attr.get_path ().as_string () == "macro_use") + return true; + + return false; +} + +std::vector> +EarlyNameResolver::accumulate_escaped_macros (AST::Module &module) +{ + if (!is_macro_use_module (module)) + return {}; + + // Parse the module's items if they haven't been expanded and the file + // should be parsed (i.e isn't hidden behind an untrue or impossible cfg + // directive) + if (module.get_kind () == AST::Module::UNLOADED) + module.load_items (); + + std::vector> escaped_macros; + + scoped (module.get_node_id (), [&module, &escaped_macros, this] { + for (auto &item : module.get_items ()) + { + if (item->get_ast_kind () == AST::Kind::MODULE) + { + auto &module = *static_cast (item.get ()); + auto new_macros = accumulate_escaped_macros (module); + + std::move (new_macros.begin (), new_macros.end (), + std::back_inserter (escaped_macros)); + + continue; + } + + item->accept_vis (*this); + + if (item->get_ast_kind () == AST::Kind::MACRO_RULES_DEFINITION) + escaped_macros.emplace_back (item->clone_item ()); + } + }); + + return escaped_macros; +} + EarlyNameResolver::EarlyNameResolver () : current_scope (UNKNOWN_NODEID), resolver (*Resolver::get ()), mappings (*Analysis::Mappings::get ()) @@ -32,6 +81,29 @@ EarlyNameResolver::EarlyNameResolver () void EarlyNameResolver::go (AST::Crate &crate) { + std::vector> new_items; + auto items = crate.take_items (); + + scoped (crate.get_node_id (), [&items, &new_items, this] { + for (auto &&item : items) + { + if (item->get_ast_kind () == AST::Kind::MODULE) + { + auto macros = accumulate_escaped_macros ( + *static_cast (item.get ())); + new_items.emplace_back (std::move (item)); + std::move (macros.begin (), macros.end (), + std::back_inserter (new_items)); + } + else + { + new_items.emplace_back (std::move (item)); + } + } + }); + + crate.set_items (std::move (new_items)); + scoped (crate.get_node_id (), [&crate, this] () { for (auto &item : crate.items) item->accept_vis (*this); @@ -550,12 +622,35 @@ EarlyNameResolver::visit (AST::Method &method) void EarlyNameResolver::visit (AST::Module &module) { - // Parse the module's items if they haven't been expanded and the file - // should be parsed (i.e isn't hidden behind an untrue or impossible cfg - // directive) if (module.get_kind () == AST::Module::UNLOADED) module.load_items (); + // so we need to only go "one scope down" for fetching macros. Macros within + // functions are still scoped only within that function. But we have to be + // careful because nested modules with #[macro_use] actually works! + std::vector> new_items; + auto items = module.take_items (); + + scoped (module.get_node_id (), [&items, &new_items, this] { + for (auto &&item : items) + { + if (item->get_ast_kind () == AST::Kind::MODULE) + { + auto macros = accumulate_escaped_macros ( + *static_cast (item.get ())); + new_items.emplace_back (std::move (item)); + std::move (macros.begin (), macros.end (), + std::back_inserter (new_items)); + } + else + { + new_items.emplace_back (std::move (item)); + } + } + }); + + module.set_items (std::move (new_items)); + scoped (module.get_node_id (), [&module, this] () { for (auto &item : module.get_items ()) item->accept_vis (*this); diff --git a/gcc/rust/resolve/rust-early-name-resolver.h b/gcc/rust/resolve/rust-early-name-resolver.h index 07281378c5bb..dfdab817d00c 100644 --- a/gcc/rust/resolve/rust-early-name-resolver.h +++ b/gcc/rust/resolve/rust-early-name-resolver.h @@ -53,6 +53,18 @@ private: current_scope = old_scope; } + /** + * Accumulate all of the nested macros which escape their module through the + * use of the #[macro_use] attribute. + * + * This function recursively accumulates macros in all of the nested modules + * of an item container (an AST::Crate or an AST::Module) and returns this new + * list of items. You can then use the `take_items` and `set_items` functions + * on these containers to replace their list of items. + */ + std::vector> + accumulate_escaped_macros (AST::Module &module); + /** * The "scope" we are currently in. * diff --git a/gcc/testsuite/rust/compile/macro_use1.rs b/gcc/testsuite/rust/compile/macro_use1.rs new file mode 100644 index 000000000000..e98eadff0ae2 --- /dev/null +++ b/gcc/testsuite/rust/compile/macro_use1.rs @@ -0,0 +1,15 @@ +#[macro_use] +mod foo { + macro_rules! a { + () => {}; + } + + macro_rules! b { + () => {}; + } +} + +fn main() { + a!(); + b!(); +} diff --git a/gcc/testsuite/rust/compile/nested_macro_use1.rs b/gcc/testsuite/rust/compile/nested_macro_use1.rs new file mode 100644 index 000000000000..8a2fd2ac82db --- /dev/null +++ b/gcc/testsuite/rust/compile/nested_macro_use1.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod foo { + #[macro_use] + mod zim { + #[macro_use] + mod zoom { + #[macro_use] + mod zum { + macro_rules! qux { + () => {}; + } + } + } + } +} + +fn main() { + qux!(); // OK +} diff --git a/gcc/testsuite/rust/compile/nested_macro_use2.rs b/gcc/testsuite/rust/compile/nested_macro_use2.rs new file mode 100644 index 000000000000..465950087109 --- /dev/null +++ b/gcc/testsuite/rust/compile/nested_macro_use2.rs @@ -0,0 +1,12 @@ +#[macro_use] +mod foo { + fn bar() { + macro_rules! baz { + () => {{}}; + } + } +} + +fn main() { + baz!(); // { dg-error "unknown macro: .baz." } +} diff --git a/gcc/testsuite/rust/execute/torture/macro_use1.rs b/gcc/testsuite/rust/execute/torture/macro_use1.rs new file mode 100644 index 000000000000..fdf553012ebf --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macro_use1.rs @@ -0,0 +1,18 @@ +#[macro_use] +mod foo { + macro_rules! a { + () => { + 15 + }; + } + + macro_rules! b { + () => { + 14 + }; + } +} + +fn main() -> i32 { + a!() + b!() - 29 +}