Skip to content

Conversation

@oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Dec 19, 2025

tracking issue: #144361

cc @theemathas can you find an unsoundness when combined with generic impls where there are no lifetimes, but reference other impls that have lifetimes with constraints? I couldn't find anything.

cc @ivarflakstad @izagawd

@rustbot
Copy link
Collaborator

rustbot commented Dec 19, 2025

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

@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 Dec 19, 2025
@rustbot rustbot added the WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) label Dec 19, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 19, 2025

r? @SparrowLii

rustbot has assigned @SparrowLii.
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

@rust-log-analyzer

This comment has been minimized.

@oli-obk oli-obk force-pushed the try_as_dyn_non_static branch 2 times, most recently from e7ef1ee to 29f1dba Compare December 19, 2025 16:44
Copy link
Contributor

@danielhenrymantilla danielhenrymantilla left a comment

Choose a reason for hiding this comment

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

Some drive-by comments; but I'm not rustc/HIR-savy, so take these with a grain of salt 🙇

View changes since this review

assert!(std::any::try_as_dyn::<_, dyn Trait>(&()).is_some());
assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[()]).is_some());
assert!(std::any::try_as_dyn::<_, dyn Trait>(&&()).is_some());
assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_u32).is_none());
Copy link
Contributor

Choose a reason for hiding this comment

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

This one is a "regression", right? With the old design we could be involving a &'static u32 which is both : 'static, and implementing Trait in the classical sense. Perhaps we'll want a try_as_dyn_static() function as well, for those interested in this use case?

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'll add this to the tracking issue

