]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: expand: Add parser for offset_of!() and builtin resolution.
authorArthur Cohen <arthur.cohen@embecosm.com>
Thu, 24 Jul 2025 09:32:43 +0000 (11:32 +0200)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 5 Aug 2025 14:36:59 +0000 (16:36 +0200)
gcc/rust/ChangeLog:

* Make-lang.in: Compile the offset_of handler.
* lang.opt: Add -frust-assume-builtin-offset-of option.
* ast/rust-ast.h: Add has_str() for const_TokenPtr.
* expand/rust-macro-builtins.cc: Map offset_of as builtin.
* expand/rust-macro-builtins.h: Declare it.
* expand/rust-macro-expand.cc (MacroExpander::expand_invoc): Add hack for calling builtin
offset_of!().
* resolve/rust-early-name-resolver-2.0.cc (Early::visit):  Likewise.
* expand/rust-macro-builtins-offset-of.cc: New file.

gcc/testsuite/ChangeLog:

* rust/compile/offset_of1.rs: New test.

gcc/rust/Make-lang.in
gcc/rust/ast/rust-ast.h
gcc/rust/expand/rust-macro-builtins-offset-of.cc [new file with mode: 0644]
gcc/rust/expand/rust-macro-builtins.cc
gcc/rust/expand/rust-macro-builtins.h
gcc/rust/expand/rust-macro-expand.cc
gcc/rust/lang.opt
gcc/rust/resolve/rust-early-name-resolver-2.0.cc
gcc/testsuite/rust/compile/offset_of1.rs [new file with mode: 0644]

index 12c9a427143fc33e76ce27e883af94720f28627d..98590d20b8ce351ef5636f4eb18fb1be81b92b01 100644 (file)
@@ -115,6 +115,7 @@ GRS_OBJS = \
     rust/rust-macro-builtins-log-debug.o \
     rust/rust-macro-builtins-test-bench.o \
     rust/rust-macro-builtins-format-args.o \
+    rust/rust-macro-builtins-offset-of.o \
     rust/rust-macro-builtins-location.o \
     rust/rust-macro-builtins-include.o \
     rust/rust-token-tree-desugar.o \
index 93d4ff132e505716441066519c8e48a1cf896d10..a367c5dd0c3006fb709a6122275bf478778045c1 100644 (file)
@@ -283,6 +283,7 @@ public:
   std::vector<std::unique_ptr<Token>> to_token_stream () const override;
 
   TokenId get_id () const { return tok_ref->get_id (); }
+  bool has_str () const { return tok_ref->has_str (); }
   const std::string &get_str () const { return tok_ref->get_str (); }
 
   location_t get_locus () const { return tok_ref->get_locus (); }
diff --git a/gcc/rust/expand/rust-macro-builtins-offset-of.cc b/gcc/rust/expand/rust-macro-builtins-offset-of.cc
new file mode 100644 (file)
index 0000000..53efe74
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2020-2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "optional.h"
+#include "rust-ast-fragment.h"
+#include "rust-ast.h"
+#include "rust-builtin-ast-nodes.h"
+#include "rust-diagnostics.h"
+#include "rust-macro-builtins-helpers.h"
+#include "rust-macro-builtins.h"
+#include "rust-macro-invoc-lexer.h"
+#include "rust-parse.h"
+
+namespace Rust {
+
+tl::optional<AST::Fragment>
+MacroBuiltin::offset_of_handler (location_t invoc_locus,
+                                AST::MacroInvocData &invoc,
+                                AST::InvocKind semicolon)
+{
+  MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto last_token = macro_end_token (invoc.get_delim_tok_tree (), parser);
+
+  auto type = parser.parse_type ();
+
+  // if we don't see a type, there might be an eager macro expansion missing
+  // FIXME: handle that
+  if (!type)
+    {
+      rust_error_at (invoc_locus, "could not parse type argument for %qs",
+                    "offset_of");
+
+      // we skip so we can still parse the field arg and check if it is correct
+      while (parser.peek_current_token ()->get_id () != COMMA
+            && parser.peek_current_token ()->get_id () != last_token)
+       parser.skip_token ();
+    }
+
+  parser.skip_token (COMMA);
+
+  auto field_tok = parser.parse_identifier_or_keyword_token ();
+  auto invalid_field = !field_tok || !field_tok->has_str ();
+
+  if (invalid_field)
+    rust_error_at (invoc_locus, "could not parse field argument for %qs",
+                  "offset_of");
+
+  if (!type || invalid_field)
+    return tl::nullopt;
+
+  auto field = Identifier (field_tok->get_str ());
+
+  // FIXME: Do we need to do anything to handle the optional comma at the end?
+  parser.maybe_skip_token (COMMA);
+
+  return AST::Fragment ({AST::SingleASTNode (std::make_unique<AST::OffsetOf> (
+                         std::move (type), field, invoc_locus))},
+                       invoc.get_delim_tok_tree ().to_token_stream ());
+}
+
+} // namespace Rust
index b58ed71ba4efdc1ceb32c978a285f9d4ef07dc06..a7ae220798525a98bf128ae364bd7ee97e9a1e50 100644 (file)
@@ -162,6 +162,9 @@ std::unordered_map<std::string, AST::MacroTranscriberFunc>
     {"Ord", MacroBuiltin::proc_macro_builtin},
     {"PartialOrd", MacroBuiltin::proc_macro_builtin},
     {"Hash", MacroBuiltin::proc_macro_builtin},
