Skip to content

Commit f6ecb16

Browse files
committed
add builtin field_of! macro
1 parent 0319e13 commit f6ecb16

File tree

13 files changed

+212
-12
lines changed

13 files changed

+212
-12
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,6 +2441,11 @@ pub enum TyKind {
24412441
ImplTrait(NodeId, #[visitable(extra = BoundKind::Impl)] GenericBounds),
24422442
/// No-op; kept solely so that we can pretty-print faithfully.
24432443
Paren(Box<Ty>),
2444+
/// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`).
2445+
///
2446+
/// Usually not written directly in user code but
2447+
/// indirectly via the macro `core::field::field_of!(...)`.
2448+
FieldOf(Box<Ty>, Vec<Ident>),
24442449
/// Unused for now.
24452450
Typeof(AnonConst),
24462451
/// This means the type should be inferred instead of it having been

compiler/rustc_ast/src/util/classify.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
294294
| ast::TyKind::Never
295295
| ast::TyKind::Tup(..)
296296
| ast::TyKind::Paren(..)
297+
| ast::TyKind::FieldOf(..)
297298
| ast::TyKind::Typeof(..)
298299
| ast::TyKind::Infer
299300
| ast::TyKind::ImplicitSelf

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13541354
self.lower_ty(ty, itctx),
13551355
self.lower_array_length_to_const_arg(length),
13561356
),
1357+
TyKind::FieldOf(container, fields) => hir::TyKind::FieldOf(
1358+
self.lower_ty(container, itctx),
1359+
self.arena.alloc_from_iter(fields.iter().map(|field| self.lower_ident(*field))),
1360+
),
13571361
TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)),
13581362
TyKind::TraitObject(bounds, kind) => {
13591363
let mut lifetime_bound = None;

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,25 @@ impl<'a> State<'a> {
13241324
self.print_expr(&length.value, FixupContext::default());
13251325
self.word("]");
13261326
}
1327+
ast::TyKind::FieldOf(container, fields) => {
1328+
self.word("builtin # field_of");
1329+
self.popen();
1330+
let ib = self.ibox(0);
1331+
self.print_type(container);
1332+
self.word(",");
1333+
self.space();
1334+
1335+
if let Some((&first, rest)) = fields.split_first() {
1336+
self.print_ident(first);
1337+
1338+
for &field in rest {
1339+
self.word(".");
1340+
self.print_ident(field);
1341+
}
1342+
}
1343+
self.end(ib);
1344+
self.pclose();
1345+
}
13271346
ast::TyKind::Typeof(e) => {
13281347
self.word("typeof(");
13291348
self.print_expr(&e.value, FixupContext::default());

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,7 @@ impl<'a> Parser<'a> {
11321132
/// Parse the field access used in offset_of, matched by `$(e:expr)+`.
11331133
/// Currently returns a list of idents. However, it should be possible in
11341134
/// future to also do array indices, which might be arbitrary expressions.
1135-
fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {
1135+
pub(super) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec<Ident>> {
11361136
let mut fields = Vec::new();
11371137
let mut trailing_dot = None;
11381138

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ impl<'a> Parser<'a> {
321321
self.parse_borrowed_pointee()?
322322
} else if self.eat_keyword_noexpect(kw::Typeof) {
323323
self.parse_typeof_ty()?
324+
} else if self.is_builtin() {
325+
self.parse_builtin_ty()?
324326
} else if self.eat_keyword(exp!(Underscore)) {
325327
// A type to be inferred `_`
326328
TyKind::Infer
@@ -763,6 +765,42 @@ impl<'a> Parser<'a> {
763765
Ok(TyKind::Typeof(expr))
764766
}
765767

768+
fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> {
769+
self.parse_builtin(|this, lo, ident| {
770+
Ok(match ident.name {
771+
sym::field_of => Some(this.parse_ty_field_of(lo)?),
772+
_ => None,
773+
})
774+
})
775+
}
776+
777+
/// Built-in macro for `field_of!` expressions.
778+
pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> {
779+
let container = self.parse_ty()?;
780+
self.expect(exp!(Comma))?;
781+
782+
let fields = self.parse_floating_field_access()?;
783+
let trailing_comma = self.eat_noexpect(&TokenKind::Comma);
784+
785+
if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) {
786+
if trailing_comma {
787+
e.note("unexpected third argument to field_of");
788+
} else {
789+
e.note("field_of expects dot-separated field and variant names");
790+
}
791+
e.emit();
792+
}
793+
794+
// Eat tokens until the macro call ends.
795+
if self.may_recover() {
796+
while !self.token.kind.is_close_delim_or_eof() {
797+
self.bump();
798+
}
799+
}
800+
801+
Ok(TyKind::FieldOf(container, fields))
802+
}
803+
766804
/// Parses a function pointer type (`TyKind::FnPtr`).
767805
/// ```ignore (illustrative)
768806
/// [unsafe] [extern "ABI"] fn (S) -> T

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ symbols! {
10401040
ffi_returns_twice,
10411041
field,
10421042
field_init_shorthand,
1043+
field_of,
10431044
field_projections,
10441045
file,
10451046
file_options,

library/core/src/field.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,15 @@ pub unsafe trait UnalignedField: Sized {
3131
#[lang = "Field"]
3232
#[unstable(feature = "field_projections", issue = "145383")]
3333
pub unsafe trait Field: UnalignedField {}
34+
35+
/// Expands to the field representing type of the given field.
36+
///
37+
/// The container type may be a `struct` or a tuple.
38+
///
39+
/// The field may be a nested field (`field1.field2`), but not an array index.
40+
/// The field must be visible to the call site.
41+
#[unstable(feature = "field_projections", issue = "145383")]
42+
#[allow_internal_unstable(builtin_syntax)]
43+
pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) {
44+
builtin # field_of($Container, $($fields)+)
45+
}

src/tools/rustfmt/src/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,8 @@ impl Rewrite for ast::Ty {
10311031
}
10321032
ast::TyKind::CVarArgs => Ok("...".to_owned()),
10331033
ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()),
1034+
// Builtin type expression, cannot be written by users.
1035+
ast::TyKind::FieldOf(..) => Err(RewriteError::Unknown),
10341036
ast::TyKind::Typeof(ref anon_const) => rewrite_call(
10351037
context,
10361038
"typeof",

tests/ui/feature-gates/feature-gate-field-projections.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(dead_code)]
22

3-
use std::field::Field; //~ ERROR: use of unstable library feature `field_projections` [E0658]
3+
use std::field::{Field, field_of}; //~ ERROR: use of unstable library feature `field_projections` [E0658]
4+
//~^ ERROR: use of unstable library feature `field_projections` [E0658]
45
use std::ptr;
56

67
fn project_ref<F: Field>(
@@ -14,4 +15,7 @@ where
1415
unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } //~ ERROR: use of unstable library feature `field_projections` [E0658]
1516
}
1617

17-
fn main() {}
18+
fn main() {
19+
struct Foo(());
20+
let _ = project_ref::<field_of!(Foo, 0)>(&Foo(())); //~ ERROR: use of unstable library feature `field_projections` [E0658]
21+
}

0 commit comments

Comments
 (0)