From: Arthur Cohen <arthur.co...@embecosm.com>

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                         |  1 +
 gcc/rust/ast/rust-ast.h                       |  1 +
 .../expand/rust-macro-builtins-offset-of.cc   | 78 +++++++++++++++++++
 gcc/rust/expand/rust-macro-builtins.cc        |  3 +
 gcc/rust/expand/rust-macro-builtins.h         |  4 +
 gcc/rust/expand/rust-macro-expand.cc          | 21 +++++
 gcc/rust/lang.opt                             |  4 +
 .../resolve/rust-early-name-resolver-2.0.cc   | 15 +++-
 gcc/testsuite/rust/compile/offset_of1.rs      | 11 +++
 9 files changed, 134 insertions(+), 4 deletions(-)
 create mode 100644 gcc/rust/expand/rust-macro-builtins-offset-of.cc
 create mode 100644 gcc/testsuite/rust/compile/offset_of1.rs

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 12c9a427143..98590d20b8c 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -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 \
diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h
index 93d4ff132e5..a367c5dd0c3 100644
--- a/gcc/rust/ast/rust-ast.h
+++ b/gcc/rust/ast/rust-ast.h
@@ -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
index 00000000000..53efe74b228
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-builtins-offset-of.cc
@@ -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
diff --git a/gcc/rust/expand/rust-macro-builtins.cc 
b/gcc/rust/expand/rust-macro-builtins.cc
index b58ed71ba4e..a7ae2207985 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -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>
diff --git a/gcc/rust/expand/rust-macro-builtins.h 
b/gcc/rust/expand/rust-macro-builtins.h
index 541e95636b1..b6c29074ad9 100644
--- a/gcc/rust/expand/rust-macro-builtins.h
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -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);
diff --git a/gcc/rust/expand/rust-macro-expand.cc 
b/gcc/rust/expand/rust-macro-expand.cc
index 475ad56a364..4c54ceff98a 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -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)
diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
index 4c48816affa..d9824f1a5ac 100644
--- a/gcc/rust/lang.opt
+++ b/gcc/rust/lang.opt
@@ -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.
diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc 
b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
index bb93d95fe8e..4fd1dd265b6 100644
--- a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
@@ -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
index 00000000000..5b796991039
--- /dev/null
+++ b/gcc/testsuite/rust/compile/offset_of1.rs
@@ -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" }
+}
-- 
2.49.0

Reply via email to