From: Liam Naddell <liamn...@gmail.com>

gcc/rust/ChangeLog:
        * expand/rust-macro-builtins-utility.cc: Add macro expansion for
        option_env with eager expansion
        * expand/rust-macro-builtins.cc: Add option_env to builtin list
        * expand/rust-macro-builtins.h: Add option_env handler to header
        file
        * resolve/rust-late-name-resolver-2.0.cc: Prevent NR2.0 from
        recursing into lang-item segments

gcc/testsuite/ChangeLog:
        * rust/compile/macros/builtin/option_env1.rs: Add success case for 
option_env
        * rust/compile/macros/builtin/option_env2.rs: Add failure case for 
option_env
        * rust/execute/torture/builtin_macro_option_env.rs: Add
        execution case for option_env
---
 .../expand/rust-macro-builtins-utility.cc     | 78 +++++++++++++++++++
 gcc/rust/expand/rust-macro-builtins.cc        |  2 +-
 gcc/rust/expand/rust-macro-builtins.h         |  4 +
 .../resolve/rust-late-name-resolver-2.0.cc    |  2 +
 .../compile/macros/builtin/option_env1.rs     | 29 +++++++
 .../compile/macros/builtin/option_env2.rs     | 27 +++++++
 .../compile/macros/builtin/option_env3.rs     | 28 +++++++
 .../torture/builtin_macro_option_env.rs       | 65 ++++++++++++++++
 8 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/rust/compile/macros/builtin/option_env1.rs
 create mode 100644 gcc/testsuite/rust/compile/macros/builtin/option_env2.rs
 create mode 100644 gcc/testsuite/rust/compile/macros/builtin/option_env3.rs
 create mode 100644 
gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs

diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc 
b/gcc/rust/expand/rust-macro-builtins-utility.cc
index 9ea854ef42d..33c7bf1f8d7 100644
--- a/gcc/rust/expand/rust-macro-builtins-utility.cc
+++ b/gcc/rust/expand/rust-macro-builtins-utility.cc
@@ -17,6 +17,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-fmt.h"
+#include "rust-ast-builder.h"
 #include "rust-macro-builtins.h"
 #include "rust-macro-builtins-helpers.h"
 
@@ -226,6 +227,83 @@ MacroBuiltin::env_handler (location_t invoc_locus, 
AST::MacroInvocData &invoc,
   return AST::Fragment ({node}, std::move (tok));
 }
 
+/* Expand builtin macro option_env!(), which inspects an environment variable 
at
+   compile time. */
+tl::optional<AST::Fragment>
+MacroBuiltin::option_env_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);
+
+  auto last_token_id = macro_end_token (invoc_token_tree, parser);
+  std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
+  bool has_error = false;
+
+  auto start = lex.get_offs ();
+  auto expanded_expr = try_expand_many_expr (parser, last_token_id,
+                                            invoc.get_expander (), has_error);
+  auto end = lex.get_offs ();
+
+  auto tokens = lex.get_token_slice (start, end);
+
+  if (has_error)
+    return AST::Fragment::create_error ();
+
+  auto pending = check_for_eager_invocations (expanded_expr);
+  if (!pending.empty ())
+    return make_eager_builtin_invocation (BuiltinMacro::OptionEnv, invoc_locus,
+                                         invoc_token_tree,
+                                         std::move (pending));
+
+  if (expanded_expr.size () != 1)
+    {
+      rust_error_at (invoc_locus, "%<option_env!%> takes 1 argument");
+      return AST::Fragment::create_error ();
+    }
+
+  if (expanded_expr.size () > 0)
+    if (!(lit_expr
+         = try_extract_string_literal_from_fragment (invoc_locus,
+                                                     expanded_expr[0])))
+      return AST::Fragment::create_error ();
+
+  parser.skip_token (last_token_id);
+
+  auto env_value = getenv (lit_expr->as_string ().c_str ());
+  AST::Builder b (invoc_locus);
+
+  if (env_value == nullptr)
+    {
+      auto none_expr = std::unique_ptr<AST::Expr> (
+       new AST::PathInExpression (LangItem::Kind::OPTION_NONE, {},
+                                  invoc_locus));
+
+      auto node = AST::SingleASTNode (std::move (none_expr));
+      std::vector<AST::SingleASTNode> nodes;
+      nodes.push_back (node);
+
+      return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> 
());
+    }
+  std::vector<std::unique_ptr<AST::Expr>> args;
+  args.push_back (b.literal_string (env_value));
+
+  std::unique_ptr<AST::Expr> some_expr
+    = b.call (std::unique_ptr<AST::Expr> (
+               new AST::PathInExpression (LangItem::Kind::OPTION_SOME, {},
+                                          invoc_locus)),
+             std::move (args));
+
+  auto node = AST::SingleASTNode (std::move (some_expr));
+
+  std::vector<AST::SingleASTNode> nodes;
+  nodes.push_back (node);
+
+  return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
+}
+
 tl::optional<AST::Fragment>
 MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
                           AST::InvocKind semicolon)
