[gcc r17-2242] gccrs: Detect errors in macro eager expansion

Arthur Cohen via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:6bf327145b1943f999c7f449a4f4ac577945fb6b

commit r17-2242-g6bf327145b1943f999c7f449a4f4ac577945fb6b
Author: Owen Avery <[email protected]>
Date:   Sun Jun 28 00:37:27 2026 -0400

    gccrs: Detect errors in macro eager expansion
    
    gcc/rust/ChangeLog:
    
            * expand/rust-macro-expand.cc
            (MacroExpander::expand_eager_invocations): Handle errors during
            eager expansion.
            (MacroExpander::expand_invoc): Avoid expanding macros which have
            pending eager invocations and return error fragments in more
            cases.
    
    gcc/testsuite/ChangeLog:
    
            * rust/compile/macros/builtin/eager4.rs: New test.
    
    Signed-off-by: Owen Avery <[email protected]>

Diff:
---
 gcc/rust/expand/rust-macro-expand.cc               | 46 ++++++++++++++--------
 .../rust/compile/macros/builtin/eager4.rs          | 11 ++++++
 2 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 8f9008020871..c06290cdfda2 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -186,8 +186,9 @@ MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
 
   // we want to build a substitution map - basically, associating a `start` and
   // `end` index for each of the pending macro invocations
-  std::map<std::pair<size_t, size_t>, std::unique_ptr<AST::MacroInvocation> &>
-    substitution_map;
+  std::map<std::pair<size_t, size_t>, AST::MacroInvocation *> substitution_map;
+
+  auto &pending = invoc.get_pending_eager_invocations ();
 
   for (size_t i = 0; i < stream.size (); i++)
     {
@@ -199,20 +200,29 @@ MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
       // offset and store them in the substitution map. Otherwise, we skip one
       // token and try parsing again
       if (invocation)
-	substitution_map.insert (
-	  {{i, parser.get_token_source ().get_offs ()},
-	   invoc.get_pending_eager_invocations ()[current_pending++]});
+	substitution_map.insert ({{i, parser.get_token_source ().get_offs ()},
+				  pending[current_pending++].get ()});
       else
 	parser.skip_token (stream[i]->get_id ());
     }
 
+  auto pending_it = pending.begin ();
   size_t current_idx = 0;
   for (auto kv : substitution_map)
     {
-      auto &to_expand = kv.second;
+      AST::MacroInvocation *to_expand = kv.second;
       expand_invoc (*to_expand, AST::InvocKind::Expr);
 
       auto fragment = take_expanded_fragment ();
+
+      if (fragment.is_error ())
+	{
+	  // skip expansion of this macro
+	  // leave current_idx as-is, and continue
+	  pending_it++;
+	  continue;
+	}
+
       auto &new_tokens = fragment.get_tokens ();
 
       auto start = kv.first.first;
@@ -233,6 +243,7 @@ MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
 	new_stream.emplace_back (tok->clone_token ());
 
       current_idx = end;
+      pending_it = pending.erase (pending_it);
     }
 
   // Once all of that is done, we copy the last remaining tokens from the
@@ -243,7 +254,6 @@ MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
   auto new_dtt
     = AST::DelimTokenTree (dtt.get_delim_type (), std::move (new_stream));
 
-  invoc.get_pending_eager_invocations ().clear ();
   invoc.get_invoc_data ().set_delim_tok_tree (new_dtt);
 }
 
@@ -263,6 +273,13 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
       push_context (ContextType::EXPR);
       expand_eager_invocations (invoc);
       pop_context ();
+
+      // if we have pending eager invocations still, don't expand
+      if (!invoc.get_pending_eager_invocations ().empty ())
+	{
+	  set_expanded_fragment (AST::Fragment::create_error ());
+	  return;
+	}
     }
 
   AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
@@ -314,7 +331,11 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
   // If there's no rule associated with the invocation, we can simply return
   // early. The early name resolver will have already emitted an error.
   if (!rules_def)
-    return;
+    {
+      // error fragment
+      set_expanded_fragment (std::move (fragment));
+      return;
+    }
 
   auto rdef = rules_def.value ();
 
@@ -331,14 +352,7 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
   else
     fragment
       = expand_decl_macro (invoc.get_locus (), invoc_data, *rdef, semicolon);
-  // fix: if the expansion is failing, we must replace the marco with an empty
-  // error or node
-  // makes sure that it doesn't panic on Rouge macro (it -> Lowering Phase)
-  // added the parsing errors in gcc/testsuite/rust/compile/issue-4213.rs
-  if (fragment.is_error ())
-    {
-      fragment = AST::Fragment::create_empty ();
-    }
+
   set_expanded_fragment (std::move (fragment));
 }
 
diff --git a/gcc/testsuite/rust/compile/macros/builtin/eager4.rs b/gcc/testsuite/rust/compile/macros/builtin/eager4.rs
new file mode 100644
index 000000000000..7b02b11294a0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/builtin/eager4.rs
@@ -0,0 +1,11 @@
+#![feature(no_core)]
+#![feature(rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! concat {
+    ($($e:expr),* $(,)?) => {{ /* built in */ }}
+}
+
+// { dg-error "could not resolve" "" { target *-*-* } .+1 }
+pub const X: &'static str = concat!("A", stringify!(B), "C", stringify!(D), "E");
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.