Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking Issue for #![feature(downcast_unchecked)] #90850

Open
1 of 3 tasks
ibraheemdev opened this issue Nov 13, 2021 · 15 comments
Open
1 of 3 tasks

Tracking Issue for #![feature(downcast_unchecked)] #90850

ibraheemdev opened this issue Nov 13, 2021 · 15 comments
Labels
C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@ibraheemdev
Copy link
Member

ibraheemdev commented Nov 13, 2021

Feature gate: #![feature(downcast_unchecked)]

This is a tracking issue for the downcast_unchecked, downcast_ref_unchecked, and downcast_mut_unchecked methods.

Public API

impl dyn Any (+ Send + Sync) {
    pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T;
    pub unsafe fn downcast_mut_unchecked<T: Any>(&mut self) -> &mut T;
}

impl<A: Allocator> Box<dyn Any (+ Send + Sync), A> {
    pub unsafe fn downcast_unchecked<T: Any>(&self) -> Box<T, A>;
}

Steps / History

Unresolved Questions

  • None yet.
@ibraheemdev ibraheemdev added C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Nov 13, 2021
@david-perez
Copy link
Contributor

Should these be added to std::error::Error too?

@ibraheemdev
Copy link
Member Author

I don't see how that would be useful. You generally don't know the concrete type of an error before downcasting.

@lunabunn
Copy link

lunabunn commented May 1, 2022

This would allow me to shed my dependency on unsafe_any. What's the state of this feature? Is it ready for an FCP?

@ibraheemdev
Copy link
Member Author

Now that {Option, Result}::unwrap_unchecked are stabilized, I wonder if there is much benefit to these methods.

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Jun 20, 2022
… r=m-ou-se

Add `{Arc, Rc}::downcast_unchecked`

Part of rust-lang#90850.
@CAD97
Copy link
Contributor

CAD97 commented Oct 6, 2022

Instead of downcast_unchecked, is there a good reason to not just have the more general

impl Box<T: ?Sized, A> {
    pub unsafe fn cast_unchecked<U>(self) -> Box<U, A> {
        debug_assert_eq!(Layout::for_value(&*self), Layout::new::<U>());
        unsafe {
            let (raw, alloc) = Box::into_raw_with_allocator(self);
            Box::from_raw_in(raw.cast(), alloc)
        }
    }
}

? I suppose you lose the debug_assert! for correct typing which is possible if you have dyn Any, though it is true that you can already any.downcast().unwrap_unchecked() for that.

To immediately answer my own question: Box doesn't want to add methods to arbitrary types like that. cast_unchecked is still useful but would probably instead be pub unsafe fn Box::<T: ?Sized, A>::cast<U>(this: Self) -> Box<U, A> to make it an associated function rather than a method which could shadow a pointee's method.

@yshui
Copy link
Contributor

yshui commented Nov 13, 2022

@ibraheemdev this still generates better code than downcast_ref().unwrap_unchecked().

pub fn cast(a: &dyn std::any::Any) -> &i32 {
    unsafe { a.downcast_ref_unchecked() }
}

compiles to

example::cast:
        mov     rax, rdi
        ret

whereas

pub fn cast(a: &dyn std::any::Any) -> &i32 {
    unsafe { a.downcast_ref().unwrap_unchecked() }
}

becomes

example::xx:
        push    rbx
        mov     rbx, rdi
        call    qword ptr [rsi + 24]
        mov     rax, rbx
        pop     rbx
        ret

Both compiled with opt-level=3

@yshui
Copy link
Contributor

yshui commented Nov 13, 2022

I think llvm didn't realize that T::type_id call is pure (because it's called through the vtable?), so it kept the call.

@yshui
Copy link
Contributor

yshui commented Nov 13, 2022

huh, why is type_id not stored directly in the vtable?

@CAD97
Copy link
Contributor

CAD97 commented Nov 14, 2022

huh, why is type_id not stored directly in the vtable?

The short version is that Rust doesn't have a way to get associated constant data from a trait object (yet).

@gh2o
Copy link
Contributor

gh2o commented Dec 9, 2022

The semantics of a.downcast_ref_unchecked() and a.downcast_ref().unwrap_unchecked() are still different. For example, with a.downcast_ref_unchecked() one could "transmute" between two types that are different but #[repr(C)] compatible in layout, whereas with a.downcast_ref().unwrap_unchecked() you'd likely be dereferencing a null pointer.

@marc0246
Copy link
Contributor

marc0246 commented May 5, 2023

I'd like to point out that in all other places in the std, the ref/mut part comes after the unchecked part. Therefore this should be downcast_unchecked_ref and downcast_unchecked_mut to stay concistent AFAICS.

@Wasabi375
Copy link

Hey, is there a reason that Box::downcast and similar functions require T to be sized? AFAICT they should work with unsized types.

@marc0246
Copy link
Contributor

@Wasabi375 It's not possible to unsize an unsized type, so it's equally not possible to downcast to an unsized type, because the source is already unsized. If you want to do something like that you need a double indirection.

@Wasabi375
Copy link

Wasabi375 commented Jun 26, 2024

I think I might have found a bug with with type inference or something that confused me, because it claimed that I couldn't call downcast_unchecked because dyn Any is not sized. But when I specify the type directly the error goes away. I'll try to create a simple example and file a proper bug report. (#127005)

@IamTheCarl
Copy link

I was curious if the code quality generated by unwrap_unchecked vs downcast_ref_unchecked had improved at all.
It seems it has.

https://rust.godbolt.org/z/77E7an7f1

Note that it seems to be condensing both functions to one since they're end product is identical. You can comment out the first one to explicitly see that the second one is equivalent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants