diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc b/gcc/rust/expand/rust-macro-builtins-utility.cc index aea2902202bb..8ea42322acd4 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,82 @@ 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); + + if (env_value == nullptr) + { + std::unique_ptr none_expr + = std::unique_ptr (new AST::PathInExpression ( + b.path_in_expression ({"core", "option", "Option", "None"}))); + + auto node = AST::SingleASTNode (std::move (none_expr)); + std::vector nodes; + nodes.push_back (node); + + return AST::Fragment (nodes, std::vector> ()); + } + 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 ({"core", "option", "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 +373,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/macros/builtin/option_env1.rs b/gcc/testsuite/rust/compile/macros/builtin/option_env1.rs new file mode 100644 index 000000000000..9c5f11d963aa --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env1.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 { + Some(T), + 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 000000000000..f39a25d84935 --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env2.rs @@ -0,0 +1,25 @@ +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! option_env { + () => {} +} + +#[lang = "sized"] +trait Sized {} + +pub mod core { + pub mod option { + pub enum Option { + Some(T), + 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 000000000000..ad95c232e09c --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env3.rs @@ -0,0 +1,26 @@ +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! option_env { + () => {} +} + +#[lang = "sized"] +trait Sized {} + +pub mod core { + pub mod option { + pub enum Option { + Some(T), + 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 000000000000..89dfc5dc5a35 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs @@ -0,0 +1,63 @@ +// { 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 { + Some(T), + 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 +}