Skip to content

Commit

Permalink
Add option_env! support
Browse files Browse the repository at this point in the history
gcc/rust/ChangeLog:
	* ast/rust-collect-lang-items.cc: Allow for the collection of
	EnumItem lang-items
	* ast/rust-collect-lang-items.h: ...
	* ast/rust-path.h: Change get_segments function to create an
	empty path for Lang items, which is useful for preventing
	various AST passes from recursing into the "segments" of a
	lang item path.
	* 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
	* hir/tree/rust-hir-path.h: Refactor
	HIR::PathInExpression to allow for lang items.
	* hir/rust-ast-lower-expr.cc: ...
	* hir/tree/rust-hir-path.cc: ...
	* resolve/rust-ast-resolve-path.cc: Add NR1.0 resolution support
	for lang-item paths.
	* typecheck/rust-hir-type-check-path.cc: Add type resolution
	support for lang-item paths.
	* typecheck/rust-hir-type-check-expr.h: Add prototype
	for helper function.
	* backend/rust-compile-resolve-path.cc: Add compile support for
	  lang-item paths.
	* checks/lints/rust-lint-marklive.cc: Add if guard to prevent
	  recursion into a lang-item path's empty 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
	* rust/compile/nr2/exclude: Some issues with nr2 need to be
	resolved before allowing option_env with NR2.0.

Signed-off-by: Liam Naddell <[email protected]>
  • Loading branch information
liamnaddell committed Jan 18, 2025
1 parent aacecba commit 8e8fdcc
Show file tree
Hide file tree
Showing 19 changed files with 388 additions and 22 deletions.
10 changes: 9 additions & 1 deletion gcc/rust/ast/rust-collect-lang-items.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ template <typename T>
void
CollectLangItems::maybe_add_lang_item (const T &item)
{
if (auto lang_item = get_lang_item_attr (item))
if (tl::optional<LangItem::Kind> lang_item = get_lang_item_attr (item))
mappings.insert_lang_item_node (lang_item.value (), item.get_node_id ());
}

Expand Down Expand Up @@ -100,5 +100,13 @@ CollectLangItems::visit (AST::StructStruct &item)
DefaultASTVisitor::visit (item);
}

void
CollectLangItems::visit (AST::EnumItem &item)
{
maybe_add_lang_item (item);

DefaultASTVisitor::visit (item);
}

} // namespace AST
} // namespace Rust
1 change: 1 addition & 0 deletions gcc/rust/ast/rust-collect-lang-items.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class CollectLangItems : public DefaultASTVisitor
void visit (AST::TraitItemType &item) override;
void visit (AST::Function &item) override;
void visit (AST::StructStruct &item) override;
void visit (AST::EnumItem &item) override;

private:
template <typename T> void maybe_add_lang_item (const T &item);
Expand Down
5 changes: 2 additions & 3 deletions gcc/rust/ast/rust-path.h
Original file line number Diff line number Diff line change
Expand Up @@ -622,15 +622,14 @@ class Path : public Pattern

std::string as_string () const override;

// TODO: this seems kinda dodgy
std::vector<PathExprSegment> &get_segments ()
{
rust_assert (kind == Kind::Regular);
// For lang-items, this returns an empty path.
return segments;
}
const std::vector<PathExprSegment> &get_segments () const
{
rust_assert (kind == Kind::Regular);
// For lang-items, this returns an empty path.
return segments;
}

Expand Down
19 changes: 17 additions & 2 deletions gcc/rust/backend/rust-compile-resolve-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,23 @@ ResolvePathRef::visit (HIR::QualifiedPathInExpression &expr)
void
ResolvePathRef::visit (HIR::PathInExpression &expr)
{
resolved = resolve (expr.get_final_segment ().get_segment (),
expr.get_mappings (), expr.get_locus (), false);
if (expr.is_lang_item ())
{
TyTy::BaseType *lookup = nullptr;
bool ok
= ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
&lookup);
rust_assert (ok);

tree t = attempt_constructor_expression_lookup (lookup, ctx,
expr.get_mappings (),
expr.get_locus ());
TREE_USED (t) = 1;
resolved = t;
}
else
resolved = resolve (expr.get_final_segment ().get_segment (),
expr.get_mappings (), expr.get_locus (), false);
}

