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
	* backend/rust-compile-struct-field-expr.cc: Refactor
	HIR::PathInExpression to allow for lang items.
	* hir/rust-ast-lower-expr.cc: ...
	* hir/rust-ast-lower.cc: ...
	* hir/tree/rust-hir-path.h: ...
	* hir/tree/rust-hir-path.cc: ...
	* typecheck/rust-hir-type-check-struct.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.

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 17, 2025
1 parent aacecba commit 1596237
Show file tree
Hide file tree
Showing 20 changed files with 334 additions and 18 deletions.
16 changes: 14 additions & 2 deletions gcc/rust/ast/rust-collect-lang-items.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ template <typename T>
void
CollectLangItems::maybe_add_lang_item (const T &item)
{
if (auto lang_item = get_lang_item_attr (item))
mappings.insert_lang_item_node (lang_item.value (), item.get_node_id ());
if (tl::optional<LangItem::Kind> lang_item = get_lang_item_attr (item))
{
rust_debug ("[CollectLangItems] Adding lang item %s",
LangItem::ToString (*lang_item).c_str ());
mappings.insert_lang_item_node (lang_item.value (), item.get_node_id ());
}
}

void
Expand Down Expand Up @@ -100,5 +104,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
4 changes: 2 additions & 2 deletions gcc/rust/backend/rust-compile-struct-field-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ CompileStructExprField::visit (HIR::StructExprFieldIdentifier &field)
HIR::PathIdentSegment ident_seg (field.get_field_name ().as_string ());
HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
HIR::GenericArgs::create_empty ());
HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
{});
HIR::PathInExpression expr (mappings_copy2, {seg}, tl::nullopt,
field.get_locus (), false, {});
translated = CompileExpr::Compile (expr, ctx);
}

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
5 changes: 3 additions & 2 deletions gcc/rust/hir/rust-ast-lower-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,9 @@ ASTLoweringExpr::visit (AST::IdentifierExpr &expr)
HIR::PathIdentSegment ident_seg (expr.get_ident ().as_string ());
HIR::PathExprSegment seg (mapping1, ident_seg, expr.get_locus (),
HIR::GenericArgs::create_empty ());
translated = new HIR::PathInExpression (mapping2, {seg}, expr.get_locus (),
false, expr.get_outer_attrs ());
translated = new HIR::PathInExpression (mapping2, {seg}, tl::nullopt,
expr.get_locus (), false,
expr.get_outer_attrs ());
}

void
Expand Down
18 changes: 13 additions & 5 deletions gcc/rust/hir/rust-ast-lower.cc
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,18 @@ 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, std::move (path_segments),
expr.get_lang_item (), expr.get_locus (),
expr.opening_scope_resolution ());
return;
}
auto &segments = expr.get_segments ();
for (auto &s : segments)
{
Expand All @@ -504,13 +516,9 @@ 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 (),
tl::nullopt, expr.get_locus (),
expr.opening_scope_resolution ());
}

Expand Down
4 changes: 3 additions & 1 deletion gcc/rust/hir/tree/rust-hir-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,14 @@ PathPattern::iterate_path_segments (std::function<bool (PathExprSegment &)> cb)

PathInExpression::PathInExpression (Analysis::NodeMapping mappings,
std::vector<PathExprSegment> path_segments,
tl::optional<LangItem::Kind> lang_item,
location_t locus,
bool has_opening_scope_resolution,
std::vector<AST::Attribute> outer_attrs)
: PathPattern (std::move (path_segments)),
PathExpr (std::move (mappings), std::move (outer_attrs)),
has_opening_scope_resolution (has_opening_scope_resolution), locus (locus)
has_opening_scope_resolution (has_opening_scope_resolution), locus (locus),
lang_item (lang_item)
{}

