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