+    /* offset_of is not declared in Rust 1.49 but still needed for
+       Rust-for-Linux, so we still create a transcriber and warn the user */
+    {"offset_of", MacroBuiltin::offset_of_handler},
 };
 
 tl::optional<BuiltinMacro>
index 541e95636b14bec312e245a66f20047049e68b61..b6c29074ad9bc60d95716c936c583478c734966a 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef RUST_MACRO_BUILTINS_H
 #define RUST_MACRO_BUILTINS_H
 
+#include "optional.h"
 #include "rust-ast.h"
 #include "rust-builtin-ast-nodes.h"
 #include "rust-ast-fragment.h"
@@ -188,6 +189,9 @@ public:
   format_args_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
                       AST::InvocKind semicolon, AST::FormatArgs::Newline nl);
 
+  static tl::optional<AST::Fragment>
+  offset_of_handler (location_t, AST::MacroInvocData &, AST::InvocKind);
+
   static tl::optional<AST::Fragment> sorry (location_t invoc_locus,
                                            AST::MacroInvocData &invoc,
                                            AST::InvocKind semicolon);
index 475ad56a3640c0d4ce2dde08cb0ee6485b13fefc..4c54ceff98ac8b3518ef485de44a03e32b5c73d6 100644 (file)
@@ -19,6 +19,7 @@
 #include "rust-macro-expand.h"
 #include "optional.h"
 #include "rust-ast-fragment.h"
+#include "rust-macro-builtins.h"
 #include "rust-macro-substitute-ctx.h"
 #include "rust-ast-full.h"
 #include "rust-ast-visitor.h"
@@ -287,6 +288,26 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc,
   // lookup the rules
   auto rules_def = mappings.lookup_macro_invocation (invoc);
 
+  // We special case the `offset_of!()` macro if the flag is here and manually
+  // resolve to the builtin transcriber we have specified
+  auto assume_builtin_offset_of
+    = flag_assume_builtin_offset_of
+      && (invoc.get_invoc_data ().get_path ().as_string () == "offset_of")
+      && !rules_def;
+
+  // TODO: This is *massive hack* which should be removed as we progress to
+  // Rust 1.71 when offset_of gets added to core
+  if (assume_builtin_offset_of)
+    {
+      fragment = MacroBuiltin::offset_of_handler (invoc.get_locus (),
+                                                 invoc_data, semicolon)
+                  .value_or (AST::Fragment::create_empty ());
+
+      set_expanded_fragment (std::move (fragment));
+
+      return;
+    }
+
   // If there's no rule associated with the invocation, we can simply return
   // early. The early name resolver will have already emitted an error.
   if (!rules_def)
index 4c48816affa52a5e208b681610384633685bb621..d9824f1a5ac29eb01ba396fd792bbd504e89374a 100644 (file)
@@ -229,4 +229,8 @@ frust-overflow-checks
 Rust Var(flag_overflow_checks) Init(1)
 Enable the overflow checks in code generation
 
+frust-assume-builtin-offset-of
+Rust Var(flag_assume_builtin_offset_of)
+Define a built-in offset_of macro in the compiler and assume it is present
+
 ; This comment is to ensure we retain the blank line above.
index bb93d95fe8e7db0b55f40e1af4985ec234f7bd74..4fd1dd265b63cec51fa1248bf78bac8cce9dee9c 100644 (file)
@@ -256,6 +256,11 @@ Early::visit (AST::MacroInvocation &invoc)
 {
   auto &path = invoc.get_invoc_data ().get_path ();
 
+  // We special case the `offset_of!()` macro if the flag is here, otherwise
+  // we accept whatever `offset_of!()` definition we resolved to.
+  auto resolve_offset_of
+    = flag_assume_builtin_offset_of && (path.as_string () == "offset_of");
+
   if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
     for (auto &pending_invoc : invoc.get_pending_eager_invocations ())
       pending_invoc->accept_vis (*this);
@@ -279,12 +284,14 @@ Early::visit (AST::MacroInvocation &invoc)
   if (!definition.has_value ())
     definition = ctx.resolve_path (path, Namespace::Macros);
 
-  // if the definition still does not have a value, then it's an error
+  // if the definition still does not have a value, then it's an error - unless
+  // we should automatically resolve offset_of!() calls
   if (!definition.has_value ())
     {
-      collect_error (Error (invoc.get_locus (), ErrorCode::E0433,
-                           "could not resolve macro invocation %qs",
-                           path.as_string ().c_str ()));
+      if (!resolve_offset_of)
+       collect_error (Error (invoc.get_locus (), ErrorCode::E0433,
+                             "could not resolve macro invocation %qs",
+                             path.as_string ().c_str ()));
       return;
     }
 
diff --git a/gcc/testsuite/rust/compile/offset_of1.rs b/gcc/testsuite/rust/compile/offset_of1.rs
new file mode 100644 (file)
index 0000000..5b79699
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-additional-options "-frust-compile-until=lowering -frust-assume-builtin-offset-of" }
+
+pub struct Foo {
+    a: i32,
+}
+
+fn main() {
+    let _ = offset_of!(Foo, a); // valid
+    let _ = offset_of!("bloop", a); // { dg-error "could not parse type" }
+    let _ = offset_of!(Foo, 15); // { dg-error "could not parse field" }
+}