Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6e612c5
rustc_hir_typeck: match all ty kinds in probe
BennoLossin Sep 6, 2025
bc7a0bb
add field_projections feature gate
BennoLossin Sep 5, 2025
1c26175
add field traits
BennoLossin Sep 5, 2025
95eab26
error on manually implementing field traits
BennoLossin Sep 5, 2025
a88b108
add unaligned_field_offset intrinsic
BennoLossin Sep 5, 2025
c9d04aa
add builtin `field_of!` macro
BennoLossin Sep 5, 2025
976272c
move `NoFieldOnType` error
BennoLossin Sep 6, 2025
52eb6be
add `FieldPath`
BennoLossin Sep 6, 2025
47c3417
use `FieldPath` in `offset_of!`
BennoLossin Sep 6, 2025
17946a5
add FRTs to HIR
BennoLossin Sep 6, 2025
36db825
add FRTs as `ty::Field`
BennoLossin Sep 6, 2025
af4f1e7
printing `ty::Field`
BennoLossin Sep 6, 2025
2a9d9b2
FRTs are `Sized`
BennoLossin Sep 6, 2025
51bac24
FRTs are uninhabited
BennoLossin Sep 6, 2025
9ad7bf9
FRTs are not trivially `Copy` & `Clone`
BennoLossin Sep 6, 2025
eae060e
FRTs are trivially `Freeze`
BennoLossin Sep 6, 2025
351f028
FRTs are not trivially `Unpin`
BennoLossin Sep 6, 2025
36b21d9
FRTs don't implement `Drop`
BennoLossin Sep 5, 2025
b82b874
add coherence check for FRTs
BennoLossin Sep 6, 2025
6c7ffa6
borrow check of FRTs (have no fields & can't be dereferenced)
BennoLossin Sep 5, 2025
bcf27f8
const eval of FRTs (they are uninhabited, so not really much to do)
BennoLossin Sep 5, 2025
743de26
add FRTs to rustc_public
BennoLossin Sep 6, 2025
014671d
FRTs & symbol names
BennoLossin Sep 6, 2025
fd8f344
FRTs & FFI
BennoLossin Sep 6, 2025
7ef9323
rustdoc support for FRTs
BennoLossin Sep 5, 2025
0bea467
add builtin impls of `Field` and `UnalignedField` for FRTs
BennoLossin Sep 6, 2025
29db7da
add values for associated items of `UnalignedField`
BennoLossin Sep 6, 2025
5e062b5
basic test
BennoLossin Sep 6, 2025
92f733b
debuginfo for FRTs
BennoLossin Sep 6, 2025
0e9e30a
fix test locations
BennoLossin Sep 8, 2025
847000f
allow field types to implement copy
BennoLossin Sep 8, 2025
fe5608a
rustc_const_eval: improve error handling
BennoLossin Sep 9, 2025
adf8036
rustc_const_eval: explain `TooGeneric` error
BennoLossin Sep 10, 2025
7547da8
remove fixme & use unreachable
BennoLossin Sep 12, 2025
674f94a
partial enum & union support
BennoLossin Sep 13, 2025
53fc6ad
make itemctxt structurally resolve field types
BennoLossin Sep 18, 2025
07e6d0e
lower_field_path: don't query the type of the last field
BennoLossin Sep 18, 2025
1ee4a9c
add cyclic test
BennoLossin Sep 18, 2025
64b720e
fix typo
BennoLossin Sep 18, 2025
b568a77
add `-Znext-solver` versions to tests
BennoLossin Sep 18, 2025
4a8f5c9
remove unaligned & unsized field support from lib & their lang items
BennoLossin Sep 18, 2025
915e8b2
add next solver trait assembly
BennoLossin Sep 18, 2025
d0e2f9e
remove `impl Field` for fields of packed structs
BennoLossin Sep 19, 2025
c7aa957
fix visibilty check
BennoLossin Sep 19, 2025
cb15775
field path lowering, do check the field's type if the kind is OffsetOf
BennoLossin Sep 20, 2025
df3daf5
allow dead code in cyclic test
BennoLossin Sep 20, 2025
eee5536
fix codegen test
BennoLossin Sep 20, 2025
5fe1380
fix clippy
BennoLossin Sep 20, 2025
acf2e31
remove re-export of `trait FieldPath`
BennoLossin Sep 23, 2025
f9d297f
make FRTs invariant
BennoLossin Sep 23, 2025
807ae15
fix relation with invariance
BennoLossin Sep 23, 2025
b2cb269
rustc_type_ir: walk: remove resolved FIXME
BennoLossin Sep 24, 2025
e6a0420
add send test
BennoLossin Sep 25, 2025
4f0462e
check for auto impls on field representing types
BennoLossin Sep 26, 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 @@ -2488,6 +2488,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, fields)`).
///
/// 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 @@ -1892,6 +1892,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
ty::Bool
| ty::Field(..)
| ty::Char
| ty::Int(_)
| ty::Uint(_)
Expand Down Expand Up @@ -1936,6 +1937,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| ty::Coroutine(_, _)
| ty::Tuple(_) => (),
ty::Bool
| ty::Field(..)
| ty::Char
| ty::Int(_)
| ty::Uint(_)
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,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),
Copy link
Member

Choose a reason for hiding this comment

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

Since this is effectively a never type, why not group it with Never here?

(I know nothing about debug info.^^)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That will print ! instead of the name of the FRT, maybe we want that, but maybe we don't?

(I also don't know anything about debug info :)

_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
};

Expand Down Expand Up @@ -1273,6 +1274,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 {
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 @@ -162,7 +162,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 @@ -325,7 +326,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
27 changes: 25 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, span_bug, 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 @@ -645,6 +646,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,

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

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

fn field_offset(
&mut self,
instance: ty::Instance<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()> {
assert_eq!(instance.args.len(), 1);
let ty = instance.args.type_at(0);
match ty.kind() {
&ty::Field(container, field_path) => {
let offset = self.nullary_op(NullOp::OffsetOf(field_path), container)?;
self.write_immediate(*offset, dest)
}
ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) | ty::Infer(..) => {
// This can happen in code which is generic over the field type.
throw_inval!(TooGeneric)
}
_ => span_bug!(self.cur_span(), "expected field representing type, found {ty}"),
}
}

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::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 @@ -778,6 +778,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
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,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 @@ -3736,6 +3736,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
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,12 @@ language_item_table! {
// Reborrowing related lang-items
Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0);
CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0);

// Experimental lang items for field projections.
Field, sym::Field, field_trait, Target::Trait, GenericRequirement::None;
FieldBase, sym::FieldBase, field_base, Target::AssocTy, GenericRequirement::None;
FieldType, sym::FieldType, field_type, Target::AssocTy, GenericRequirement::None;
FieldOffset, sym::FieldOffset, field_offset, Target::AssocConst, GenericRequirement::None;
}

/// The requirement imposed on the generics of a lang item
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,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}`
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::fabsf128
| sym::fadd_algebraic
| sym::fdiv_algebraic
| sym::field_offset
| sym::floorf16
| sym::floorf32
| sym::floorf64
Expand Down Expand Up @@ -749,6 +750,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::field_offset => (1, 0, Vec::new(), tcx.types.usize),

other => {
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
return;
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,17 @@ pub(super) fn check_item<'tcx>(
.with_span_label(sp, "auto trait")
.emit());
}
if is_auto && let rustc_hir::TyKind::FieldOf(..) = impl_.self_ty.kind {
res = res.and(Err(tcx
.dcx()
.struct_span_err(
item.span,
"impls of auto traits for field representing types not supported",
)
.with_span_label(impl_.self_ty.span, "field representing type")
.with_span_label(of_trait.trait_ref.path.span, "auto trait")
.emit()));
}
match header.polarity {
ty::ImplPolarity::Positive => {
res = res.and(check_impl(tcx, item, impl_));
Expand Down
Loading
Loading