-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Stabilize feature(trait_upcasting)
#134367
base: master
Are you sure you want to change the base?
Stabilize feature(trait_upcasting)
#134367
Conversation
(so that it doesn't talk about trait upcasting stabilization in the future tense)
Some changes occurred in tests/ui/sanitizer cc @rust-lang/project-exploit-mitigations, @rcvalle |
Could you please provide some links that specify what has been done since the last stabilization? Ideally both the issues and the resulting fix PRs. Ty for doing this tho :) Gna nominate for T-lang but this should be an easy decision. |
And maybe link to the last stabilization too |
This comment has been minimized.
This comment has been minimized.
The Miri subtree was changed cc @rust-lang/miri |
Stabilization reportThe core of the feature hasn't changed since the last attempt to stabilize it. But as a reminder this feature allows "upcasting" trait Sub: Super {}
trait Super {}
fn upcast(x: &dyn Sub) -> &dyn Super {
x // implicit coercion
} This is a long wanted feature that people used workarounds for for a long while now. One possible downside is that this forces us into including more data in the vtables. However, our measurements show that the overhead is mostly negligible. Also note that we ate already including this overhead on stable for countless versions and no one ever complained. Another possible downside is that this feature this allows upcasting of raw trait pointers in safe code. That puts constraints on their library invariant (safety invariant) -- specifically, even I believe that the feature is well tested and is ready for stabilization. Previous stabilization attempt problemsAfter the last attempt to stabilize this feature @steffahn found two unsound interactions between trait upcasting and pointer casting (one of which also required Both issues were since fixed in #120248 by adding additional checks for casting pointers, to uphold the library invariant of pointers to trait objects which is needed for this feature. No new issues were found since. |
(@RalfJung I'm pretty sure
I-lang-easy-decision
|
@traviscross told me the opposite -- it still needs to be nominated with the regular label. The label description should probably clarify or else people will inevitably keep getting this wrong. |
That's a very strong statement.^^ I'd say there is at least one other downside, or point worth mentioning: this allows upcasting of raw trait pointers in safe code. That puts constraints on their library invariant (safety invariant) -- specifically, even I'm fine with that, and AFAIK @rust-lang/types agrees, but it is a choice we are making here that should be called out explicitly. @rust-lang/opsem is still discussing what to do with the language invariant for raw trait pointers; having that be different from the library invariant is likely surprising but OTOH we still might want a weaker invariant here. |
@RalfJung I did call this out, but maybe not explicitly enough ("the only" slipped from an earlier draft). I've updated the wording to more clearly highlight this. |
worth noting that we did formally decide this here: #101336 |
@rfcbot fcp merge I'm so excited for this. It's going to make Salsa 3.0 work a lot better (along with a bunch of other code...). |
Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
@rfcbot reviewed |
I'm in favour here. One thing I wanted to double-check: how are these encoded in MIR? Are they https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adjustment/enum.PointerCoercion.html#variant.Unsize or https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.CastKind.html#variant.PtrToPtr? I'm hoping it's the former, as implied by the "convert between wide pointers" doc. (Asking because if it's the latter I'm worried the mir-opts might be unsound in conjunction with this.) |
They are a form of unsizing in both HIR and MIR. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
@rustbot labels -I-lang-nominated We discussed this on the lang call today, and it's now in FCP. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not actually sure what to do with this lint. "Future incompatible" doesn't make sense anymore, since this PR is actually adding the breaking change... should I make it into a normal lint? Should I make it deny-by-default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be time to do a crater run for switching it to deny-by-default.
The conservative case is to keep it warn and switch it later - I think that's okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Particularly, I could very much imagine people having these impls around today precisely because trait upcasting isn't stable. And so switching it later might be much easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Future incompatible" doesn't make sense anymore, since this PR is actually adding the breaking change
So we can't keep this a lint and proceed with compilation when it triggers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait this is a FutureReleaseSemanticsChange
, I've never seen one of those... I see now. Yeah probably the lint should just be removed, but Cc @rust-lang/lang to be sure. This is about #89460.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think disallowing the impl is silly, it can be useful, for example in combination with genetic code. I'm fine with making this a normal lint or removing the lint altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I nominate for T-lang discussion again, since there doesn't seem to be a consensus of what to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that's good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW we currently do not warn for Deref
impls that are shadowed by other kinds of coercions, for example:
array->slice unsizing example
use std::ops::Deref;
struct Wrap<T: ?Sized>(T);
impl Deref for Wrap<[u8; 0]> {
type Target = Wrap<[u8]>;
fn deref(&self) -> &Self::Target {
println!("deref!");
self
}
}
fn main() {
let _: &Wrap<[u8]> = &Wrap([]); // doesn't print "deref!"
let _: &Wrap<[u8]> = Deref::deref(&Wrap([])); // prints "deref!"
}
subtyping example
use std::ops::Deref;
struct Wrap<T: ?Sized>(T);
impl Deref for Wrap<fn(&())> {
type Target = Wrap<fn(&'static ())>;
fn deref(&self) -> &Self::Target {
println!("deref!");
self
}
}
fn main() {
let _: &Wrap<fn(&'static ())> = &Wrap((|_| ()) as fn(&())); // doesn't print "deref!"
let _: &Wrap<fn(&'static ())> = Deref::deref(&Wrap((|_| ()) as fn(&()))); // prints "deref!"
}
So IMO if we keep this lint, it should apply to all Deref
impls that are shadowed by any kind of coercion and not be arbitrarily restricted to the specific Deref
impls that will no longer be called after this PR merges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go that round I'd prefer to remove the lint & leave implementing the full shadowed_deref
lint as a followup.
@rfcbot reviewed (Already checked my box, but rfcbot doesn't seem to register this.) |
I'm re-nominating for lang discussion, specifically to ask what to do with the #![deny(deref_into_dyn_supertrait)]
#![allow(dead_code)]
use core::ops::Deref;
trait A {}
trait B: A {}
impl<'a> Deref for dyn 'a + B {
type Target = dyn A;
fn deref(&self) -> &Self::Target {
todo!()
}
}
fn take_a(_: &dyn A) {}
fn take_b(b: &dyn B) {
take_a(b);
}
The impl is not harmful, but is mostly useless, since it will no be called in coercions (after this PR). It might in some cases be useful, if generic code expects Either way the question is: what to do with the lint? The options are:
I'm strongly in favor of options 2 and 3, since I do not see a reason to forbid the impl -- it is not harmful (mostly just silly/useless). I'd probably keep the lint as a normal warning (warn by default), to highlight that it's shadowed by built in unsizing most of the time. Some discussion may be found in this thread: #134367 (comment). |
Particularly interesting from this discussion is the observation by @lukas-code that we allow and do not lint currently when #![feature(trait_upcasting)]
use core::ops::Deref;
struct W<T: ?Sized>(T);
impl Deref for W<[u8; 0]> {
type Target = W<[u8]>;
fn deref(&self) -> &Self::Target {
panic!()
}
}
trait Super {}
trait Sub: Super {}
impl<'a> Deref for W<dyn 'a + Sub> {
type Target = W<dyn Super>;
fn deref(&self) -> &Self::Target {
panic!()
}
}
impl Super for () {}
impl Sub for () {}
fn main() {
// Existing:
let _: &W<[u8]> = &*&W([]); // No panic.
let _: &W<[u8]> = &**&W([]); // Panic.
// New:
let _: &W<dyn Super> = &*(&W(()) as &W<dyn Sub>); // No panic.
let _: &W<dyn Super> = &**(&W(()) as &W<dyn Sub>); // Panic.
} |
Dropping by to just say that rust-analyzer currently throws a type mismatch on trait upcasting coercions as the feature isn't implemented there yet rust-lang/rust-analyzer#18083 |
This feature was "done" for a while now, I think it's finally time to stabilize it! (stabilization report pending)
cc reference PR: rust-lang/reference#1622, tracking issue: #65991
r? compiler-errors