bool
Expand Down
11 changes: 11 additions & 0 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 @@ -283,6 +284,7 @@ class PathInExpression : public PathPattern, public PathExpr
// Constructor
PathInExpression (Analysis::NodeMapping mappings,
std::vector<PathExprSegment> path_segments,
tl::optional<LangItem::Kind> lang_item = tl::nullopt,
location_t locus = UNDEF_LOCATION,
bool has_opening_scope_resolution = false,
std::vector<AST::Attribute> outer_attrs
Expand Down Expand Up @@ -324,6 +326,12 @@ class PathInExpression : public PathPattern, public PathExpr
{
return mappings;
}
bool is_lang_item () { return lang_item != tl::nullopt; }
LangItem::Kind get_lang_item ()
{
rust_assert (is_lang_item ());
return *lang_item;
}

protected:
/* Use covariance to implement clone function as returning this object rather
Expand All @@ -339,6 +347,9 @@ class PathInExpression : public PathPattern, public PathExpr
{
return new PathInExpression (*this);
}

private:
tl::optional<LangItem::Kind> lang_item;
};

/* Base class for segments used in type paths - not abstract (represents an
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
42 changes: 42 additions & 0 deletions gcc/rust/typecheck/rust-hir-type-check-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "rust-hir-type-check-expr.h"
#include "rust-hir-type-check-type.h"
#include "rust-hir-type-check-item.h"
#include "rust-hir-type-check-enumitem.h"
#include "rust-hir-trait-resolve.h"
#include "rust-substitution-mapper.h"
#include "rust-hir-path-probe.h"
Expand Down Expand Up @@ -175,12 +176,53 @@ TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
expr.get_mappings (), expr.get_locus ());
}

/*
* Perform type resolution on a HIR::PathInExpression which is backed by a Lang
* Item, for example, Some, or None.
*/
void
TypeCheckExpr::handle_enum_lang_item (HIR::PathInExpression &expr)
{
// Find the lang item's definition
Analysis::Mappings &mappings = Analysis::Mappings::get ();
NodeId lang_item_node = mappings.get_lang_item_node (expr.get_lang_item ());
tl::optional<HirId> ohid = mappings.lookup_node_to_hir (lang_item_node);
rust_assert (ohid != tl::nullopt);
HirId hid1 = *ohid;
std::pair<HIR::Enum *, HIR::EnumItem *> ohi
= mappings.lookup_hir_enumitem (hid1);

HIR::Enum &enum_def = *(ohi.first);
HIR::EnumItem &variant_def = *(ohi.second);

TyTy::BaseType *bl = TypeCheckItem::Resolve (enum_def);
TyTy::VariantDef *vde
= TypeCheckEnumItem::Resolve (variant_def, INT64_MAX - 1);

context->insert_variant_definition (expr.get_mappings ().get_hirid (),
vde->get_id ());
bl = SubstMapper::InferSubst (bl, expr.get_locus ());
resolver->insert_resolved_misc (expr.get_mappings ().get_nodeid (),
expr.get_mappings ().get_nodeid ());

infered = bl;
rust_assert (bl != nullptr);
return;
}

void
TypeCheckExpr::visit (HIR::PathInExpression &expr)
{
NodeId resolved_node_id = UNKNOWN_NODEID;
size_t offset = -1;

// FIXME: This only handles enum items, but lang-item PathInExpressions might
// later refer to other types...
if (expr.is_lang_item ())
return this->handle_enum_lang_item (expr);

TyTy::BaseType *tyseg = resolve_root_path (expr, &offset, &resolved_node_id);
rust_assert (tyseg != nullptr);
if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
return;

Expand Down
4 changes: 2 additions & 2 deletions gcc/rust/typecheck/rust-hir-type-check-struct.cc
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
HIR::PathIdentSegment ident_seg (field.get_field_name ().as_string ());
HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
HIR::GenericArgs::create_empty ());
HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
{});
HIR::PathInExpression expr (mappings_copy2, {seg}, tl::nullopt,
field.get_locus (), false, {});
TyTy::BaseType *value = TypeCheckExpr::Resolve (expr);
location_t value_locus = expr.get_locus ();

Expand Down
Loading

0 comments on commit 1596237

Please sign in to comment.