tree
Expand Down
7 changes: 4 additions & 3 deletions gcc/rust/checks/lints/rust-lint-marklive.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ MarkLive::visit (HIR::PathInExpression &expr)
{
// We should iterate every path segment in order to mark the struct which
// is used in expression like Foo::bar(), we should mark the Foo alive.
expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
return visit_path_segment (seg);
});
if (!expr.is_lang_item ())
expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
return visit_path_segment (seg);
});

// after iterate the path segments, we should mark functions and associated
// functions alive.
Expand Down
78 changes: 78 additions & 0 deletions 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,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)
Expand Down
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
15 changes: 11 additions & 4 deletions gcc/rust/hir/rust-ast-lower.cc
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,17 @@ void
ASTLowerPathInExpression::visit (AST::PathInExpression &expr)
{
std::vector<HIR::PathExprSegment> path_segments;
auto crate_num = mappings.get_current_crate ();
Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);
if (expr.get_path_kind () == AST::Path::Kind::LangItem)
{
translated = new HIR::PathInExpression (mapping, expr.get_lang_item (),
expr.get_locus (),
expr.opening_scope_resolution ());
return;
}
auto &segments = expr.get_segments ();
for (auto &s : segments)
{
Expand All @@ -504,10 +515,6 @@ ASTLowerPathInExpression::visit (AST::PathInExpression &expr)
HIR::PathExprSegment *lowered_seg = &path_segments.back ();
mappings.insert_hir_path_expr_seg (lowered_seg);
}
auto crate_num = mappings.get_current_crate ();
Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
mappings.get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);

translated = new HIR::PathInExpression (mapping, std::move (path_segments),
expr.get_locus (),
Expand Down
12 changes: 12 additions & 0 deletions gcc/rust/hir/tree/rust-hir-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,15 @@ PathExprSegment::operator= (PathExprSegment const &other)
void
PathPattern::iterate_path_segments (std::function<bool (PathExprSegment &)> cb)
{
rust_assert (!is_lang_item ());
for (auto it = segments.begin (); it != segments.end (); it++)
{
if (!cb (*it))
return;
}
}

// Path constructor
PathInExpression::PathInExpression (Analysis::NodeMapping mappings,
std::vector<PathExprSegment> path_segments,
location_t locus,
Expand All @@ -150,6 +152,16 @@ PathInExpression::PathInExpression (Analysis::NodeMapping mappings,
has_opening_scope_resolution (has_opening_scope_resolution), locus (locus)
{}

// Lang-item constructor.
PathInExpression::PathInExpression (Analysis::NodeMapping mappings,
LangItem::Kind lang_item, location_t locus,
bool has_opening_scope_resolution,
std::vector<AST::Attribute> outer_attrs)
: PathPattern (lang_item),
PathExpr (std::move (mappings), std::move (outer_attrs)),
has_opening_scope_resolution (has_opening_scope_resolution), locus (locus)
{}

bool
PathInExpression::is_self () const

Expand Down
57 changes: 49 additions & 8 deletions gcc/rust/hir/tree/rust-hir-path.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rust-hir-type-no-bounds.h"
#include "rust-hir-pattern-abstract.h"
#include "rust-hir-expr-abstract.h"
#include "optional.h"

namespace Rust {
namespace HIR {
Expand Down Expand Up @@ -231,12 +232,15 @@ class PathExprSegment
class PathPattern : public Pattern
{
std::vector<PathExprSegment> segments;
tl::optional<LangItem::Kind> lang_item;

protected:
PathPattern (std::vector<PathExprSegment> segments)
: segments (std::move (segments))
: segments (std::move (segments)), lang_item (tl::nullopt)
{}

PathPattern (LangItem::Kind li) : segments ({}), lang_item (li) {}

// Returns whether path has segments.
bool has_segments () const { return !segments.empty (); }

Expand All @@ -246,23 +250,53 @@ class PathPattern : public Pattern
convert_to_simple_path (bool with_opening_scope_resolution) const;

public:
bool is_lang_item () const { return lang_item != tl::nullopt; }
LangItem::Kind get_lang_item () const
{
rust_assert (lang_item != tl::nullopt);
return *lang_item;
}
/* Returns whether the path is a single segment (excluding qualified path
* initial as segment). */
bool is_single_segment () const { return segments.size () == 1; }
bool is_single_segment () const
{
rust_assert (!is_lang_item ());
return segments.size () == 1;
}

std::string as_string () const override;

void iterate_path_segments (std::function<bool (PathExprSegment &)> cb);

size_t get_num_segments () const { return segments.size (); }
size_t get_num_segments () const
{
rust_assert (!is_lang_item ());
return segments.size ();
}

std::vector<PathExprSegment> &get_segments () { return segments; }
std::vector<PathExprSegment> &get_segments ()
{
rust_assert (!is_lang_item ());
return segments;
}

const std::vector<PathExprSegment> &get_segments () const { return segments; }
const std::vector<PathExprSegment> &get_segments () const
{
rust_assert (!is_lang_item ());
return segments;
}

PathExprSegment &get_root_seg () { return segments.at (0); }
PathExprSegment &get_root_seg ()
{
rust_assert (!is_lang_item ());
return segments.at (0);
}

const PathExprSegment &get_final_segment () const { return segments.back (); }
const PathExprSegment &get_final_segment () const
{
rust_assert (!is_lang_item ());
return segments.back ();
}

PatternType get_pattern_type () const override final
{
Expand All @@ -280,14 +314,21 @@ class PathInExpression : public PathPattern, public PathExpr
public:
std::string as_string () const override;

// Constructor
// Path constructor
PathInExpression (Analysis::NodeMapping mappings,
std::vector<PathExprSegment> path_segments,
location_t locus = UNDEF_LOCATION,
bool has_opening_scope_resolution = false,
std::vector<AST::Attribute> outer_attrs
= std::vector<AST::Attribute> ());

// Lang-item constructor.
PathInExpression (Analysis::NodeMapping mappings, LangItem::Kind lang_item,
location_t locus = UNDEF_LOCATION,
bool has_opening_scope_resolution = false,
std::vector<AST::Attribute> outer_attrs
= std::vector<AST::Attribute> ());

// Creates an error state path in expression.
static PathInExpression create_error ()
{
Expand Down
6 changes: 6 additions & 0 deletions gcc/rust/resolve/rust-ast-resolve-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ ResolvePath::resolve_path (AST::PathInExpression &expr)
NodeId resolved_node_id = UNKNOWN_NODEID;
NodeId module_scope_id = resolver->peek_current_module_scope ();
NodeId previous_resolved_node_id = module_scope_id;
if (expr.get_path_kind () == AST::Path::Kind::LangItem)
{
resolved_node_id
= Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
return resolved_node_id;
}
for (size_t i = 0; i < expr.get_segments ().size (); i++)
{
auto &segment = expr.get_segments ().at (i);
Expand Down
2 changes: 2 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-check-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class TypeCheckExpr : private TypeCheckBase, private HIR::HIRExpressionVisitor
validate_arithmetic_type (const TyTy::BaseType *tyty,
HIR::ArithmeticOrLogicalExpr::ExprType expr_type);

void handle_enum_lang_item (HIR::PathInExpression &expr);

/* The return value of TypeCheckExpr::Resolve */
TyTy::BaseType *infered;
};
Expand Down
Loading

0 comments on commit 8e8fdcc

Please sign in to comment.