https://gcc.gnu.org/g:57a148f47e0d5ab56df6207a1372f4cc4b4c7792

commit r17-3133-g57a148f47e0d5ab56df6207a1372f4cc4b4c7792
Author: Yap Zhi Heng <[email protected]>
Date:   Mon Jul 27 20:47:35 2026 +0800

    gccrs: Backport `cfg_select!` macro
    
    This macro is used in newer versions of `core` to compile `c_char`, that is 
used to compile
    `CStr` which has widespread use in Rust for Linux.
    
    gcc/rust/ChangeLog:
    
            * expand/rust-macro-builtins.h (BuiltinMacro::CfgSelect): New enum 
variant.
            (MacroBuiltin::cfg_select_handler): New function definition.
            * expand/rust-macro-builtins.cc (MacroBuiltin::builtins): New 
cfg_select entry.
            (MacroBuiltin::builtin_transcribers): New cfg_select_handler entry.
            * expand/rust-macro-builtins-utility.cc 
(MacroBuiltin::cfg_select_handler): Implement
            parsing of compilation configuration predicates and expansion of 
block expressions based
            on which predicate was matched.
    
    gcc/testsuite/ChangeLog:
            * rust/compile/c_string_null_byte_check.rs: Update definition of 
`c_char`.
            * rust/execute/torture/c_string_ensure_null_term.rs: Ditto.
            * rust/execute/torture/c_string.rs: Ditto.
    
    Signed-off-by: Yap Zhi Heng <[email protected]>

Diff:
---
 gcc/rust/expand/rust-macro-builtins-utility.cc     | 134 +++++++++++++++++++++
 gcc/rust/expand/rust-macro-builtins.cc             |   2 +
 gcc/rust/expand/rust-macro-builtins.h              |   5 +
 .../rust/compile/c_string_null_byte_check.rs       |  33 ++++-
 gcc/testsuite/rust/execute/torture/c_string.rs     |  37 +++++-
 .../execute/torture/c_string_ensure_null_term.rs   |  39 +++++-
 gcc/testsuite/rust/execute/torture/cfg_select1.rs  |  31 +++++
 gcc/testsuite/rust/execute/torture/cfg_select2.rs  |  31 +++++
 8 files changed, 301 insertions(+), 11 deletions(-)

diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc 
b/gcc/rust/expand/rust-macro-builtins-utility.cc
index 6ee771ec0ed4..10be561a0ff9 100644
--- a/gcc/rust/expand/rust-macro-builtins-utility.cc
+++ b/gcc/rust/expand/rust-macro-builtins-utility.cc
@@ -21,6 +21,7 @@
 #include "rust-macro-builtins.h"
 #include "rust-macro-builtins-helpers.h"
 #include "rust-session-manager.h"
