Skip to content

Commit dbd090c

Browse files
authored
Rollup merge of #110694 - est31:builtin, r=petrochenkov
Implement builtin # syntax and use it for offset_of!(...) Add `builtin #` syntax to the parser, as well as a generic infrastructure to support both item and expression position builtin syntaxes. The PR also uses this infrastructure for the implementation of the `offset_of!` macro, added by #106934. cc `@petrochenkov` `@DrMeepster` cc #110680 `builtin #` tracking issue cc #106655 `offset_of!` tracking issue
2 parents ff30b8c + 83b4df4 commit dbd090c

25 files changed

+333
-136
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
603603
gate_all!(yeet_expr, "`do yeet` expression is experimental");
604604
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
605605
gate_all!(const_closures, "const closures are experimental");
606+
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
606607

607608
if !visitor.features.negative_bounds {
608609
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -556,8 +556,7 @@ impl<'a> State<'a> {
556556
self.pclose();
557557
}
558558
ast::ExprKind::OffsetOf(container, fields) => {
559-
// FIXME: This should have its own syntax, distinct from a macro invocation.
560-
self.word("offset_of!");
559+
self.word("builtin # offset_of");
561560
self.popen();
562561
self.rbox(0, Inconsistent);
563562
self.print_type(container);

compiler/rustc_builtin_macros/messages.ftl

-4
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,6 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
150150
*[more] arguments
151151
} in format string, but {$desc}
152152
153-
builtin_macros_offset_of_expected_field = expected field
154-
155-
builtin_macros_offset_of_expected_two_args = expected 2 arguments
156-
157153
builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on items
158154
159155
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests

compiler/rustc_builtin_macros/src/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ mod format;
4444
mod format_foreign;
4545
mod global_allocator;
4646
mod log_syntax;
47-
mod offset_of;
4847
mod source_util;
4948
mod test;
5049
mod trace_macros;
@@ -92,7 +91,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
9291
line: source_util::expand_line,
9392
log_syntax: log_syntax::expand_log_syntax,
9493
module_path: source_util::expand_mod,
95-
offset_of: offset_of::expand_offset_of,
9694
option_env: env::expand_option_env,
9795
core_panic: edition_panic::expand_panic,
9896
std_panic: edition_panic::expand_panic,

compiler/rustc_builtin_macros/src/offset_of.rs

-99
This file was deleted.

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ declare_features! (
313313
(active, async_closure, "1.37.0", Some(62290), None),
314314
/// Allows async functions to be declared, implemented, and used in traits.
315315
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
316+
/// Allows builtin # foo() syntax
317+
(active, builtin_syntax, "CURRENT_RUSTC_VERSION", Some(110680), None),
316318
/// Allows `c"foo"` literals.
317319
(active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None),
318320
/// Treat `extern "C"` function as nounwind.

compiler/rustc_parse/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are inva
257257
.tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access
258258
.tuple_exception_line_3 = see issue #60210 <https://github.com/rust-lang/rust/issues/60210> for more information
259259
260+
parse_expected_builtin_ident = expected identifier after `builtin #`
261+
262+
parse_unknown_builtin_construct = unknown `builtin #` construct `{$name}`
263+
260264
parse_non_string_abi_literal = non-string ABI literal
261265
.suggestion = specify the ABI with a string literal
262266

compiler/rustc_parse/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2644,3 +2644,18 @@ pub(crate) struct MalformedCfgAttr {
26442644
pub span: Span,
26452645
pub sugg: &'static str,
26462646
}
2647+
2648+
#[derive(Diagnostic)]
2649+
#[diag(parse_unknown_builtin_construct)]
2650+
pub(crate) struct UnknownBuiltinConstruct {
2651+
#[primary_span]
2652+
pub span: Span,
2653+
pub name: Symbol,
2654+
}
2655+
2656+
#[derive(Diagnostic)]
2657+
#[diag(parse_expected_builtin_ident)]
2658+
pub(crate) struct ExpectedBuiltinIdent {
2659+
#[primary_span]
2660+
pub span: Span,
2661+
}

compiler/rustc_parse/src/parser/expr.rs

+61
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,8 @@ impl<'a> Parser<'a> {
13001300
})
13011301
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
13021302
self.parse_expr_array_or_repeat(Delimiter::Bracket)
1303+
} else if self.is_builtin() {
1304+
self.parse_expr_builtin()
13031305
} else if self.check_path() {
13041306
self.parse_expr_path_start()
13051307
} else if self.check_keyword(kw::Move)
@@ -1766,6 +1768,61 @@ impl<'a> Parser<'a> {
17661768
self.maybe_recover_from_bad_qpath(expr)
17671769
}
17681770

1771+
/// Parse `builtin # ident(args,*)`.
1772+
fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
1773+
self.parse_builtin(|this, lo, ident| {
1774+
if ident.name == sym::offset_of {
1775+
return Ok(Some(this.parse_expr_offset_of(lo)?));
1776+
}
1777+
1778+
Ok(None)
1779+
})
1780+
}
1781+
1782+
pub(crate) fn parse_builtin<T>(
1783+
&mut self,
1784+
parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option<T>>,
1785+
) -> PResult<'a, T> {
1786+
let lo = self.token.span;
1787+
1788+
self.bump(); // `builtin`
1789+
self.bump(); // `#`
1790+
1791+
let Some((ident, false)) = self.token.ident() else {
1792+
let err = errors::ExpectedBuiltinIdent { span: self.token.span }
1793+
.into_diagnostic(&self.sess.span_diagnostic);
1794+
return Err(err);
1795+
};
1796+
self.sess.gated_spans.gate(sym::builtin_syntax, ident.span);
1797+
self.bump();
1798+
1799+
self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?;
1800+
let ret = if let Some(res) = parse(self, lo, ident)? {
1801+
Ok(res)
1802+
} else {
1803+
let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name }
1804+
.into_diagnostic(&self.sess.span_diagnostic);
1805+
return Err(err);
1806+
};
1807+
self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?;
1808+
1809+
ret
1810+
}
1811+
1812+
pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
1813+
let container = self.parse_ty()?;
1814+
self.expect(&TokenKind::Comma)?;
1815+
1816+
let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false };
1817+
let (fields, _trailing, _recovered) = self.parse_seq_to_before_end(
1818+
&TokenKind::CloseDelim(Delimiter::Parenthesis),
1819+
seq_sep,
1820+
Parser::parse_field_name,
1821+
)?;
1822+
let span = lo.to(self.token.span);
1823+
Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into())))
1824+
}
1825+
17691826
/// Returns a string literal if the next token is a string literal.
17701827
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
17711828
/// and returns `None` if the next token is not literal at all.
@@ -2835,6 +2892,10 @@ impl<'a> Parser<'a> {
28352892
})
28362893
}
28372894

2895+
pub(crate) fn is_builtin(&self) -> bool {
2896+
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
2897+
}
2898+
28382899
/// Parses a `try {...}` expression (`try` token already eaten).
28392900
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
28402901
let (attrs, body) = self.parse_inner_attrs_and_block()?;

compiler/rustc_parse/src/parser/item.rs

+8
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ impl<'a> Parser<'a> {
265265
// UNION ITEM
266266
self.bump(); // `union`
267267
self.parse_item_union()?
268+
} else if self.is_builtin() {
269+
// BUILTIN# ITEM
270+
return self.parse_item_builtin();
268271
} else if self.eat_keyword(kw::Macro) {
269272
// MACROS 2.0 ITEM
270273
self.parse_item_decl_macro(lo)?
@@ -434,6 +437,11 @@ impl<'a> Parser<'a> {
434437
}
435438
}
436439

