Skip to content

Commit

Permalink
[ #1806 ] Add option_env support
Browse files Browse the repository at this point in the history
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

gcc/testsuite/ChangeLog:
	* rust/compile/option_env1.rs:
	Add success case for option_env
	* rust/compile/option_env2.rs:
	Add failure case for option_env
	* rust/compile/option_env3.rs:
	Add second failure case for option_env
	* rust/execute/torture/builtin_macro_option_env.rs:
	Add execution case for option_env

Signed-off-by: Liam Naddell <[email protected]>
  • Loading branch information
liamnaddell committed Jul 23, 2024
1 parent bfee9e0 commit 6262158
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 2 deletions.
83 changes: 82 additions & 1 deletion gcc/rust/expand/rust-macro-builtins-utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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<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);
std::string option = "Option";

if (env_value == nullptr)
{
std::string none = "None";
std::unique_ptr<AST::Expr> none_expr
= std::unique_ptr<AST::Expr> (new AST::PathInExpression (
b.path_in_expression (std::vector ({option, none}))));

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::string some = "Some";
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 (b.path_in_expression (
std::vector<std::string> ({option, some})))),
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)
Expand Down Expand Up @@ -296,4 +377,4 @@ MacroBuiltin::stringify_handler (location_t invoc_locus,
return AST::Fragment ({node}, std::move (token));
}

} // namespace Rust
} // namespace Rust
2 changes: 1 addition & 1 deletion gcc/rust/expand/rust-macro-builtins.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
4 changes: 4 additions & 0 deletions gcc/rust/expand/rust-macro-builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ class MacroBuiltin
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);
Expand Down
21 changes: 21 additions & 0 deletions gcc/testsuite/rust/compile/option_env1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(rustc_attrs)]

#[rustc_builtin_macro]
macro_rules! option_env {
() => {}
}

#[lang = "sized"]
trait Sized {}

enum Option<T> {
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");
}
20 changes: 20 additions & 0 deletions gcc/testsuite/rust/compile/option_env2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![feature(rustc_attrs)]

#[rustc_builtin_macro]
macro_rules! option_env {
() => {}
}

#[lang = "sized"]
trait Sized {}

enum Option<T> {
Some(T),
None,
}


fn main() {
let _: Option<&str> = option_env!(42);
// { dg-error "argument must be a string literal" "" { target *-*-* } .-1 }
}
20 changes: 20 additions & 0 deletions gcc/testsuite/rust/compile/option_env3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![feature(rustc_attrs)]

#[rustc_builtin_macro]
macro_rules! option_env {
() => {}
}

#[lang = "sized"]
trait Sized {}

enum Option<T> {
Some(T),
None,
}


fn main() {
let _: Option<&str> = option_env!("A","B");
// { dg-error "'option_env!' takes 1 argument" "" { target *-*-* } .-1 }
}
57 changes: 57 additions & 0 deletions gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs
Original file line number Diff line number Diff line change
@@ -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<T> {
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
}

0 comments on commit 6262158

Please sign in to comment.