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 \
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 (); }
--- /dev/null
+// 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
{"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>
#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"
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);
#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"
// 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)
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.
{
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);
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;
}
--- /dev/null
+// { 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" }
+}