diff --git a/gcc/rust/ast/rust-collect-lang-items.cc b/gcc/rust/ast/rust-collect-lang-items.cc index 11c3297d2a9d..d1ddfc90b4e6 100644 --- a/gcc/rust/ast/rust-collect-lang-items.cc +++ b/gcc/rust/ast/rust-collect-lang-items.cc @@ -64,7 +64,7 @@ template void CollectLangItems::maybe_add_lang_item (const T &item) { - if (auto lang_item = get_lang_item_attr (item)) + if (tl::optional lang_item = get_lang_item_attr (item)) mappings.insert_lang_item_node (lang_item.value (), item.get_node_id ()); } @@ -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 diff --git a/gcc/rust/ast/rust-collect-lang-items.h b/gcc/rust/ast/rust-collect-lang-items.h index 39cb4be31a0c..ddb34a914ed7 100644 --- a/gcc/rust/ast/rust-collect-lang-items.h +++ b/gcc/rust/ast/rust-collect-lang-items.h @@ -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 void maybe_add_lang_item (const T &item); diff --git a/gcc/rust/ast/rust-path.h b/gcc/rust/ast/rust-path.h index c5afc8f883d4..5d8fa7af7ac6 100644 --- a/gcc/rust/ast/rust-path.h +++ b/gcc/rust/ast/rust-path.h @@ -622,15 +622,14 @@ class Path : public Pattern std::string as_string () const override; - // TODO: this seems kinda dodgy std::vector &get_segments () { - rust_assert (kind == Kind::Regular); + // For lang-items, this returns an empty path. return segments; } const std::vector &get_segments () const { - rust_assert (kind == Kind::Regular); + // For lang-items, this returns an empty path. return segments; } diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc index 049b0d86b523..ac04bf443b1b 100644 --- a/gcc/rust/backend/rust-compile-resolve-path.cc +++ b/gcc/rust/backend/rust-compile-resolve-path.cc @@ -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 diff --git a/gcc/rust/checks/lints/rust-lint-marklive.cc b/gcc/rust/checks/lints/rust-lint-marklive.cc index 91550c7a568f..b0d0f1a5b1c9 100644 --- a/gcc/rust/checks/lints/rust-lint-marklive.cc +++ b/gcc/rust/checks/lints/rust-lint-marklive.cc @@ -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. diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc b/gcc/rust/expand/rust-macro-builtins-utility.cc index ff64879c5eae..28829a18f956 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,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 +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) + { + auto none_expr = std::unique_ptr ( + new AST::PathInExpression (LangItem::Kind::OPTION_NONE, {}, + invoc_locus)); + + 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 (LangItem::Kind::OPTION_SOME, {}, + invoc_locus)), + 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) 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/rust/hir/rust-ast-lower.cc b/gcc/rust/hir/rust-ast-lower.cc index e33f5794a621..136c6c3d7e2c 100644 --- a/gcc/rust/hir/rust-ast-lower.cc +++ b/gcc/rust/hir/rust-ast-lower.cc @@ -495,6 +495,17 @@ void ASTLowerPathInExpression::visit (AST::PathInExpression &expr) { std::vector 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) { @@ -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 (), diff --git a/gcc/rust/hir/tree/rust-hir-path.cc b/gcc/rust/hir/tree/rust-hir-path.cc index 7db2b25b5aaf..ac4ee0a28436 100644 --- a/gcc/rust/hir/tree/rust-hir-path.cc +++ b/gcc/rust/hir/tree/rust-hir-path.cc @@ -133,6 +133,7 @@ PathExprSegment::operator= (PathExprSegment const &other) void PathPattern::iterate_path_segments (std::function cb) { + rust_assert (!is_lang_item ()); for (auto it = segments.begin (); it != segments.end (); it++) { if (!cb (*it)) @@ -140,6 +141,7 @@ PathPattern::iterate_path_segments (std::function cb) } } +// Path constructor PathInExpression::PathInExpression (Analysis::NodeMapping mappings, std::vector path_segments, location_t locus, @@ -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 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 diff --git a/gcc/rust/hir/tree/rust-hir-path.h b/gcc/rust/hir/tree/rust-hir-path.h index f622addcc64b..3cb8567443a0 100644 --- a/gcc/rust/hir/tree/rust-hir-path.h +++ b/gcc/rust/hir/tree/rust-hir-path.h @@ -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 { @@ -231,12 +232,15 @@ class PathExprSegment class PathPattern : public Pattern { std::vector segments; + tl::optional lang_item; protected: PathPattern (std::vector 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 (); } @@ -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 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 &get_segments () { return segments; } + std::vector &get_segments () + { + rust_assert (!is_lang_item ()); + return segments; + } - const std::vector &get_segments () const { return segments; } + const std::vector &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 { @@ -280,7 +314,7 @@ class PathInExpression : public PathPattern, public PathExpr public: std::string as_string () const override; - // Constructor + // Path constructor PathInExpression (Analysis::NodeMapping mappings, std::vector path_segments, location_t locus = UNDEF_LOCATION, @@ -288,6 +322,13 @@ class PathInExpression : public PathPattern, public PathExpr std::vector outer_attrs = std::vector ()); + // Lang-item constructor. + PathInExpression (Analysis::NodeMapping mappings, LangItem::Kind lang_item, + location_t locus = UNDEF_LOCATION, + bool has_opening_scope_resolution = false, + std::vector outer_attrs + = std::vector ()); + // Creates an error state path in expression. static PathInExpression create_error () { diff --git a/gcc/rust/resolve/rust-ast-resolve-path.cc b/gcc/rust/resolve/rust-ast-resolve-path.cc index b50f94f6c283..25415275b4c1 100644 --- a/gcc/rust/resolve/rust-ast-resolve-path.cc +++ b/gcc/rust/resolve/rust-ast-resolve-path.cc @@ -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); diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h index 3ceef7a521e7..a72abe0b2eb9 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.h +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h @@ -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; }; diff --git a/gcc/rust/typecheck/rust-hir-type-check-path.cc b/gcc/rust/typecheck/rust-hir-type-check-path.cc index 9e156725d5b8..faf67443d0db 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-path.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-path.cc @@ -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" @@ -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 ohid = mappings.lookup_node_to_hir (lang_item_node); + rust_assert (ohid != tl::nullopt); + HirId hid1 = *ohid; + std::pair 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; 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..cf9f65b0ea4a --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env1.rs @@ -0,0 +1,29 @@ +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! option_env { + () => {} +} + +#[lang = "sized"] +trait Sized {} + +pub mod core { + pub mod option { + pub enum Option { + #[lang = "Some"] + Some(T), + #[lang = "None"] + 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..63f72545fd9d --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env2.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 { + #[lang = "Some"] + Some(T), + #[lang = "None"] + 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..ad6dd4c21da2 --- /dev/null +++ b/gcc/testsuite/rust/compile/macros/builtin/option_env3.rs @@ -0,0 +1,28 @@ +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! option_env { + () => {} +} + +#[lang = "sized"] +trait Sized {} + +pub mod core { + pub mod option { + pub enum Option { + #[lang = "Some"] + Some(T), + #[lang = "None"] + 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/compile/nr2/exclude b/gcc/testsuite/rust/compile/nr2/exclude index 0f482df2f005..b40f71b3f399 100644 --- a/gcc/testsuite/rust/compile/nr2/exclude +++ b/gcc/testsuite/rust/compile/nr2/exclude @@ -143,4 +143,5 @@ additional-trait-bounds2.rs auto_traits3.rs issue-3140.rs cmp1.rs +macros/builtin/option_env1.rs # please don't delete the trailing newline 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..56fbeaab602d --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/builtin_macro_option_env.rs @@ -0,0 +1,65 @@ +// { 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 { + #[lang = "Some"] + Some(T), + #[lang = "None"] + 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 +}