Skip to content

Commit

Permalink
Rollup merge of #110694 - est31:builtin, r=petrochenkov
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Dylan-DPC authored May 9, 2023
2 parents ff30b8c + 83b4df4 commit dbd090c
Show file tree
Hide file tree
Showing 25 changed files with 333 additions and 136 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");

if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,7 @@ impl<'a> State<'a> {
self.pclose();
}
ast::ExprKind::OffsetOf(container, fields) => {
// FIXME: This should have its own syntax, distinct from a macro invocation.
self.word("offset_of!");
self.word("builtin # offset_of");
self.popen();
self.rbox(0, Inconsistent);
self.print_type(container);
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,6 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
*[more] arguments
} in format string, but {$desc}
builtin_macros_offset_of_expected_field = expected field
builtin_macros_offset_of_expected_two_args = expected 2 arguments
builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on items
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ mod format;
mod format_foreign;
mod global_allocator;
mod log_syntax;
mod offset_of;
mod source_util;
mod test;
mod trace_macros;
Expand Down Expand Up @@ -92,7 +91,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
line: source_util::expand_line,
log_syntax: log_syntax::expand_log_syntax,
module_path: source_util::expand_mod,
offset_of: offset_of::expand_offset_of,
option_env: env::expand_option_env,
core_panic: edition_panic::expand_panic,
std_panic: edition_panic::expand_panic,
Expand Down
99 changes: 0 additions & 99 deletions compiler/rustc_builtin_macros/src/offset_of.rs

This file was deleted.

2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ declare_features! (
(active, async_closure, "1.37.0", Some(62290), None),
/// Allows async functions to be declared, implemented, and used in traits.
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
/// Allows builtin # foo() syntax
(active, builtin_syntax, "CURRENT_RUSTC_VERSION", Some(110680), None),
/// Allows `c"foo"` literals.
(active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None),
/// Treat `extern "C"` function as nounwind.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are inva
.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
.tuple_exception_line_3 = see issue #60210 <https://github.com/rust-lang/rust/issues/60210> for more information
parse_expected_builtin_ident = expected identifier after `builtin #`
parse_unknown_builtin_construct = unknown `builtin #` construct `{$name}`
parse_non_string_abi_literal = non-string ABI literal
.suggestion = specify the ABI with a string literal
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2644,3 +2644,18 @@ pub(crate) struct MalformedCfgAttr {
pub span: Span,
pub sugg: &'static str,
}

#[derive(Diagnostic)]
#[diag(parse_unknown_builtin_construct)]
pub(crate) struct UnknownBuiltinConstruct {
#[primary_span]
pub span: Span,
pub name: Symbol,
}

#[derive(Diagnostic)]
#[diag(parse_expected_builtin_ident)]
pub(crate) struct ExpectedBuiltinIdent {
#[primary_span]
pub span: Span,
}
61 changes: 61 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,8 @@ impl<'a> Parser<'a> {
})
} else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
self.parse_expr_array_or_repeat(Delimiter::Bracket)
} else if self.is_builtin() {
self.parse_expr_builtin()
} else if self.check_path() {
self.parse_expr_path_start()
} else if self.check_keyword(kw::Move)
Expand Down Expand Up @@ -1766,6 +1768,61 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(expr)
}

/// Parse `builtin # ident(args,*)`.
fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
self.parse_builtin(|this, lo, ident| {
if ident.name == sym::offset_of {
return Ok(Some(this.parse_expr_offset_of(lo)?));
}

Ok(None)
})
}

pub(crate) fn parse_builtin<T>(
&mut self,
parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option<T>>,
) -> PResult<'a, T> {
let lo = self.token.span;

self.bump(); // `builtin`
self.bump(); // `#`

let Some((ident, false)) = self.token.ident() else {
let err = errors::ExpectedBuiltinIdent { span: self.token.span }
.into_diagnostic(&self.sess.span_diagnostic);
return Err(err);
};
self.sess.gated_spans.gate(sym::builtin_syntax, ident.span);
self.bump();

self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?;
let ret = if let Some(res) = parse(self, lo, ident)? {
Ok(res)
} else {
let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name }
.into_diagnostic(&self.sess.span_diagnostic);
return Err(err);
};
self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?;

ret
}

pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
let container = self.parse_ty()?;
self.expect(&TokenKind::Comma)?;

let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false };
let (fields, _trailing, _recovered) = self.parse_seq_to_before_end(
&TokenKind::CloseDelim(Delimiter::Parenthesis),
seq_sep,
Parser::parse_field_name,
)?;
let span = lo.to(self.token.span);
Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into())))
}

/// Returns a string literal if the next token is a string literal.
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
/// and returns `None` if the next token is not literal at all.
Expand Down Expand Up @@ -2835,6 +2892,10 @@ impl<'a> Parser<'a> {
})
}

pub(crate) fn is_builtin(&self) -> bool {
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
}

/// Parses a `try {...}` expression (`try` token already eaten).
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
let (attrs, body) = self.parse_inner_attrs_and_block()?;
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ impl<'a> Parser<'a> {
// UNION ITEM
self.bump(); // `union`
self.parse_item_union()?
} else if self.is_builtin() {
// BUILTIN# ITEM
return self.parse_item_builtin();
} else if self.eat_keyword(kw::Macro) {
// MACROS 2.0 ITEM
self.parse_item_decl_macro(lo)?
Expand Down Expand Up @@ -434,6 +437,11 @@ impl<'a> Parser<'a> {
}
}

fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
// To be expanded
return Ok(None);
}

/// Parses an item macro, e.g., `item!();`.
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> {
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ impl<'a> Parser<'a> {
attrs,
errors::InvalidVariableDeclarationSub::UseLetNotVar,
)?
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
} else if self.check_path()
&& !self.token.is_qpath_start()
&& !self.is_path_start_item()
&& !self.is_builtin()
{
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
// that starts like a path (1 token), but it fact not a path.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ symbols! {

// Weak keywords, have special meaning only in specific contexts.
Auto: "auto",
Builtin: "builtin",
Catch: "catch",
Default: "default",
MacroRules: "macro_rules",
Expand Down Expand Up @@ -440,6 +441,7 @@ symbols! {
breakpoint,
bridge,
bswap,
builtin_syntax,
c_str,
c_str_literals,
c_unwind,
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1315,9 +1315,9 @@ impl<T> SizedTypeProperties for T {}
///
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
/// ```
#[unstable(feature = "offset_of", issue = "106655")]
#[rustc_builtin_macro]
#[cfg(not(bootstrap))]
#[unstable(feature = "offset_of", issue = "106655")]
#[allow_internal_unstable(builtin_syntax)]
pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) {
/* compiler built-in */
builtin # offset_of($Container, $($fields).+)
}
16 changes: 8 additions & 8 deletions tests/mir-opt/const_prop/offset_of.concrete.ConstProp.diff
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@

bb0: {
StorageLive(_1); // scope 0 at $DIR/offset_of.rs:+1:9: +1:10
- _1 = OffsetOf(Alpha, [0]); // scope 0 at $DIR/offset_of.rs:+1:13: +1:33
+ _1 = const 4_usize; // scope 0 at $DIR/offset_of.rs:+1:13: +1:33
- _1 = OffsetOf(Alpha, [0]); // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ _1 = const 4_usize; // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:10
- _2 = OffsetOf(Alpha, [1]); // scope 1 at $DIR/offset_of.rs:+2:13: +2:33
+ _2 = const 0_usize; // scope 1 at $DIR/offset_of.rs:+2:13: +2:33
- _2 = OffsetOf(Alpha, [1]); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ _2 = const 0_usize; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
- _3 = OffsetOf(Alpha, [2, 0]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:36
+ _3 = const 2_usize; // scope 2 at $DIR/offset_of.rs:+3:14: +3:36
- _3 = OffsetOf(Alpha, [2, 0]); // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ _3 = const 2_usize; // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
- _4 = OffsetOf(Alpha, [2, 1]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:36
+ _4 = const 3_usize; // scope 3 at $DIR/offset_of.rs:+4:14: +4:36
- _4 = OffsetOf(Alpha, [2, 1]); // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ _4 = const 3_usize; // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
_0 = const (); // scope 0 at $DIR/offset_of.rs:+0:15: +5:2
StorageDead(_4); // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
StorageDead(_3); // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
Expand Down
8 changes: 4 additions & 4 deletions tests/mir-opt/const_prop/offset_of.generic.ConstProp.diff
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@

bb0: {
StorageLive(_1); // scope 0 at $DIR/offset_of.rs:+1:9: +1:11
_1 = OffsetOf(Gamma<T>, [0]); // scope 0 at $DIR/offset_of.rs:+1:14: +1:37
_1 = OffsetOf(Gamma<T>, [0]); // scope 0 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_2); // scope 1 at $DIR/offset_of.rs:+2:9: +2:11
_2 = OffsetOf(Gamma<T>, [1]); // scope 1 at $DIR/offset_of.rs:+2:14: +2:37
_2 = OffsetOf(Gamma<T>, [1]); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_3); // scope 2 at $DIR/offset_of.rs:+3:9: +3:11
_3 = OffsetOf(Delta<T>, [1]); // scope 2 at $DIR/offset_of.rs:+3:14: +3:37
_3 = OffsetOf(Delta<T>, [1]); // scope 2 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
StorageLive(_4); // scope 3 at $DIR/offset_of.rs:+4:9: +4:11
_4 = OffsetOf(Delta<T>, [2]); // scope 3 at $DIR/offset_of.rs:+4:14: +4:37
_4 = OffsetOf(Delta<T>, [2]); // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
_0 = const (); // scope 0 at $DIR/offset_of.rs:+0:17: +5:2
StorageDead(_4); // scope 3 at $DIR/offset_of.rs:+5:1: +5:2
StorageDead(_3); // scope 2 at $DIR/offset_of.rs:+5:1: +5:2
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/feature-gates/feature-gate-builtin_syntax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
struct Foo {
v: u8,
w: u8,
}
fn main() {
builtin # offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable
}
12 changes: 12 additions & 0 deletions tests/ui/feature-gates/feature-gate-builtin_syntax.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0658]: `builtin #` syntax is unstable
--> $DIR/feature-gate-builtin_syntax.rs:6:15
|
LL | builtin # offset_of(Foo, v);
| ^^^^^^^^^
|
= note: see issue #110680 <https://github.com/rust-lang/rust/issues/110680> for more information
= help: add `#![feature(builtin_syntax)]` to the crate attributes to enable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
Loading

0 comments on commit dbd090c

Please sign in to comment.