Skip to content

Conversation

@dingxiangfei2009
Copy link
Contributor

@dingxiangfei2009 dingxiangfei2009 commented Nov 18, 2025

The new checks are we check the pair of constituent types for same shapes structurally, and demand capability for dispatch for ADTs as usual; and we demand source generic parameter to be unsized of another for sanity.

Fix #148727

cc @theemathas

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Nov 18, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 18, 2025

r? @WaffleLapkin

rustbot has assigned @WaffleLapkin.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot

This comment has been minimized.

@dingxiangfei2009
Copy link
Contributor Author

dingxiangfei2009 commented Nov 18, 2025

@theemathas What do you think? Can we break DispatchFromDyn further?

@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from 75de383 to 88de0f9 Compare November 18, 2025 21:39
@rustbot

This comment has been minimized.

@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from 88de0f9 to b7f84d2 Compare November 18, 2025 21:40
@theemathas

This comment has been minimized.

@theemathas
Copy link
Contributor

The documentation for DispatchFromDyn also needs to be changed.

@dingxiangfei2009
Copy link
Contributor Author

which is not possible

I am not sure. We have an option to make it possible. The question is, would it be a bad idea?

In any case, let me add a stop-gap to disallow this case.

@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from b7f84d2 to 8c95fd8 Compare November 19, 2025 10:31
@rustbot

This comment has been minimized.

@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from 8c95fd8 to fdf32a0 Compare November 19, 2025 10:39
@jackh726 jackh726 added T-types Relevant to the types team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 19, 2025
@jackh726
Copy link
Member

r? types

@rustbot rustbot assigned jackh726 and unassigned WaffleLapkin Nov 19, 2025
@dingxiangfei2009
Copy link
Contributor Author

Discussion under t-lang: #t-lang > Should we support dyn dispatch on `&Adt<_>`?

I think we are quite convinced that &Adt<_> as receiver is not dyn-compatible.

Comment on lines +73 to +74
impl<'a, T: ?Sized, U: ?Sized> DispatchFromDyn<&'a Ptr<U>> for &'a Ptr<T> {}
//~^ ERROR the trait `DispatchFromDyn` does not allow dispatch through references
Copy link
Contributor

Choose a reason for hiding this comment

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

Mentioning references here seems awfully specific. What about Box<Ptr<U>> or similar?

Copy link
Contributor Author

@dingxiangfei2009 dingxiangfei2009 Nov 26, 2025

Choose a reason for hiding this comment

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

I am in the middle of updating the documentation on this trait.

Box<Ptr<U>> falls in the second case of the new documentation. We would foremost require Ptr<T>: DispatchFromDyn<Ptr<U>> among the other requirements.

.label = can't implement cross-crate trait for type in another crate
hir_analysis_dispatch_from_dyn_multiple_refs = the trait `DispatchFromDyn` does not allow dispatch through references
.note = the trait `DispatchFromDyn` may only be implemented when dispatch goes through at most one reference to a generic
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this text not show up in the test error output?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I forgot about a #[note] attribute on the error type. Now it should work.

@theemathas
Copy link
Contributor

theemathas commented Nov 20, 2025

This code doesn't compile with this PR. Should it?

#![feature(dispatch_from_dyn, arbitrary_self_types, unsize)]

use std::marker::{PhantomData, Unsize};
use std::ops::{DispatchFromDyn, Receiver};

pub struct Inner<T: ?Sized>(T);
#[repr(transparent)]
pub struct Outer<T: ?Sized>(PhantomData<T>, Inner<T>);

fn wrap<T: ?Sized>(x: &Inner<T>) -> &Outer<T> {
    // SAFETY: We're using repr(transparent)
    unsafe { &*(x as *const Inner<T> as *const Outer<T>) }
}

impl<'a, T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<&'a Outer<U>> for &'a Outer<T> {}
impl<T: ?Sized> Receiver for Outer<T> {
    type Target = T;
}

pub trait Trait {
    fn method(self: &Outer<Self>);
}

struct Thing(i32);
impl Trait for Thing {
    fn method(self: &Outer<Self>) {
        println!("{}", self.1.0.0);
    }
}

fn main() {
    let inner: &Inner<dyn Trait> = &Inner(Thing(1));
    let x: &Outer<dyn Trait> = wrap(inner);
    x.method();
}
error: the trait `DispatchFromDyn` does not allow dispatch through references
  --> src\main.rs:15:1
   |
15 | impl<'a, T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<&'a Outer<U>> for &'a Outer<T> {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: could not compile `foo` (bin "foo") due to 1 previous error

@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from fdf32a0 to e0eee5f Compare November 26, 2025 18:23
@rustbot

This comment has been minimized.

@dingxiangfei2009
Copy link
Contributor Author

To answer the comment I have jotted down the reason that we do not want to compile the example in the trait documentation.

@rust-log-analyzer

This comment has been minimized.

@theemathas
Copy link
Contributor

theemathas commented Nov 26, 2025

@dingxiangfei2009 Your comment in the code unfortunately does not answer my latest comment in this PR. My comment does not involve a double pointer indirection.

The new checks are we check the pair of constituent types for same
shapes structurally, and demand capability for dispatch for ADTs
as usual; and we demand source generic parameter to be unsized of
another for sanity.

Signed-off-by: Xiangfei Ding <dingxiangfei2009@protonmail.ch>
@dingxiangfei2009 dingxiangfei2009 force-pushed the dispatch-from-dyn-due-diligence branch from 1a2dd94 to af90725 Compare November 28, 2025 10:26
@rustbot
Copy link
Collaborator

rustbot commented Nov 28, 2025

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@dingxiangfei2009
Copy link
Contributor Author

Quote @theemathas

@dingxiangfei2009 Your comment in the code unfortunately does not answer my latest comment in this PR. My comment does not involve a double pointer indirection.

I don't think my text articulates my thoughts right. &Outer<T> -> &Outer<U> is not a double pointer indirection, true, and the point is that there are too many "indirection"s.

Maybe I should reword it. The pre-requisite of a valid DispatchFromDyn relation from a type A to a type B is that

  • A and B are of the same shape, sharing the same concrete type constructor, excluding aliases and generics
  • A and B are both generic over two distinct type parameter T and U respectively
  • T: Unsize<U>
  • Most importantly, the "downcast" of a value of B back to A is supported. Here we can give an exhaustive list of supported "downcasts" and we can add more as supported types grow.
    • A := &T from B := &U, or A := &mut T from B := &mut U
      • This works today by dispatching the call to the corresponding method after transparently discarding the metadata
    • A := Adt<T> from B := Adt<U> when T: DispatchFromDyn<U> and Adt<_> is reasonable
      • This works today by delegating the dispatch to the only field in A after transparently discarding the metadata

The last bullet point will answer the important question...

Should it?

... which is no, because we don't know how to "downcast" &Outer<U> back to &Outer<T> because that metadata is actually behind one layer of "indirection." The argument against &mut Outer<U> back to &mut Outer<T> case should run like this. The "downcast" mutation would need to leave the address unchanged and, in turn, necessitate mutation of the data behind this &mut borrow. The transformation is no longer transparent and actually would change type of the place behind that borrow. I don't see a way to support it in Rust.

The A := &T case, on the other hand, has no indirection because the metadata is directly attached to the &U pointer. So this "downcast" is simple for us to implement.

I hope that this would be a better answer to the earlier question. I am not sure how much this content could go into the documentation. Please advise.

@Darksonn
Copy link
Contributor

This impl should be illegal:

impl<'a, T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<&'a Outer<U>> for &'a Outer<T> {}

It compiles because it falls into the

EITHER Self and T are either both references or both raw pointers; in either case, with the same mutability.

case in the built-in check. But the reference / raw pointer types are supposed to only have the impls found in the stdlib. If they can have more, there's going to be all sorts of problems. We need to reject all downstream impls of DispatchFromDyn for references and raw pointers.

Probably DispatchFromDyn should be "anti-fundamental" so that impl DispatchFromDyn<_> for Fundamental<LocalType> is rejected in general, which would reject the above impl. It's similar to what has been discussed elsewhere as being needed for DerefMut in #145608 and CoerceUnsize in #148899. cc @lcnr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unsoundness and ICE due to DispatchFromDyn allowing bogus impls on references.

7 participants