about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2024-06-12 15:45:00 +0200
committerGitHub <noreply@github.com>2024-06-12 15:45:00 +0200
commitd1414c5e0dcfcd65325f575ad6393e47bf0f2502 (patch)
tree3a6e1e6c6697da4c36a366bd121cef33e5290759
parent51a58c59f3ff8ec39da77c1c3d0b780568b9c202 (diff)
parent6e3134972ed18e095c27378287b85c0ad455e35a (diff)
downloadrust-d1414c5e0dcfcd65325f575ad6393e47bf0f2502.tar.gz
rust-d1414c5e0dcfcd65325f575ad6393e47bf0f2502.zip
Rollup merge of #126242 - yaahc:simplify-provider, r=jhpratt
Simplify provider api to improve llvm ir

This PR seeks to resolve the last concern in https://github.com/rust-lang/rust/issues/99301#issuecomment-1699427740

We resolve the issue by moving the type_id to be stored in the `Request` itself rather than being accessed through the `Erased` trait, letting the compiler infer that the value of the type id will not change between lookups.

### LLVM Codegen

**Before**

```
; <provider_test::MyError as core::error::Error>::provide
; Function Attrs: nonlazybind uwtable
define void `@"_ZN61_$LT$provider_test..MyError$u20$as$u20$core..error..Error$GT$7provide17hd9c9de412063aa73E"(ptr` noalias nocapture noundef nonnull readonly align 1 %self, ptr noundef nonnull align 1 %request.0, ptr noalias nocapture noundef readonly align 8 dereferenceable(32) %request.1) unnamed_addr #0 personality ptr `@rust_eh_personality` {
start:
  %0 = getelementptr inbounds i8, ptr %request.1, i64 24
  %self.1.val.i = load ptr, ptr %0, align 8
  %1 = tail call { i64, i64 } %self.1.val.i(ptr noundef nonnull align 1 %request.0), !noalias !15
  %2 = extractvalue { i64, i64 } %1, 0
  %3 = extractvalue { i64, i64 } %1, 1
  %_18.i.i = icmp ne i64 %2, 1101338453689927725
  %_2.i.i = icmp ne i64 %3, 472224167662714873
  %or.cond.i.not.i = select i1 %_18.i.i, i1 true, i1 %_2.i.i
  br i1 %or.cond.i.not.i, label %_ZN4core5error7Request7provide17h8f8125d2543333e0E.exit, label %bb2.i
```

**After**

```
; <provider_test::MyError as provider_test::Error>::provide
; Function Attrs: mustprogress nofree norecurse nosync nounwind nonlazybind willreturn memory(argmem: readwrite) uwtable
define void `@"_ZN63_$LT$provider_test..MyError$u20$as$u20$provider_test..Error$GT$7provide17h5bbf091795a6d359E"(ptr` noalias nocapture noundef nonnull readonly align 1 %self, ptr nocapture noundef nonnull align 8 %request.0, ptr noalias nocapture noundef readonly align 8 dereferenceable(24) %request.1) unnamed_addr #2 personality ptr `@rust_eh_personality` {
start:
  %_19.i = load i64, ptr %request.0, align 8, !noalias !3, !noundef !3
  switch i64 %_19.i, label %_ZN13provider_test7Request7provide17heb3ee140962e3b2fE.exit [
    i64 7665305208997882008, label %bb12.i
    i64 7050211241160863540, label %bb12.i3
    i64 9112786072622981063, label %bb12.i11
  ]
```
-rw-r--r--library/core/src/error.rs49
1 files changed, 23 insertions, 26 deletions
diff --git a/library/core/src/error.rs b/library/core/src/error.rs
index da18fdc6e1d..042a8c9925f 100644
--- a/library/core/src/error.rs
+++ b/library/core/src/error.rs
@@ -404,9 +404,9 @@ fn request_by_type_tag<'a, I>(err: &'a (impl Error + ?Sized)) -> Option<I::Reifi
 where
     I: tags::Type<'a>,
 {
-    let mut tagged = TaggedOption::<'a, I>(None);
+    let mut tagged = Tagged { tag_id: TypeId::of::<I>(), value: TaggedOption::<'a, I>(None) };
     err.provide(tagged.as_request());
-    tagged.0
+    tagged.value.0
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -507,16 +507,9 @@ where
 ///
 #[unstable(feature = "error_generic_member_access", issue = "99301")]
 #[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435
-pub struct Request<'a>(dyn Erased<'a> + 'a);
+pub struct Request<'a>(Tagged<dyn Erased<'a> + 'a>);
 
 impl<'a> Request<'a> {
-    /// Create a new `&mut Request` from a `&mut dyn Erased` trait object.
-    fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Request<'a> {
-        // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Request<'a>` is safe since
-        // `Request` is repr(transparent).
-        unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Request<'a>) }
-    }
-
     /// Provide a value or other type with only static lifetimes.
     ///
     /// # Examples
@@ -940,27 +933,28 @@ pub(crate) mod tags {
 #[repr(transparent)]
 pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option<I::Reified>);
 
-impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> {
+impl<'a, I: tags::Type<'a>> Tagged<TaggedOption<'a, I>> {
     pub(crate) fn as_request(&mut self) -> &mut Request<'a> {
-        Request::new(self as &mut (dyn Erased<'a> + 'a))
+        let erased = self as &mut Tagged<dyn Erased<'a> + 'a>;
+        // SAFETY: transmuting `&mut Tagged<dyn Erased<'a> + 'a>` to `&mut Request<'a>` is safe since
+        // `Request` is repr(transparent).
+        unsafe { &mut *(erased as *mut Tagged<dyn Erased<'a>> as *mut Request<'a>) }
     }
 }
 
 /// Represents a type-erased but identifiable object.
 ///
 /// This trait is exclusively implemented by the `TaggedOption` type.
-unsafe trait Erased<'a>: 'a {
-    /// The `TypeId` of the erased type.
-    fn tag_id(&self) -> TypeId;
-}
+unsafe trait Erased<'a>: 'a {}
 
-unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {
-    fn tag_id(&self) -> TypeId {
-        TypeId::of::<I>()
-    }
+unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {}
+
+struct Tagged<E: ?Sized> {
+    tag_id: TypeId,
+    value: E,
 }
 
-impl<'a> dyn Erased<'a> + 'a {
+impl<'a> Tagged<dyn Erased<'a> + 'a> {
     /// Returns some reference to the dynamic value if it is tagged with `I`,
     /// or `None` otherwise.
     #[inline]
@@ -968,9 +962,9 @@ impl<'a> dyn Erased<'a> + 'a {
     where
         I: tags::Type<'a>,
     {
-        if self.tag_id() == TypeId::of::<I>() {
+        if self.tag_id == TypeId::of::<I>() {
             // SAFETY: Just checked whether we're pointing to an I.
-            Some(unsafe { &*(self as *const Self).cast::<TaggedOption<'a, I>>() })
+            Some(&unsafe { &*(self as *const Self).cast::<Tagged<TaggedOption<'a, I>>>() }.value)
         } else {
             None
         }
@@ -983,9 +977,12 @@ impl<'a> dyn Erased<'a> + 'a {
     where
         I: tags::Type<'a>,
     {
-        if self.tag_id() == TypeId::of::<I>() {
-            // SAFETY: Just checked whether we're pointing to an I.
-            Some(unsafe { &mut *(self as *mut Self).cast::<TaggedOption<'a, I>>() })
+        if self.tag_id == TypeId::of::<I>() {
+            Some(
+                // SAFETY: Just checked whether we're pointing to an I.
+                &mut unsafe { &mut *(self as *mut Self).cast::<Tagged<TaggedOption<'a, I>>>() }
+                    .value,
+            )
         } else {
             None
         }