-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Trait objects can call nonexistent concrete methods #50781
Comments
Edit 3: This really isn't the way to fix this issue, you may skip this comment. I did some investigation. The ideal fix is to catch this at declaration site, in the well-formedness (WF) check. Failing that we should be catching it at the call site, but that code dodged all checks. Edit: Actually with #48214 in some cases that WF check is downgraded to a lint, so we must also get the call site check right to fix this for all cases. The presence of a The WF check for the Lines 75 to 78 in 2a3f536
only to completely ignore any trait predicates containing an escaping region in compute_trait_ref , so the predicate is not checked at all for WF.Lines 191 to 196 in 2a3f536
I'm not sure how to fix but this certainly looks like guilty code. |
Skip the binder in less places during WF check. I thought this might help fix rust-lang#50781, but I couldn't actually observe any effect for this changes. Maybe it's still a worthwhile refactoring since skipping binders is generally a bad thing. There is a (manageble) conflict with rust-lang#50183.
OK, so, this first of all is not dependent on #48557. For example, this example will reproduce the problem on stable: https://play.rust-lang.org/?gist=8c8b887b74da66782339e3a79c21c378&version=stable&mode=debug The problem is rather a flaw in the object safety rules. Specifically, the object safety rules are supposed to prevent rust/src/librustc/traits/object_safety.rs Lines 132 to 134 in dbd10f8
But for some reason we seem to permit rust/src/librustc/traits/object_safety.rs Lines 167 to 170 in dbd10f8
I'm actually..not sure why we do that. It seems ok on the trait level, as that is validated at the impl site, but not at the method level. |
Yes my previous comment was really misled, has nothing to do with HRTB or closures, see this minimization: trait Trait {}
trait X {
fn foo(&self) where Self: Trait;
}
impl X for () {
fn foo(&self) {}
}
impl Trait for dyn X {}
pub fn main() {
<X as X>::foo(&()); // Segfault at opt-level 0, SIGILL otherwise.
} |
This is virtually certain to cause regressions, needs crater. In rust-lang#50781 it was discovered that our object safety rules are not sound because we allow `Self` in where clauses without restrain. This PR is a direct fix to the rules so that we disallow methods with unsound where clauses. This currently uses hard error to measure impact, but we will want to downgrade it to a future compat error. Fixes rust-lang#50781. r? @nikomatsakis
…t-safe, r=<try> `Self` in where clauses may not be object safe Needs crater, virtually certain to cause regressions. In #50781 it was discovered that our object safety rules are not sound because we allow `Self` in where clauses without restrain. This PR is a direct fix to the rules so that we disallow methods with unsound where clauses. This currently uses hard error to measure impact, but we will want to downgrade it to a future compat error. Fixes #50781. r? @nikomatsakis
Marking as P-high since the fix may break code. |
So I was discussing this problem with myself (I don't think @leodasvacas is around =) on Discord. You can search for this GUID to find it: 3b75dc47-42f1-44da-a39e-562f5875e932 However, the TL;DR is that we don't have to forbid all where clauses like So I think we should loosen the conditions in the PR, which should help with regressions. |
OK, so I was maybe not completely right. Let me summarize what @leodasvacas and I said on Discord. We currently have two paths forward that permit us to close the soundness hole but not break as many crates. Background: my summary from previous comment is still basically right. For a trait to be object safe, we need to ensure that — if there is a where-clause on a method like Proposal 1. Auto traits are special. Currently, we do not permit external auto traits to be implemented for anything but structs/enums (example). We do, apparently, permit it for local auto traits. We also permit impls for types like I believe the reason for this disparity was that this would give the "auto trait definer" the chance to decide whether these "primitive impls" for built-in types were generated according to the default rules (recursively visit the data) or some other criteria, just as one can do for structs. However, auto traits are unstable, so these rules are subject to change. The reasoning behind this prohibition is that — when we ask whether a That said, this isn't really a necessary restriction. I think the current implementation is also coherent (no pun intended) and makes a measure of sense. (Well, to be consistent, we probably could allow user-defined impls for Interlude. If you think about it, the heart of the previous proposal was basically banning the ability to implement traits for a impl Trait for dyn Trait { .. } This makes sense, since the compiler is going to supply the impl Trait for dyn (Trait + Trait2) { .. } (Weirdly, Proposal 2. Action at a distance In a way, it'd be nice to prevent implementation traits for @leodasvacas pointed out that we could do a more narrow restriction though. You could say that, if you have Among other things, this implies that things like |
Nominating for lang team discussion. |
Fwiw the I think that proposal 2 would be best from a language point of view, it sounds like bad design in principle, but the problematic situations never happen in practice so it would be a an edge case fix for an edge case problem. However it may be impractical to implement, I don't think the compiler can readily tell us if an impl that looks like |
Forgive me if I'm not fully understanding this issue, but would it be a good solution to have fn foo(&self) where Self: Trait; simply imply this? fn foo(&self) where Self: Trait + Sized; So methods marked with Being able to |
I'm going through the |
…li-obk don't ICE on invalid dyn calls Due to rust-lang#50781 this is actually reachable. Fixes rust-lang/miri#2432 r? `@oli-obk`
…li-obk don't ICE on invalid dyn calls Due to rust-lang#50781 this is actually reachable. Fixes rust-lang/miri#2432 r? ``@oli-obk``
trait Trait {}
trait X {
fn foo(&self) where Self: Trait;
}
impl X for () {
fn foo(&self) {}
}
impl Trait for dyn X {}
pub fn main() {
<X as X>::foo(&()); // Segfault at opt-level 0, SIGILL otherwise.
} still segfaults and miri says
|
Autotrait bounds on dyn-safe trait methods This PR is a successor to rust-lang#106604 implementing the approach encouraged by rust-lang#106604 (comment). **I propose making it legal to use autotraits as trait bounds on the `Self` type of trait methods in a trait object.** rust-lang#51443 (comment) justifies why this use case is particularly important in the context of the async-trait crate. ```rust #![feature(auto_traits)] #![deny(where_clauses_object_safety)] auto trait AutoTrait {} trait MyTrait { fn f(&self) where Self: AutoTrait; } fn main() { let _: &dyn MyTrait; } ``` Previously this would fail with: ```console error: the trait `MyTrait` cannot be made into an object --> src/main.rs:7:8 | 7 | fn f(&self) where Self: AutoTrait; | ^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue rust-lang#51443 <rust-lang#51443> note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> --> src/main.rs:7:8 | 6 | trait MyTrait { | ------- this trait cannot be made into an object... 7 | fn f(&self) where Self: AutoTrait; | ^ ...because method `f` references the `Self` type in its `where` clause = help: consider moving `f` to another trait ``` In order for this to be sound without hitting rust-lang#50781, **I further propose that we disallow handwritten autotrait impls that apply to trait objects.** Both of the following were previously allowed (_on nightly_) and no longer allowed in my proposal: ```rust auto trait AutoTrait {} trait MyTrait {} impl AutoTrait for dyn MyTrait {} // NOT ALLOWED impl<T: ?Sized> AutoTrait for T {} // NOT ALLOWED ``` (`impl<T> AutoTrait for T {}` remains allowed.) After this change, traits with a default impl are implemented for a trait object **if and only if** the autotrait is one of the trait object's trait bounds (or a supertrait of a bound). In other words `dyn Trait + AutoTrait` always implements AutoTrait while `dyn Trait` never implements AutoTrait. Fixes dtolnay/async-trait#228. r? `@lcnr`
Autotrait bounds on dyn-safe trait methods This PR is a successor to rust-lang#106604 implementing the approach encouraged by rust-lang#106604 (comment). **I propose making it legal to use autotraits as trait bounds on the `Self` type of trait methods in a trait object.** rust-lang#51443 (comment) justifies why this use case is particularly important in the context of the async-trait crate. ```rust #![feature(auto_traits)] #![deny(where_clauses_object_safety)] auto trait AutoTrait {} trait MyTrait { fn f(&self) where Self: AutoTrait; } fn main() { let _: &dyn MyTrait; } ``` Previously this would fail with: ```console error: the trait `MyTrait` cannot be made into an object --> src/main.rs:7:8 | 7 | fn f(&self) where Self: AutoTrait; | ^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue rust-lang#51443 <rust-lang#51443> note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> --> src/main.rs:7:8 | 6 | trait MyTrait { | ------- this trait cannot be made into an object... 7 | fn f(&self) where Self: AutoTrait; | ^ ...because method `f` references the `Self` type in its `where` clause = help: consider moving `f` to another trait ``` In order for this to be sound without hitting rust-lang#50781, **I further propose that we disallow handwritten autotrait impls that apply to trait objects.** Both of the following were previously allowed (_on nightly_) and no longer allowed in my proposal: ```rust auto trait AutoTrait {} trait MyTrait {} impl AutoTrait for dyn MyTrait {} // NOT ALLOWED impl<T: ?Sized> AutoTrait for T {} // NOT ALLOWED ``` (`impl<T> AutoTrait for T {}` remains allowed.) After this change, traits with a default impl are implemented for a trait object **if and only if** the autotrait is one of the trait object's trait bounds (or a supertrait of a bound). In other words `dyn Trait + AutoTrait` always implements AutoTrait while `dyn Trait` never implements AutoTrait. Fixes dtolnay/async-trait#228. r? `@lcnr`
There's one potential (partial) fix that I haven't seen discussed, that would continue to support many of the use-cases people have for the trait bounds in question. Notably, it would allow Let's look at the MCVE for this issue: trait Trait {}
trait X {
fn foo(&self) where Self: Trait;
}
impl X for () {
fn foo(&self) {}
}
impl Trait for dyn X {}
pub fn main() {
<dyn X as X>::foo(&()); // Segfault at opt-level 0, SIGILL otherwise.
} The source of the problem is that there is no source for the implementation of trait Trait {}
trait X {
// `foo` now has a default impl
fn foo(&self) where Self: Trait {}
}
impl X for () {}
impl Trait for dyn X {}
pub fn main() {
<dyn X as X>::foo(&());
} In this modified example, there is a potential source for the One wrinkle is that additional rules would be necessary to prevent lifetime-based specialization. Consider: #![feature(trivial_bounds)]
trait Trait {}
impl Trait for &'static () {}
trait X {
// `foo` now has a default impl
fn foo(&self) where Self: Trait {}
}
impl<'a> X for &'a () {
fn foo(&self) where Self: Trait {
println!("`Self: 'static` must surely hold at this point");
}
}
impl<'a> Trait for dyn X + 'a {}
pub fn test<'a>(r: &'a ()) {
<dyn X + 'a as X>::foo(&r);
} In this case, the implementation of |
[crater] make `where_clauses_object_safety` forbid cc rust-lang#50781 r? lcnr
[crater] make `where_clauses_object_safety` forbid cc rust-lang#50781 r? lcnr
…li-obk Make `WHERE_CLAUSES_OBJECT_SAFETY` a regular object safety violation #### The issue In rust-lang#50781, we have known about unsound `where` clauses in function arguments: ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } // `where` clause satisfied for the object, meaning that the function now *looks* callable. impl Impossible for dyn Foo {} fn main() { let x: &dyn Foo = &&(); x.impossible(); } ``` ... which currently segfaults at runtime because we try to call a method in the vtable that doesn't exist. :( #### What did u change This PR removes the `WHERE_CLAUSES_OBJECT_SAFETY` lint and instead makes it a regular object safety violation. I choose to make this into a hard error immediately rather than a `deny` because of the time that has passed since this lint was authored, and the single (1) regression (see below). That means that it's OK to mention `where Self: Trait` where clauses in your trait, but making such a trait into a `dyn Trait` object will report an object safety violation just like `where Self: Sized`, etc. ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; // <~ This definition is valid, just not object-safe. } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } fn main() { let x: &dyn Foo = &&(); // <~ THIS is where we emit an error. } ``` #### Regressions From a recent crater run, there's only one crate that relies on this behavior: rust-lang#124305 (comment). The crate looks unmaintained and there seems to be no dependents. #### Further We may later choose to relax this (e.g. when the where clause is implied by the supertraits of the trait or something), but this is not something I propose to do in this FCP. For example, given: ``` trait Tr { fn f(&self) where Self: Blanket; } impl<T: ?Sized> Blanket for T {} ``` Proving that some placeholder `S` implements `S: Blanket` would be sufficient to prove that the same (blanket) impl applies for both `Concerete: Blanket` and `dyn Trait: Blanket`. Repeating here that I don't think we need to implement this behavior right now. ---- r? lcnr
…-obk Make `WHERE_CLAUSES_OBJECT_SAFETY` a regular object safety violation #### The issue In rust-lang#50781, we have known about unsound `where` clauses in function arguments: ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } // `where` clause satisfied for the object, meaning that the function now *looks* callable. impl Impossible for dyn Foo {} fn main() { let x: &dyn Foo = &&(); x.impossible(); } ``` ... which currently segfaults at runtime because we try to call a method in the vtable that doesn't exist. :( #### What did u change This PR removes the `WHERE_CLAUSES_OBJECT_SAFETY` lint and instead makes it a regular object safety violation. I choose to make this into a hard error immediately rather than a `deny` because of the time that has passed since this lint was authored, and the single (1) regression (see below). That means that it's OK to mention `where Self: Trait` where clauses in your trait, but making such a trait into a `dyn Trait` object will report an object safety violation just like `where Self: Sized`, etc. ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; // <~ This definition is valid, just not object-safe. } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } fn main() { let x: &dyn Foo = &&(); // <~ THIS is where we emit an error. } ``` #### Regressions From a recent crater run, there's only one crate that relies on this behavior: rust-lang#124305 (comment). The crate looks unmaintained and there seems to be no dependents. #### Further We may later choose to relax this (e.g. when the where clause is implied by the supertraits of the trait or something), but this is not something I propose to do in this FCP. For example, given: ``` trait Tr { fn f(&self) where Self: Blanket; } impl<T: ?Sized> Blanket for T {} ``` Proving that some placeholder `S` implements `S: Blanket` would be sufficient to prove that the same (blanket) impl applies for both `Concerete: Blanket` and `dyn Trait: Blanket`. Repeating here that I don't think we need to implement this behavior right now. ---- r? lcnr
…-obk Make `WHERE_CLAUSES_OBJECT_SAFETY` a regular object safety violation #### The issue In rust-lang#50781, we have known about unsound `where` clauses in function arguments: ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } // `where` clause satisfied for the object, meaning that the function now *looks* callable. impl Impossible for dyn Foo {} fn main() { let x: &dyn Foo = &&(); x.impossible(); } ``` ... which currently segfaults at runtime because we try to call a method in the vtable that doesn't exist. :( #### What did u change This PR removes the `WHERE_CLAUSES_OBJECT_SAFETY` lint and instead makes it a regular object safety violation. I choose to make this into a hard error immediately rather than a `deny` because of the time that has passed since this lint was authored, and the single (1) regression (see below). That means that it's OK to mention `where Self: Trait` where clauses in your trait, but making such a trait into a `dyn Trait` object will report an object safety violation just like `where Self: Sized`, etc. ```rust trait Impossible {} trait Foo { fn impossible(&self) where Self: Impossible; // <~ This definition is valid, just not object-safe. } impl Foo for &() { fn impossible(&self) where Self: Impossible, {} } fn main() { let x: &dyn Foo = &&(); // <~ THIS is where we emit an error. } ``` #### Regressions From a recent crater run, there's only one crate that relies on this behavior: rust-lang#124305 (comment). The crate looks unmaintained and there seems to be no dependents. #### Further We may later choose to relax this (e.g. when the where clause is implied by the supertraits of the trait or something), but this is not something I propose to do in this FCP. For example, given: ``` trait Tr { fn f(&self) where Self: Blanket; } impl<T: ?Sized> Blanket for T {} ``` Proving that some placeholder `S` implements `S: Blanket` would be sufficient to prove that the same (blanket) impl applies for both `Concerete: Blanket` and `dyn Trait: Blanket`. Repeating here that I don't think we need to implement this behavior right now. ---- r? lcnr
Fixed by #125380. Right? I hope I'm not missing something. |
This code segfaults at runtime
cc #48557 - Makes this easier to come across.
The text was updated successfully, but these errors were encountered: