[COMMITTED 74/77] gccrs: Backport `cfg_select!` macro

[email protected]
Newsgroups gmane.comp.gcc.rust,gmane.comp.gcc.patches
Message-ID <[email protected]>
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]>
---
 .../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 6ee771ec0ed..10be561a0ff 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 02856e97cee..df651fd9bbc 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 98ff5264573..b0c2db57f05 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 89a4bcdcaef..6c872e00076 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 9f4cd5c036a..9df72b8f033 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 60da8a1dd9e..a880bb735a3 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 00000000000..5a37dcc6cc2
--- /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 00000000000..fe0ad608ec7
--- /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
-- 
2.50.1
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.