Comment on lines +89 to +92
if let TypingMode::Reflection = ecx.typing_mode()
&& !cx.is_fully_generic_for_reflection(impl_def_id)
{
return Err(NoSolution);
Copy link
Contributor

@danielhenrymantilla danielhenrymantilla Dec 19, 2025

Choose a reason for hiding this comment

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

This is way over my head, lol, but just to sanity-check: could this branch be hoisted to the beginning of the function, as in, any impl that is not fully_generic_for_reflection(), when checking in Reflection mode, ought to result in Err(NoSolution), right? As in, it's not specific to this double-Negative branch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it's only relevant if the polarities match. Otherwise it's not a match anyway. So while it could be lifted to the front, it's not really necessary either. No strong opinion, but also easily changeable without affecting behavior

@BoxyUwU
Copy link
Member

BoxyUwU commented Dec 19, 2025

r? BoxyUwU

@rustbot rustbot assigned BoxyUwU and unassigned SparrowLii Dec 19, 2025
@theemathas

This comment has been minimized.

@theemathas

This comment has been minimized.

@theemathas

This comment has been minimized.

@BoxyUwU BoxyUwU added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 30, 2025
@oli-obk oli-obk force-pushed the try_as_dyn_non_static branch from 29f1dba to fe33b0c Compare January 7, 2026 12:41
@rustbot
Copy link
Collaborator

rustbot commented Jan 7, 2026

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.

@oli-obk oli-obk force-pushed the try_as_dyn_non_static branch 2 times, most recently from 175bf5b to dfa5c33 Compare January 7, 2026 12:44
@oli-obk oli-obk force-pushed the try_as_dyn_non_static branch from dfa5c33 to 30f5641 Compare January 7, 2026 12:44

fn extend(a: &Payload) -> &'static Payload {
// TODO: should panic at the `unwrap` here
let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap();
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 don't have a solution for this yet. The easy hammer is to reject traits that have such methods. Nicer would be to figure out how to make a bound that ensures the dyn Trait's lifetimes are shorter than the arguments'

Copy link
Member

Choose a reason for hiding this comment

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

Can we check during mir_typeck that the source type outlives the lifetime of the trait object type?

Copy link
Member

Choose a reason for hiding this comment

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

ah its not the intrinsic 🤔 yeah thats tricky

@rust-log-analyzer

This comment has been minimized.

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 13, 2026

The hacky solution is obviously not a general fix. But I think it's progress. As a next step I will add the input type as a generic parameter on TryAsDynCompat, at which point we should be able to enforce (in borrowck) that the input type outlives any lifetimes on the dyn Trait or its generic parameters. So if a generic parameter T has a 'static bound, it could be used as an input type for a try_as_dyn irrespective of the bounds on the dyn Trait. In the other direction, we will likely end up rejecting many traits that have generic parameters as the bounds are not something that can be written in Rust.

@theemathas
Copy link
Contributor

The current PR state is still unsound, because it doesn't check where Self: 'static bounds in supertraits. The following code segfaults with this PR.

#![feature(try_as_dyn)]

use std::any::try_as_dyn;

type Payload = Box<i32>;

trait Super {
    fn as_static(&self) -> &'static Payload
    where
        Self: 'static;
}

trait Sub: Super {}

impl<'a> Super for &'a Payload {
    fn as_static(&self) -> &'static Payload
    where
        Self: 'static,
    {
        *self
    }
}

impl<'a> Sub for &'a Payload {}

fn main() {
    let storage: Box<Payload> = Box::new(Box::new(1i32));
    let wrong: &'static Payload = extend(&*storage);
    drop(storage);
    println!("{wrong}");
}

fn extend(a: &Payload) -> &'static Payload {
    let b: &(dyn Sub + 'static) = try_as_dyn::<&Payload, dyn Sub + 'static>(&a).unwrap();
    b.as_static()
}

@theemathas
Copy link
Contributor

theemathas commented Jan 16, 2026

The PR is also unsound in another way: It does not check for the existence of an implied Self: 'static bound. The following code segfaults with this PR, using the dyn-clone crate version 1.0.20.

#![feature(try_as_dyn)]

use std::any::try_as_dyn;

use dyn_clone::{DynClone, clone_box};

type Payload = Box<i32>;

trait Trait: DynClone {
    fn as_static(&'static self) -> &'static Payload;  // &'static self has an implied Self: 'static bound
}

impl<'a> Trait for &'a Payload {
    fn as_static(&'static self) -> &'static Payload {
        *self
    }
}

fn extend<'a>(x: &'a Payload) -> &'static Payload {
    let dyn_ref: &(dyn Trait + 'static) =
        try_as_dyn::<&'a Payload, dyn Trait + 'static>(&x).unwrap();
    let dyn_box: Box<dyn Trait + 'static> = clone_box(dyn_ref);
    let dyn_static: &'static (dyn Trait + 'static) = Box::leak(dyn_box);
    dyn_static.as_static()
}

fn main() {
    let storage: Box<Payload> = Box::new(Box::new(1i32));
    let wrong: &'static Payload = extend(&*storage);
    drop(storage);
    println!("{wrong}");
}

@theemathas
Copy link
Contributor

Another unsoundness: Seems like some lifetimes were not checked? The following code segfaults with this PR.

#![feature(try_as_dyn)]

use std::any::try_as_dyn;

type Payload = Box<i32>;

trait Trait<'a> {
    fn as_payload(&self) -> &'a Payload;
}

impl<'a> Trait<'a> for &'a Payload {
    fn as_payload(&self) -> &'a Payload {
        *self
    }
}

fn extend<'a>(x: &'a Payload) -> &'static Payload {
    let dyn_ref: &(dyn Trait<'static> + 'static) =
        try_as_dyn::<&'a Payload, dyn Trait<'static> + 'static>(&x).unwrap();
    dyn_ref.as_payload()
}

fn main() {
    let storage: Box<Payload> = Box::new(Box::new(1i32));
    let wrong: &'static Payload = extend(&*storage);
    drop(storage);
    println!("{wrong}");
}

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 16, 2026

Yes I know 😅 its why I wrote it's not a fix. All the examples you are giving would be fixed by actually checking lifetimes at the call site, which I'm planning as per my next comment

@theemathas
Copy link
Contributor

we should be able to enforce (in borrowck) that the input type outlives any lifetimes on the dyn Trait or its generic parameters

Why is an "outlives" requirement sufficient, as opposed to a "lifetimes are all equal to each other" requirement?

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

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants