about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-08-05 03:51:40 +0200
committerGitHub <noreply@github.com>2025-08-05 03:51:40 +0200
commit58a7b873cf3717ae426599cb6f86ae8b9366aea2 (patch)
treea3d75094e4b82f4ced74d484ad7906cb5b27bd24
parent99a19398846b4e28540347fe187f066edfce7f85 (diff)
parentcf7b67420b9927c6154b43bab17917183d931f93 (diff)
Rollup merge of #144890 - WaffleLapkin:project_fields, r=lcnr
Add `InterpCx::project_fields`

I was hoping for a much bigger improvement and this is lukewarm at best ^^'

Still, I think this makes sense.
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs9
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs15
-rw-r--r--compiler/rustc_const_eval/src/lib.rs1
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs10
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs41
6 files changed, 44 insertions, 33 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index f72c4418081..d05871bfc77 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -199,6 +199,15 @@ where
         base.offset_with_meta(offset, OffsetMode::Inbounds, meta, field_layout, self)
     }
 
+    /// Projects multiple fields at once. See [`Self::project_field`] for details.
+    pub fn project_fields<P: Projectable<'tcx, M::Provenance>, const N: usize>(
+        &self,
+        base: &P,
+        fields: [FieldIdx; N],
+    ) -> InterpResult<'tcx, [P; N]> {
+        fields.try_map(|field| self.project_field(base, field))
+    }
+
     /// Downcasting to an enum variant.
     pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>(
         &self,
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index a27b6646131..82c50fac6c0 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -121,25 +121,24 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
 
                 // `Box` has two fields: the pointer we care about, and the allocator.
                 assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
-                let (unique_ptr, alloc) = (
-                    self.ecx().project_field(v, FieldIdx::ZERO)?,
-                    self.ecx().project_field(v, FieldIdx::ONE)?,
-                );
+                let [unique_ptr, alloc] =
+                    self.ecx().project_fields(v, [FieldIdx::ZERO, FieldIdx::ONE])?;
+
                 // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
                 // (which means another 2 fields, the second of which is a `PhantomData`)
                 assert_eq!(unique_ptr.layout().fields.count(), 2);
-                let (nonnull_ptr, phantom) = (
-                    self.ecx().project_field(&unique_ptr, FieldIdx::ZERO)?,
-                    self.ecx().project_field(&unique_ptr, FieldIdx::ONE)?,
-                );
+                let [nonnull_ptr, phantom] =
+                    self.ecx().project_fields(&unique_ptr, [FieldIdx::ZERO, FieldIdx::ONE])?;
                 assert!(
                     phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
                     "2nd field of `Unique` should be PhantomData but is {:?}",
                     phantom.layout().ty,
                 );
+
                 // ... that contains a `NonNull`... (gladly, only a single field here)
                 assert_eq!(nonnull_ptr.layout().fields.count(), 1);
                 let raw_ptr = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // the actual raw ptr
+
                 // ... whose only field finally is a raw ptr we can dereference.
                 self.visit_box(ty, &raw_ptr)?;
 
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index bf7a79dcb20..3840cdf7575 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -2,6 +2,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![doc(rust_logo)]
+#![feature(array_try_map)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
 #![feature(decl_macro)]
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index c437934eaab..5249b32eca4 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -42,12 +42,12 @@ fn alloc_caller_location<'tcx>(
     let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
 
     // Initialize fields.
-    ecx.write_immediate(filename, &ecx.project_field(&location, FieldIdx::from_u32(0)).unwrap())
-        .expect("writing to memory we just allocated cannot fail");
-    ecx.write_scalar(line, &ecx.project_field(&location, FieldIdx::from_u32(1)).unwrap())
-        .expect("writing to memory we just allocated cannot fail");
-    ecx.write_scalar(col, &ecx.project_field(&location, FieldIdx::from_u32(2)).unwrap())
+    let [filename_field, line_field, col_field] =
+        ecx.project_fields(&location, [0, 1, 2].map(FieldIdx::from_u32)).unwrap();
+    ecx.write_immediate(filename, &filename_field)
         .expect("writing to memory we just allocated cannot fail");
+    ecx.write_scalar(line, &line_field).expect("writing to memory we just allocated cannot fail");
+    ecx.write_scalar(col, &col_field).expect("writing to memory we just allocated cannot fail");
 
     location
 }
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 803b645c8f7..e5cc23c213d 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -57,6 +57,7 @@
 #![feature(sized_hierarchy)]
 #![feature(try_blocks)]
 #![feature(try_trait_v2)]
+#![feature(try_trait_v2_residual)]
 #![feature(try_trait_v2_yeet)]
 #![feature(type_alias_impl_trait)]
 #![feature(yeet_expr)]
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 3e895c6b280..77427a12d11 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -793,36 +793,37 @@ impl Drop for Guard {
 /// We also make things panic if this type is ever implicitly dropped.
 #[derive(Debug)]
 #[must_use]
-pub struct InterpResult_<'tcx, T> {
+pub struct InterpResult<'tcx, T = ()> {
     res: Result<T, InterpErrorInfo<'tcx>>,
     guard: Guard,
 }
 
-// Type alias to be able to set a default type argument.
-pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>;
-
-impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> {
+impl<'tcx, T> ops::Try for InterpResult<'tcx, T> {
     type Output = T;
-    type Residual = InterpResult_<'tcx, convert::Infallible>;
+    type Residual = InterpResult<'tcx, convert::Infallible>;
 
     #[inline]
     fn from_output(output: Self::Output) -> Self {
-        InterpResult_::new(Ok(output))
+        InterpResult::new(Ok(output))
     }
 
     #[inline]
     fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> {
         match self.disarm() {
             Ok(v) => ops::ControlFlow::Continue(v),
-            Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))),
+            Err(e) => ops::ControlFlow::Break(InterpResult::new(Err(e))),
         }
     }
 }
 
-impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> {
+impl<'tcx, T> ops::Residual<T> for InterpResult<'tcx, convert::Infallible> {
+    type TryType = InterpResult<'tcx, T>;
+}
+
+impl<'tcx, T> ops::FromResidual for InterpResult<'tcx, T> {
     #[inline]
     #[track_caller]
-    fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self {
+    fn from_residual(residual: InterpResult<'tcx, convert::Infallible>) -> Self {
         match residual.disarm() {
             Err(e) => Self::new(Err(e)),
         }
@@ -830,7 +831,7 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> {
 }
 
 // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`.
-impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult_<'tcx, T> {
+impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult<'tcx, T> {
     #[inline]
     fn from_residual(ops::Yeet(e): ops::Yeet<InterpErrorKind<'tcx>>) -> Self {
         Self::new(Err(e.into()))
@@ -840,7 +841,7 @@ impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResu
 // Allow `?` on `Result<_, InterpError>` in functions returning `InterpResult_`.
 // This is useful e.g. for `option.ok_or_else(|| err_ub!(...))`.
 impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> ops::FromResidual<Result<convert::Infallible, E>>
-    for InterpResult_<'tcx, T>
+    for InterpResult<'tcx, T>
 {
     #[inline]
     fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
@@ -863,7 +864,7 @@ impl<'tcx, T, V: FromIterator<T>> FromIterator<InterpResult<'tcx, T>> for Interp
     }
 }
 
-impl<'tcx, T> InterpResult_<'tcx, T> {
+impl<'tcx, T> InterpResult<'tcx, T> {
     #[inline(always)]
     fn new(res: Result<T, InterpErrorInfo<'tcx>>) -> Self {
         Self { res, guard: Guard }
@@ -890,7 +891,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
 
     #[inline]
     pub fn map<U>(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> {
-        InterpResult_::new(self.disarm().map(f))
+        InterpResult::new(self.disarm().map(f))
     }
 
     #[inline]
@@ -898,7 +899,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
         self,
         f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>,
     ) -> InterpResult<'tcx, T> {
-        InterpResult_::new(self.disarm().map_err(f))
+        InterpResult::new(self.disarm().map_err(f))
     }
 
     #[inline]
@@ -906,7 +907,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
         self,
         f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>,
     ) -> InterpResult<'tcx, T> {
-        InterpResult_::new(self.disarm().map_err(|mut e| {
+        InterpResult::new(self.disarm().map_err(|mut e| {
             e.0.kind = f(e.0.kind);
             e
         }))
@@ -914,7 +915,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
 
     #[inline]
     pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> {
-        InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind)))
+        InterpResult::new(self.disarm().inspect_err(|e| f(&e.0.kind)))
     }
 
     #[inline]
@@ -937,7 +938,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
 
     #[inline]
     pub fn and_then<U>(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> {
-        InterpResult_::new(self.disarm().and_then(|t| f(t).disarm()))
+        InterpResult::new(self.disarm().and_then(|t| f(t).disarm()))
     }
 
     /// Returns success if both `self` and `other` succeed, while ensuring we don't
@@ -952,7 +953,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
                 // Discard the other error.
                 drop(other.disarm());
                 // Return `self`.
-                InterpResult_::new(Err(e))
+                InterpResult::new(Err(e))
             }
         }
     }
@@ -960,5 +961,5 @@ impl<'tcx, T> InterpResult_<'tcx, T> {
 
 #[inline(always)]
 pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> {
-    InterpResult_::new(Ok(x))
+    InterpResult::new(Ok(x))
 }