diff --git a/gcc/rust/ast/rust-collect-lang-items.cc b/gcc/rust/ast/rust-collect-lang-items.cc index 11c3297d2a9..7501358ee97 100644 --- a/gcc/rust/ast/rust-collect-lang-items.cc +++ b/gcc/rust/ast/rust-collect-lang-items.cc @@ -64,8 +64,12 @@ template 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 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 @@ -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 diff --git a/gcc/rust/ast/rust-collect-lang-items.h b/gcc/rust/ast/rust-collect-lang-items.h index 39cb4be31a0..ddb34a914ed 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 c5afc8f883d..5d8fa7af7ac 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-struct-field-expr.cc b/gcc/rust/backend/rust-compile-struct-field-expr.cc index e10ea57c285..391e3f7d95d 100644 --- a/gcc/rust/backend/rust-compile-struct-field-expr.cc +++ b/gcc/rust/backend/rust-compile-struct-field-expr.cc @@ -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); } diff --git a/gcc/rust/expand/rust-macro-builtins-utility.cc b/gcc/rust/expand/rust-macro-builtins-utility.cc index ff64879c5ea..28829a18f95 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 07a1f3cc8d1..59b8f6a4aba 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 b0c4a648bd1..1d17b438497 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-expr.cc b/gcc/rust/hir/rust-ast-lower-expr.cc index 1b061b3d50d..4cad983f71d 100644 --- a/gcc/rust/hir/rust-ast-lower-expr.cc +++ b/gcc/rust/hir/rust-ast-lower-expr.cc @@ -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 diff --git a/gcc/rust/hir/rust-ast-lower.cc b/gcc/rust/hir/rust-ast-lower.cc index e33f5794a62..20c0c915389 100644 --- a/gcc/rust/hir/rust-ast-lower.cc +++ b/gcc/rust/hir/rust-ast-lower.cc @@ -495,6 +495,18 @@ 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, 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) { @@ -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 ()); } diff --git a/gcc/rust/hir/tree/rust-hir-path.cc b/gcc/rust/hir/tree/rust-hir-path.cc index 7db2b25b5aa..0519c352d5c 100644 --- a/gcc/rust/hir/tree/rust-hir-path.cc +++ b/gcc/rust/hir/tree/rust-hir-path.cc @@ -142,12 +142,14 @@ PathPattern::iterate_path_segments (std::function cb) PathInExpression::PathInExpression (Analysis::NodeMapping mappings, std::vector path_segments, + tl::optional lang_item, location_t locus, bool has_opening_scope_resolution, std::vector 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 diff --git a/gcc/rust/hir/tree/rust-hir-path.h b/gcc/rust/hir/tree/rust-hir-path.h index f622addcc64..3925c5f10ee 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 { @@ -283,6 +284,7 @@ class PathInExpression : public PathPattern, public PathExpr // Constructor PathInExpression (Analysis::NodeMapping mappings, std::vector path_segments, + tl::optional lang_item = tl::nullopt, location_t locus = UNDEF_LOCATION, bool has_opening_scope_resolution = false, std::vector outer_attrs @@ -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 @@ -339,6 +347,9 @@ class PathInExpression : public PathPattern, public PathExpr { return new PathInExpression (*this); } + +private: + tl::optional lang_item; }; /* Base class for segments used in type paths - not abstract (represents an diff --git a/gcc/rust/resolve/rust-ast-resolve-path.cc b/gcc/rust/resolve/rust-ast-resolve-path.cc index b50f94f6c28..25415275b4c 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 3ceef7a521e..a72abe0b2eb 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 9e156725d5b..faf67443d0d 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/rust/typecheck/rust-hir-type-check-struct.cc b/gcc/rust/typecheck/rust-hir-type-check-struct.cc index 1931f08ff4d..4322546cb3c 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-struct.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-struct.cc @@ -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 (); 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 00000000000..cf9f65b0ea4 --- /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 00000000000..63f72545fd9 --- /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 00000000000..ad6dd4c21da --- /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 0f482df2f00..b40f71b3f39 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 00000000000..56fbeaab602 --- /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 +}