From: Yap Zhi Heng <[email protected]>
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]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/b470ae200be3802a4799571d7e67315a9b71aad2
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4714
.../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 ++++-
.../rust/execute/torture/c_string.rs | 37 ++++-
.../torture/c_string_ensure_null_term.rs | 39 ++++-
.../rust/execute/torture/cfg_select1.rs | 31 ++++
.../rust/execute/torture/cfg_select2.rs | 31 ++++
8 files changed, 301 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/rust/execute/torture/cfg_select1.rs
create mode 100644 gcc/testsuite/rust/execute/torture/cfg_select2.rs
diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc
b/gcc/rust/expand/rust-macro-builtins-utility.cc
index 6ee771ec0..10be561a0 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 02856e97c..df651fd9b 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 98ff52645..b0c2db57f 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 89a4bcdca..6c872e000 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 9f4cd5c03..9df72b8f0 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 60da8a1dd..a880bb735 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 000000000..5a37dcc6c
--- /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 000000000..fe0ad608e
--- /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
base-commit: efaeba75ab09c066482927f8ef845b8f12c2d6fd
--
2.54.0