Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
95d2452
rustc_hir_typeck: match all ty kinds in probe
BennoLossin Sep 6, 2025
03707aa
add field_projections feature gate
BennoLossin Sep 5, 2025
154f5cb
add field traits
BennoLossin Sep 5, 2025
b4901a8
error on manually implementing field traits
BennoLossin Sep 5, 2025
0319e13
add unaligned_field_offset intrinsic
BennoLossin Sep 5, 2025
f6ecb16
add builtin `field_of!` macro
BennoLossin Sep 5, 2025
6202eac
move `NoFieldOnType` error
BennoLossin Sep 6, 2025
297bdad
add `FieldPath`
BennoLossin Sep 6, 2025
f92a04b
use `FieldPath` in `offset_of!`
BennoLossin Sep 6, 2025
b6b2961
add FRTs to HIR
BennoLossin Sep 6, 2025
f6a9247
add FRTs as `ty::Field`
BennoLossin Sep 6, 2025
8848a57
printing `ty::Field`
BennoLossin Sep 6, 2025
f92fc96
FRTs are `Sized`
BennoLossin Sep 6, 2025
437303e
FRTs are uninhabited
BennoLossin Sep 6, 2025
eded109
FRTs are not trivially `Copy` & `Clone`
BennoLossin Sep 6, 2025
ca4ca97
FRTs are trivially `Freeze`
BennoLossin Sep 6, 2025
a63c681
FRTs are not trivially `Unpin`
BennoLossin Sep 6, 2025
a75ba70
FRTs don't implement `Drop`
BennoLossin Sep 5, 2025
baf2b38
add coherence check for FRTs
BennoLossin Sep 6, 2025
14f4d3e
borrow check of FRTs (have no fields & can't be dereferenced)
BennoLossin Sep 5, 2025
e907a0e
const eval of FRTs (they are uninhabited, so not really much to do)
BennoLossin Sep 5, 2025
3ce340a
add FRTs to rustc_public
BennoLossin Sep 6, 2025
867fb8d
FRTs & symbol names
BennoLossin Sep 6, 2025
3a33d16
FRTs & FFI
BennoLossin Sep 6, 2025
c0ca97f
rustdoc support for FRTs
BennoLossin Sep 5, 2025
b273c2b
add builtin impls of `Field` and `UnalignedField` for FRTs
BennoLossin Sep 6, 2025
6be7991
add values for associated items of `UnalignedField`
BennoLossin Sep 6, 2025
0428f2a
basic test
BennoLossin Sep 6, 2025
a6b191a
debuginfo for FRTs
BennoLossin Sep 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2441,6 +2441,11 @@ pub enum TyKind {
ImplTrait(NodeId, #[visitable(extra = BoundKind::Impl)] GenericBounds),
/// No-op; kept solely so that we can pretty-print faithfully.
Paren(Box<Ty>),
/// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`).
///
/// Usually not written directly in user code but
/// indirectly via the macro `core::field::field_of!(...)`.
FieldOf(Box<Ty>, Vec<Ident>),
/// Unused for now.
Typeof(AnonConst),
/// This means the type should be inferred instead of it having been
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
| ast::TyKind::Never
| ast::TyKind::Tup(..)
| ast::TyKind::Paren(..)
| ast::TyKind::FieldOf(..)
| ast::TyKind::Typeof(..)
| ast::TyKind::Infer
| ast::TyKind::ImplicitSelf
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_ty(ty, itctx),
self.lower_array_length_to_const_arg(length),
),
TyKind::FieldOf(container, fields) => hir::TyKind::FieldOf(
self.lower_ty(container, itctx),
self.arena.alloc_from_iter(fields.iter().map(|field| self.lower_ident(*field))),
),
TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)),
TyKind::TraitObject(bounds, kind) => {
let mut lifetime_bound = None;
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,25 @@ impl<'a> State<'a> {
self.print_expr(&length.value, FixupContext::default());
self.word("]");
}
ast::TyKind::FieldOf(container, fields) => {
self.word("builtin # field_of");
self.popen();
let ib = self.ibox(0);
self.print_type(container);
self.word(",");
self.space();

if let Some((&first, rest)) = fields.split_first() {
self.print_ident(first);

for &field in rest {
self.word(".");
self.print_ident(field);
}
}
self.end(ib);
self.pclose();
}
ast::TyKind::Typeof(e) => {
self.word("typeof(");
self.print_expr(&e.value, FixupContext::default());
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
ty::Bool
| ty::Field(..)
| ty::Char
| ty::Int(_)
| ty::Uint(_)
Expand Down Expand Up @@ -1937,6 +1938,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| ty::Coroutine(_, _)
| ty::Tuple(_) => (),
ty::Bool
| ty::Field(..)
| ty::Char
| ty::Int(_)
| ty::Uint(_)
Expand Down
30 changes: 27 additions & 3 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,6 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => {
build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id)
}
// Some `Box` are newtyped pointers, make debuginfo aware of that.
// Only works if the allocator argument is a 1-ZST and hence irrelevant for layout
// (or if there is no allocator argument).
ty::Adt(def, args)
if def.is_box()
&& args.get(1).is_none_or(|arg| cx.layout_of(arg.expect_ty()).is_1zst()) =>
Expand All @@ -473,6 +470,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
ty::Field(..) => build_field_type_di_node(cx, unique_type_id),
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
};

Expand Down Expand Up @@ -1261,6 +1259,32 @@ fn build_closure_env_di_node<'ll, 'tcx>(
)
}

fn build_field_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
) -> DINodeCreationResult<'ll> {
let ty = unique_type_id.expect_ty();
let ty::Field(_, _) = ty.kind() else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small nit here is that we don't need the check since the caller has checked the kind. We don't actually need any info about the container or the field path... Even if we do, we can pass them through from the args I suppose.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied what the other functions do 🤷 should I still change it?

bug!("build_field_type_di_node() called with non-field-type: {ty:?}")
};
let type_name = compute_debuginfo_type_name(cx.tcx, ty, false);
type_map::build_type_with_children(
cx,
type_map::stub(
cx,
Stub::Struct,
unique_type_id,
&type_name,
None,
cx.size_and_align_of(ty),
None,
DIFlags::FlagZero,
),
|_, _| smallvec![],
NO_GENERICS,
)
}

/// Build the debuginfo node for a Rust `union` type.
fn build_union_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ fn push_debuginfo_type_name<'tcx>(
push_generic_params_internal(tcx, args, output, visited);
}
}
ty::Field(container, field_path) => {
output.push_str("field_of!(");
push_debuginfo_type_name(tcx, container, qualified, output, visited);
output.push_str(", ");
field_path.walk(tcx, container, |_, name, _, last| {
output.push_str(name.as_str());
if !last {
output.push('.');
}
std::ops::ControlFlow::<()>::Continue(())
});
output.push(')');
}
ty::Tuple(component_types) => {
if cpp_like_debuginfo {
output.push_str("tuple$<");
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ fn const_to_valtree_inner<'tcx>(
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
}

ty::Never
ty::Field(..)
| ty::Never
| ty::Error(_)
| ty::Foreign(..)
| ty::Infer(ty::FreshIntTy(_))
Expand Down Expand Up @@ -323,7 +324,9 @@ pub fn valtree_to_const_value<'tcx>(

op_to_const(&ecx, &place.into(), /* for diagnostics */ false)
}
ty::Never

ty::Field(..)
| ty::Never
| ty::Error(_)
| ty::Foreign(..)
| ty::Infer(ty::FreshIntTy(_))
Expand Down
22 changes: 20 additions & 2 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use std::assert_matches::assert_matches;
use rustc_abi::{FieldIdx, HasDataLayout, Size};
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic, NullOp};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{bug, ty};
use rustc_middle::{bug, err_inval, ty};
use rustc_span::{Symbol, sym};
use tracing::trace;

Expand Down Expand Up @@ -188,6 +188,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
| ty::CoroutineWitness(..)
| ty::UnsafeBinder(_)
| ty::Never
| ty::Field(..)
| ty::Tuple(_)
| ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx),
};
Expand Down Expand Up @@ -637,6 +638,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
rustc_apfloat::Round::NearestTiesToEven,
)?,

sym::unaligned_field_offset => self.unaligned_field_offset(instance, dest)?,

// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
}
Expand All @@ -646,6 +649,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(true)
}