diff --git a/gcc/rust/expand/rust-macro-builtins.cc 
b/gcc/rust/expand/rust-macro-builtins.cc
index 2457bc030f8..804cfed521a 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -120,8 +120,8 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
     {"format_args_nl", format_args_maker (AST::FormatArgs::Newline::Yes)},
     {"asm", inline_asm_maker (AST::AsmKind::Inline)},
     {"global_asm", inline_asm_maker (AST::AsmKind::Global)},
+    {"option_env", MacroBuiltin::option_env_handler},
     /* Unimplemented macro builtins */
-    {"option_env", MacroBuiltin::sorry},
     {"concat_idents", MacroBuiltin::sorry},
     {"module_path", MacroBuiltin::sorry},
     {"log_syntax", MacroBuiltin::sorry},
diff --git a/gcc/rust/expand/rust-macro-builtins.h 
b/gcc/rust/expand/rust-macro-builtins.h
index 6a9b31cb80c..ff06ebf2289 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -159,6 +159,10 @@ public:
                                                  AST::MacroInvocData &invoc,
                                                  AST::InvocKind semicolon);
 
+  static tl::optional<AST::Fragment>
+  option_env_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+                     AST::InvocKind semicolon);
+
   static tl::optional<AST::Fragment> cfg_handler (location_t invoc_locus,
                                                  AST::MacroInvocData &invoc,
                                                  AST::InvocKind semicolon);
diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc 
b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
index bc973a009f0..a3b8e6a515f 100644
--- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
@@ -254,6 +254,8 @@ Late::visit (AST::PathInExpression &expr)
   // TODO: How do we have a nice error with `can't capture dynamic environment
   // in a function item` error here?
   // do we emit it in `get<Namespace::Labels>`?
+  if (expr.is_lang_item ())
+    return;
 
   auto resolved
     = ctx.values.resolve_path (expr.get_segments ()).or_else ([&] () {
diff --git a/gcc/testsuite/rust/compile/macros/builtin/option_env1.rs 
b/gcc/testsuite/rust/compile/macros/builtin/option_env1.rs
new file mode 100644
index 00000000000..cf9f65b0ea4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/builtin/option_env1.rs
@@ -0,0 +1,29 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+    () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod option {
+        pub enum Option<T> {
+            #[lang = "Some"]
+            Some(T),
+            #[lang = "None"]
+            None,
+        }
+    }
+}
+
+use core::option::Option;
+
+
+fn main() {
+    // Both a guaranteed-to-exist variable and a failed find should compile
+    let _: Option<&str> = option_env!("PWD");
+    let _: Option<&str> = option_env!("PROBABLY_DOESNT_EXIST");
+}
diff --git a/gcc/testsuite/rust/compile/macros/builtin/option_env2.rs 
b/gcc/testsuite/rust/compile/macros/builtin/option_env2.rs
new file mode 100644
index 00000000000..63f72545fd9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/builtin/option_env2.rs
@@ -0,0 +1,27 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+    () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod option {
+        pub enum Option<T> {
+            #[lang = "Some"]
+            Some(T),
+            #[lang = "None"]
+            None,
+        }
+    }
+}
+
+use core::option::Option;
+
+fn main() {
+    let _: Option<&str> = option_env!(42);
+    // { dg-error "argument must be a string literal" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/macros/builtin/option_env3.rs 
b/gcc/testsuite/rust/compile/macros/builtin/option_env3.rs
new file mode 100644
index 00000000000..ad6dd4c21da
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macros/builtin/option_env3.rs
@@ -0,0 +1,28 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+    () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod option {
+        pub enum Option<T> {
+            #[lang = "Some"]
+            Some(T),
+            #[lang = "None"]
+            None,
+        }
+    }
+}
+
+use core::option::Option;
+
+
+fn main() {
+    let _: Option<&str> = option_env!("A","B");
+    // { dg-error "'option_env!' takes 1 argument" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs 
b/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs
new file mode 100644
index 00000000000..56fbeaab602
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs
@@ -0,0 +1,65 @@
+// { dg-output "VALUE\r*\nVALUE\r*\n" }
+// { dg-set-compiler-env-var ENV_MACRO_TEST "VALUE" }
+
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+    () => {{}};
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+pub mod core {
+    pub mod option {
+        pub enum Option<T> {
+            #[lang = "Some"]
+            Some(T),
+            #[lang = "None"]
+            None,
+        }
+    }
+}
+
+use core::option::Option;
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s\n" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+macro_rules! env_macro_test {
+    () => { "ENV_MACRO_TEST" }
+}
+
+fn main() -> i32 {
+    let val0: Option<&'static str> = option_env!("ENV_MACRO_TEST");
+
+    
+    match val0 {
+        Option::None => {},
+        Option::Some(s) => {
+            print(s);
+        }
+    }
+
+    //eager expansion test
+    let val1: Option<&'static str> = option_env!(env_macro_test!(),);
+
+    match val1 {
+        Option::None => {},
+        Option::Some(s) => {
+            print(s);
+        }
+    }
+    0
+}
-- 
2.45.2

Reply via email to