From acc8c0bd65a057c7e61d3e70556e66b66d3f45b7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 28 Dec 2025 13:05:09 +0000 Subject: [PATCH 01/14] Reduce usage of FnAbi in codegen_llvm_intrinsic_call --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 88 ++++++++++++++++++- compiler/rustc_codegen_llvm/src/intrinsic.rs | 30 ++++++- 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 36ea76cbc51a0..f138b87d3405f 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -4,10 +4,10 @@ mod simd; #[cfg(feature = "master")] use std::iter; -#[cfg(feature = "master")] -use gccjit::Type; use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp}; #[cfg(feature = "master")] +use gccjit::{FnAttribute, Type}; +#[cfg(feature = "master")] use rustc_abi::ExternAbi; use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::MemFlags; @@ -22,13 +22,18 @@ use rustc_codegen_ssa::traits::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, IntrinsicCallBuilderMethods, LayoutTypeCodegenMethods, }; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; +#[cfg(feature = "master")] +use rustc_session::config; use rustc_span::{Span, Symbol, sym}; +#[cfg(feature = "master")] +use rustc_target::callconv::ArgAttributes; use rustc_target::callconv::{ArgAbi, PassMode}; -use crate::abi::{FnAbiGccExt, GccType}; +use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; use crate::builder::Builder; use crate::common::{SignType, TypeReflection}; use crate::context::CodegenCx; @@ -621,7 +626,82 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } else { self.linkage.set(FunctionType::Extern); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - let fn_ty = fn_abi.gcc_type(self); + assert!(!fn_abi.ret.is_indirect()); + assert!(!fn_abi.c_variadic); + + let return_type = match fn_abi.ret.mode { + PassMode::Ignore => self.type_void(), + PassMode::Direct(_) | PassMode::Pair(..) => { + fn_abi.ret.layout.immediate_gcc_type(self) + } + PassMode::Cast { .. } | PassMode::Indirect { .. } => { + unreachable!() + } + }; + + #[cfg(feature = "master")] + let mut non_null_args = Vec::new(); + + #[cfg(feature = "master")] + let mut apply_attrs = + |mut ty: Type<'gcc>, attrs: &ArgAttributes, arg_index: usize| { + if self.sess().opts.optimize == config::OptLevel::No { + return ty; + } + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) { + ty = ty.make_restrict() + } + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) { + non_null_args.push(arg_index as i32 + 1); + } + ty + }; + #[cfg(not(feature = "master"))] + let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes, _arg_index: usize| ty; + + let mut argument_tys = Vec::with_capacity(fn_abi.args.len()); + for arg in fn_abi.args.iter() { + match arg.mode { + PassMode::Ignore => {} + PassMode::Pair(a, b) => { + let arg_pos = argument_tys.len(); + argument_tys.push(apply_attrs( + arg.layout.scalar_pair_element_gcc_type(self, 0), + &a, + arg_pos, + )); + argument_tys.push(apply_attrs( + arg.layout.scalar_pair_element_gcc_type(self, 1), + &b, + arg_pos + 1, + )); + } + PassMode::Direct(attrs) => argument_tys.push(apply_attrs( + arg.layout.immediate_gcc_type(self), + &attrs, + argument_tys.len(), + )), + PassMode::Indirect { .. } | PassMode::Cast { .. } => { + unreachable!() + } + } + } + + #[cfg(feature = "master")] + let fn_attrs = if non_null_args.is_empty() { + Vec::new() + } else { + vec![FnAttribute::NonNull(non_null_args)] + }; + + let fn_ty = FnAbiGcc { + return_type, + arguments_type: argument_tys, + is_c_variadic: false, + on_stack_param_indices: FxHashSet::default(), + #[cfg(feature = "master")] + fn_attributes: fn_attrs, + }; let func = match sym { "llvm.fma.f16" => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 481f75f337d63..7327137fbbdd0 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -649,7 +649,32 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // FIXME remove usage of fn_abi let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); assert!(!fn_abi.ret.is_indirect()); - let fn_ty = fn_abi.llvm_type(self); + assert!(!fn_abi.c_variadic); + + let llreturn_ty = match &fn_abi.ret.mode { + PassMode::Ignore => self.type_void(), + PassMode::Direct(_) | PassMode::Pair(..) => fn_abi.ret.layout.immediate_llvm_type(self), + PassMode::Cast { .. } | PassMode::Indirect { .. } => { + unreachable!() + } + }; + + let mut llargument_tys = Vec::with_capacity(fn_abi.args.len()); + for arg in &fn_abi.args { + match &arg.mode { + PassMode::Ignore => {} + PassMode::Direct(_) => llargument_tys.push(arg.layout.immediate_llvm_type(self)), + PassMode::Pair(..) => { + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 0, true)); + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 1, true)); + } + PassMode::Indirect { .. } | PassMode::Cast { .. } => { + unreachable!() + } + }; + } + + let fn_ty = self.type_func(&llargument_tys, llreturn_ty); let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) { llfn @@ -665,12 +690,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let llfn = declare_raw_fn( self, sym, - fn_abi.llvm_cconv(self), + llvm::CCallConv, llvm::UnnamedAddr::Global, llvm::Visibility::Default, fn_ty, ); - fn_abi.apply_attrs_llfn(self, llfn, Some(instance)); llfn }; From f1ab0036585e64bffbdc8ac20e7b0a5fb9507e95 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:47:29 +0000 Subject: [PATCH 02/14] Don't compute FnAbi for LLVM intrinsics in backends --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 101 ++---------------- compiler/rustc_codegen_llvm/src/intrinsic.rs | 48 +++++---- 2 files changed, 34 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index f138b87d3405f..de262994fa458 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -4,9 +4,9 @@ mod simd; #[cfg(feature = "master")] use std::iter; -use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp}; #[cfg(feature = "master")] -use gccjit::{FnAttribute, Type}; +use gccjit::Type; +use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp}; #[cfg(feature = "master")] use rustc_abi::ExternAbi; use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; @@ -24,16 +24,16 @@ use rustc_codegen_ssa::traits::{ }; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; -use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; -use rustc_middle::ty::{self, Instance, Ty}; #[cfg(feature = "master")] -use rustc_session::config; +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; -#[cfg(feature = "master")] -use rustc_target::callconv::ArgAttributes; use rustc_target::callconv::{ArgAbi, PassMode}; -use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; +#[cfg(feature = "master")] +use crate::abi::FnAbiGccExt; +use crate::abi::GccType; use crate::builder::Builder; use crate::common::{SignType, TypeReflection}; use crate::context::CodegenCx; @@ -625,83 +625,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc *func } else { self.linkage.set(FunctionType::Extern); - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - assert!(!fn_abi.ret.is_indirect()); - assert!(!fn_abi.c_variadic); - - let return_type = match fn_abi.ret.mode { - PassMode::Ignore => self.type_void(), - PassMode::Direct(_) | PassMode::Pair(..) => { - fn_abi.ret.layout.immediate_gcc_type(self) - } - PassMode::Cast { .. } | PassMode::Indirect { .. } => { - unreachable!() - } - }; - - #[cfg(feature = "master")] - let mut non_null_args = Vec::new(); - - #[cfg(feature = "master")] - let mut apply_attrs = - |mut ty: Type<'gcc>, attrs: &ArgAttributes, arg_index: usize| { - if self.sess().opts.optimize == config::OptLevel::No { - return ty; - } - if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) { - ty = ty.make_restrict() - } - if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) { - non_null_args.push(arg_index as i32 + 1); - } - ty - }; - #[cfg(not(feature = "master"))] - let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes, _arg_index: usize| ty; - - let mut argument_tys = Vec::with_capacity(fn_abi.args.len()); - for arg in fn_abi.args.iter() { - match arg.mode { - PassMode::Ignore => {} - PassMode::Pair(a, b) => { - let arg_pos = argument_tys.len(); - argument_tys.push(apply_attrs( - arg.layout.scalar_pair_element_gcc_type(self, 0), - &a, - arg_pos, - )); - argument_tys.push(apply_attrs( - arg.layout.scalar_pair_element_gcc_type(self, 1), - &b, - arg_pos + 1, - )); - } - PassMode::Direct(attrs) => argument_tys.push(apply_attrs( - arg.layout.immediate_gcc_type(self), - &attrs, - argument_tys.len(), - )), - PassMode::Indirect { .. } | PassMode::Cast { .. } => { - unreachable!() - } - } - } - - #[cfg(feature = "master")] - let fn_attrs = if non_null_args.is_empty() { - Vec::new() - } else { - vec![FnAttribute::NonNull(non_null_args)] - }; - - let fn_ty = FnAbiGcc { - return_type, - arguments_type: argument_tys, - is_c_variadic: false, - on_stack_param_indices: FxHashSet::default(), - #[cfg(feature = "master")] - fn_attributes: fn_attrs, - }; let func = match sym { "llvm.fma.f16" => { @@ -714,13 +637,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.intrinsics.borrow_mut().insert(sym.to_string(), func); - self.on_stack_function_params - .borrow_mut() - .insert(func, fn_ty.on_stack_param_indices); - #[cfg(feature = "master")] - for fn_attr in fn_ty.fn_attributes { - func.add_attribute(fn_attr); - } + self.on_stack_function_params.borrow_mut().insert(func, FxHashSet::default()); crate::attributes::from_fn_attrs(self, func, instance); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 7327137fbbdd0..13402edd13e26 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -646,32 +646,34 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) -> Self::Value { let tcx = self.tcx(); - // FIXME remove usage of fn_abi - let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - assert!(!fn_abi.ret.is_indirect()); - assert!(!fn_abi.c_variadic); - - let llreturn_ty = match &fn_abi.ret.mode { - PassMode::Ignore => self.type_void(), - PassMode::Direct(_) | PassMode::Pair(..) => fn_abi.ret.layout.immediate_llvm_type(self), - PassMode::Cast { .. } | PassMode::Indirect { .. } => { - unreachable!() + let fn_ty = instance.ty(tcx, self.typing_env()); + let fn_sig = match *fn_ty.kind() { + ty::FnDef(def_id, args) => { + tcx.instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args)) } + _ => unreachable!(), }; + assert!(!fn_sig.c_variadic); - let mut llargument_tys = Vec::with_capacity(fn_abi.args.len()); - for arg in &fn_abi.args { - match &arg.mode { - PassMode::Ignore => {} - PassMode::Direct(_) => llargument_tys.push(arg.layout.immediate_llvm_type(self)), - PassMode::Pair(..) => { - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 0, true)); - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 1, true)); - } - PassMode::Indirect { .. } | PassMode::Cast { .. } => { - unreachable!() - } - }; + let ret_layout = self.layout_of(fn_sig.output()); + let llreturn_ty = if ret_layout.is_zst() { + self.type_void() + } else { + ret_layout.immediate_llvm_type(self) + }; + + let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len()); + for &arg in fn_sig.inputs() { + let arg_layout = self.layout_of(arg); + if arg_layout.is_zst() { + continue; + } + if let BackendRepr::ScalarPair(_, _) = arg_layout.backend_repr { + llargument_tys.push(arg_layout.scalar_pair_element_llvm_type(self, 0, true)); + llargument_tys.push(arg_layout.scalar_pair_element_llvm_type(self, 1, true)); + continue; + } + llargument_tys.push(arg_layout.immediate_llvm_type(self)); } let fn_ty = self.type_func(&llargument_tys, llreturn_ty); From fe9715b5e8fb9e425fa14ce2830a3faafd3adf78 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 Jan 2026 18:44:38 +0000 Subject: [PATCH 03/14] Remove support for ScalarPair unadjusted arguments --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 13402edd13e26..d7a928f492f0e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -668,11 +668,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { if arg_layout.is_zst() { continue; } - if let BackendRepr::ScalarPair(_, _) = arg_layout.backend_repr { - llargument_tys.push(arg_layout.scalar_pair_element_llvm_type(self, 0, true)); - llargument_tys.push(arg_layout.scalar_pair_element_llvm_type(self, 1, true)); - continue; - } llargument_tys.push(arg_layout.immediate_llvm_type(self)); } From d4454e59d3021557aead4591dea0390bf9d8e68b Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 29 Jan 2026 11:47:21 +0100 Subject: [PATCH 04/14] add regression test --- tests/ui/layout/rigid-alias-no-params.rs | 30 +++++++++++++++++++ ...und-unsatisfied-item-bounds-mit-opt-ice.rs | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/ui/layout/rigid-alias-no-params.rs diff --git a/tests/ui/layout/rigid-alias-no-params.rs b/tests/ui/layout/rigid-alias-no-params.rs new file mode 100644 index 0000000000000..bc2622eb7ca64 --- /dev/null +++ b/tests/ui/layout/rigid-alias-no-params.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -O -Cdebug-assertions=on +//@ build-pass + +// A regression test for #151791. Computing the layout of +// `>::Archived` fails as the alias +// is still rigid as the where-bound in scope shadows the impl. +// +// This previously caused an incorrect error during MIR optimizations. + +struct ArchivedString; + +pub trait ArchiveWith<'a> { + type Archived; +} + +struct AsOwned; +impl ArchiveWith<'_> for AsOwned { + type Archived = ArchivedString; +} + +fn foo<'a>() +where + AsOwned: ArchiveWith<'a>, +{ + let _ = unsafe { &*std::ptr::dangling::<>::Archived>() }; +} + +fn main() { + foo(); +} diff --git a/tests/ui/where-clauses/projection-bound-unsatisfied-item-bounds-mit-opt-ice.rs b/tests/ui/where-clauses/projection-bound-unsatisfied-item-bounds-mit-opt-ice.rs index 80eec709eecbc..9448c8a2f9112 100644 --- a/tests/ui/where-clauses/projection-bound-unsatisfied-item-bounds-mit-opt-ice.rs +++ b/tests/ui/where-clauses/projection-bound-unsatisfied-item-bounds-mit-opt-ice.rs @@ -2,7 +2,7 @@ //@ build-pass // A regression test for #149081. The environment of `size` and `align` -// currently means that the item bound of`T::Assoc` doesn't hold. This can +// currently means that the item bound of `T::Assoc` doesn't hold. This can // result in normalization failures and ICE during MIR optimizations. // // This will no longer be an issue once #149283 is implemented. From aa7c785e8a273fb8ceb0a93e282e8b7c5a3072cd Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 5 Feb 2026 17:44:01 +0900 Subject: [PATCH 05/14] Add note for `?Sized` params in int-ptr casts diag --- compiler/rustc_hir_typeck/src/cast.rs | 12 ++++++++++++ compiler/rustc_hir_typeck/src/errors.rs | 8 ++++++++ tests/ui/cast/fat-ptr-cast.stderr | 2 ++ 3 files changed, 22 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3f13a102684e0..b5094d736dd57 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -572,6 +572,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { let metadata = known_metadata.unwrap_or("type-specific metadata"); let known_wide = known_metadata.is_some(); let span = self.cast_span; + let param_note = (!known_wide) + .then(|| match cast_ty.kind() { + ty::RawPtr(pointee, _) => match pointee.kind() { + ty::Param(param) => { + Some(errors::IntToWideParamNote { param: param.name }) + } + _ => None, + }, + _ => None, + }) + .flatten(); fcx.dcx().emit_err(errors::IntToWide { span, metadata, @@ -579,6 +590,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { cast_ty, expr_if_nightly, known_wide, + param_note, }); } CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 0f330c3021c0f..60ac2acaec36c 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -590,6 +590,14 @@ pub(crate) struct IntToWide<'tcx> { )] pub expr_if_nightly: Option, pub known_wide: bool, + #[subdiagnostic] + pub param_note: Option, +} + +#[derive(Subdiagnostic)] +#[note("the type parameter `{$param}` is not known to be `Sized`, so this pointer may be wide")] +pub(crate) struct IntToWideParamNote { + pub param: Symbol, } #[derive(Subdiagnostic)] diff --git a/tests/ui/cast/fat-ptr-cast.stderr b/tests/ui/cast/fat-ptr-cast.stderr index 2b0bceebf15ca..c6354122f5685 100644 --- a/tests/ui/cast/fat-ptr-cast.stderr +++ b/tests/ui/cast/fat-ptr-cast.stderr @@ -81,6 +81,8 @@ LL | let s = 0 as *const T; | - ^^^^^^^^ creating a `*const T` requires both an address and type-specific metadata | | | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` + | + = note: the type parameter `T` is not known to be `Sized`, so this pointer may be wide error: aborting due to 11 previous errors From 3b705eae4fe9f16efd8193fc5dc822fc9e66e5da Mon Sep 17 00:00:00 2001 From: xizheyin Date: Mon, 9 Feb 2026 14:24:59 +0800 Subject: [PATCH 06/14] Add ui test insufficient-suggestion-issue-141679.rs --- .../insufficient-suggestion-issue-141679.rs | 8 ++++++++ .../insufficient-suggestion-issue-141679.stderr | 11 +++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs create mode 100644 tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr diff --git a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs new file mode 100644 index 0000000000000..daf8693b52642 --- /dev/null +++ b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs @@ -0,0 +1,8 @@ +use std::rc::Rc; +pub struct Foo; + +pub type Function = Rc; + +impl Function {} +//~^ ERROR cannot define inherent `impl` for a type outside of the crate where the type is defined [E0116] +fn main(){} \ No newline at end of file diff --git a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr new file mode 100644 index 0000000000000..cbb94ea4d367f --- /dev/null +++ b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr @@ -0,0 +1,11 @@ +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/insufficient-suggestion-issue-141679.rs:6:1 + | +LL | impl Function {} + | ^^^^^^^^^^^^^ impl for type defined outside of crate + | + = note: define and implement a trait or new type instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0116`. From ed90b3566960797ca7afbeca6c64ff042bf52158 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Mon, 9 Feb 2026 15:06:55 +0800 Subject: [PATCH 07/14] Add note when inherent impl for a alias type defined outside of the crate --- .../src/coherence/inherent_impls.rs | 17 ++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 18 +++++++++++++++++- tests/ui/error-codes/E0116.stderr | 3 ++- .../insufficient-suggestion-issue-141679.rs | 2 +- ...insufficient-suggestion-issue-141679.stderr | 8 +++++++- .../no-attr-empty-impl.stderr | 12 ++++++++---- .../no-other-unrelated-errors.stderr | 3 ++- .../ui/traits/trait-or-new-type-instead.stderr | 3 ++- 8 files changed, 55 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index edaf33e493c04..588747f46d17d 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -110,7 +110,22 @@ impl<'tcx> InherentCollect<'tcx> { Ok(()) } else { let impl_span = self.tcx.def_span(impl_def_id); - Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideNew { span: impl_span })) + let mut err = errors::InherentTyOutsideNew { span: impl_span, note: None }; + + if let hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) = + self.tcx.hir_node_by_def_id(impl_def_id).expect_item().expect_impl().self_ty.kind + && let rustc_hir::def::Res::Def(DefKind::TyAlias, def_id) = path.res + { + let ty_name = self.tcx.def_path_str(def_id); + let alias_ty_name = self.tcx.type_of(def_id).skip_binder().to_string(); + err.note = Some(errors::InherentTyOutsideNewAliasNote { + span: self.tcx.def_span(def_id), + ty_name, + alias_ty_name, + }); + } + + Err(self.tcx.dcx().emit_err(err)) } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a23b42ae0981..fb79789df76ea 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1223,11 +1223,27 @@ pub(crate) struct InherentTyOutsideRelevant { #[derive(Diagnostic)] #[diag("cannot define inherent `impl` for a type outside of the crate where the type is defined", code = E0116)] -#[note("define and implement a trait or new type instead")] +#[help( + "consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it" +)] +#[note( + "for more details about the orphan rules, see " +)] pub(crate) struct InherentTyOutsideNew { #[primary_span] #[label("impl for type defined outside of crate")] pub span: Span, + #[subdiagnostic] + pub note: Option, +} + +#[derive(Subdiagnostic)] +#[note("`{$ty_name}` does not define a new type, only an alias of `{$alias_ty_name}` defined here")] +pub(crate) struct InherentTyOutsideNewAliasNote { + #[primary_span] + pub span: Span, + pub ty_name: String, + pub alias_ty_name: String, } #[derive(Diagnostic)] diff --git a/tests/ui/error-codes/E0116.stderr b/tests/ui/error-codes/E0116.stderr index 1ea5a57f46db1..20e3b196226af 100644 --- a/tests/ui/error-codes/E0116.stderr +++ b/tests/ui/error-codes/E0116.stderr @@ -4,7 +4,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl Vec {} | ^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error: aborting due to 1 previous error diff --git a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs index daf8693b52642..b13b6f418d425 100644 --- a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs +++ b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.rs @@ -5,4 +5,4 @@ pub type Function = Rc; impl Function {} //~^ ERROR cannot define inherent `impl` for a type outside of the crate where the type is defined [E0116] -fn main(){} \ No newline at end of file +fn main(){} diff --git a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr index cbb94ea4d367f..a62f7f82ba9d9 100644 --- a/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr +++ b/tests/ui/incoherent-inherent-impls/insufficient-suggestion-issue-141679.stderr @@ -4,7 +4,13 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl Function {} | ^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see +note: `Function` does not define a new type, only an alias of `Rc` defined here + --> $DIR/insufficient-suggestion-issue-141679.rs:4:1 + | +LL | pub type Function = Rc; + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr b/tests/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr index f8491697910c0..de61c3900d461 100644 --- a/tests/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr +++ b/tests/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr @@ -4,7 +4,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl extern_crate::StructWithAttr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined --> $DIR/no-attr-empty-impl.rs:7:1 @@ -12,7 +13,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl extern_crate::StructNoAttr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined --> $DIR/no-attr-empty-impl.rs:10:1 @@ -20,7 +22,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl extern_crate::EnumWithAttr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined --> $DIR/no-attr-empty-impl.rs:13:1 @@ -28,7 +31,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl extern_crate::EnumNoAttr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error[E0390]: cannot define inherent `impl` for primitive types --> $DIR/no-attr-empty-impl.rs:16:1 diff --git a/tests/ui/incoherent-inherent-impls/no-other-unrelated-errors.stderr b/tests/ui/incoherent-inherent-impls/no-other-unrelated-errors.stderr index 2a33262f83898..f01817e294430 100644 --- a/tests/ui/incoherent-inherent-impls/no-other-unrelated-errors.stderr +++ b/tests/ui/incoherent-inherent-impls/no-other-unrelated-errors.stderr @@ -4,7 +4,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl Vec {} | ^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error: aborting due to 1 previous error diff --git a/tests/ui/traits/trait-or-new-type-instead.stderr b/tests/ui/traits/trait-or-new-type-instead.stderr index 5f5aa3ac56982..ad12a84a4b808 100644 --- a/tests/ui/traits/trait-or-new-type-instead.stderr +++ b/tests/ui/traits/trait-or-new-type-instead.stderr @@ -4,7 +4,8 @@ error[E0116]: cannot define inherent `impl` for a type outside of the crate wher LL | impl Option { | ^^^^^^^^^^^^^^^^^ impl for type defined outside of crate | - = note: define and implement a trait or new type instead + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see error: aborting due to 1 previous error From c9b5c934ca7acc33ff4aebbe1630cb02bb4c0f1f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 10 Feb 2026 11:39:06 +0100 Subject: [PATCH 08/14] Fix passing/returning structs with the 64-bit SPARC ABI Co-authored-by: beetrees --- compiler/rustc_abi/src/callconv/reg.rs | 1 + compiler/rustc_target/src/callconv/sparc64.rs | 342 +++++++++--------- tests/assembly-llvm/sparc-struct-abi.rs | 193 +++++++++- tests/codegen-llvm/cast-target-abi.rs | 16 +- 4 files changed, 366 insertions(+), 186 deletions(-) diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 66c8056d0c2af..66d4dca00726f 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -35,6 +35,7 @@ impl Reg { reg_ctor!(f32, Float, 32); reg_ctor!(f64, Float, 64); + reg_ctor!(f128, Float, 128); } impl Reg { diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index fc732170dcb73..f55d03d89e8ff 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,214 +1,203 @@ -// FIXME: This needs an audit for correctness and completeness. - use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Scalar, Size, TyAbiInterface, - TyAndLayout, + Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, + TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; -use crate::spec::{Env, HasTargetSpec, Os}; - -#[derive(Clone, Debug)] -struct Sdata { - pub prefix: [Option; 8], - pub prefix_index: usize, - pub last_offset: Size, - pub has_float: bool, - pub arg_attribute: ArgAttribute, -} - -fn arg_scalar(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata -where - C: HasDataLayout, -{ - let dl = cx.data_layout(); - - if !matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) { - return data; - } - - data.has_float = true; - - if !data.last_offset.is_aligned(dl.f64_align) && data.last_offset < offset { - if data.prefix_index == data.prefix.len() { - return data; - } - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i32().size; - } - - for _ in 0..((offset - data.last_offset).bits() / 64) - .min((data.prefix.len() - data.prefix_index) as u64) - { - data.prefix[data.prefix_index] = Some(Reg::i64()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i64().size; - } +use crate::spec::{HasTargetSpec, Os}; - if data.last_offset < offset { - if data.prefix_index == data.prefix.len() { - return data; - } - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset = data.last_offset + Reg::i32().size; - } - - if data.prefix_index == data.prefix.len() { - return data; - } +// NOTE: GCC and Clang/LLVM have disagreements that the ABI doesn't resolve, we match the +// Clang/LLVM behavior in these cases. - if scalar.primitive() == Primitive::Float(Float::F32) { - data.arg_attribute = ArgAttribute::InReg; - data.prefix[data.prefix_index] = Some(Reg::f32()); - data.last_offset = offset + Reg::f32().size; - } else { - data.prefix[data.prefix_index] = Some(Reg::f64()); - data.last_offset = offset + Reg::f64().size; - } - data.prefix_index += 1; - data +#[derive(Copy, Clone)] +enum DoubleWord { + F64, + F128Start, + F128End, + Words([Word; 2]), } -fn arg_scalar_pair( - cx: &C, - scalar1: &Scalar, - scalar2: &Scalar, - mut offset: Size, - mut data: Sdata, -) -> Sdata -where - C: HasDataLayout, -{ - data = arg_scalar(cx, scalar1, offset, data); - match (scalar1.primitive(), scalar2.primitive()) { - (Primitive::Float(Float::F32), _) => offset += Reg::f32().size, - (_, Primitive::Float(Float::F64)) => offset += Reg::f64().size, - (Primitive::Int(i, _signed), _) => offset += i.size(), - (Primitive::Pointer(_), _) => offset += Reg::i64().size, - _ => {} - } - - if !offset.bytes().is_multiple_of(4) - && matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64)) - { - offset += Size::from_bytes(4 - (offset.bytes() % 4)); - } - data = arg_scalar(cx, scalar2, offset, data); - data +#[derive(Copy, Clone)] +enum Word { + F32, + Integer, } -fn parse_structure<'a, Ty, C>( +fn classify<'a, Ty, C>( cx: &C, - layout: TyAndLayout<'a, Ty>, - mut data: Sdata, - mut offset: Size, -) -> Sdata -where + arg_layout: &TyAndLayout<'a, Ty>, + offset: Size, + double_words: &mut [DoubleWord; 4], +) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if let FieldsShape::Union(_) = layout.fields { - return data; - } - - match layout.backend_repr { - BackendRepr::Scalar(scalar) => { - data = arg_scalar(cx, &scalar, offset, data); - } - BackendRepr::Memory { .. } => { - for i in 0..layout.fields.count() { - if offset < layout.fields.offset(i) { - offset = layout.fields.offset(i); + // If this function does not update the `double_words` array, the value will be passed via + // integer registers. The array is initialized with `DoubleWord::Words([Word::Integer; 2])`. + + match arg_layout.backend_repr { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(float) => { + if offset.is_aligned(Ord::min(*float.align(cx), Align::EIGHT)) { + let index = offset.bytes_usize() / 8; + match float { + Float::F128 => { + double_words[index] = DoubleWord::F128Start; + double_words[index + 1] = DoubleWord::F128End; + } + Float::F64 => { + double_words[index] = DoubleWord::F64; + } + Float::F32 => match &mut double_words[index] { + DoubleWord::Words(words) => { + words[(offset.bytes_usize() % 8) / 4] = Word::F32; + } + _ => unreachable!(), + }, + Float::F16 => { + // Match LLVM by passing `f16` in integer registers. + } + } + } else { + /* pass unaligned floats in integer registers */ } - data = parse_structure(cx, layout.field(cx, i), data.clone(), offset); } - } - _ => { - if let BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr { - data = arg_scalar_pair(cx, scalar1, scalar2, offset, data); + Primitive::Int(_, _) | Primitive::Pointer(_) => { /* pass in integer registers */ } + }, + BackendRepr::SimdVector { .. } => {} + BackendRepr::ScalableVector { .. } => {} + BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") } - } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + if arg_layout.is_transparent() { + let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1; + classify(cx, &non_1zst_elem, offset, double_words); + } + } + } + FieldsShape::Array { .. } => {} + FieldsShape::Arbitrary { .. } => match arg_layout.variants { + Variants::Multiple { .. } => {} + Variants::Single { .. } | Variants::Empty => { + // Match Clang by ignoring whether a struct is packed and just considering + // whether individual fields are aligned. GCC currently uses only integer + // registers when passing packed structs. + for i in arg_layout.fields.index_by_increasing_offset() { + classify( + cx, + &arg_layout.field(cx, i), + offset + arg_layout.fields.offset(i), + double_words, + ); + } + } + }, + }, } - - data } -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size) -where +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + in_registers_max: Size, + total_double_word_count: &mut usize, +) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + // 64-bit SPARC allocates argument stack space in 64-bit chunks (double words), some of which + // are promoted to registers based on their position on the stack. + + // Keep track of the total number of double words used by arguments so far. This allows padding + // arguments to be inserted where necessary to ensure that 16-aligned arguments are passed in an + // aligned set of registers. + + let pad = !total_double_word_count.is_multiple_of(2) && arg.layout.align.abi.bytes() == 16; + // The number of double words used by this argument. + let double_word_count = arg.layout.size.bytes_usize().div_ceil(8); + // The number of double words before this argument, including any padding. + let start_double_word_count = *total_double_word_count + usize::from(pad); + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + *total_double_word_count += 1; return; } + if !arg.layout.is_aggregate() { arg.extend_integer_width_to(64); + *total_double_word_count = start_double_word_count + double_word_count; return; } let total = arg.layout.size; if total > in_registers_max { arg.make_indirect(); + *total_double_word_count += 1; return; } - match arg.layout.fields { - FieldsShape::Primitive => unreachable!(), - FieldsShape::Array { .. } => { - // Arrays are passed indirectly - arg.make_indirect(); - return; - } - FieldsShape::Union(_) => { - // Unions and are always treated as a series of 64-bit integer chunks - } - FieldsShape::Arbitrary { .. } => { - // Structures with floating point numbers need special care. - - let mut data = parse_structure( - cx, - arg.layout, - Sdata { - prefix: [None; 8], - prefix_index: 0, - last_offset: Size::ZERO, - has_float: false, - arg_attribute: ArgAttribute::default(), - }, - Size::ZERO, - ); - - if data.has_float { - // Structure { float, int, int } doesn't like to be handled like - // { float, long int }. Other way around it doesn't mind. - if data.last_offset < arg.layout.size - && !data.last_offset.bytes().is_multiple_of(8) - && data.prefix_index < data.prefix.len() - { - data.prefix[data.prefix_index] = Some(Reg::i32()); - data.prefix_index += 1; - data.last_offset += Reg::i32().size; - } + *total_double_word_count = start_double_word_count + double_word_count; - let mut rest_size = arg.layout.size - data.last_offset; - if !rest_size.bytes().is_multiple_of(8) && data.prefix_index < data.prefix.len() { - data.prefix[data.prefix_index] = Some(Reg::i32()); - rest_size = rest_size - Reg::i32().size; - } + const ARGUMENT_REGISTERS: usize = 8; - arg.cast_to( - CastTarget::prefixed(data.prefix, Uniform::new(Reg::i64(), rest_size)) - .with_attrs(data.arg_attribute.into()), - ); - return; + let mut double_words = [DoubleWord::Words([Word::Integer; 2]); ARGUMENT_REGISTERS / 2]; + classify(cx, &arg.layout, Size::ZERO, &mut double_words); + + let mut regs = [None; ARGUMENT_REGISTERS]; + let mut i = 0; + let mut push = |reg| { + regs[i] = Some(reg); + i += 1; + }; + let mut attrs = ArgAttribute::empty(); + + for (index, double_word) in double_words.into_iter().enumerate() { + if arg.layout.size.bytes_usize() <= index * 8 { + break; + } + match double_word { + // `f128` must be aligned to be assigned a float register. + DoubleWord::F128Start if (start_double_word_count + index).is_multiple_of(2) => { + push(Reg::f128()); + } + DoubleWord::F128Start => { + // Clang currently handles this case nonsensically, always returning a packed + // `struct { long double x; }` in an aligned quad floating-point register even when + // the `long double` isn't aligned on the stack, which also makes all future + // arguments get passed in the wrong registers. This passes the `f128` in integer + // registers when it is unaligned, same as with `f32` and `f64`. + push(Reg::i64()); + push(Reg::i64()); + } + DoubleWord::F128End => {} // Already handled by `F128Start` + DoubleWord::F64 => push(Reg::f64()), + DoubleWord::Words([Word::Integer, Word::Integer]) => push(Reg::i64()), + DoubleWord::Words(words) => { + attrs |= ArgAttribute::InReg; + for word in words { + match word { + Word::F32 => push(Reg::f32()), + Word::Integer => push(Reg::i32()), + } + } } } } - arg.cast_to(Uniform::new(Reg::i64(), total)); + let cast_target = match regs { + [Some(reg), None, rest @ ..] => { + // Just a single register is needed for this value. + debug_assert!(rest.iter().all(|x| x.is_none())); + CastTarget::from(reg) + } + _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)), + }; + + arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), pad); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -217,23 +206,26 @@ where C: HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() && fn_abi.ret.layout.is_sized() { - classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32)); + // A return value of 32 bytes or smaller is passed via registers. + classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32), &mut 0); } + // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. + let passes_zsts = matches!(cx.target_spec().os, Os::Linux); + + let mut double_word_count = 0; for arg in fn_abi.args.iter_mut() { if !arg.layout.is_sized() { continue; } if arg.is_ignore() { - // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. - if cx.target_spec().os == Os::Linux - && matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc) - && arg.layout.is_zst() - { + if passes_zsts && arg.layout.is_zst() { arg.make_indirect_from_ignore(); + double_word_count += 1; } - return; + continue; } - classify_arg(cx, arg, Size::from_bytes(16)); + // An argument of 16 bytes or smaller is passed via registers. + classify_arg(cx, arg, Size::from_bytes(16), &mut double_word_count); } } diff --git a/tests/assembly-llvm/sparc-struct-abi.rs b/tests/assembly-llvm/sparc-struct-abi.rs index c6e83b6f8dabe..41ac2be09b653 100644 --- a/tests/assembly-llvm/sparc-struct-abi.rs +++ b/tests/assembly-llvm/sparc-struct-abi.rs @@ -9,6 +9,7 @@ #![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] +#![feature(f128)] extern crate minicore; use minicore::*; @@ -21,8 +22,33 @@ pub struct Franta { d: f32, } +#[repr(C, packed)] +struct Misaligned(i32, f64); + +#[repr(C)] +struct AlignToMakeAssemblyShorter(T, f64); + +#[repr(C)] +pub struct Floats(i32, f32, f64, f128); + +#[repr(C)] +pub struct LessFloats(f32, i32, f64); + +#[repr(C)] +pub struct NotMisaligned(i32, Misaligned); + +#[repr(C, align(16))] +pub struct Align16(f64, i32, i32); + +impl Copy for Misaligned {} +impl Copy for AlignToMakeAssemblyShorter {} +impl Copy for Floats {} +impl Copy for LessFloats {} +impl Copy for NotMisaligned {} +impl Copy for Align16 {} + // NB: due to delay slots the `ld` following the call is actually executed before the call. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn callee(arg: Franta) { // CHECK-LABEL: callee: // CHECK: st %f3, [[PLACE_D:.*]] @@ -54,7 +80,7 @@ extern "C" { fn tail_call_avoidance_fn(); } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn caller() { // CHECK-LABEL: caller: // CHECK: ld [{{.*}}], %f0 @@ -62,7 +88,168 @@ pub unsafe extern "C" fn caller() { // CHECK: ld [{{.*}}], %f2 // CHECK: ld [{{.*}}], %f3 // CHECK: call opaque_callee - // CHECK: mov 3, %o2 + // CHECK: mov 3, %o2 opaque_callee(Franta { a: 1.0, b: 2.0, c: 3.0, d: 4.0 }, 3); tail_call_avoidance_fn(); } + +// Check that misaligned floats aren't promoted to floating point registers. +// CHECK-LABEL: misaligned_arg: +#[unsafe(no_mangle)] +extern "C" fn misaligned_arg(x: &mut AlignToMakeAssemblyShorter, value: Misaligned) { + // CHECK: srlx %o2, 32, %o2 + // CHECK-NEXT: stx %o1, [%o0] + // CHECK-NEXT: retl + // CHECK-NEXT: st %o2, [%o0+8] + x.0 = value; +} + +// CHECK-LABEL: misaligned_ret: +#[unsafe(no_mangle)] +extern "C" fn misaligned_ret(x: &AlignToMakeAssemblyShorter) -> Misaligned { + // CHECK: ld [%o0+8], %o1 + // CHECK-NEXT: ldx [%o0], %o0 + // CHECK-NEXT: retl + // CHECK-NEXT: sllx %o1, 32, %o1 + x.0 +} + +// Check structs where 32 >= size > 16 are promoted to register only as an argument. +// Also check that the various floating-point types are promoted to the correct registers. +// CHECK-LABEL: floats_arg: +#[unsafe(no_mangle)] +extern "C" fn floats_arg(x: &mut Floats, value: Floats) { + // CHECK: ldx [%o1+24], %o2 + // CHECK-NEXT: ldx [%o1+16], %o3 + // CHECK-NEXT: ldx [%o1+8], %o4 + // CHECK-NEXT: ldx [%o1], %o1 + // CHECK-NEXT: stx %o2, [%o0+24] + // CHECK-NEXT: stx %o3, [%o0+16] + // CHECK-NEXT: stx %o4, [%o0+8] + // CHECK-NEXT: retl + // CHECK-NEXT: stx %o1, [%o0] + *x = value; +} + +// CHECK-LABEL: floats_ret: +#[unsafe(no_mangle)] +extern "C" fn floats_ret(x: &Floats) -> Floats { + // CHECK: ld [%o0+4], %f1 + // CHECK-NEXT: ldd [%o0+8], %f2 + // CHECK-NEXT: ldd [%o0+16], %f4 + // CHECK-NEXT: ld [%o0], %o1 + // CHECK-NEXT: ldd [%o0+24], %f6 + // CHECK-NEXT: retl + // CHECK-NEXT: sllx %o1, 32, %o0 + *x +} + +// Check float promotion when passing as an argument with a struct where size <= 16. +// CHECK-LABEL: less_floats_arg: +#[unsafe(no_mangle)] +extern "C" fn less_floats_arg(x: &mut LessFloats, value: LessFloats) { + // CHECK: st %f2, [%o0] + // CHECK-NEXT: st %o1, [%o0+4] + // CHECK-NEXT: retl + // CHECK-NEXT: std %f4, [%o0+8] + *x = value; +} + +// CHECK-LABEL: less_floats_ret: +#[unsafe(no_mangle)] +extern "C" fn less_floats_ret(x: &LessFloats) -> LessFloats { + // CHECK: ld [%o0], %f0 + // CHECK-NEXT: ldd [%o0+8], %f2 + // CHECK-NEXT: retl + // CHECK-NEXT: ld [%o0+4], %o0 + *x +} + +// Check fields are promoted if they are aligned in the overall structure. +// This matches Clang's behaviour but not GCC's. +// CHECK-LABEL: not_misaligned_arg: +#[unsafe(no_mangle)] +extern "C" fn not_misaligned_arg( + x: &mut AlignToMakeAssemblyShorter, + value: NotMisaligned, +) { + // CHECK: stx %o1, [%o0] + // CHECK-NEXT: retl + // CHECK-NEXT: std %f4, [%o0+8] + x.0 = value; +} + +// CHECK-LABEL: not_misaligned_ret: +#[unsafe(no_mangle)] +extern "C" fn not_misaligned_ret(x: &AlignToMakeAssemblyShorter) -> NotMisaligned { + // CHECK: ldx [%o0], %o1 + // CHECK-NEXT: ldd [%o0+8], %f2 + // CHECK-NEXT: retl + // CHECK-NEXT: mov %o1, %o0 + x.0 +} + +// Check that 16-aligned structs are allocated the correct registers. +// CHECK-LABEL: align_16_arg: +#[unsafe(no_mangle)] +extern "C" fn align_16_arg(x: &mut Align16, value: Align16) { + // CHECK: std %f4, [%o0] + // CHECK-NEXT: retl + // CHECK-NEXT: stx %o3, [%o0+8] + *x = value; +} + +// CHECK-LABEL: align_16_ret: +#[unsafe(no_mangle)] +extern "C" fn align_16_ret(x: &Align16) -> Align16 { + // CHECK: ldd [%o0], %f0 + // CHECK-NEXT: retl + // CHECK-NEXT: ldx [%o0+8], %o1 + *x +} + +// Check ZST args don't prevent further arguments from being processed. +// CHECK-LABEL: zst_arg: +#[unsafe(no_mangle)] +extern "C" fn zst_arg(_: (), value: LessFloats, x: &mut LessFloats) { + // CHECK: st %f0, [%o2] + // CHECK-NEXT: st %o0, [%o2+4] + // CHECK-NEXT: retl + // CHECK-NEXT: std %f2, [%o2+8] + *x = value; +} + +#[repr(C)] +struct I32F32Input { + a: i32, + b: f32, +} + +#[repr(C)] +struct I32F32Output { + b: f32, + a: i32, +} + +// The clang/LLVM implementation mentions that this case requires special handling. +// CHECK-LABEL: i32_f32: +#[unsafe(no_mangle)] +extern "C" fn i32_f32(input: I32F32Input) -> I32F32Output { + // CHECK: srlx %o0, 32, %o0 + // CHECK-NEXT: fmovs %f1, %f0 + // CHECK-NEXT: retl + // CHECK-NEXT: nop + I32F32Output { a: input.a, b: input.b } +} + +#[repr(C)] +pub struct C { + a: f64, + b: f32, +} + +// regression test for https://github.com/rust-lang/rust/issues/147883. +#[unsafe(no_mangle)] +pub extern "C" fn foo(c: C) -> C { + c +} diff --git a/tests/codegen-llvm/cast-target-abi.rs b/tests/codegen-llvm/cast-target-abi.rs index 101e73e33c915..6f1ab4572ee0d 100644 --- a/tests/codegen-llvm/cast-target-abi.rs +++ b/tests/codegen-llvm/cast-target-abi.rs @@ -119,7 +119,7 @@ pub extern "C" fn returns_twou16s() -> TwoU16s { // aarch64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) // loongarch64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) // powerpc64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) -// sparc64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) +// sparc64-SAME: ([[ABI_TYPE:{ i64, i64 }]] {{.*}}[[ABI_VALUE:%.+]]) // x86_64-SAME: ([[ABI_TYPE:{ i64, i16 }]] {{.*}}[[ABI_VALUE:%.+]]) #[no_mangle] #[inline(never)] @@ -148,7 +148,7 @@ pub extern "C" fn returns_fiveu16s() -> FiveU16s { // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i64 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i16 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // aarch64: ret [[ABI_TYPE]] [[ABI_VALUE]] @@ -217,7 +217,7 @@ pub extern "C" fn returns_doubledouble() -> DoubleDouble { // aarch64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) // loongarch64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) // powerpc64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) -// sparc64-SAME: ([[ABI_TYPE:\[2 x i64\]]] {{.*}}[[ABI_VALUE:%.+]]) +// sparc64-SAME: ([[ABI_TYPE:{ i64, i64 }]] {{.*}}[[ABI_VALUE:%.+]]) // x86_64-SAME: ([[ABI_TYPE:{ i64, i32 }]] {{.*}}[[ABI_VALUE:%.+]]) #[no_mangle] #[inline(never)] @@ -246,7 +246,7 @@ pub extern "C" fn returns_three32s() -> Three32s { // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i64 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i32 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // aarch64: ret [[ABI_TYPE]] [[ABI_VALUE]] @@ -399,7 +399,7 @@ pub fn call_fiveu16s() { // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // powerpc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i64 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i16 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // CHECK: call void @receives_fiveu16s([[ABI_TYPE]] [[ABI_VALUE]]) @@ -424,7 +424,7 @@ pub fn return_fiveu16s() -> FiveU16s { // aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_fiveu16s() // loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_fiveu16s() - // sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_fiveu16s() + // sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ i64, i64 }]] @returns_fiveu16s() // x86_64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ i64, i16 }]] @returns_fiveu16s() // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] @@ -595,7 +595,7 @@ pub fn call_three32s() { // aarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // powerpc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] - // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] + // sparc64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i64 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // x86_64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ i64, i32 }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] // CHECK: call void @receives_three32s([[ABI_TYPE]] [[ABI_VALUE]]) @@ -619,7 +619,7 @@ pub fn return_three32s() -> Three32s { // aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_three32s() // loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_three32s() - // sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:\[2 x i64\]]] @returns_three32s() + // sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ i64, i64 }]] @returns_three32s() // x86_64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ i64, i32 }]] @returns_three32s() // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]] From 39a532445aeea34ad5ebcb4ec9512563be1a44c9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 10 Feb 2026 18:00:42 +0000 Subject: [PATCH 09/14] prevent incorrect layout error aliases may be rigid even if they don't reference params. If the alias isn't well-formed, trying to normalize it as part of the input should have already failed --- compiler/rustc_ty_utils/src/layout.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 62f3667ad7f4f..bebc0707e26d9 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -764,14 +764,20 @@ fn layout_of_uncached<'tcx>( } ty::Alias(..) => { - // NOTE(eddyb) `layout_of` query should've normalized these away, - // if that was possible, so there's no reason to try again here. - let err = if ty.has_param() { + // In case we're still in a generic context, aliases might be rigid. E.g. + // if we've got a `T: Trait` where-bound, `T::Assoc` cannot be normalized + // in the current context. + // + // For some builtin traits, generic aliases can be rigid even in an empty environment, + // e.g. `::Metadata`. + // + // Due to trivial bounds, this can even be the case if the alias does not reference + // any generic parameters, e.g. a `for<'a> u32: Trait<'a>` where-bound means that + // `>::Assoc` is rigid. + let err = if ty.has_param() || !cx.typing_env.param_env.caller_bounds().is_empty() { LayoutError::TooGeneric(ty) } else { - // This is only reachable with unsatisfiable predicates. For example, if we have - // `u8: Iterator`, then we can't compute the layout of `::Item`. - LayoutError::Unknown(ty) + unreachable!("invalid rigid alias in layout_of after normalization: {ty:?}"); }; return Err(error(cx, err)); } From 337abba988bda85e1340d892e3c6a0c128ca9675 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 10 Feb 2026 18:00:54 +0000 Subject: [PATCH 10/14] fix rustdoc test --- tests/rustdoc-html/type-layout.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/rustdoc-html/type-layout.rs b/tests/rustdoc-html/type-layout.rs index 482b8b597dd30..84d2aa8060079 100644 --- a/tests/rustdoc-html/type-layout.rs +++ b/tests/rustdoc-html/type-layout.rs @@ -64,6 +64,12 @@ pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); //@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; +pub trait Project { type Assoc; } +// We can't compute layout as the alias stays rigid. A `LayoutError::TooGeneric` is returned. +//@ hasraw type_layout/struct.RigidAlias.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' +//@ !hasraw - 'Size: ' +pub struct RigidAlias(<() as Project>::Assoc) where for<'a> (): Project; + //@ !hasraw type_layout/trait.MyTrait.html 'Size: ' pub trait MyTrait {} @@ -92,9 +98,3 @@ pub enum Uninhabited {} //@ hasraw type_layout/struct.Uninhabited2.html 'Size: ' //@ hasraw - '8 bytes (uninhabited)' pub struct Uninhabited2(std::convert::Infallible, u64); - -pub trait Project { type Assoc; } -// We can't compute layout. A `LayoutError::Unknown` is returned. -//@ hasraw type_layout/struct.Unknown.html 'Unable to compute type layout.' -//@ !hasraw - 'Size: ' -pub struct Unknown(<() as Project>::Assoc) where for<'a> (): Project; From f53eed56d2ae49064b79abd49e4f849d4f41edc3 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Fri, 30 Jan 2026 15:29:22 +0100 Subject: [PATCH 11/14] Borrowck: simplify diagnostics for placeholders. This essentially folds the call to `region_from_element` into `RegionInferenceContext`, and simplifies the error variant for this case. It also clarifies the type information on the methods called to emphasise the fact that they only ever use placeholder regions in the diagnostics, and completely ignore any other element. --- .../src/diagnostics/bound_region_errors.rs | 32 ++++---- .../src/diagnostics/region_errors.rs | 73 +++++++++++-------- .../rustc_borrowck/src/region_infer/mod.rs | 8 +- 3 files changed, 62 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 6ed07cf9b1c8c..a927c30fae325 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_ use tracing::{debug, instrument}; use crate::MirBorrowckCtxt; -use crate::region_infer::values::RegionElement; use crate::session_diagnostics::{ HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError, }; @@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } + /// Report an error where an element erroneously made its way into `placeholder`. pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion<'tcx>, - error_element: RegionElement<'tcx>, + error_element: Option>, cause: ObligationCause<'tcx>, ) { match *self { @@ -146,14 +146,14 @@ pub(crate) trait TypeOpInfo<'tcx> { ) -> Option>; /// Constraints require that `error_element` appear in the - /// values of `placeholder`, but this cannot be proven to + /// values of `placeholder`, but this cannot be proven to /// hold. Report an error. #[instrument(level = "debug", skip(self, mbcx))] fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion<'tcx>, - error_element: RegionElement<'tcx>, + error_element: Option>, cause: ObligationCause<'tcx>, ) { let tcx = mbcx.infcx.tcx; @@ -172,19 +172,17 @@ pub(crate) trait TypeOpInfo<'tcx> { ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound), ); - let error_region = - if let RegionElement::PlaceholderRegion(error_placeholder) = error_element { - let adjusted_universe = - error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); - adjusted_universe.map(|adjusted| { - ty::Region::new_placeholder( - tcx, - ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound), - ) - }) - } else { - None - }; + // FIXME: one day this should just be error_element, + // and this method shouldn't do anything. + let error_region = error_element.and_then(|e| { + let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32()); + adjusted_universe.map(|adjusted| { + ty::Region::new_placeholder( + tcx, + ty::PlaceholderRegion::new(adjusted.into(), e.bound), + ) + }) + }); debug!(?placeholder_region); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 17f1988a17c40..69a83a35f212e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -29,7 +29,6 @@ use tracing::{debug, instrument, trace}; use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; -use crate::region_infer::values::RegionElement; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, @@ -104,15 +103,9 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// Higher-ranked subtyping error. - BoundUniversalRegionError { - /// The placeholder free region. - longer_fr: RegionVid, - /// The region element that erroneously must be outlived by `longer_fr`. - error_element: RegionElement<'tcx>, - /// The placeholder region. - placeholder: ty::PlaceholderRegion<'tcx>, - }, + /// 'p outlives 'r, which does not hold. 'p is always a placeholder + /// and 'r is some other region. + PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid }, /// Any other lifetime error. RegionError { @@ -360,28 +353,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - RegionErrorKind::BoundUniversalRegionError { + RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - placeholder, - error_element, + illegally_outlived_r, } => { - let error_vid = self.regioncx.region_from_element(longer_fr, &error_element); - - // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let cause = self - .regioncx - .best_blame_constraint( - longer_fr, - NllRegionVariableOrigin::Placeholder(placeholder), - error_vid, - ) - .0 - .cause; - - let universe = placeholder.universe; - let universe_info = self.regioncx.universe_info(universe); - - universe_info.report_erroneous_element(self, placeholder, error_element, cause); + self.report_erroneous_rvid_reaches_placeholder(longer_fr, illegally_outlived_r) } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { @@ -412,6 +388,43 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlives_suggestion.add_suggestion(self); } + /// Report that `longer_fr: error_vid`, which doesn't hold, + /// where `longer_fr` is a placeholder. + fn report_erroneous_rvid_reaches_placeholder( + &mut self, + longer_fr: RegionVid, + error_vid: RegionVid, + ) { + use NllRegionVariableOrigin::*; + + let origin_longer = self.regioncx.definitions[longer_fr].origin; + + let Placeholder(placeholder) = origin_longer else { + bug!("Expected {longer_fr:?} to come from placeholder!"); + }; + + // FIXME: Is throwing away the existential region really the best here? + let error_region = match self.regioncx.definitions[error_vid].origin { + FreeRegion | Existential { .. } => None, + Placeholder(other_placeholder) => Some(other_placeholder), + }; + + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. + let cause = + self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause; + + // FIXME these methods should have better names, and also probably not be this generic. + // FIXME note that we *throw away* the error element here! We probably want to + // thread it through the computation further down and use it, but there currently isn't + // anything there to receive it. + self.regioncx.universe_info(placeholder.universe).report_erroneous_element( + self, + placeholder, + error_region, + cause, + ); + } + /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6ed70b39c5b7f..5cdda777723b3 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1379,11 +1379,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { .elements_contained_in(longer_fr_scc) .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) { + let illegally_outlived_r = self.region_from_element(longer_fr, &error_element); // Stop after the first error, it gets too noisy otherwise, and does not provide more information. - errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - error_element, - placeholder, + illegally_outlived_r, }); } else { debug!("check_bound_universal_region: all bounds satisfied"); @@ -1572,7 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Get the region outlived by `longer_fr` and live at `element`. - pub(crate) fn region_from_element( + fn region_from_element( &self, longer_fr: RegionVid, element: &RegionElement<'tcx>, From 846e4ee5dccfc21f13563a9b0af575c139129bc2 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sun, 8 Feb 2026 10:56:46 +0000 Subject: [PATCH 12/14] Add FCW for derive helper attributes that will conflict with built-in attributes --- .../src/attributes/proc_macro_attrs.rs | 10 +++ compiler/rustc_lint/src/early/diagnostics.rs | 4 ++ compiler/rustc_lint/src/lints.rs | 4 ++ compiler/rustc_lint_defs/src/builtin.rs | 70 +++++++++++++++++++ compiler/rustc_lint_defs/src/lib.rs | 1 + .../ui/attributes/ambiguous_derive_helpers.rs | 15 ++++ .../ambiguous_derive_helpers.stderr | 16 +++++ 7 files changed, 120 insertions(+) create mode 100644 tests/ui/attributes/ambiguous_derive_helpers.rs create mode 100644 tests/ui/attributes/ambiguous_derive_helpers.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index 3674aa7124abb..f9ace7e25d1b3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -1,3 +1,6 @@ +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS; + use super::prelude::*; const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets = @@ -126,6 +129,13 @@ fn parse_derive_like( cx.expected_identifier(ident.span); return None; } + if rustc_feature::is_builtin_attr_name(ident.name) { + cx.emit_lint( + AMBIGUOUS_DERIVE_HELPERS, + AttributeLintKind::AmbiguousDeriveHelpers, + ident.span, + ); + } attributes.push(ident.name); } } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7681eedc75ed0..3da2b1bf4069e 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -383,6 +383,10 @@ pub fn decorate_attribute_lint( lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag) } + &AttributeLintKind::AmbiguousDeriveHelpers => { + lints::AmbiguousDeriveHelpers.decorate_lint(diag) + } + &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => { lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0aa5199cffc6e..206a46d5416c7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3752,6 +3752,10 @@ pub(crate) struct DocAliasDuplicated { #[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] pub(crate) struct DocAutoCfgExpectsHideOrShow; +#[derive(LintDiagnostic)] +#[diag("there exists a built-in attribute with the same name")] +pub(crate) struct AmbiguousDeriveHelpers; + #[derive(LintDiagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 488e3a70b6695..9e80ddbd551f9 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -17,6 +17,7 @@ declare_lint_pass! { AARCH64_SOFTFLOAT_NEON, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, + AMBIGUOUS_DERIVE_HELPERS, AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, @@ -4267,6 +4268,75 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_derive_helpers` lint detects cases where a derive macro's helper attribute + /// is the same name as that of a built-in attribute. + /// + /// ### Example + /// + /// ```rust,ignore (proc-macro) + /// #![crate_type = "proc-macro"] + /// #![deny(ambiguous_derive_helpers)] + /// + /// use proc_macro::TokenStream; + /// + /// #[proc_macro_derive(Trait, attributes(ignore))] + /// pub fn example(input: TokenStream) -> TokenStream { + /// TokenStream::new() + /// } + /// ``` + /// + /// Produces: + /// + /// ```text + /// warning: there exists a built-in attribute with the same name + /// --> file.rs:5:39 + /// | + /// 5 | #[proc_macro_derive(Trait, attributes(ignore))] + /// | ^^^^^^ + /// | + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #151152 + /// = note: `#[deny(ambiguous_derive_helpers)]` (part of `#[deny(future_incompatible)]`) on by default + /// ``` + /// + /// ### Explanation + /// + /// Attempting to use this helper attribute will throw an error: + /// + /// ```rust,ignore (needs-dependency) + /// #[derive(Trait)] + /// struct Example { + /// #[ignore] + /// fields: () + /// } + /// ``` + /// + /// Produces: + /// + /// ```text + /// error[E0659]: `ignore` is ambiguous + /// --> src/lib.rs:5:7 + /// | + /// 5 | #[ignore] + /// | ^^^^^^ ambiguous name + /// | + /// = note: ambiguous because of a name conflict with a builtin attribute + /// = note: `ignore` could refer to a built-in attribute + /// note: `ignore` could also refer to the derive helper attribute defined here + /// --> src/lib.rs:3:10 + /// | + /// 3 | #[derive(Trait)] + /// | ^^^^^ + /// ``` + pub AMBIGUOUS_DERIVE_HELPERS, + Warn, + "detects derive helper attributes that are ambiguous with built-in attributes", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #151276), + }; +} + declare_lint! { /// The `private_interfaces` lint detects types in a primary interface of an item, /// that are more private than the item itself. Primary interface of an item is all diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0c454ec60f4a6..a1b8b135819a0 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -800,6 +800,7 @@ pub enum AttributeLintKind { attr_name: Symbol, }, DocInvalid, + AmbiguousDeriveHelpers, DocUnknownInclude { span: Span, inner: &'static str, diff --git a/tests/ui/attributes/ambiguous_derive_helpers.rs b/tests/ui/attributes/ambiguous_derive_helpers.rs new file mode 100644 index 0000000000000..aee498a7067a5 --- /dev/null +++ b/tests/ui/attributes/ambiguous_derive_helpers.rs @@ -0,0 +1,15 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![deny(ambiguous_derive_helpers)] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Trait, attributes(ignore))] //~ ERROR there exists a built-in attribute with the same name +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +pub fn deriving(input: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/tests/ui/attributes/ambiguous_derive_helpers.stderr b/tests/ui/attributes/ambiguous_derive_helpers.stderr new file mode 100644 index 0000000000000..a1eb8d172c25f --- /dev/null +++ b/tests/ui/attributes/ambiguous_derive_helpers.stderr @@ -0,0 +1,16 @@ +error: there exists a built-in attribute with the same name + --> $DIR/ambiguous_derive_helpers.rs:11:39 + | +LL | #[proc_macro_derive(Trait, attributes(ignore))] + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #151276 +note: the lint level is defined here + --> $DIR/ambiguous_derive_helpers.rs:5:9 + | +LL | #![deny(ambiguous_derive_helpers)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 3c5840c19ff58823d4c338cf52037e6cb712f3f5 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Thu, 1 Jan 2026 10:57:25 +0100 Subject: [PATCH 13/14] add field representing types --- Cargo.lock | 2 + compiler/rustc_abi/Cargo.toml | 1 + compiler/rustc_abi/src/layout/ty.rs | 4 +- compiler/rustc_ast/src/ast.rs | 5 + compiler/rustc_ast/src/util/classify.rs | 1 + compiler/rustc_ast_lowering/src/lib.rs | 8 + compiler/rustc_ast_pretty/src/pprust/state.rs | 17 ++ compiler/rustc_borrowck/src/lib.rs | 2 + .../src/debuginfo/type_names.rs | 2 + .../src/const_eval/type_info.rs | 1 + .../src/const_eval/valtrees.rs | 4 +- .../src/interpret/intrinsics.rs | 22 +++ .../rustc_const_eval/src/interpret/stack.rs | 1 + .../src/interpret/validity.rs | 1 + .../rustc_const_eval/src/util/type_name.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 10 + compiler/rustc_hir/src/intravisit.rs | 7 + compiler/rustc_hir/src/lang_items.rs | 6 + .../rustc_hir_analysis/src/check/intrinsic.rs | 2 + .../src/coherence/inherent_impls.rs | 23 ++- .../src/coherence/orphan.rs | 4 + compiler/rustc_hir_analysis/src/errors.rs | 9 + .../src/hir_ty_lowering/mod.rs | 177 +++++++++++++++++- compiler/rustc_hir_analysis/src/lib.rs | 2 +- .../src/variance/constraints.rs | 4 + compiler/rustc_hir_pretty/src/lib.rs | 13 +- compiler/rustc_hir_typeck/src/cast.rs | 1 + compiler/rustc_hir_typeck/src/errors.rs | 9 - compiler/rustc_hir_typeck/src/expr.rs | 4 +- .../src/infer/canonical/canonicalizer.rs | 1 + .../rustc_lint/src/types/improper_ctypes.rs | 6 + compiler/rustc_middle/Cargo.toml | 1 + compiler/rustc_middle/src/ty/adt.rs | 74 +++++++- compiler/rustc_middle/src/ty/context.rs | 1 + .../src/ty/context/impl_interner.rs | 10 +- compiler/rustc_middle/src/ty/error.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 1 + compiler/rustc_middle/src/ty/offload_meta.rs | 9 +- compiler/rustc_middle/src/ty/print/mod.rs | 2 + compiler/rustc_middle/src/ty/print/pretty.rs | 25 ++- .../src/ty/significant_drop_order.rs | 1 + .../rustc_middle/src/ty/structural_impls.rs | 3 + compiler/rustc_middle/src/ty/sty.rs | 33 +++- compiler/rustc_middle/src/ty/util.rs | 7 + .../src/move_paths/builder.rs | 2 + .../src/dataflow_const_prop.rs | 2 +- .../src/canonical/canonicalizer.rs | 1 + .../rustc_next_trait_solver/src/coherence.rs | 1 + .../src/solve/assembly/mod.rs | 8 + .../src/solve/assembly/structural_traits.rs | 10 +- .../src/solve/effect_goals.rs | 7 + .../src/solve/normalizes_to/mod.rs | 25 +++ .../src/solve/trait_goals.rs | 23 +++ compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 49 +++++ compiler/rustc_passes/src/check_export.rs | 1 + compiler/rustc_passes/src/input_stats.rs | 2 + compiler/rustc_pattern_analysis/src/rustc.rs | 1 + compiler/rustc_privacy/src/lib.rs | 1 + compiler/rustc_public/src/ty.rs | 2 + .../src/unstable/convert/internal.rs | 1 + .../src/unstable/convert/stable/ty.rs | 1 + compiler/rustc_public/src/visitor.rs | 1 + .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 3 + .../cfi/typeid/itanium_cxx_abi/transform.rs | 1 + compiler/rustc_span/src/symbol.rs | 5 + compiler/rustc_symbol_mangling/src/export.rs | 1 + compiler/rustc_symbol_mangling/src/v0.rs | 3 + .../traits/fulfillment_errors.rs | 1 + .../src/traits/effects.rs | 3 +- .../src/traits/project.rs | 16 +- .../src/traits/query/dropck_outlives.rs | 2 + .../src/traits/select/candidate_assembly.rs | 32 +++- .../src/traits/select/confirmation.rs | 2 + .../src/traits/select/mod.rs | 4 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_ty_utils/src/instance.rs | 16 ++ compiler/rustc_ty_utils/src/layout.rs | 2 + compiler/rustc_ty_utils/src/needs_drop.rs | 1 + compiler/rustc_ty_utils/src/ty.rs | 2 + compiler/rustc_type_ir/src/fast_reject.rs | 8 + compiler/rustc_type_ir/src/flags.rs | 4 + compiler/rustc_type_ir/src/inherent.rs | 12 ++ compiler/rustc_type_ir/src/interner.rs | 2 + compiler/rustc_type_ir/src/lang_items.rs | 3 + compiler/rustc_type_ir/src/outlives.rs | 1 + compiler/rustc_type_ir/src/relate.rs | 9 + compiler/rustc_type_ir/src/ty_kind.rs | 5 + compiler/rustc_type_ir/src/walk.rs | 3 + library/core/src/field.rs | 35 ++++ library/core/src/intrinsics/mod.rs | 14 ++ library/core/src/lib.rs | 3 + library/std/src/lib.rs | 2 + src/librustdoc/clean/mod.rs | 28 +++ src/librustdoc/clean/types.rs | 4 + src/librustdoc/html/format.rs | 5 + src/librustdoc/html/render/search_index.rs | 1 + src/librustdoc/json/conversions.rs | 2 + .../passes/collect_intra_doc_links.rs | 1 + .../clippy/clippy_lints/src/dereference.rs | 2 + .../clippy_utils/src/check_proc_macro.rs | 1 + .../clippy/clippy_utils/src/hir_utils.rs | 9 +- src/tools/rustfmt/src/types.rs | 8 + tests/debuginfo/function-names.rs | 3 +- tests/ui/README.md | 6 + .../feature-gate-field-projections.rs | 21 +++ .../feature-gate-field-projections.stderr | 83 ++++++++ .../auxiliary/extern-crate.rs | 6 + .../incoherent-impl.next.stderr | 27 +++ .../incoherent-impl.old.stderr | 27 +++ .../incoherent-impl.rs | 28 +++ .../invalid.next.stderr | 71 +++++++ .../invalid.old.stderr | 71 +++++++ tests/ui/field_representing_types/invalid.rs | 32 ++++ .../non-struct.next.stderr | 27 +++ .../non-struct.old.stderr | 27 +++ .../ui/field_representing_types/non-struct.rs | 27 +++ .../nonexistent.next.stderr | 124 ++++++++++++ .../nonexistent.old.stderr | 124 ++++++++++++ .../field_representing_types/nonexistent.rs | 49 +++++ .../not-field-if-packed.next.stderr | 15 ++ .../not-field-if-packed.old.stderr | 15 ++ .../not-field-if-packed.rs | 17 ++ tests/ui/field_representing_types/offset.rs | 51 +++++ .../privacy.next.stderr | 15 ++ .../privacy.old.stderr | 15 ++ tests/ui/field_representing_types/privacy.rs | 34 ++++ .../projections.next.stderr | 32 ++++ .../projections.old.stderr | 32 ++++ .../field_representing_types/projections.rs | 58 ++++++ tests/ui/field_representing_types/traits.rs | 29 +++ .../weird-impls.next.stderr | 30 +++ .../weird-impls.old.stderr | 30 +++ .../field_representing_types/weird-impls.rs | 37 ++++ tests/ui/reflection/dump.bit64.run.stdout | 4 +- tests/ui/symbol-names/basic.legacy.stderr | 4 +- .../ui/symbol-names/issue-60925.legacy.stderr | 4 +- 138 files changed, 2038 insertions(+), 55 deletions(-) create mode 100644 library/core/src/field.rs create mode 100644 tests/ui/feature-gates/feature-gate-field-projections.rs create mode 100644 tests/ui/feature-gates/feature-gate-field-projections.stderr create mode 100644 tests/ui/field_representing_types/auxiliary/extern-crate.rs create mode 100644 tests/ui/field_representing_types/incoherent-impl.next.stderr create mode 100644 tests/ui/field_representing_types/incoherent-impl.old.stderr create mode 100644 tests/ui/field_representing_types/incoherent-impl.rs create mode 100644 tests/ui/field_representing_types/invalid.next.stderr create mode 100644 tests/ui/field_representing_types/invalid.old.stderr create mode 100644 tests/ui/field_representing_types/invalid.rs create mode 100644 tests/ui/field_representing_types/non-struct.next.stderr create mode 100644 tests/ui/field_representing_types/non-struct.old.stderr create mode 100644 tests/ui/field_representing_types/non-struct.rs create mode 100644 tests/ui/field_representing_types/nonexistent.next.stderr create mode 100644 tests/ui/field_representing_types/nonexistent.old.stderr create mode 100644 tests/ui/field_representing_types/nonexistent.rs create mode 100644 tests/ui/field_representing_types/not-field-if-packed.next.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-packed.old.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-packed.rs create mode 100644 tests/ui/field_representing_types/offset.rs create mode 100644 tests/ui/field_representing_types/privacy.next.stderr create mode 100644 tests/ui/field_representing_types/privacy.old.stderr create mode 100644 tests/ui/field_representing_types/privacy.rs create mode 100644 tests/ui/field_representing_types/projections.next.stderr create mode 100644 tests/ui/field_representing_types/projections.old.stderr create mode 100644 tests/ui/field_representing_types/projections.rs create mode 100644 tests/ui/field_representing_types/traits.rs create mode 100644 tests/ui/field_representing_types/weird-impls.next.stderr create mode 100644 tests/ui/field_representing_types/weird-impls.old.stderr create mode 100644 tests/ui/field_representing_types/weird-impls.rs diff --git a/Cargo.lock b/Cargo.lock index 1a930b64f4581..5bf597f0aa68c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3431,6 +3431,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "serde", "tracing", ] @@ -4250,6 +4251,7 @@ dependencies = [ "rustc_target", "rustc_thread_pool", "rustc_type_ir", + "serde", "smallvec", "thin-vec", "tracing", diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 83d96d8d04daf..4776454204f32 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -15,6 +15,7 @@ rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } +serde = { version = "1.0.125", features = ["derive"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index aafb124986e14..036d1c5f06cad 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -33,7 +33,7 @@ rustc_index::newtype_index! { /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct FieldIdx {} @@ -57,7 +57,7 @@ rustc_index::newtype_index! { /// /// `struct`s, `tuples`, and `unions`s are considered to have a single variant /// with variant index zero, aka [`FIRST_VARIANT`]. - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, serde::Serialize)] #[encodable] #[orderable] pub struct VariantIdx { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fa323b7cf581a..c0130a58cb6b7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2553,6 +2553,11 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), + /// 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, Option, Ident), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 2e494b968b6bd..43ef6897b79cf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::ImplicitSelf | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) + | ast::TyKind::FieldOf(..) | ast::TyKind::Dummy | ast::TyKind::Err(..) => break None, } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ff5edfc799439..c89fd8acb2a3e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1497,6 +1497,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Pat(ty, pat) => { hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span)) } + TyKind::FieldOf(ty, variant, field) => hir::TyKind::FieldOf( + self.lower_ty_alloc(ty, itctx), + self.arena.alloc(hir::TyFieldPath { + variant: variant.map(|variant| self.lower_ident(variant)), + field: self.lower_ident(*field), + }), + ), + TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c8874ed99dca9..f4168301bc5df 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1377,6 +1377,23 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + ast::TyKind::FieldOf(ty, variant, field) => { + self.word("builtin # field_of"); + self.popen(); + let ib = self.ibox(0); + self.print_type(ty); + self.word(","); + self.space(); + + if let Some(variant) = variant { + self.print_ident(*variant); + self.word("."); + } + self.print_ident(*field); + + self.end(ib); + self.pclose(); + } } self.end(ib); } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 38b73a07a6891..9f07f6199695b 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1890,6 +1890,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -1934,6 +1935,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 1c88a8da5ea94..f3a1d42e96d7b 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -216,6 +216,8 @@ fn push_debuginfo_type_name<'tcx>( write!(output, "{:?}", t).unwrap(); } } + // FIXME(FRTs): implement debuginfo for field representing types + ty::FRT(..) => todo!(), ty::Slice(inner_type) => { if cpp_like_debuginfo { output.push_str("slice2$<"); diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index f8881f0968bb4..2d2300497f6e9 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -136,6 +136,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } ty::Adt(_, _) + | ty::FRT(_, _) | ty::Foreign(_) | ty::Pat(_, _) | ty::FnDef(..) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index b771addb8df55..efd07f7186f77 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -90,7 +90,7 @@ fn const_to_valtree_inner<'tcx>( } match ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { *num_nodes += 1; Ok(ty::ValTree::zst(tcx)) } @@ -273,7 +273,7 @@ pub fn valtree_to_const_value<'tcx>( // create inner `MPlace`s which are filled recursively. // FIXME: Does this need an example? match *cv.ty.kind() { - ty::FnDef(..) => { + ty::FnDef(..) | ty::FRT(..) => { assert!(cv.valtree.is_zst()); mir::ConstValue::ZeroSized } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 2ea5e4a25c116..c2c2c4e6c7ccb 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -222,6 +222,27 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } + sym::field_offset => { + let frt_ty = instance.args.type_at(0); + + let (ty, field) = match frt_ty.kind() { + &ty::FRT(ty, field) => (ty, field), + 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, got {frt_ty}") + } + }; + let layout = self.layout_of(ty)?; + let cx = ty::layout::LayoutCx::new(*self.tcx, self.typing_env); + + let layout = layout.for_variant(&cx, field.variant); + let offset = layout.fields.offset(field.field.index()).bytes(); + + self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; + } sym::vtable_for => { let tp_ty = instance.args.type_at(0); let result_ty = instance.args.type_at(1); @@ -300,6 +321,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::UnsafeBinder(_) | ty::Never | ty::Tuple(_) + | ty::FRT(..) | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), }; let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d886..ba9c2c7c212ac 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -507,6 +507,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5d8ae42f5eccd..72f926eb69e08 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -785,6 +785,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { | ty::Dynamic(..) | ty::Closure(..) | ty::Pat(..) + | ty::FRT(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => interp_ok(false), // Some types only occur during typechecking, they have no layout. diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index db651811551f3..5564277ac5053 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -34,6 +34,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::Float(_) | ty::Str | ty::Pat(_, _) + | ty::FRT(..) | ty::Array(_, _) | ty::Slice(_) | ty::RawPtr(_, _) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index add09c3ea58b0..76c0c76822bb1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -494,6 +494,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 diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68fc7653b6368..183d2a596e96e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1737,6 +1737,12 @@ impl<'hir> Block<'hir> { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TyFieldPath { + pub variant: Option, + pub field: Ident, +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct TyPat<'hir> { #[stable_hasher(ignore)] @@ -3800,6 +3806,10 @@ pub enum TyKind<'hir, Unambig = ()> { Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>), + /// Field representing type (`field_of!(Struct, field)`). + /// + /// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`. + FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath), /// `TyKind::Infer` means the type should be inferred instead of it having been /// specified. This can appear anywhere in a type. /// diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 21bcf53b5619f..e29fdd284b385 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1047,6 +1047,13 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) - try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } + TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + try_visit!(visitor.visit_ty_unambig(ty)); + if let Some(variant) = *variant { + try_visit!(visitor.visit_ident(variant)); + } + try_visit!(visitor.visit_ident(*field)); + } } V::Result::output() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 557f76208bfe6..9ccdf77f50269 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -448,6 +448,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); + + // Field projection related lang-items + Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0); + FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0); + FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0); + FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 22ee490b81a7b..3045714105d22 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -117,6 +117,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 @@ -296,6 +297,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index edaf33e493c04..56fa95bdb8f7c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -15,7 +15,7 @@ use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; -use rustc_span::ErrorGuaranteed; +use rustc_span::{ErrorGuaranteed, Span}; use crate::errors; @@ -160,12 +160,16 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let mut self_ty = self.tcx.peel_off_free_alias_tys(self_ty); - // We allow impls on pattern types exactly when we allow impls on the base type. - // FIXME(pattern_types): Figure out the exact coherence rules we want here. - while let ty::Pat(base, _) = *self_ty.kind() { - self_ty = base; - } + let self_ty = self.tcx.peel_off_free_alias_tys(self_ty); + self.check_impl_self_ty(self_ty, id, item_span) + } + + fn check_impl_self_ty( + &mut self, + self_ty: Ty<'tcx>, + id: LocalDefId, + item_span: Span, + ) -> Result<(), ErrorGuaranteed> { match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -175,7 +179,10 @@ impl<'tcx> InherentCollect<'tcx> { ty::Dynamic(..) => { Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } - ty::Pat(_, _) => unreachable!(), + // We allow impls on pattern types exactly when we allow impls on the base type. + // FIXME(pattern_types): Figure out the exact coherence rules we want here. + ty::Pat(base, _) => self.check_impl_self_ty(base, id, item_span), + ty::FRT(ty, _) => self.check_impl_self_ty(ty, id, item_span), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f1e138dbcb97a..fb22cd4974502 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -150,6 +150,10 @@ pub(crate) fn orphan_check_impl( NonlocalImpl::DisallowBecauseNonlocal }, ), + ty::FRT(..) => ( + LocalImpl::Disallow { problematic_kind: "field representing type" }, + NonlocalImpl::DisallowOther, + ), // extern { type OpaqueType; } // impl AutoTrait for OpaqueType {} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a23b42ae0981..b92d66e1d552b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1420,6 +1420,15 @@ pub struct NoVariantNamed<'tcx> { pub ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] +pub struct NoFieldOnType<'tcx> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, + pub field: Ident, +} + // FIXME(fmease): Deduplicate: #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 1578f098dd953..24284be3d3e3c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -21,6 +21,7 @@ pub mod generics; use std::slice; +use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -38,7 +39,7 @@ use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ - self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, + self, Const, FieldId, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast, fold_regions, }; use rustc_middle::{bug, span_bug}; @@ -51,11 +52,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::check_c_variadic_abi; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; +use crate::{NoVariantNamed, check_c_variadic_abi}; /// The context in which an implied bound is being added to a item being lowered (i.e. a sizedness /// trait or a default trait) @@ -3028,6 +3029,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.record_ty(pat.hir_id, ty, pat.span); pat_ty } + hir::TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => self.lower_field_of( + self.lower_ty(ty), + self.item_def_id(), + ty.span, + hir_ty.hir_id, + *variant, + *field, + ), hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; @@ -3069,6 +3078,168 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + fn lower_field_of( + &self, + ty: Ty<'tcx>, + item_def_id: LocalDefId, + ty_span: Span, + hir_id: HirId, + variant: Option, + field: Ident, + ) -> Ty<'tcx> { + let dcx = self.dcx(); + let tcx = self.tcx(); + match ty.kind() { + ty::Adt(def, _) => { + let base_did = def.did(); + let kind_name = tcx.def_descr(base_did); + let (variant_idx, variant) = if def.is_enum() { + let Some(variant) = variant else { + let err = dcx + .create_err(NoVariantNamed { span: field.span, ident: field, ty }) + .with_span_help( + field.span.shrink_to_lo(), + "you might be missing a variant here: `Variant.`", + ) + .emit(); + return Ty::new_error(tcx, err); + }; + + if let Some(res) = def + .variants() + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == variant) + { + res + } else { + let err = dcx + .create_err(NoVariantNamed { span: variant.span, ident: variant, ty }) + .emit(); + return Ty::new_error(tcx, err); + } + } else { + if let Some(variant) = variant { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + variant.span, + E0609, + "{kind_name} `{adt_path}` does not have any variants", + ) + .with_span_label(variant.span, "variant unknown") + .emit(); + } + (FIRST_VARIANT, def.non_enum_variant()) + }; + let block = tcx.local_def_id_to_hir_id(item_def_id); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block); + if let Some((field_idx, field)) = variant + .fields + .iter_enumerated() + .find(|(_, f)| f.ident(tcx).normalize_to_macros_2_0() == ident) + { + if field.vis.is_accessible_from(def_scope, tcx) { + tcx.check_stability(field.did, Some(hir_id), ident.span, None); + } else { + let adt_path = tcx.def_path_str(base_did); + struct_span_code_err!( + dcx, + ident.span, + E0616, + "field `{ident}` of {kind_name} `{adt_path}` is private", + ) + .with_span_label(ident.span, "private field") + .emit(); + } + Ty::new_field_representing_type( + tcx, + ty, + FieldId { variant: variant_idx, field: field_idx }, + ) + } else { + let err = + dcx.create_err(NoFieldOnType { span: ident.span, field: ident, ty }).emit(); + Ty::new_error(tcx, err) + } + } + ty::Tuple(tys) => { + let index = match field.as_str().parse::() { + Ok(idx) => idx, + Err(_) => { + let err = + dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + return Ty::new_error(tcx, err); + } + }; + if field.name != sym::integer(index) { + bug!("we parsed above, but now not equal?"); + } + if tys.get(index).is_some() { + Ty::new_field_representing_type( + tcx, + ty, + FieldId { variant: FIRST_VARIANT, field: index.into() }, + ) + } else { + let err = dcx.create_err(NoFieldOnType { span: field.span, field, ty }).emit(); + Ty::new_error(tcx, err) + } + } + // FIXME(FRTs): support type aliases + /* + ty::Alias(AliasTyKind::Free, ty) => { + return self.lower_field_of( + ty, + item_def_id, + ty_span, + hir_id, + variant, + field, + ); + }*/ + ty::Alias(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("could not resolve fields of `{ty}`")), + ), + ty::Error(err) => Ty::new_error(tcx, *err), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::FRT(_, _) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Slice(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("type `{ty}` doesn't have fields")), + ), + ty::Infer(_) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("cannot use `{ty}` in this position")), + ), + // FIXME(FRTs): support these types? + ty::Array(..) | ty::Pat(..) => Ty::new_error( + tcx, + dcx.span_err(ty_span, format!("type `{ty}` is not yet supported in `field_of!`")), + ), + } + } + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: Option) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 6214106d422b5..925a4982e8333 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -83,7 +83,7 @@ mod impl_wf_check; mod outlives; mod variance; -pub use errors::NoVariantNamed; +pub use errors::{NoFieldOnType, NoVariantNamed}; use rustc_abi::{CVariadicStatus, ExternAbi}; use rustc_hir as hir; use rustc_hir::def::DefKind; diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ce4668736b570..011a3f1dc2166 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -255,6 +255,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } + ty::FRT(ty, _) => { + self.add_constraints_from_ty(current, ty, variance); + } + ty::Slice(typ) => { self.add_constraints_from_ty(current, typ, variance); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index fc6cc4e672517..ec0688b64f3dd 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -19,7 +19,7 @@ use rustc_hir::attrs::{AttributeKind, PrintAttribute}; use rustc_hir::{ BindingMode, ByRef, ConstArg, ConstArgExprField, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, - PreciseCapturingArg, RangeEnd, Term, TyPatKind, + PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, }; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; @@ -464,6 +464,17 @@ impl<'a> State<'a> { self.word(" is "); self.print_ty_pat(pat); } + hir::TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + self.word("field_of!("); + self.print_type(ty); + self.word(", "); + if let Some(variant) = *variant { + self.print_ident(variant); + self.word("."); + } + self.print_ident(*field); + self.word(")"); + } } self.end(ib) } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3f13a102684e0..9074e378c7cd3 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -137,6 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a5de1014dd7a3..289b298978232 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -494,15 +494,6 @@ impl HelpUseLatestEdition { } } -#[derive(Diagnostic)] -#[diag("no field `{$field}` on type `{$ty}`", code = E0609)] -pub(crate) struct NoFieldOnType<'tcx> { - #[primary_span] - pub(crate) span: Span, - pub(crate) ty: Ty<'tcx>, - pub(crate) field: Ident, -} - #[derive(Diagnostic)] #[diag("no field named `{$field}` on enum variant `{$container}::{$ident}`", code = E0609)] pub(crate) struct NoFieldOnVariant<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5b40531f94627..ac2af89c518b1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -20,8 +20,8 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; -use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; +use rustc_hir_analysis::{NoFieldOnType, NoVariantNamed}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; use rustc_infer::traits::query::NoSolution; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; @@ -44,7 +44,7 @@ use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, - FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, + FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 89ea6324d8543..53d81d1a259e2 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -420,6 +420,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::Alias(..) | ty::Foreign(..) | ty::Pat(..) + | ty::FRT(..) | ty::Param(..) => { if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index fb9b55efa2204..c8d84f0a411d0 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -594,6 +594,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // but only the base type is relevant for being representable in FFI. ty::Pat(base, ..) => self.visit_type(state, base), + ty::FRT(..) => FfiUnsafe { + ty, + reason: inline_fluent!("field representing types have no C equivalent"), + help: None, + }, + // Primitive types with a stable representation. ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index f9d5e7c027894..09c5417f39787 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -32,6 +32,7 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_thread_pool = { path = "../rustc_thread_pool" } rustc_type_ir = { path = "../rustc_type_ir" } +serde = { version = "1.0.125", features = ["derive"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 510c546f82a4e..7f2019e16a227 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str; -use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, ReprOptions, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; @@ -14,7 +14,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::{IndexSlice, IndexVec}; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; use rustc_type_ir::solve::AdtDestructorKind; @@ -212,6 +212,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_struct() } + fn is_packed(self) -> bool { + self.repr().packed() + } + fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option>> { Some(interner.type_of(self.non_enum_variant().tail_opt()?.did)) } @@ -681,3 +685,69 @@ pub enum Representability { Representable, Infinite(ErrorGuaranteed), } + +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + Debug, + Encodable, + Decodable, + HashStable, + serde::Serialize +)] +#[rustc_pass_by_value] +pub struct FieldId { + pub variant: VariantIdx, + pub field: FieldIdx, +} + +impl FieldId { + pub fn ty<'tcx>(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Adt(def, args) => def.variants()[self.variant].fields[self.field].ty(tcx, args), + ty::Tuple(tys) => { + debug_assert_eq!(FIRST_VARIANT, self.variant); + tys[self.field.index()] + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::FRT(_, _) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Array(..) + | ty::Pat(..) + | ty::Slice(..) + | ty::Error(_) + | ty::Alias(..) => bug!( + "don't expect to see this TyKind here, should be handled by lowering hir to mir" + ), + } + } +} + +impl<'tcx> rustc_type_ir::inherent::FieldId> for FieldId { + fn ty(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + FieldId::ty(self, tcx, ty) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f638dd80864cb..022a8d5f8a87a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1867,6 +1867,7 @@ impl<'tcx> TyCtxt<'tcx> { Infer, Alias, Pat, + FRT, Foreign )?; diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5a15d132048dd..4ad6fb9f0266c 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -21,8 +21,8 @@ use crate::traits::solve::{ self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect, }; use crate::ty::{ - self, Clause, Const, List, ParamTy, Pattern, PolyExistentialPredicate, Predicate, Region, Ty, - TyCtxt, + self, Clause, Const, FieldId, List, ParamTy, Pattern, PolyExistentialPredicate, Predicate, + Region, Ty, TyCtxt, }; #[allow(rustc::usage_of_ty_tykind)] @@ -188,6 +188,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } + type FieldId = FieldId; + fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { match self.def_kind(alias.def_id) { DefKind::AssocTy => { @@ -514,6 +516,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -755,6 +758,8 @@ bidirectional_lang_item_map! { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -786,6 +791,7 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 66542525d2841..03784b2420f67 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -186,6 +186,7 @@ impl<'tcx> Ty<'tcx> { ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), ty::Pat(..) => "pattern type".into(), + ty::FRT(..) => "field representing type".into(), ty::Slice(_) => "slice".into(), ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d3b4654a8d799..17c500fabd585 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -847,6 +847,7 @@ where | ty::Uint(_) | ty::Float(_) | ty::FnPtr(..) + | ty::FRT(..) | ty::Never | ty::FnDef(..) | ty::CoroutineWitness(..) diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 67c00765ed57b..83a82b6d3a7bb 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -82,9 +82,12 @@ impl MappingFlags { MappingFlags::LITERAL | MappingFlags::IMPLICIT } - ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Alias(_, _) | ty::Param(_) => { - MappingFlags::TO - } + ty::Adt(_, _) + | ty::FRT(..) + | ty::Tuple(_) + | ty::Array(_, _) + | ty::Alias(_, _) + | ty::Param(_) => MappingFlags::TO, ty::RawPtr(_, Not) | ty::Ref(_, _, Not) => MappingFlags::TO, diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 0fd68e74e0441..456447a342d11 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -296,6 +296,8 @@ fn characteristic_def_id_of_type_cached<'a>( characteristic_def_id_of_type_cached(subty, visited) } + ty::FRT(ty, _) => characteristic_def_id_of_type_cached(ty, visited), + ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 02b804c1ab29c..af289081e42cb 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::iter; use std::ops::{Deref, DerefMut}; -use rustc_abi::{ExternAbi, Size}; +use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; @@ -721,6 +721,29 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty.print(self)?; write!(self, ") is {pat:?}")?; } + ty::FRT(ty, field) => { + write!(self, "field_of!(")?; + ty.print(self)?; + write!(self, ", ")?; + match ty.kind() { + ty::Adt(def, _) => { + let variant = if def.is_enum() { + let variant = &def.variants()[field.variant]; + write!(self, "{}.", variant.name)?; + variant + } else { + def.non_enum_variant() + }; + write!(self, "{}", variant.fields[field.field].name)?; + } + ty::Tuple(_) => { + debug_assert_eq!(field.variant, FIRST_VARIANT); + write!(self, "{}", field.field.index())?; + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + } + write!(self, ")")?; + } ty::RawPtr(ty, mutbl) => { write!(self, "*{} ", mutbl.ptr_str())?; ty.print(self)?; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index f1aa7076d98ac..b7cdb26c7d117 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -136,6 +136,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Placeholder(_) | ty::Infer(_) | ty::Slice(_) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8707b03e4b8f2..693a646c90571 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -376,6 +376,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { } ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?), ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), + ty::FRT(ty, field) => ty::FRT(ty.try_fold_with(folder)?, field), ty::Bool | ty::Char @@ -415,6 +416,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineClosure(did, args) => ty::CoroutineClosure(did, args.fold_with(folder)), ty::Alias(kind, data) => ty::Alias(kind, data.fold_with(folder)), ty::Pat(ty, pat) => ty::Pat(ty.fold_with(folder), pat.fold_with(folder)), + ty::FRT(ty, field) => ty::FRT(ty.fold_with(folder), field), ty::Bool | ty::Char @@ -467,6 +469,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { try_visit!(ty.visit_with(visitor)); pat.visit_with(visitor) } + ty::FRT(ty, _field) => ty.visit_with(visitor), ty::Error(guar) => guar.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4be30d8b6c918..ddf79fbeb4c25 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -26,8 +26,8 @@ use crate::infer::canonical::Canonical; use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty, - TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, Discr, FieldId, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, + Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -487,6 +487,15 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Pat(base, pat)) } + #[inline] + pub fn new_field_representing_type( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + field_id: FieldId, + ) -> Ty<'tcx> { + Ty::new(tcx, FRT(ty, field_id)) + } + #[inline] #[instrument(level = "debug", skip(tcx))] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { @@ -1063,6 +1072,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_pat(interner, ty, pat) } + fn new_field_representing_type(tcx: TyCtxt<'tcx>, ty: Self, field_id: FieldId) -> Self { + Self::new_field_representing_type(tcx, ty, field_id) + } + fn new_unsafe_binder(interner: TyCtxt<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>) -> Self { Ty::new_unsafe_binder(interner, ty) } @@ -1082,6 +1095,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { fn has_unsafe_fields(self) -> bool { Ty::has_unsafe_fields(self) } + + fn is_packed(self) -> bool { + Ty::is_packed(self) + } } /// Type utilities @@ -1136,6 +1153,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), Adt(..)) } + #[inline] + pub fn is_packed(self) -> bool { + matches!(self.kind(), Adt(def, _) if def.repr().packed()) + } + #[inline] pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) @@ -1629,6 +1651,7 @@ impl<'tcx> Ty<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Adt(..) + | ty::FRT(..) | ty::Foreign(_) | ty::Str | ty::Array(..) @@ -1680,6 +1703,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Closure(..) | ty::CoroutineClosure(..) + | ty::FRT(..) | ty::Never | ty::Error(_) // Extern types have metadata = (). @@ -1870,6 +1894,7 @@ impl<'tcx> Ty<'tcx> { | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -1923,6 +1948,9 @@ impl<'tcx> Ty<'tcx> { // ZST which can't be named are fine. ty::FnDef(..) => true, + // ZST. + ty::FRT(..) => true, + ty::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. @@ -1993,6 +2021,7 @@ impl<'tcx> Ty<'tcx> { | ty::Array(..) | ty::Foreign(_) | ty::Pat(_, _) + | ty::FRT(..) | ty::FnDef(..) | ty::UnsafeBinder(..) | ty::Dynamic(..) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index e01a5721a5e03..ae3631ea1717d 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1166,6 +1166,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1207,6 +1208,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(_, _) | ty::FnDef(..) @@ -1258,6 +1260,7 @@ impl<'tcx> Ty<'tcx> { | ty::Char | ty::Str | ty::Never + | ty::FRT(..) | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) @@ -1429,6 +1432,9 @@ impl<'tcx> Ty<'tcx> { // Raw pointers use bitwise comparison. ty::RawPtr(_, _) | ty::FnPtr(..) => true, + // FRTs are ZSTs. + ty::FRT(..) => true, + // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1508,6 +1514,7 @@ pub fn needs_drop_components_with_async<'tcx>( | ty::Uint(_) | ty::Float(_) | ty::Never + | ty::FRT(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Char diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index ced9bd735ba2b..0790d26e9ed91 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -154,6 +154,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) @@ -196,6 +197,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 604f1da1a3abb..5bbace972eefb 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -867,7 +867,7 @@ fn try_write_constant<'tcx>( match ty.kind() { // ZSTs. Nothing to do. - ty::FnDef(..) => {} + ty::FnDef(..) | ty::FRT(..) => {} // Those are scalars, must be handled above. ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index ce2be24adc586..312d41dc6deca 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -381,6 +381,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index c370fd24a1bb3..4e99d8de6ea65 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -408,6 +408,7 @@ where // For fundamental types, we just look inside of them. ty::Ref(_, ty, _) => ty.visit_with(self), + ty::FRT(ty, _) => ty.visit_with(self), ty::Adt(def, args) => { if self.def_id_is_local(def.def_id()) { ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63f246db8a5fb..885f60956402a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -348,6 +348,11 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Vec>; + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -617,6 +622,7 @@ where Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } + Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal), _ => Err(NoSolution), } }; @@ -690,6 +696,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -810,6 +817,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 05ea217c1de08..e21da4a1d0902 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -35,6 +35,7 @@ where | ty::Float(_) | ty::FnDef(..) | ty::FnPtr(..) + | ty::FRT(..) | ty::Error(_) | ty::Never | ty::Char => Ok(ty::Binder::dummy(vec![])), @@ -117,7 +118,7 @@ where match ty.kind() { // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness - // impl {Meta,}Sized for Closure, CoroutineClosure + // impl {Meta,}Sized for FRT, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -132,6 +133,7 @@ where | ty::CoroutineWitness(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -194,7 +196,7 @@ where { match ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -389,6 +391,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Never | ty::Tuple(_) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) @@ -784,6 +789,7 @@ pub(in crate::solve) fn const_conditions_for_destruct( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => Ok(vec![]), // Coroutines and closures could implement `[const] Drop`, diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 2837b8565f603..4da76b88b5de0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -430,6 +430,13 @@ where ) -> Vec> { unreachable!("Unsize is not const") } + + fn consider_builtin_field_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal<::Interner, Self>, + ) -> Result::Interner>, NoSolution> { + unreachable!("Field is not const") + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 70c28421c57ea..1d0c33ca6b5ca 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -631,6 +631,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -884,6 +885,7 @@ where | ty::Float(..) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -950,6 +952,29 @@ where ) -> Result, NoSolution> { unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + let self_ty = goal.predicate.self_ty(); + let ty::FRT(container, field) = self_ty.kind() else { + return Err(NoSolution); + }; + + let ty = if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldBase) { + container + } else if ecx.cx().is_lang_item(goal.predicate.def_id(), SolverLangItem::FieldType) { + field.ty(ecx.cx(), container) + } else { + panic!("unexpected associated type {:?} in `Field`", goal.predicate) + }; + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 003841f3af3fd..fb44f9a87581c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -735,6 +735,7 @@ where | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -841,6 +842,27 @@ where } }) } + + fn consider_builtin_field_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + if let ty::FRT(ty, _) = goal.predicate.self_ty().kind() + && match ty.kind() { + ty::Adt(def, _) => def.is_struct() && !def.is_packed(), + ty::Tuple(..) => true, + _ => false, + } + { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } else { + Err(NoSolution) + } + } } /// Small helper function to change the `def_id` of a trait predicate - this is not normally @@ -1241,6 +1263,7 @@ where | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 05216be06ff56..f6a3155d08514 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1140,7 +1140,7 @@ impl<'a> Parser<'a> { /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. - fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { + pub(crate) fn parse_floating_field_access(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); let mut trailing_dot = None; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6ff165eb22b71..eae129b22cfb0 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -329,6 +329,8 @@ impl<'a> Parser<'a> { self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty(lo)? + } else if self.is_builtin() { + self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -803,6 +805,53 @@ impl<'a> Parser<'a> { Ok(TyKind::Err(guar)) } + fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { + self.parse_builtin(|this, lo, ident| { + Ok(match ident.name { + sym::field_of => Some(this.parse_ty_field_of(lo)?), + _ => None, + }) + }) + } + + /// Built-in macro for `field_of!` expressions. + pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { + let container = self.parse_ty()?; + self.expect(exp!(Comma))?; + + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { + if trailing_comma { + e.note("unexpected third argument to field_of"); + } else { + e.note("field_of expects dot-separated field and variant names"); + } + e.emit(); + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !self.token.kind.is_close_delim_or_eof() { + self.bump(); + } + } + + match fields.len() { + 0 => Err(self.dcx().struct_span_err( + self.token.span, + "`field_of!` expects dot-separated field and variant names", + )), + 1 => Ok(TyKind::FieldOf(container, None, fields[0])), + 2 => Ok(TyKind::FieldOf(container, Some(fields[0]), fields[1])), + _ => Err(self.dcx().struct_span_err( + fields.iter().map(|f| f.span).collect::>(), + "`field_of!` only supports a single field or a variant with a field", + )), + } + } + /// Parses a function pointer type (`TyKind::FnPtr`). /// ```ignore (illustrative) /// [unsafe] [extern "ABI"] fn (S) -> T diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index fee920221e1d1..1295a7a814461 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -301,6 +301,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::Str | ty::Tuple(_) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 23fbb9ab3a2b7..d731c524ca637 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -410,6 +410,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TraitObject, Infer, Pat, + FieldOf, Err ] ); @@ -680,6 +681,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Tup, Path, Pat, + FieldOf, TraitObject, ImplTrait, Paren, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index dc38f2d8bc70f..965597cdc4071 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -407,6 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Adt(..) | ty::Tuple(..) => { ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) } } + ty::FRT(..) => ConstructorSet::Struct { empty: false }, ty::Ref(..) => ConstructorSet::Ref, ty::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9a952bb721951..936d49c9aed88 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -290,6 +290,7 @@ where | ty::RawPtr(..) | ty::Ref(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Param(..) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index d4f128f87d6ff..e640485a74e73 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; +use rustc_middle::ty::FieldId; use serde::Serialize; use super::abi::ReprOptions; @@ -554,6 +555,7 @@ pub enum RigidTy { Str, Array(Ty, TyConst), Pat(Ty, Pattern), + FRT(Ty, FieldId), Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 8594f65100415..c8b22e3e8048e 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -153,6 +153,7 @@ impl RustcInternal for RigidTy { RigidTy::Pat(ty, pat) => { rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) } + RigidTy::FRT(ty, field) => rustc_ty::TyKind::FRT(ty.internal(tables, tcx), *field), RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 70cdae43126c4..5fe30327cd34d 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -413,6 +413,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Pat(ty, pat) => { TyKind::RigidTy(RigidTy::Pat(ty.stable(tables, cx), pat.stable(tables, cx))) } + ty::FRT(ty, field) => TyKind::RigidTy(RigidTy::FRT(ty.stable(tables, cx), *field)), ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables, cx))), ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables, cx), mutbl.stable(tables, cx))) diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index acc3334769613..d7591146f4955 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -158,6 +158,7 @@ impl Visitable for RigidTy { c.visit(visitor) } RigidTy::Pat(t, _p) => t.visit(visitor), + RigidTy::FRT(t, _f) => t.visit(visitor), RigidTy::Slice(inner) => inner.visit(visitor), RigidTy::RawPtr(ty, _) => ty.visit(visitor), RigidTy::Ref(reg, ty, _) => { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 5505fe82cea65..3f9e781801be4 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -434,6 +434,9 @@ pub(crate) fn encode_ty<'tcx>( typeid.push_str(&s); } + // FIXME(FRTs): add mangling support + ty::FRT(..) => todo!(), + ty::Slice(ty0) => { // u5sliceIE as vendor extended type let mut s = String::from("u5sliceI"); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 971ac9348fc4c..f348b200d85d7 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -60,6 +60,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { | ty::Foreign(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Slice(..) | ty::Str | ty::Tuple(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0f248925602ff..9e8179c701f5c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1084,7 +1084,12 @@ symbols! { ffi_pure, ffi_returns_twice, field, + field_base, field_init_shorthand, + field_of, + field_offset, + field_projections, + field_type, file, file_options, flags, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index c99ba1d58f31f..31a1381f218eb 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -105,6 +105,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 95cbb9e07ebb7..9a573a2bbf64e 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -510,6 +510,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.print_pat(pat)?; } + // FIXME(FRTs): add mangling support + ty::FRT(..) => todo!("mangle {ty}"), + ty::Array(ty, len) => { self.push("A"); ty.print(self)?; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1de088f19cc7f..c47918ba1d6aa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1883,6 +1883,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::CoroutineClosure(..) => Some(21), ty::Pat(..) => Some(22), ty::UnsafeBinder(..) => Some(23), + ty::FRT(..) => Some(24), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 66c949a38cea7..3a57600ba3644 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -328,7 +328,7 @@ fn evaluate_host_effect_for_copy_clone_goal<'tcx>( let self_ty = obligation.predicate.self_ty(); let constituent_tys = match *self_ty.kind() { // impl Copy/Clone for FnDef, FnPtr - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => Ok(ty::Binder::dummy(vec![])), // Implementations are provided in core ty::Uint(_) @@ -470,6 +470,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( | ty::FnPtr(..) | ty::Never | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::FRT(..) | ty::Error(_) => thin_vec![], // Coroutines and closures could implement `[const] Drop`, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5bbbad0b5bbfa..f901d6b941316 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -992,7 +992,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | LangItem::FnOnce | LangItem::AsyncFn | LangItem::AsyncFnMut - | LangItem::AsyncFnOnce, + | LangItem::AsyncFnOnce + | LangItem::Field, ) => true, Some(LangItem::AsyncFnKindHelper) => { // FIXME(async_closures): Validity constraints here could be cleaned up. @@ -1023,6 +1024,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1078,6 +1080,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1547,6 +1550,17 @@ fn confirm_builtin_candidate<'cx, 'tcx>( } }); (metadata_ty.into(), obligations) + } else if tcx.is_lang_item(trait_def_id, LangItem::Field) { + let &ty::FRT(container, field) = self_ty.kind() else { + bug!("only `field_of!()` can implement `Field`") + }; + if tcx.is_lang_item(item_def_id, LangItem::FieldBase) { + (container.into(), PredicateObligations::new()) + } else if tcx.is_lang_item(item_def_id, LangItem::FieldType) { + (field.ty(tcx, container).into(), PredicateObligations::new()) + } else { + bug!("unexpected associated type {:?} in `Field`", obligation.predicate); + } } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 2e60805cd10a5..2aada90e0d985 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -43,6 +43,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) + | ty::FRT(..) | ty::Error(_) => true, // `T is PAT` and `[T]` have same properties as T. @@ -275,6 +276,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::Float(_) | ty::Str | ty::Never + | ty::FRT(..) | ty::Foreign(..) | ty::RawPtr(..) | ty::Ref(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f5bf74a799192..da5a532c440ab 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -128,6 +128,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut candidates, ); } + Some(LangItem::Field) => { + self.assemble_candidates_for_field_trait(obligation, &mut candidates); + } _ => { // We re-match here for traits that can have both builtin impls and user written impls. // After the builtin impls we need to also add user written impls, which we do not want to @@ -691,6 +694,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -865,6 +869,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(..) | ty::Slice(_) | ty::Adt(..) | ty::RawPtr(_, _) @@ -1134,7 +1139,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.kind() { // These impls are built-in because we cannot express sufficiently // generic impls in libcore. - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => { + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Tuple(..) + | ty::Pat(..) + | ty::FRT(..) => { candidates.vec.push(BuiltinCandidate); } @@ -1249,6 +1259,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => { candidates.vec.push(SizedCandidate); } @@ -1331,6 +1342,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) | ty::Dynamic(_, _) @@ -1367,6 +1379,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(..) @@ -1418,6 +1431,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -1439,4 +1453,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_field_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if let ty::FRT(ty, _) = obligation.predicate.self_ty().skip_binder().kind() + && match ty.kind() { + ty::Adt(def, _) => def.is_struct() && !def.repr().packed(), + ty::Tuple(..) => true, + _ => false, + } + { + candidates.vec.push(BuiltinCandidate); + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4f65b30775ed0..8d90aebe30bd3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -265,6 +265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::FnPtrTrait | LangItem::PointeeTrait | LangItem::Tuple + | LangItem::Field | LangItem::Unpin, ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), @@ -1299,6 +1300,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(..) | ty::Never | ty::Pat(..) + | ty::FRT(..) | ty::Dynamic(..) | ty::Str | ty::Slice(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4469afc3a4ebf..4818f919b6d14 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2159,6 +2159,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never + | ty::FRT(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { @@ -2196,7 +2197,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec>> { match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::FRT(..) => ty::Binder::dummy(vec![]), ty::Uint(_) | ty::Int(_) @@ -2302,6 +2303,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::FnPtr(..) | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::FRT(..) | ty::Never | ty::Char => { ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] }) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d383cb9d91dd4..a93050163c9ba 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -794,7 +794,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } } - ty::RawPtr(_, _) => { + ty::RawPtr(_, _) | ty::FRT(..) => { // Simple cases that are WF if their type args are WF. } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cbe15eb54787f..cad69e6198990 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -380,6 +380,22 @@ fn resolve_associated_item<'tcx>( assert_eq!(name, sym::transmute); let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::Field) { + if tcx.is_lang_item(trait_item_id, LangItem::FieldOffset) { + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::FRT(..) => {} + _ => bug!("expected field representing type, found {self_ty}"), + } + Some(Instance { + def: ty::InstanceKind::Item( + tcx.lang_items().get(LangItem::FieldOffset).unwrap(), + ), + args: rcvr_args, + }) + } else { + bug!("unexpected associated associated item") + } } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 62f3667ad7f4f..f0cbcac8f5ec5 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -379,6 +379,8 @@ fn layout_of_uncached<'tcx>( tcx.mk_layout(layout) } + ty::FRT(..) => cx.layout_of(tcx.types.unit)?.layout, + // Basic scalars. ty::Bool => tcx.mk_layout(LayoutData::scalar( cx, diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 06eef7e95145e..6f3a9b0359286 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -290,6 +290,7 @@ where | ty::RawPtr(..) | ty::FnDef(..) | ty::Pat(..) + | ty::FRT(..) | ty::FnPtr(..) | ty::Tuple(_) | ty::Bound(..) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index d6a29aba22b90..22ab03b033d58 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -34,6 +34,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::FnDef(..) | ty::FnPtr(..) | ty::Array(..) + | ty::FRT(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -377,6 +378,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index ed6416a7f55f2..e66be092082c3 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -46,6 +46,8 @@ pub enum SimplifiedType { Function(usize), UnsafeBinder, Placeholder, + /// Field representing type. + FRT, Error, } @@ -127,6 +129,7 @@ pub fn simplify_type( ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), ty::Pat(ty, ..) => simplify_type(cx, ty, treat_params), + ty::FRT(..) => Some(SimplifiedType::FRT), ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !cx.trait_is_auto(principal_def_id) => { @@ -298,6 +301,7 @@ impl matches!(rhs.kind(), ty::FRT(rhs_ty, rhs_field) if + lhs_field == rhs_field && self.types_may_unify_inner(lhs_ty, rhs_ty, depth) + ), + ty::UnsafeBinder(lhs_ty) => match rhs.kind() { ty::UnsafeBinder(rhs_ty) => { self.types_may_unify(lhs_ty.skip_binder(), rhs_ty.skip_binder()) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 8b057e5866cd9..a232411e87599 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -322,6 +322,10 @@ impl FlagComputation { self.add_ty_pat(pat); } + ty::FRT(ty, _field) => { + self.add_ty(ty); + } + ty::Slice(tt) => self.add_ty(tt), ty::RawPtr(ty, _) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index c9580d589d217..a72890b54383b 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -119,6 +119,8 @@ pub trait Ty>: fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + fn new_field_representing_type(interner: I, ty: Self, field: I::FieldId) -> Self; + fn new_unsafe_binder(interner: I, ty: ty::Binder) -> Self; fn tuple_fields(self) -> I::Tys; @@ -152,6 +154,9 @@ pub trait Ty>: /// Checks whether this type is an ADT that has unsafe fields. fn has_unsafe_fields(self) -> bool; + /// Checks whether this type is an ADT that is `repr(packed)`. + fn is_packed(self) -> bool; + fn fn_sig(self, interner: I) -> ty::Binder> { self.kind().fn_sig(interner) } @@ -174,6 +179,7 @@ pub trait Ty>: | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) @@ -577,6 +583,8 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_struct(self) -> bool; + fn is_packed(self) -> bool; + /// Returns the type of the struct tail. /// /// Expects the `AdtDef` to be a struct. If it is not, then this will panic. @@ -600,6 +608,10 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn destructor(self, interner: I) -> Option; } +pub trait FieldId: Copy + Debug + Hash + Eq { + fn ty(self, interner: I, ty: I::Ty) -> I::Ty; +} + pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { fn caller_bounds(self) -> impl SliceLike; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 8f446cdfba6d5..9d202c778aef9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -210,6 +210,8 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; + type FieldId: FieldId; + fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; fn alias_term_kind(self, alias: ty::AliasTerm) -> ty::AliasTermKind; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 39b575ebab63b..f1c45a4d98b5e 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -9,6 +9,8 @@ pub enum SolverLangItem { CoroutineReturn, CoroutineYield, DynMetadata, + FieldBase, + FieldType, FutureOutput, Metadata, // tidy-alphabetical-end @@ -36,6 +38,7 @@ pub enum SolverTraitLangItem { Destruct, DiscriminantKind, Drop, + Field, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 300e5c0b46956..671eb9f2ea2b5 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -198,6 +198,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Foreign(_) | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 3610605462ba9..c0f2621c07780 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -511,6 +511,15 @@ pub fn structurally_relate_tys>( Ok(Ty::new_pat(cx, ty, pat)) } + (ty::FRT(a_ty, a_field), ty::FRT(b_ty, b_field)) => { + let ty = relation.relate(a_ty, b_ty)?; + if a_field == b_field { + Ok(Ty::new_field_representing_type(cx, ty, a_field)) + } else { + Err(TypeError::Mismatch) + } + } + (ty::UnsafeBinder(a_binder), ty::UnsafeBinder(b_binder)) => { Ok(Ty::new_unsafe_binder(cx, relation.binders(*a_binder, *b_binder)?)) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 7cb71387e8680..fef605b3cd175 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -106,6 +106,9 @@ pub enum TyKind { /// Only supports integer range patterns for now. Pat(I::Ty, I::Pat), + /// Field representing type (`field_of!(Struct, field)`). + FRT(I::Ty, I::FieldId), + /// The pointee of an array slice. Written as `[T]`. Slice(I::Ty), @@ -298,6 +301,7 @@ impl TyKind { | ty::Str | ty::Array(_, _) | ty::Pat(_, _) + | ty::FRT(_, _) | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) @@ -350,6 +354,7 @@ impl fmt::Debug for TyKind { Str => write!(f, "str"), Array(t, c) => write!(f, "[{t:?}; {c:?}]"), Pat(t, p) => write!(f, "pattern_type!({t:?} is {p:?})"), + FRT(t, i) => write!(f, "field_of!({t:?}, {i:?})"), Slice(t) => write!(f, "[{:?}]", &t), RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index e48d598a5328d..a07fde2d00975 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -92,6 +92,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg push_ty_pat::(stack, pat); stack.push(ty.into()); } + ty::FRT(ty, _) => { + stack.push(ty.into()); + } ty::Array(ty, len) => { stack.push(len.into()); stack.push(ty.into()); diff --git a/library/core/src/field.rs b/library/core/src/field.rs new file mode 100644 index 0000000000000..62590a3ec78c8 --- /dev/null +++ b/library/core/src/field.rs @@ -0,0 +1,35 @@ +//! Field Reflection + +/// Type representing a field of a `struct`, `union`, `enum` variant or tuple. +/// +/// # Safety +/// +/// Given a valid value of type `Self::Base`, there exists a valid value of type `Self::Type` at +/// byte offset `OFFSET`. +#[lang = "field"] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_deny_explicit_impl] +#[rustc_dyn_incompatible_trait] +pub unsafe trait Field: Sized { + /// The type of the base where this field exists in. + #[lang = "field_base"] + type Base; + + /// The type of the field. + #[lang = "field_type"] + type Type; + + /// The offset of the field in bytes. + #[lang = "field_offset"] + const OFFSET: usize = crate::intrinsics::field_offset::(); +} + +/// Expands to the field representing type of the given field. +/// +/// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the +/// variant must also be specified. Only a single field is supported. +#[unstable(feature = "field_projections", issue = "145383")] +#[allow_internal_unstable(builtin_syntax)] +pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { + builtin # field_of($Container, $($fields)+) +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3ddea90652d16..f0ffe12e13924 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2811,6 +2811,20 @@ pub const fn align_of() -> usize; #[lang = "offset_of"] pub const fn offset_of(variant: u32, field: u32) -> usize; +/// The offset of a field queried by its field representing type. +/// +/// Returns the offset of the field represented by `F`. This function essentially does the same as +/// the [`offset_of`] intrinsic, but expects the field to be represented by a generic rather than +/// the variant and field indices. This also is a safe intrinsic and can only be evaluated at +/// compile-time, so it should only appear in constants or inline const blocks. +/// +/// There should be no need to call this intrinsic manually, as its value is used to define +/// [`Field::OFFSET`](crate::field::Field::OFFSET), which is publicly accessible. +#[rustc_intrinsic] +#[unstable(feature = "field_projections", issue = "145383")] +#[rustc_const_unstable(feature = "field_projections", issue = "145383")] +pub const fn field_offset() -> usize; + /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 17cf6b3714f50..3b3568c3da65c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -155,6 +155,7 @@ #![feature(extern_types)] #![feature(f16)] #![feature(f128)] +#![feature(field_projections)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(funnel_shifts)] @@ -311,6 +312,8 @@ pub mod bstr; pub mod cell; pub mod char; pub mod ffi; +#[unstable(feature = "field_projections", issue = "145383")] +pub mod field; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub mod io; pub mod iter; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index dcde208fac77b..1f46866bf2301 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -497,6 +497,8 @@ pub use core::cmp; pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[unstable(feature = "field_projections", issue = "145383")] +pub use core::field; #[stable(feature = "futures_api", since = "1.36.0")] pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e1..87bfab0ef25bb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1808,6 +1808,14 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), + TyKind::FieldOf(ty, hir::TyFieldPath { variant, field }) => { + let field_str = if let Some(variant) = variant { + format!("{variant}.{field}") + } else { + format!("{field}") + }; + Type::FRT(Box::new(clean_ty(ty, cx)), field_str.into()) + } TyKind::Array(ty, const_arg) => { // NOTE(min_const_generics): We can't use `const_eval_poly` for constants // as we currently do not supply the parent generics to anonymous constants @@ -2049,6 +2057,26 @@ pub(crate) fn clean_middle_ty<'tcx>( Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), format!("{pat:?}").into_boxed_str(), ), + ty::FRT(ty, field) => { + let field_str = match ty.kind() { + ty::Adt(def, _) => { + if def.is_enum() { + let variant = &def.variants()[field.variant]; + format!("{}.{}", variant.name, variant.fields[field.field].name) + } else { + format!("{}", def.non_enum_variant().fields[field.field].name) + } + } + ty::Tuple(_) => { + format!("{}", field.field.index()) + } + _ => bug!("unexpected ty in resolved FRT: {ty}"), + }; + Type::FRT( + Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + field_str.into_boxed_str(), + ) + } ty::Array(ty, n) => { let n = cx.tcx.normalize_erasing_regions(cx.typing_env(), n); let n = print_const(cx, n); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c145929534d97..60987caab5019 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1321,6 +1321,7 @@ pub(crate) enum Type { /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), + FRT(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` @@ -1534,6 +1535,7 @@ impl Type { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, + Type::FRT(..) => PrimitiveType::FRT, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, @@ -1581,6 +1583,7 @@ pub(crate) enum PrimitiveType { Slice, Array, Pat, + FRT, Tuple, Unit, RawPointer, @@ -1736,6 +1739,7 @@ impl PrimitiveType { Char => sym::char, Array => sym::array, Pat => sym::pat, + FRT => sym::field_of, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f38a21bd1ff36..44f6374802974 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -958,6 +958,11 @@ fn fmt_type( fmt::Display::fmt(&print_type(t, cx), f)?; write!(f, " is {pat}") } + clean::Type::FRT(t, field) => { + write!(f, "field_of!(")?; + fmt::Display::fmt(&print_type(t, cx), f)?; + write!(f, ", {field})") + } clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( f, PrimitiveType::Array, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 30b534003da17..0dd311b073396 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -2041,6 +2041,7 @@ fn get_index_type_id( } // Not supported yet clean::Type::Pat(..) + | clean::Type::FRT(..) | clean::Generic(_) | clean::SelfTy | clean::ImplTrait(_) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 2edf7891be400..59ce937fa3002 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -579,6 +579,8 @@ impl FromClean for Type { type_: Box::new(t.into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, + // FIXME(FRTs): implement + clean::Type::FRT(..) => todo!(), ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a68e9dc87ae52..db758fcc19dce 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -526,6 +526,7 @@ fn ty_to_res<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), ty::Tuple(_) => Res::Primitive(Tuple), ty::Pat(..) => Res::Primitive(Pat), + ty::FRT(..) => Res::Primitive(FRT), ty::Array(..) => Res::Primitive(Array), ty::Slice(_) => Res::Primitive(Slice), ty::RawPtr(_, _) => Res::Primitive(RawPointer), diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 32fd4afb122e6..2bdbdfd905257 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -855,6 +855,7 @@ impl TyCoercionStability { | TyKind::Ptr(_) | TyKind::FnPtr(_) | TyKind::Pat(..) + | TyKind::FieldOf(..) | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) => Self::Deref, @@ -907,6 +908,7 @@ impl TyCoercionStability { | ty::Uint(_) | ty::Array(..) | ty::Pat(..) + | ty::FRT(..) | ty::Float(_) | ty::RawPtr(..) | ty::FnPtr(..) diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 7fb8616072a59..7d50466c86605 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -532,6 +532,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { // experimental | TyKind::Pat(..) + | TyKind::FieldOf(..) // unused | TyKind::CVarArgs diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index abc6885bef2d1..b4966071d20fd 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -14,7 +14,7 @@ use rustc_hir::{ GenericParam, GenericParamKind, GenericParamSource, Generics, HirId, HirIdMap, InlineAsmOperand, ItemId, ItemKind, LetExpr, Lifetime, LifetimeKind, LifetimeParamKind, Node, ParamName, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PreciseCapturingArgKind, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind, + TyFieldPath, TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::LateContext; @@ -1529,6 +1529,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); self.hash_ty_pat(pat); }, + TyKind::FieldOf(ty, TyFieldPath { variant, field }) => { + self.hash_ty(ty); + if let Some(variant) = variant { + self.hash_name(variant.name); + } + self.hash_name(field.name); + }, TyKind::Ptr(mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 2d7bc59c62788..d040a5ca5a3d6 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1035,6 +1035,14 @@ impl Rewrite for ast::Ty { let pat = pat.rewrite_result(context, shape)?; Ok(format!("{ty} is {pat}")) } + ast::TyKind::FieldOf(ref ty, ref variant, ref field) => { + let ty = ty.rewrite_result(context, shape)?; + if let Some(variant) = variant { + Ok(format!("field_of!({ty}, {variant}.{field})")) + } else { + Ok(format!("field_of!({ty}, {field})")) + } + } ast::TyKind::UnsafeBinder(ref binder) => { let mut result = String::new(); if binder.generic_params.is_empty() { diff --git a/tests/debuginfo/function-names.rs b/tests/debuginfo/function-names.rs index e2f106e565439..abba8814c6663 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -39,7 +39,7 @@ // Const generic parameter //@ gdb-command:info functions -q function_names::const_generic_fn.* //@ gdb-check:[...]static fn function_names::const_generic_fn_bool(); -//@ gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#ffa3db4ca1d52dce}>(); +//@ gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#5177fe61e1757625}>(); //@ gdb-check:[...]static fn function_names::const_generic_fn_signed_int<-7>(); //@ gdb-check:[...]static fn function_names::const_generic_fn_unsigned_int<14>(); @@ -85,6 +85,7 @@ use std::ops::Coroutine; use std::pin::Pin; + use Mod1::TestTrait2; fn main() { diff --git a/tests/ui/README.md b/tests/ui/README.md index 237cfb9c4f071..164e60c0237b6 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -611,6 +611,12 @@ See: - [`ffi_const` | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/ffi-const.html) - [`ffi_pure` | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/ffi-pure.html) +## `tests/ui/field_representing_types`: `#![feature(field_projections)]` + +Tests for field representing types `field_of!(Struct, field)`. + +See: [Tracking Issue for Field Projections #145383](https://github.com/rust-lang/rust/issues/145383) + ## `tests/ui/float` See: [Tracking Issue for `f16` and `f128` float types #116909](https://github.com/rust-lang/rust/issues/116909) diff --git a/tests/ui/feature-gates/feature-gate-field-projections.rs b/tests/ui/feature-gates/feature-gate-field-projections.rs new file mode 100644 index 0000000000000..5fa6b1ea30810 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.rs @@ -0,0 +1,21 @@ +#![allow(dead_code)] + +use std::field::{Field, field_of}; //~ ERROR: use of unstable library feature `field_projections` [E0658] +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +use std::ptr; + +fn project_ref( + //~^ ERROR: use of unstable library feature `field_projections` [E0658] + r: &F::Base, //~ ERROR: use of unstable library feature `field_projections` [E0658] +) -> &F::Type +//~^ ERROR: use of unstable library feature `field_projections` [E0658] +where + F::Type: Sized, //~ ERROR: use of unstable library feature `field_projections` [E0658] +{ + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } //~ ERROR: use of unstable library feature `field_projections` [E0658] +} + +fn main() { + struct Foo(()); + let _ = project_ref::(&Foo(())); //~ ERROR: use of unstable library feature `field_projections` [E0658] +} diff --git a/tests/ui/feature-gates/feature-gate-field-projections.stderr b/tests/ui/feature-gates/feature-gate-field-projections.stderr new file mode 100644 index 0000000000000..01074d49fa862 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-field-projections.stderr @@ -0,0 +1,83 @@ +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:20:27 + | +LL | let _ = project_ref::(&Foo(())); + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:18 + | +LL | use std::field::{Field, field_of}; + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:3:25 + | +LL | use std::field::{Field, field_of}; + | ^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:7:19 + | +LL | fn project_ref( + | ^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:13:5 + | +LL | F::Type: Sized, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:9:9 + | +LL | r: &F::Base, + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:10:7 + | +LL | ) -> &F::Type + | ^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `field_projections` + --> $DIR/feature-gate-field-projections.rs:15:42 + | +LL | unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } + | ^^^^^^^^^ + | + = note: see issue #145383 for more information + = help: add `#![feature(field_projections)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/field_representing_types/auxiliary/extern-crate.rs b/tests/ui/field_representing_types/auxiliary/extern-crate.rs new file mode 100644 index 0000000000000..8e7e6d2679b44 --- /dev/null +++ b/tests/ui/field_representing_types/auxiliary/extern-crate.rs @@ -0,0 +1,6 @@ +pub struct Point { + pub x: usize, + pub y: usize, +} + +pub trait ForeignTrait {} diff --git a/tests/ui/field_representing_types/incoherent-impl.next.stderr b/tests/ui/field_representing_types/incoherent-impl.next.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.next.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:25:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.old.stderr b/tests/ui/field_representing_types/incoherent-impl.old.stderr new file mode 100644 index 0000000000000..8e5450eb47bcb --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.old.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:22:1 + | +LL | impl ForeignTrait for field_of!(Point, x) {} + | ^^^^^^^^^^^^^^^^^^^^^^------------------- + | | + | `Point` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/incoherent-impl.rs:25:1 + | +LL | impl ForeignTrait for field_of!((usize, usize), 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^---------------------------- + | | + | this is not defined in the current crate because tuples are always foreign + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/field_representing_types/incoherent-impl.rs b/tests/ui/field_representing_types/incoherent-impl.rs new file mode 100644 index 0000000000000..38969222f66d1 --- /dev/null +++ b/tests/ui/field_representing_types/incoherent-impl.rs @@ -0,0 +1,28 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ aux-build:extern-crate.rs +#![expect(incomplete_features)] +#![feature(field_projections)] +extern crate extern_crate; + +use std::field::field_of; + +use extern_crate::{ForeignTrait, Point}; + +pub trait MyTrait {} + +impl MyTrait for field_of!(Point, x) {} +impl MyTrait for field_of!(Player, pos) {} +impl MyTrait for field_of!((usize, usize), 0) {} + +pub struct Player { + pos: Point, +} + +impl ForeignTrait for field_of!(Point, x) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] +impl ForeignTrait for field_of!(Player, pos) {} +impl ForeignTrait for field_of!((usize, usize), 0) {} +//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types [E0117] + +fn main() {} diff --git a/tests/ui/field_representing_types/invalid.next.stderr b/tests/ui/field_representing_types/invalid.next.stderr new file mode 100644 index 0000000000000..2a5e884a5e3f9 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -0,0 +1,71 @@ +error: unexpected end of macro invocation + --> $DIR/invalid.rs:23:28 + | +LL | let _: field_of!(Struct); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: unexpected end of macro invocation + --> $DIR/invalid.rs:24:29 + | +LL | let _: field_of!(Struct,); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:expr` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:25:37 + | +LL | let _: field_of!(Struct, field, extra); + | ^^^^^ no rules expected this token in macro call + | + = note: while trying to match sequence end + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:27:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:27:12 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:29:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:29:12 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `field_of!` only supports a single field or a variant with a field + --> $DIR/invalid.rs:31:30 + | +LL | let _: field_of!(Struct, field1.field2.field3); + | ------------------^^^^^^-^^^^^^-^^^^^^- + | | + | in this macro invocation + | this macro call doesn't expand to a type + +error: aborting due to 8 previous errors + diff --git a/tests/ui/field_representing_types/invalid.old.stderr b/tests/ui/field_representing_types/invalid.old.stderr new file mode 100644 index 0000000000000..2a5e884a5e3f9 --- /dev/null +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -0,0 +1,71 @@ +error: unexpected end of macro invocation + --> $DIR/invalid.rs:23:28 + | +LL | let _: field_of!(Struct); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: unexpected end of macro invocation + --> $DIR/invalid.rs:24:29 + | +LL | let _: field_of!(Struct,); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:expr` + --> $SRC_DIR/core/src/field.rs:LL:COL + +error: no rules expected `extra` + --> $DIR/invalid.rs:25:37 + | +LL | let _: field_of!(Struct, field, extra); + | ^^^^^ no rules expected this token in macro call + | + = note: while trying to match sequence end + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:27:28 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:27:12 + | +LL | let _: field_of!(Enum, Variant..field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: offset_of expects dot-separated field and variant names + --> $DIR/invalid.rs:29:30 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^ + +error: `field_of!` expects dot-separated field and variant names + --> $DIR/invalid.rs:29:12 + | +LL | let _: field_of!(Struct, [42]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | in this macro invocation + | this macro call doesn't expand to a type + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `field_of!` only supports a single field or a variant with a field + --> $DIR/invalid.rs:31:30 + | +LL | let _: field_of!(Struct, field1.field2.field3); + | ------------------^^^^^^-^^^^^^-^^^^^^- + | | + | in this macro invocation + | this macro call doesn't expand to a type + +error: aborting due to 8 previous errors + diff --git a/tests/ui/field_representing_types/invalid.rs b/tests/ui/field_representing_types/invalid.rs new file mode 100644 index 0000000000000..d1fc217db7ddd --- /dev/null +++ b/tests/ui/field_representing_types/invalid.rs @@ -0,0 +1,32 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +pub struct Struct { + field: i32, +} + +pub union Union { + field: i32, +} + +pub enum Enum { + Variant { field: i32 }, +} + +pub trait Trait {} + +fn main() { + let _: field_of!(Struct); //~ ERROR: unexpected end of macro invocation + let _: field_of!(Struct,); //~ ERROR: unexpected end of macro invocation + let _: field_of!(Struct, field, extra); //~ ERROR: no rules expected `extra` + // FIXME(FRTs): adjust error message to mention `field_of!` & prevent double errors + let _: field_of!(Enum, Variant..field); //~ ERROR: offset_of expects dot-separated field and variant names + //~^ ERROR: `field_of!` expects dot-separated field and variant names + let _: field_of!(Struct, [42]); //~ ERROR: offset_of expects dot-separated field and variant names + //~^ ERROR: `field_of!` expects dot-separated field and variant names + let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of!` only supports a single field or a variant with a field +} diff --git a/tests/ui/field_representing_types/non-struct.next.stderr b/tests/ui/field_representing_types/non-struct.next.stderr new file mode 100644 index 0000000000000..7a6c33c3b6f54 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:22:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyUnion, field)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:25:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyEnum, A.a)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/non-struct.old.stderr b/tests/ui/field_representing_types/non-struct.old.stderr new file mode 100644 index 0000000000000..7a6c33c3b6f54 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.old.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:22:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyUnion, field)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied + --> $DIR/non-struct.rs:25:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyEnum, A.a)` + | +note: required by a bound in `assert_field` + --> $DIR/non-struct.rs:18:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/non-struct.rs b/tests/ui/field_representing_types/non-struct.rs new file mode 100644 index 0000000000000..50fd4b0e6b0e7 --- /dev/null +++ b/tests/ui/field_representing_types/non-struct.rs @@ -0,0 +1,27 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub union MyUnion { + field: u32, + other: i32, +} + +pub enum MyEnum { + A { a: i32, b: i64 }, + B { x: i64, y: i32 }, +} + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): improve this error message, point to the `union`. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyUnion, field): std::field::Field` is not satisfied [E0277] + // FIXME(FRTs): improve this error message, point to the `enum`. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyEnum, A.a): std::field::Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_representing_types/nonexistent.next.stderr b/tests/ui/field_representing_types/nonexistent.next.stderr new file mode 100644 index 0000000000000..76026ab4a496f --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.next.stderr @@ -0,0 +1,124 @@ +error[E0573]: expected type, found function `main` + --> $DIR/nonexistent.rs:43:22 + | +LL | let _: field_of!(main, field); + | ^^^^ not a type + +error[E0609]: no field `other` on type `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ + +error[E0609]: no field `other` on type `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ + +error[E0609]: no field `other` on type `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ + +error[E0609]: no field `0` on type `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), field); + | ^^^^^ + +error: type `i32` doesn't have fields + --> $DIR/nonexistent.rs:36:22 + | +LL | let _: field_of!(i32, field); + | ^^^ + +error: type `[Struct]` doesn't have fields + --> $DIR/nonexistent.rs:37:22 + | +LL | let _: field_of!([Struct], field); + | ^^^^^^^^ + +error: type `[Struct; 42]` is not yet supported in `field_of!` + --> $DIR/nonexistent.rs:38:22 + | +LL | let _: field_of!([Struct; 42], field); + | ^^^^^^^^^^^^ + +error: type `&'static Struct` doesn't have fields + --> $DIR/nonexistent.rs:39:22 + | +LL | let _: field_of!(&'static Struct, field); + | ^^^^^^^^^^^^^^^ + +error: type `*const Struct` doesn't have fields + --> $DIR/nonexistent.rs:40:22 + | +LL | let _: field_of!(*const Struct, field); + | ^^^^^^^^^^^^^ + +error: type `fn() -> Struct` doesn't have fields + --> $DIR/nonexistent.rs:41:22 + | +LL | let _: field_of!(fn() -> Struct, field); + | ^^^^^^^^^^^^^^ + +error: type `dyn Trait` doesn't have fields + --> $DIR/nonexistent.rs:42:22 + | +LL | let _: field_of!(dyn Trait, field); + | ^^^^^^^^^ + +error: cannot use `_` in this position + --> $DIR/nonexistent.rs:44:22 + | +LL | let _: field_of!(_, field); + | ^ + +error: type `T` doesn't have fields + --> $DIR/nonexistent.rs:48:22 + | +LL | let _: field_of!(T, field); + | ^ + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0573, E0599, E0609. +For more information about an error, try `rustc --explain E0573`. diff --git a/tests/ui/field_representing_types/nonexistent.old.stderr b/tests/ui/field_representing_types/nonexistent.old.stderr new file mode 100644 index 0000000000000..76026ab4a496f --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.old.stderr @@ -0,0 +1,124 @@ +error[E0573]: expected type, found function `main` + --> $DIR/nonexistent.rs:43:22 + | +LL | let _: field_of!(main, field); + | ^^^^ not a type + +error[E0609]: no field `other` on type `Struct` + --> $DIR/nonexistent.rs:23:30 + | +LL | let _: field_of!(Struct, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Struct` + --> $DIR/nonexistent.rs:24:30 + | +LL | let _: field_of!(Struct, 0); + | ^ + +error[E0609]: no field `other` on type `Union` + --> $DIR/nonexistent.rs:25:29 + | +LL | let _: field_of!(Union, other); + | ^^^^^ + +error[E0609]: no field `0` on type `Union` + --> $DIR/nonexistent.rs:26:29 + | +LL | let _: field_of!(Union, 0); + | ^ + +error[E0609]: no field `other` on type `Enum` + --> $DIR/nonexistent.rs:28:36 + | +LL | let _: field_of!(Enum, Variant.other); + | ^^^^^ + +error[E0609]: no field `0` on type `Enum` + --> $DIR/nonexistent.rs:29:36 + | +LL | let _: field_of!(Enum, Variant.0); + | ^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:31:28 + | +LL | let _: field_of!(Enum, OtherVariant.field); + | ^^^^^^^^^^^^ + +error[E0599]: no variant named `OtherVariant` found for enum `Enum` + --> $DIR/nonexistent.rs:32:28 + | +LL | let _: field_of!(Enum, OtherVariant.0); + | ^^^^^^^^^^^^ + +error[E0609]: no field `2` on type `((), ())` + --> $DIR/nonexistent.rs:33:32 + | +LL | let _: field_of!(((), ()), 2); + | ^ + +error[E0609]: no field `field` on type `((), ())` + --> $DIR/nonexistent.rs:34:32 + | +LL | let _: field_of!(((), ()), field); + | ^^^^^ + +error: type `i32` doesn't have fields + --> $DIR/nonexistent.rs:36:22 + | +LL | let _: field_of!(i32, field); + | ^^^ + +error: type `[Struct]` doesn't have fields + --> $DIR/nonexistent.rs:37:22 + | +LL | let _: field_of!([Struct], field); + | ^^^^^^^^ + +error: type `[Struct; 42]` is not yet supported in `field_of!` + --> $DIR/nonexistent.rs:38:22 + | +LL | let _: field_of!([Struct; 42], field); + | ^^^^^^^^^^^^ + +error: type `&'static Struct` doesn't have fields + --> $DIR/nonexistent.rs:39:22 + | +LL | let _: field_of!(&'static Struct, field); + | ^^^^^^^^^^^^^^^ + +error: type `*const Struct` doesn't have fields + --> $DIR/nonexistent.rs:40:22 + | +LL | let _: field_of!(*const Struct, field); + | ^^^^^^^^^^^^^ + +error: type `fn() -> Struct` doesn't have fields + --> $DIR/nonexistent.rs:41:22 + | +LL | let _: field_of!(fn() -> Struct, field); + | ^^^^^^^^^^^^^^ + +error: type `dyn Trait` doesn't have fields + --> $DIR/nonexistent.rs:42:22 + | +LL | let _: field_of!(dyn Trait, field); + | ^^^^^^^^^ + +error: cannot use `_` in this position + --> $DIR/nonexistent.rs:44:22 + | +LL | let _: field_of!(_, field); + | ^ + +error: type `T` doesn't have fields + --> $DIR/nonexistent.rs:48:22 + | +LL | let _: field_of!(T, field); + | ^ + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0573, E0599, E0609. +For more information about an error, try `rustc --explain E0573`. diff --git a/tests/ui/field_representing_types/nonexistent.rs b/tests/ui/field_representing_types/nonexistent.rs new file mode 100644 index 0000000000000..74083666af76d --- /dev/null +++ b/tests/ui/field_representing_types/nonexistent.rs @@ -0,0 +1,49 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +pub struct Struct { + field: i32, +} + +pub union Union { + field: i32, +} + +pub enum Enum { + Variant { field: i32 }, +} + +pub trait Trait {} + +fn main() { + let _: field_of!(Struct, other); //~ ERROR: no field `other` on type `Struct` [E0609] + let _: field_of!(Struct, 0); //~ ERROR: no field `0` on type `Struct` [E0609] + let _: field_of!(Union, other); //~ ERROR: no field `other` on type `Union` [E0609] + let _: field_of!(Union, 0); //~ ERROR: no field `0` on type `Union` [E0609] + // FIXME(FRTs): make the error mention the variant too. + let _: field_of!(Enum, Variant.other); //~ ERROR: no field `other` on type `Enum` [E0609] + let _: field_of!(Enum, Variant.0); //~ ERROR: no field `0` on type `Enum` [E0609] + // FIXME(FRTs): select correct error code + let _: field_of!(Enum, OtherVariant.field); //~ ERROR: no variant named `OtherVariant` found for enum `Enum` + let _: field_of!(Enum, OtherVariant.0); //~ ERROR: no variant named `OtherVariant` found for enum `Enum` + let _: field_of!(((), ()), 2); //~ ERROR: no field `2` on type `((), ())` [E0609] + let _: field_of!(((), ()), field); //~ ERROR: no field `field` on type `((), ())` [E0609] + + let _: field_of!(i32, field); //~ ERROR: type `i32` doesn't have fields + let _: field_of!([Struct], field); //~ ERROR: type `[Struct]` doesn't have fields + let _: field_of!([Struct; 42], field); //~ ERROR: type `[Struct; 42]` is not yet supported in `field_of!` + let _: field_of!(&'static Struct, field); //~ ERROR: type `&'static Struct` doesn't have fields + let _: field_of!(*const Struct, field); //~ ERROR: type `*const Struct` doesn't have fields + let _: field_of!(fn() -> Struct, field); //~ ERROR: type `fn() -> Struct` doesn't have fields + let _: field_of!(dyn Trait, field); //~ ERROR: type `dyn Trait` doesn't have fields + let _: field_of!(main, field); //~ ERROR: expected type, found function `main` + let _: field_of!(_, field); //~ ERROR: cannot use `_` in this position +} + +fn generic() { + let _: field_of!(T, field); //~ ERROR: type `T` doesn't have fields +} diff --git a/tests/ui/field_representing_types/not-field-if-packed.next.stderr b/tests/ui/field_representing_types/not-field-if-packed.next.stderr new file mode 100644 index 0000000000000..a4ff5875a87a1 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-packed.rs:15:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-packed.old.stderr b/tests/ui/field_representing_types/not-field-if-packed.old.stderr new file mode 100644 index 0000000000000..a4ff5875a87a1 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.old.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-packed.rs:15:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-packed.rs:11:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-packed.rs b/tests/ui/field_representing_types/not-field-if-packed.rs new file mode 100644 index 0000000000000..7391ef312ba2c --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-packed.rs @@ -0,0 +1,17 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +#[repr(packed)] +pub struct MyStruct(usize); + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): improve this error message, point to the `repr(packed)` span. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_representing_types/offset.rs b/tests/ui/field_representing_types/offset.rs new file mode 100644 index 0000000000000..20b7c8b6d036d --- /dev/null +++ b/tests/ui/field_representing_types/offset.rs @@ -0,0 +1,51 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; +use std::ptr; + +#[repr(C)] +pub struct Struct { + a: i32, + b: i64, +} + +// FIXME(FRTs): need to mark these fields as used by the `field_of!` macro. +#[expect(dead_code)] +pub union Union { + a: i32, + b: i64, +} + +#[repr(C, i8)] +pub enum Enum { + A { a: i32, b: i64 }, + B { x: i64, y: i32 }, +} + +fn project_ref<'a, T, F: Field>(r: &'a T) -> &'a F::Type { + unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() } +} + +fn main() { + assert_eq!(::OFFSET, 0); + assert_eq!(::OFFSET, 8); + + let _: field_of!(Union, a); + let _: field_of!(Union, b); + + let _: field_of!(Enum, A.a); + let _: field_of!(Enum, A.b); + let _: field_of!(Enum, B.x); + let _: field_of!(Enum, B.y); + + let s = Struct { a: 42, b: 24 }; + let r = &s; + let a = project_ref::(r); + let b = project_ref::(r); + assert_eq!(*a, 42); + assert_eq!(*b, 24); +} diff --git a/tests/ui/field_representing_types/privacy.next.stderr b/tests/ui/field_representing_types/privacy.next.stderr new file mode 100644 index 0000000000000..9df19529d3fab --- /dev/null +++ b/tests/ui/field_representing_types/privacy.next.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `Struct` is private + --> $DIR/privacy.rs:28:30 + | +LL | let _: field_of!(Struct, a); + | ^ private field + +error[E0616]: field `a` of union `foo::Union` is private + --> $DIR/privacy.rs:30:29 + | +LL | let _: field_of!(Union, a); + | ^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/privacy.old.stderr b/tests/ui/field_representing_types/privacy.old.stderr new file mode 100644 index 0000000000000..9df19529d3fab --- /dev/null +++ b/tests/ui/field_representing_types/privacy.old.stderr @@ -0,0 +1,15 @@ +error[E0616]: field `a` of struct `Struct` is private + --> $DIR/privacy.rs:28:30 + | +LL | let _: field_of!(Struct, a); + | ^ private field + +error[E0616]: field `a` of union `foo::Union` is private + --> $DIR/privacy.rs:30:29 + | +LL | let _: field_of!(Union, a); + | ^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0616`. diff --git a/tests/ui/field_representing_types/privacy.rs b/tests/ui/field_representing_types/privacy.rs new file mode 100644 index 0000000000000..a25be8e780bad --- /dev/null +++ b/tests/ui/field_representing_types/privacy.rs @@ -0,0 +1,34 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::field_of; + +mod foo { + pub struct Struct { + a: i32, + pub b: i32, + } + + pub union Union { + a: i32, + pub b: u32, + } + + pub enum Enum { + A { field: i32 }, + B(i32), + } +} + +use foo::{Enum, Struct, Union}; + +fn main() { + let _: field_of!(Struct, a); //~ ERROR: field `a` of struct `Struct` is private [E0616] + let _: field_of!(Struct, b); + let _: field_of!(Union, a); //~ ERROR: field `a` of union `foo::Union` is private [E0616] + let _: field_of!(Union, b); + let _: field_of!(Enum, A.field); + let _: field_of!(Enum, B.0); +} diff --git a/tests/ui/field_representing_types/projections.next.stderr b/tests/ui/field_representing_types/projections.next.stderr new file mode 100644 index 0000000000000..bf3b41cc23a54 --- /dev/null +++ b/tests/ui/field_representing_types/projections.next.stderr @@ -0,0 +1,32 @@ +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:39:18 + | +LL | _: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:45:19 + | +LL | _x: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `::Assoc` + --> $DIR/projections.rs:24:22 + | +LL | let _: field_of!(::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:51:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:56:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/field_representing_types/projections.old.stderr b/tests/ui/field_representing_types/projections.old.stderr new file mode 100644 index 0000000000000..bf3b41cc23a54 --- /dev/null +++ b/tests/ui/field_representing_types/projections.old.stderr @@ -0,0 +1,32 @@ +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:39:18 + | +LL | _: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:45:19 + | +LL | _x: field_of!(<::Assoc as Special>::Assoc, field), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `::Assoc` + --> $DIR/projections.rs:24:22 + | +LL | let _: field_of!(::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:51:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: could not resolve fields of `<::Assoc as Special>::Assoc` + --> $DIR/projections.rs:56:22 + | +LL | let _: field_of!(<::Assoc as Special>::Assoc, other); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/field_representing_types/projections.rs b/tests/ui/field_representing_types/projections.rs new file mode 100644 index 0000000000000..625f22e25e991 --- /dev/null +++ b/tests/ui/field_representing_types/projections.rs @@ -0,0 +1,58 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] + +use std::field::field_of; + +struct Struct { + field: u32, +} + +type Alias = Struct; + +trait Trait { + type Assoc; +} + +impl Trait for Struct { + type Assoc = Struct; +} + +fn main() { + let _: field_of!(Alias, field); + let _: field_of!(::Assoc, field); + //~^ ERROR: could not resolve fields of `::Assoc` +} + +trait Special { + type Assoc; +} + +trait Constraint: Trait {} + +impl Special for Struct { + type Assoc = Self; +} + +fn with_constraint1( + _: field_of!(<::Assoc as Special>::Assoc, field), + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +) { +} + +fn with_constraint2( + _x: field_of!(<::Assoc as Special>::Assoc, field), + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +) { +} + +fn with_constraint3() { + let _: field_of!(<::Assoc as Special>::Assoc, field); + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +} + +fn with_constraint_invalid_field() { + let _: field_of!(<::Assoc as Special>::Assoc, other); + //~^ ERROR: could not resolve fields of `<::Assoc as Special>::Assoc` +} diff --git a/tests/ui/field_representing_types/traits.rs b/tests/ui/field_representing_types/traits.rs new file mode 100644 index 0000000000000..6b5bb15f9ee9a --- /dev/null +++ b/tests/ui/field_representing_types/traits.rs @@ -0,0 +1,29 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +//@ run-pass +#![feature(field_projections, freeze)] +#![expect(incomplete_features, dead_code)] +use std::field::field_of; +use std::marker::{Freeze, Unpin}; + +struct Struct { + field: u32, +} + +union Union { + field: u32, +} + +enum Enum { + Variant1 { field: u32 }, + Variant2(u32), +} + +fn assert_traits() {} + +fn main() { + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); +} diff --git a/tests/ui/field_representing_types/weird-impls.next.stderr b/tests/ui/field_representing_types/weird-impls.next.stderr new file mode 100644 index 0000000000000..427d5e756af48 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.next.stderr @@ -0,0 +1,30 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/weird-impls.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `field_of!(MyStruct, 0)` + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:21:1 + | +LL | unsafe impl Field for field_of!(MyStruct2, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:30:1 + | +LL | unsafe impl Field for MyField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0120, E0321, E0322. +For more information about an error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/weird-impls.old.stderr b/tests/ui/field_representing_types/weird-impls.old.stderr new file mode 100644 index 0000000000000..427d5e756af48 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.old.stderr @@ -0,0 +1,30 @@ +error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions + --> $DIR/weird-impls.rs:10:15 + | +LL | impl Drop for field_of!(MyStruct, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^ must be a struct, enum, or union in the current crate + | + = note: this error originates in the macro `field_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `field_of!(MyStruct, 0)` + --> $DIR/weird-impls.rs:15:1 + | +LL | unsafe impl Send for field_of!(MyStruct, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:21:1 + | +LL | unsafe impl Field for field_of!(MyStruct2, 0) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error[E0322]: explicit impls for the `Field` trait are not permitted + --> $DIR/weird-impls.rs:30:1 + | +LL | unsafe impl Field for MyField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Field` not allowed + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0120, E0321, E0322. +For more information about an error, try `rustc --explain E0120`. diff --git a/tests/ui/field_representing_types/weird-impls.rs b/tests/ui/field_representing_types/weird-impls.rs new file mode 100644 index 0000000000000..1fd58ba0da060 --- /dev/null +++ b/tests/ui/field_representing_types/weird-impls.rs @@ -0,0 +1,37 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub struct MyStruct(()); + +impl Drop for field_of!(MyStruct, 0) { + //~^ ERROR: the `Drop` trait may only be implemented for local structs, enums, and unions [E0120] + fn drop(&mut self) {} +} + +unsafe impl Send for field_of!(MyStruct, 0) {} +//~^ ERROR: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `field_of!(MyStruct, 0)` [E0321] + +#[repr(packed)] +pub struct MyStruct2(usize); + +unsafe impl Field for field_of!(MyStruct2, 0) { + //~^ ERROR: explicit impls for the `Field` trait are not permitted [E0322] + type Base = MyStruct2; + type Type = usize; + const OFFSET: usize = 0; +} + +pub struct MyField; + +unsafe impl Field for MyField { + //~^ ERROR: explicit impls for the `Field` trait are not permitted [E0322] + type Base = (); + type Type = (); + const OFFSET: usize = 0; +} + +fn main() {} diff --git a/tests/ui/reflection/dump.bit64.run.stdout b/tests/ui/reflection/dump.bit64.run.stdout index efae226539515..41b7c35d132a8 100644 --- a/tests/ui/reflection/dump.bit64.run.stdout +++ b/tests/ui/reflection/dump.bit64.run.stdout @@ -11,7 +11,7 @@ Type { offset: 1, }, Field { - ty: TypeId(0x41223169ff28813ba79b7268a2a968d9), + ty: TypeId(0x76973a66f24df6db7173e51528fa5bec), offset: 2, }, ], @@ -179,7 +179,7 @@ Type { Type { kind: Reference( Reference { - pointee: TypeId(0x641e3def269c37acc6dcb92bf8c5f196), + pointee: TypeId(0x2a675f7e9d3320493042d9e4b41ba9eb), mutable: false, }, ), diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index a028f4331725e..6f1777c84c191 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h1dddcfd03744167fE) +error: symbol-name(_ZN5basic4main17h9dcd691dfd66f09dE) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h1dddcfd03744167f) +error: demangling(basic::main::h9dcd691dfd66f09d) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index 14cbd877d9f8a..397ae379a3f48 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h4b3099ec5dc5d306E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h0c5a913a7866cf0eE) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h4b3099ec5dc5d306) +error: demangling(issue_60925::foo::Foo::foo::h0c5a913a7866cf0e) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] From 0de45db240476e15001aa0cad2f0943a4b1e51d6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 10 Feb 2026 14:01:31 +1100 Subject: [PATCH 14/14] Clarify names of `QueryVTable` functions for "executing" a query This also changes the signature of `call_query_method` to not return a value, because its only caller immediately discards the value anyway. --- compiler/rustc_middle/src/query/plumbing.rs | 17 ++++++++++++-- compiler/rustc_query_impl/src/execution.rs | 15 ++++++++---- compiler/rustc_query_impl/src/lib.rs | 13 +++++++---- compiler/rustc_query_impl/src/plumbing.rs | 26 ++++++++++++++------- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index b80f096ec9935..3c844eac1fca0 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -49,8 +49,21 @@ pub struct QueryVTable<'tcx, C: QueryCache> { // Offset of this query's cache field in the QueryCaches struct pub query_cache: usize, pub will_cache_on_disk_for_key_fn: Option>, - pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, - pub compute_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + + /// Function pointer that calls `tcx.$query(key)` for this query and + /// discards the returned value. + /// + /// This is a weird thing to be doing, and probably not what you want. + /// It is used for loading query results from disk-cache in some cases. + pub call_query_method_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key), + + /// Function pointer that actually calls this query's provider. + /// Also performs some associated secondary tasks; see the macro-defined + /// implementation in `mod invoke_provider_fn` for more details. + /// + /// This should be the only code that calls the provider function. + pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + pub try_load_from_disk_fn: Option>, pub is_loadable_from_disk_fn: Option>, pub hash_result: HashResult, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 88604c91d0259..c0d7a86acad21 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -416,7 +416,8 @@ fn execute_job_non_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( } let prof_timer = qcx.tcx.prof.query_provider(); - let result = qcx.start_query(job_id, query.depth_limit(), || query.compute(qcx, key)); + // Call the query provider. + let result = qcx.start_query(job_id, query.depth_limit(), || query.invoke_provider(qcx, key)); let dep_node_index = qcx.tcx.dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -459,18 +460,21 @@ fn execute_job_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>( let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || { if query.anon() { - return dep_graph_data - .with_anon_task_inner(qcx.tcx, query.dep_kind(), || query.compute(qcx, key)); + // Call the query provider inside an anon task. + return dep_graph_data.with_anon_task_inner(qcx.tcx, query.dep_kind(), || { + query.invoke_provider(qcx, key) + }); } // `to_dep_node` is expensive for some `DepKind`s. let dep_node = dep_node_opt.unwrap_or_else(|| query.construct_dep_node(qcx.tcx, &key)); + // Call the query provider. dep_graph_data.with_task( dep_node, (qcx, query), key, - |(qcx, query), key| query.compute(qcx, key), + |(qcx, query), key| query.invoke_provider(qcx, key), query.hash_result(), ) }); @@ -547,7 +551,8 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache, const FLAGS: Quer let prof_timer = qcx.tcx.prof.query_provider(); // The dep-graph for this computation is already in-place. - let result = qcx.tcx.dep_graph.with_ignore(|| query.compute(qcx, *key)); + // Call the query provider. + let result = qcx.tcx.dep_graph.with_ignore(|| query.invoke_provider(qcx, *key)); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a33bdd22a7970..c79990ac1da4c 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -107,15 +107,18 @@ impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'t } } - // Don't use this method to compute query results, instead use the methods on TyCtxt. + /// Calls `tcx.$query(key)` for this query, and discards the returned value. + /// See [`QueryVTable::call_query_method_fn`] for details of this strange operation. #[inline(always)] - fn execute_query(self, tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value { - (self.vtable.execute_query)(tcx, key) + fn call_query_method(self, tcx: TyCtxt<'tcx>, key: C::Key) { + (self.vtable.call_query_method_fn)(tcx, key) } + /// Calls the actual provider function for this query. + /// See [`QueryVTable::invoke_provider_fn`] for more details. #[inline(always)] - fn compute(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value { - (self.vtable.compute_fn)(qcx.tcx, key) + fn invoke_provider(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value { + (self.vtable.invoke_provider_fn)(qcx.tcx, key) } #[inline(always)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index eb195550b2e50..a61793facec6a 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -457,7 +457,9 @@ fn try_load_from_on_disk_cache<'tcx, C: QueryCache, const FLAGS: QueryFlags>( panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) }); if query.will_cache_on_disk_for_key(tcx, &key) { - let _ = query.execute_query(tcx, key); + // Call `tcx.$query(key)` for its side-effect of loading the disk-cached + // value into memory. + query.call_query_method(tcx, key); } } @@ -625,14 +627,15 @@ macro_rules! define_queries { } } - /// Defines a `compute` function for this query, to be used as a - /// function pointer in the query's vtable. - mod compute_fn { + /// Defines an `invoke_provider` function that calls the query's provider, + /// to be used as a function pointer in the query's vtable. + /// + /// To mark a short-backtrace boundary, the function's actual name + /// (after demangling) must be `__rust_begin_short_backtrace`. + mod invoke_provider_fn { use super::*; use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased}; - /// This function would be named `compute`, but we also want it - /// to mark the boundaries of an omitted region in backtraces. #[inline(never)] pub(crate) fn __rust_begin_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, @@ -643,10 +646,13 @@ macro_rules! define_queries { // Call the actual provider function for this query. let provided_value = call_provider!([$($modifiers)*][tcx, $name, key]); + rustc_middle::ty::print::with_reduced_queries!({ tracing::trace!(?provided_value); }); + // Erase the returned value, because `QueryVTable` uses erased values. + // For queries with `arena_cache`, this also arena-allocates the value. provided_to_erased(tcx, provided_value) } } @@ -666,8 +672,12 @@ macro_rules! define_queries { } { None }), - execute_query: |tcx, key| erase::erase_val(tcx.$name(key)), - compute_fn: self::compute_fn::__rust_begin_short_backtrace, + call_query_method_fn: |tcx, key| { + // Call the query method for its side-effect of loading a value + // from disk-cache; the caller doesn't need the value. + let _ = tcx.$name(key); + }, + invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, try_load_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, prev_index, index| { // Check the `cache_on_disk_if` condition for this key.