fn unaligned_field_offset(
&mut self,
instance: ty::Instance<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()> {
assert_eq!(instance.args.len(), 1);
match instance.args.type_at(0).kind() {
&ty::Field(container, field_path) => {
let offset = self.nullary_op(NullOp::OffsetOf(field_path), container)?;
self.write_immediate(*offset, dest)
}
_ => Err(err_inval!(TooGeneric)).into(),
}
}

pub(super) fn eval_nondiverging_intrinsic(
&mut self,
intrinsic: &NonDivergingIntrinsic<'tcx>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Field(..)
| ty::Error(_) => true,

ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
interp_ok(true)
}
ty::Never => throw_validation_failure!(self.path, NeverVal),
ty::Field(..) => throw_validation_failure!(self.path, NeverVal),
ty::Foreign(..) | ty::FnDef(..) => {
// Nothing to check.
interp_ok(true)
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_const_eval/src/util/type_name.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::Write;
use std::ops::ControlFlow;

use rustc_data_structures::intern::Interned;
use rustc_hir::def_id::{CrateNum, DefId};
Expand Down Expand Up @@ -58,6 +59,29 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> {
| ty::CoroutineClosure(def_id, args)
| ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
ty::Field(container, field_path) => {
write!(self, "field_of!(")?;
self.print_type(container)?;
write!(self, ", ")?;
field_path
.walk(self.tcx, container, |_, name, _, last| {
match write!(self, "{name}") {
Ok(()) => ControlFlow::Continue(()),
Err(err) => ControlFlow::Break(Err(err)),
}?;
if !last {
match write!(self, ".") {
Ok(()) => ControlFlow::Continue(()),
Err(err) => ControlFlow::Break(Err(err)),
}
} else {
ControlFlow::Continue(())
}
})
.unwrap_or(Ok(()))?;
write!(self, ")")?;
Ok(())
}

ty::Alias(ty::Free, _) => bug!("type_name: unexpected free alias"),
ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"),
Expand Down
23 changes: 23 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0806.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
`UnalignedField` or `Field` was manually implemented, which is not allowed.

Erroneous code examples:

```compile_fail,E0806
use std::field::UnalignedField;

pub struct MyStruct;

unsafe impl UnalignedField for MyStruct {
type Base = ();
type Type = ();
const OFFSET: usize = 0;
}
```

```compile_fail,E0806
use std::field::{Field, field_of};

pub struct MyStruct(usize);

unsafe impl Field for field_of!(MyStruct, 0) {}
```
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ E0802: 0802,
E0803: 0803,
E0804: 0804,
E0805: 0805,
E0806: 0806,
);
)
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,8 @@ declare_features! (
(unstable, ffi_const, "1.45.0", Some(58328)),
/// Allows the use of `#[ffi_pure]` on foreign functions.
(unstable, ffi_pure, "1.45.0", Some(58329)),
/// Experimental field projections.
(incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)),
/// Controlling the behavior of fmt::Debug
(unstable, fmt_debug, "1.82.0", Some(129709)),
/// Allows using `#[align(...)]` on function items
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3710,6 +3710,8 @@ pub enum TyKind<'hir, Unambig = ()> {
/// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair
/// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size.
TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>),
/// Field representing type (`field_of!`)
FieldOf(&'hir Ty<'hir>, &'hir [Ident]),
/// Unused for now.
Typeof(&'hir AnonConst),
/// Placeholder for a type that has failed to be defined.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,12 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -
}
try_visit!(visitor.visit_lifetime(lifetime));
}
TyKind::FieldOf(ty, fields) => {
try_visit!(visitor.visit_ty_unambig(ty));
for field in fields {
try_visit!(visitor.visit_ident(*field));
}
}
TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
TyKind::InferDelegation(..) | TyKind::Err(_) => {}
TyKind::Pat(ty, pat) => {
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,14 @@ language_item_table! {

// Reborrowing related lang-items
Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0);

// Experimental lang items for field projections.
Field, sym::Field, field_trait, Target::Trait, GenericRequirement::None;
UnalignedField, sym::UnalignedField, unaligned_field_trait, Target::Trait, GenericRequirement::None;
UnalignedFieldBase, sym::UnalignedFieldBase, unaligned_field_base, Target::AssocTy, GenericRequirement::None;
UnalignedFieldType, sym::UnalignedFieldType, unaligned_field_type, Target::AssocTy, GenericRequirement::None;
UnalignedFieldOFFSET, sym::UnalignedFieldOFFSET, unaligned_field_offset, Target::AssocConst, GenericRequirement::None;
UnalignedFieldOffset, sym::unaligned_field_offset, unaligned_field_offset_getter, Target::Fn, GenericRequirement::Exact(1);
}

/// The requirement imposed on the generics of a lang item
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ hir_analysis_drop_impl_on_wrong_item =
the `{$trait_}` trait may only be implemented for local structs, enums, and unions
.label = must be a struct, enum, or union in the current crate

hir_analysis_field_trait_impl =
the `{$trait_}` trait may not be implemented manually

hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported

hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
Expand Down Expand Up @@ -612,3 +615,4 @@ hir_analysis_wrong_number_of_generic_arguments_to_intrinsic =
[one] parameter
*[other] parameters
}
hir_analysis_no_field_on_type = no field `{$field}` on type `{$ty}`
5 changes: 4 additions & 1 deletion compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::prefetch_write_data
| sym::prefetch_read_instruction
| sym::prefetch_write_instruction
| sym::const_eval_select => hir::Safety::Safe,
| sym::const_eval_select
| sym::unaligned_field_offset => hir::Safety::Safe,
_ => hir::Safety::Unsafe,
};

Expand Down Expand Up @@ -673,6 +674,8 @@ pub(crate) fn check_intrinsic_type(
| sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)),
sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit),

sym::unaligned_field_offset => (1, 0, Vec::new(), tcx.types.usize),

other => {
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
return;
Expand Down
Loading
Loading