+#include "rust-stmt.h"
 
 namespace Rust {
 
@@ -344,6 +345,139 @@ MacroBuiltin::cfg_handler (location_t invoc_locus, 
AST::MacroInvocData &invoc,
   return AST::Fragment ({literal_exp}, std::move (tok));
 }
 
+tl::optional<AST::Fragment>
+MacroBuiltin::cfg_select_handler (location_t invoc_locus,
+                                 AST::MacroInvocData &invoc,
+                                 AST::InvocKind semicolon)
+{
+  auto invoc_token_tree = invoc.get_delim_tok_tree ();
+  MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+
+  Parser<MacroInvocLexer> parser (lex);
+
+  if (!parser.skip_token (LEFT_CURLY))
+    {
+      rust_error_at (invoc_locus, "expected %<(%> in %<cfg_select!%>");
+      return AST::Fragment::create_error ();
+    }
+
+  std::vector<AST::SingleASTNode> matched_body_nodes;
+  std::vector<std::unique_ptr<AST::Token>> matched_body_tokens;
+  bool has_match = false;
+
+  while (lex.peek_token ()->get_id () != RIGHT_CURLY
+        && lex.peek_token ()->get_id () != END_OF_FILE)
+    {
+      if (lex.peek_token ()->get_id () == UNDERSCORE)
+       {
+         // wildcard predicate
+         lex.skip_token (); // consume '_'
+         has_match = true;
+       }
+      else
+       {
+         size_t pred_start = lex.get_offs ();
+
+         // parse the predicate (until =>)
+         while (lex.peek_token ()->get_id () != MATCH_ARROW)
+           {
+             if (lex.peek_token ()->get_id () == END_OF_FILE)
+               {
+                 rust_error_at (invoc_locus,
+                                "unterminated %<cfg_select!%>arm");
+                 return AST::Fragment::create_error ();
+               }
+             lex.skip_token ();
+           }
+
+         size_t pred_end = lex.get_offs ();
+
+         std::vector<const_TokenPtr> synth;
+         synth.emplace_back (Token::make (LEFT_PAREN, invoc_locus));
+         auto pred_tokens = lex.get_token_slice (pred_start, pred_end);
+         for (auto &t : pred_tokens)
+           synth.emplace_back (t->get_tok_ptr ());
+         synth.emplace_back (Token::make (RIGHT_PAREN, invoc_locus));
+
+         AST::AttributeParser attr_parser (std::move (synth));
+         auto items = attr_parser.parse_meta_item_seq ();
+         if (items.size () != 1)
+           {
+             rust_error_at (invoc_locus,
+                            "  %<cfg_select!%> arm predicate must "
+                            "be a single cfg expression");
+             return AST::Fragment::create_error ();
+           }
+
+         bool result
+           = items[0]->check_cfg_predicate (Session::get_instance ());
+         if (result)
+           has_match = true;
+       }
+
+      if (!parser.skip_token (MATCH_ARROW))
+       {
+         rust_error_at (lex.peek_token ()->get_locus (),
+                        "expected %<=>%> in %<cfg_select!%> arm");
+         return AST::Fragment::create_error ();
+       }
+
+      // parse the body (after =>)
+      // always parse the body regardless of whether has_match is set, so lex
+      // will be at the next predicate in the next loop
+      size_t body_start = lex.get_offs ();
+      auto block_res = parser.parse_block_expr ();
+      if (has_match)
+       {
+         size_t body_end = lex.get_offs ();
+         if (!block_res)
+           {
+             rust_error_at (lex.peek_token ()->get_locus (),
+                            "failed to parse %<cfg_select!%> arm body");
+             return AST::Fragment::create_error ();
+           }
+
+         auto block = std::move (*block_res);
+         for (auto &stmt : block->get_statements ())
+           {
+             if (stmt->get_stmt_kind () == AST::Stmt::Kind::Item)
+               {
+                 AST::Stmt *raw = stmt.release ();
+                 matched_body_nodes.emplace_back (std::unique_ptr<AST::Item> (
+                   static_cast<AST::Item *> (raw)));
+               }
+             else
+               {
+                 matched_body_nodes.emplace_back (std::move (stmt));
+               }
+           }
+         if (block->has_tail_expr ())
+           {
+             auto tail = block->take_tail_expr ();
+             matched_body_nodes.emplace_back (AST::SingleASTNode (
+               std::make_unique<AST::ExprStmt> (std::move (tail), invoc_locus,
+                                                false)));
+           }
+
+         matched_body_tokens = lex.get_token_slice (body_start, body_end);
+         break;
+       }
+
+      parser.maybe_skip_token (COMMA);
+    }
+
+  if (!has_match)
+    {
+      rust_error_at (
+       invoc_locus,
+       "no %<cfg_select!%> arm matched and no %<_%> arm was provided");
+      return AST::Fragment::create_error ();
+    }
+
+  return AST::Fragment (std::move (matched_body_nodes),
+                       std::move (matched_body_tokens));
+}
+
 tl::optional<AST::Fragment>
 MacroBuiltin::stringify_handler (location_t invoc_locus,
                                 AST::MacroInvocData &invoc,
diff --git a/gcc/rust/expand/rust-macro-builtins.cc 
b/gcc/rust/expand/rust-macro-builtins.cc
index 02856e97cee1..df651fd9bbcc 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -55,6 +55,7 @@ const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins 
= {{
   {"env", BuiltinMacro::Env},
   {"option_env", BuiltinMacro::OptionEnv},
   {"cfg", BuiltinMacro::Cfg},
+  {"cfg_select", BuiltinMacro::CfgSelect},
   {"include", BuiltinMacro::Include},
   {"format_args", BuiltinMacro::FormatArgs},
   {"format_args_nl", BuiltinMacro::FormatArgsNl},
@@ -124,6 +125,7 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
     {"concat", MacroBuiltin::concat_handler},
     {"env", MacroBuiltin::env_handler},
     {"cfg", MacroBuiltin::cfg_handler},
+    {"cfg_select", MacroBuiltin::cfg_select_handler},
     {"include", MacroBuiltin::include_handler},
     {"format_args", format_args_maker (AST::FormatArgs::Newline::No)},
     {"format_args_nl", format_args_maker (AST::FormatArgs::Newline::Yes)},
diff --git a/gcc/rust/expand/rust-macro-builtins.h 
b/gcc/rust/expand/rust-macro-builtins.h
index 98ff5264573d..b0c2db57f058 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -49,6 +49,7 @@ enum class BuiltinMacro
   Env,
   OptionEnv,
   Cfg,
+  CfgSelect,
   Include,
   FormatArgs,
   FormatArgsNl,
@@ -168,6 +169,10 @@ public:
                                                  AST::MacroInvocData &invoc,
                                                  AST::InvocKind semicolon);
 
+  static tl::optional<AST::Fragment>
+  cfg_select_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+                     AST::InvocKind semicolon);
+
   static tl::optional<AST::Fragment>
   include_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
                   AST::InvocKind semicolon);
diff --git a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs 
b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
index 89a4bcdcaeff..6c872e000761 100644
--- a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
+++ b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
@@ -1,8 +1,37 @@
 // { dg-additional-options "-frust-c-style-string-literals" }
-#![feature(no_core, lang_items)]
+#![feature(no_core, lang_items, rustc_attrs)]
 #![no_core]
 
-type c_char = u8;
+#[rustc_builtin_macro]
+macro_rules! cfg_select {
+    () => {{}};
+}
+
+cfg_select! {
+    all(
+        not(windows),
+        not(target_vendor = "apple"),
+        not(target_os = "vita"),
+        any(
+            target_arch = "aarch64",
+            target_arch = "arm",
+            target_arch = "csky",
+            target_arch = "hexagon",
+            target_arch = "msp430",
+            target_arch = "powerpc",
+            target_arch = "powerpc64",
+            target_arch = "riscv32",
+            target_arch = "riscv64",
+            target_arch = "s390x",
+            target_arch = "xtensa",
+        )
+    ) => {
+        pub type c_char = u8;
+    }
+    _ => {
+        pub type c_char = i8;
+    }
+}
 
 #[lang = "CStr"]
 #[repr(transparent)]
diff --git a/gcc/testsuite/rust/execute/torture/c_string.rs 
b/gcc/testsuite/rust/execute/torture/c_string.rs
index 9f4cd5c036a5..9df72b8f033a 100644
--- a/gcc/testsuite/rust/execute/torture/c_string.rs
+++ b/gcc/testsuite/rust/execute/torture/c_string.rs
@@ -1,13 +1,42 @@
 // { dg-additional-options "-frust-c-style-string-literals" }
 // { dg-output "gccrs" }
-#![feature(no_core, lang_items)]
+#![feature(no_core, lang_items, rustc_attrs)]
 #![no_core]
 
-extern "C" {
-    fn printf(s: *const u8, ...);
+#[rustc_builtin_macro]
+macro_rules! cfg_select {
+    () => {{}};
 }
 
-type c_char = u8;
+cfg_select! {
+    all(
+        not(windows),
+        not(target_vendor = "apple"),
+        not(target_os = "vita"),
+        any(
+            target_arch = "aarch64",
+            target_arch = "arm",
+            target_arch = "csky",
+            target_arch = "hexagon",
+            target_arch = "msp430",
+            target_arch = "powerpc",
+            target_arch = "powerpc64",
+            target_arch = "riscv32",
+            target_arch = "riscv64",
+            target_arch = "s390x",
+            target_arch = "xtensa",
+        )
+    ) => {
+        pub type c_char = u8;
+    }
+    _ => {
+        pub type c_char = i8;
+    }
+}
+
+extern "C" {
+    fn printf(s: *const c_char, ...);
+}
 
 #[lang = "CStr"]
 #[repr(transparent)]
diff --git a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs 
b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
index 60da8a1dd9e3..a880bb735a3d 100644
--- a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
+++ b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
@@ -1,5 +1,5 @@
 // { dg-additional-options "-frust-c-style-string-literals" }
-#![feature(no_core, intrinsics, staged_api, lang_items)]
+#![feature(no_core, intrinsics, staged_api, lang_items, rustc_attrs)]
 #![no_core]
 
 #[lang = "sized"]
@@ -26,11 +26,40 @@ impl<T> *const T {
     }
 }
 
-extern "C" {
-    fn printf(s: *const u8, ...);
+#[rustc_builtin_macro]
+macro_rules! cfg_select {
+    () => {{}};
 }
 
-type c_char = u8;
+cfg_select! {
+    all(
+        not(windows),
+        not(target_vendor = "apple"),
+        not(target_os = "vita"),
+        any(
+            target_arch = "aarch64",
+            target_arch = "arm",
+            target_arch = "csky",
+            target_arch = "hexagon",
+            target_arch = "msp430",
+            target_arch = "powerpc",
+            target_arch = "powerpc64",
+            target_arch = "riscv32",
+            target_arch = "riscv64",
+            target_arch = "s390x",
+            target_arch = "xtensa",
+        )
+    ) => {
+        pub type c_char = u8;
+    }
+    _ => {
+        pub type c_char = i8;
+    }
+}
+
+extern "C" {
+    fn printf(s: *const c_char, ...);
+}
 
 #[lang = "CStr"]
 #[repr(transparent)]
@@ -44,7 +73,7 @@ impl CStr {
     }
 }
 
-pub fn main() -> u8 {
+pub fn main() -> c_char {
     let a = c"gccrs";
     let val = unsafe { a.to_ptr().add(5) };
     unsafe { *val }
diff --git a/gcc/testsuite/rust/execute/torture/cfg_select1.rs 
b/gcc/testsuite/rust/execute/torture/cfg_select1.rs
new file mode 100644
index 000000000000..5a37dcc6cc2a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg_select1.rs
@@ -0,0 +1,31 @@
+// { dg-additional-options "-frust-cfg=A=\"foo\"" }
+// { dg-output "wildcard\r*\n" }
+#![feature(no_core, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! cfg_select {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn main() -> i32 {
+    cfg_select! {
+        A = "bar" => {
+            unsafe {
+                let a = "none\n\0";
+                printf(a as *const str as *const i8);
+            }
+        },
+        _ => {
+            unsafe {
+                let a = "wildcard\n\0";
+                printf(a as *const str as *const i8);
+            }
+        }
+    }
+    return 0;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/execute/torture/cfg_select2.rs 
b/gcc/testsuite/rust/execute/torture/cfg_select2.rs
new file mode 100644
index 000000000000..fe0ad608ec71
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg_select2.rs
@@ -0,0 +1,31 @@
+// { dg-additional-options "-frust-cfg=A=\"foo\"" }
+// { dg-output "pass\r*\n" }
+#![feature(no_core, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! cfg_select {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn main() -> i32 {
+    cfg_select! {
+        A = "foo" => {
+            unsafe {
+                let a = "pass\n\0";
+                printf(a as *const str as *const i8);
+            }
+        }
+        _ => {
+            unsafe {
+                let a = "fail\n\0";
+                printf(a as *const str as *const i8);
+            }
+        }
+    }
+    return 0;
+}
\ No newline at end of file

Reply via email to