diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc b/gcc/rust/expand/rust-macro-builtins-utility.cc
index aea2902202bb..ab32d45e313d 100644
--- a/gcc/rust/expand/rust-macro-builtins-utility.cc
+++ b/gcc/rust/expand/rust-macro-builtins-utility.cc
@@ -17,6 +17,7 @@
// .
#include "rust-fmt.h"
+#include "rust-ast-builder.h"
#include "rust-macro-builtins.h"
#include "rust-macro-builtins-helpers.h"
@@ -226,6 +227,86 @@ 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
+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 parser (lex);
+
+ auto last_token_id = macro_end_token (invoc_token_tree, parser);
+ std::unique_ptr 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, "% 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);
+ std::string option = "Option";
+
+ if (env_value == nullptr)
+ {
+ std::string none = "None";
+ std::unique_ptr none_expr
+ = std::unique_ptr (new AST::PathInExpression (
+ b.path_in_expression (std::vector ({option, none}))));
+
+ auto node = AST::SingleASTNode (std::move (none_expr));
+ std::vector nodes;
+ nodes.push_back (node);
+
+ return AST::Fragment (nodes, std::vector> ());
+ }
+ std::string some = "Some";
+ std::vector> args;
+ args.push_back (b.literal_string (env_value));
+
+ std::unique_ptr some_expr
+ = b.call (std::unique_ptr (
+ new AST::PathInExpression (b.path_in_expression (
+ std::vector ({option, some})))),
+ std::move (args));
+
+ auto node = AST::SingleASTNode (std::move (some_expr));
+
+ std::vector nodes;
+ nodes.push_back (node);
+
+ return AST::Fragment (nodes, std::vector> ());
+}
+
tl::optional
MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
AST::InvocKind semicolon)
@@ -296,4 +377,4 @@ MacroBuiltin::stringify_handler (location_t invoc_locus,
return AST::Fragment ({node}, std::move (token));
}
-} // namespace Rust
\ No newline at end of file
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc
index 07a1f3cc8d1b..59b8f6a4abab 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -120,8 +120,8 @@ std::unordered_map
{"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 b0c4a648bd17..1d17b438497c 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -159,6 +159,10 @@ class MacroBuiltin
AST::MacroInvocData &invoc,
AST::InvocKind semicolon);
+ static tl::optional
+ option_env_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
+ AST::InvocKind semicolon);
+
static tl::optional cfg_handler (location_t invoc_locus,
AST::MacroInvocData &invoc,
AST::InvocKind semicolon);
diff --git a/gcc/testsuite/rust/compile/option_env1.rs b/gcc/testsuite/rust/compile/option_env1.rs
new file mode 100644
index 000000000000..5f48b7d0ade4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/option_env1.rs
@@ -0,0 +1,21 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+ () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+enum Option {
+ Some(T),
+ None,
+}
+
+
+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/option_env2.rs b/gcc/testsuite/rust/compile/option_env2.rs
new file mode 100644
index 000000000000..40e24081ac1a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/option_env2.rs
@@ -0,0 +1,20 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+ () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+enum Option {
+ Some(T),
+ None,
+}
+
+
+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/option_env3.rs b/gcc/testsuite/rust/compile/option_env3.rs
new file mode 100644
index 000000000000..aee668c25177
--- /dev/null
+++ b/gcc/testsuite/rust/compile/option_env3.rs
@@ -0,0 +1,20 @@
+#![feature(rustc_attrs)]
+
+#[rustc_builtin_macro]
+macro_rules! option_env {
+ () => {}
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+enum Option {
+ Some(T),
+ None,
+}
+
+
+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 000000000000..e84a0955e4be
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs
@@ -0,0 +1,57 @@
+// { 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 {}
+
+enum Option {
+ Some(T),
+ None
+}
+
+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
+}