440+
fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
441+
// To be expanded
442+
return Ok(None);
443+
}
444+
437445
/// Parses an item macro, e.g., `item!();`.
438446
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> {
439447
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`

compiler/rustc_parse/src/parser/stmt.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ impl<'a> Parser<'a> {
9090
attrs,
9191
errors::InvalidVariableDeclarationSub::UseLetNotVar,
9292
)?
93-
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
93+
} else if self.check_path()
94+
&& !self.token.is_qpath_start()
95+
&& !self.is_path_start_item()
96+
&& !self.is_builtin()
97+
{
9498
// We have avoided contextual keywords like `union`, items with `crate` visibility,
9599
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
96100
// that starts like a path (1 token), but it fact not a path.

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ symbols! {
9595

9696
// Weak keywords, have special meaning only in specific contexts.
9797
Auto: "auto",
98+
Builtin: "builtin",
9899
Catch: "catch",
99100
Default: "default",
100101
MacroRules: "macro_rules",
@@ -440,6 +441,7 @@ symbols! {
440441
breakpoint,
441442
bridge,
442443
bswap,
444+
builtin_syntax,
443445
c_str,
444446
c_str_literals,
445447
c_unwind,

library/core/src/mem/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1315,9 +1315,9 @@ impl<T> SizedTypeProperties for T {}
13151315
///
13161316
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
13171317
/// ```
1318-
#[unstable(feature = "offset_of", issue = "106655")]
1319-
#[rustc_builtin_macro]
13201318
#[cfg(not(bootstrap))]
1319+
#[unstable(feature = "offset_of", issue = "106655")]
1320+
#[allow_internal_unstable(builtin_syntax)]
13211321
pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) {
1322-
/* compiler built-in */
1322+
builtin # offset_of($Container, $($fields).+)
13231323
}

tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@
2222

2323
bb0: {
2424
StorageLive(_1); // scope 0 at $DIR/offset_of.rs:+1:9: +1:10
25-
- _1 = OffsetOf(Alpha, [0]); // scope 0 at $DIR/offset_of.rs:+1:13: +1:33
26-
+ _1 = const 4_usize; // scope 0 at $DIR/offset_of.rs:+1:13: +1:33
25+
- _1 = OffsetOf(Alpha, [0]); // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
26+
+ _1 = const 4_usize; // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
2727
StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:10
28-
- _2 = OffsetOf(Alpha, [1]); // scope 1 at $DIR/offset_of.rs:+2:13: +2:33
29-
+ _2 = const 0_usize; // scope 1 at $DIR/offset_of.rs:+2:13: +2:33
28+
- _2 = OffsetOf(Alpha, [1]); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
29+
+ _2 = const 0_usize; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
3030
StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
31-
- _3 = OffsetOf(Alpha, [2, 0]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:36
32-
+ _3 = const 2_usize; // scope 2 at $DIR/offset_of.rs:+3:14: +3:36
31+
- _3 = OffsetOf(Alpha, [2, 0]); // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
32+
+ _3 = const 2_usize; // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
3333
StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
34-
- _4 = OffsetOf(Alpha, [2, 1]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:36
35-
+ _4 = const 3_usize; // scope 3 at $DIR/offset_of.rs:+4:14: +4:36
34+
- _4 = OffsetOf(Alpha, [2, 1]); // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
35+
+ _4 = const 3_usize; // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
3636
_0 = const (); // scope 0 at $DIR/offset_of.rs:+0:15: +5:2
3737
StorageDead(_4); // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
3838
StorageDead(_3); // scope 2 at $DIR/offset_of.rs:+5:1: +5:2

tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222

2323
bb0: {
2424
StorageLive(_1); // scope 0 at $DIR/offset_of.rs:+1:9: +1:11
25-
_1 = OffsetOf(Gamma<T>, [0]); // scope 0 at $DIR/offset_of.rs:+1:14: +1:37
25+
_1 = OffsetOf(Gamma<T>, [0]); // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
2626
StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:11
27-
_2 = OffsetOf(Gamma<T>, [1]); // scope 1 at $DIR/offset_of.rs:+2:14: +2:37
27+
_2 = OffsetOf(Gamma<T>, [1]); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
2828
StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
29-
_3 = OffsetOf(Delta<T>, [1]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:37
29+
_3 = OffsetOf(Delta<T>, [1]); // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
3030
StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
31-
_4 = OffsetOf(Delta<T>, [2]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:37
31+
_4 = OffsetOf(Delta<T>, [2]); // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
3232
_0 = const (); // scope 0 at $DIR/offset_of.rs:+0:17: +5:2
3333
StorageDead(_4); // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
3434
StorageDead(_3); // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
struct Foo {
2+
v: u8,
3+
w: u8,
4+
}
5+
fn main() {
6+
builtin # offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: `builtin #` syntax is unstable
2+
--> $DIR/feature-gate-builtin_syntax.rs:6:15
3+
|
4+
LL | builtin # offset_of(Foo, v);
5+
| ^^^^^^^^^
6+
|
7+
= note: see issue #110680 <https://github.com/rust-lang/rust/issues/110680> for more information
8+
= help: add `#![feature(builtin_syntax)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)