-
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
Item bounds can reference self projections and still be object safe #122804
Item bounds can reference self projections and still be object safe #122804
Conversation
3e77591
to
3184f93
Compare
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.
would like to have an fcp for this tho. probably should mention about the fact that you can already kind of do this via supertraits, and also that we only assemble alias bound candidates for rigid aliases and <dyn Trait as Trait>::Assoc
is never going to be a rigid alias so the item bounds shouldn't affect the trait object
Ya, I realized during discussion I should probably explain exactly why this is sound, specifically discussing the way we do |
☔ The latest upstream changes (presumably #125001) made this pull request unmergeable. Please resolve the merge conflicts. |
3184f93
to
1dcf764
Compare
This is ready I believe :3 @rfcbot fcp merge |
Team member @compiler-errors 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! See this document for info about what commands tagged team members can give me. |
@rfcbot reviewed |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
@bors r+ |
…ference-self, r=BoxyUwU Item bounds can reference self projections and still be object safe ### Background Currently, we have some interesting rules about where `Self` is allowed to be mentioned in objects. Specifically, we allow mentioning `Self` behind associated types (e.g. `fn foo(&self) -> Self::Assoc`) only if that `Self` type comes from the trait we're defining or its supertraits: ``` trait Foo { fn good() -> Self::Assoc; // GOOD :) fn bad() -> <Self as OtherTrait>::Assoc; // BAD! } ``` And more specifically, these `Self::Assoc` projections are *only* allowed to show up in: * (A1) Method signatures * (A2) Where clauses on traits, GATs and methods But `Self::Assoc` projections are **not** allowed to show up in: * (B1) Supertrait bounds (specifically: all *super-predicates*, which includes the projections that come from elaboration, and not just the traits themselves). * (B2) Item bounds of associated types The reason for (B1) is interesting: specifically, it arises from the fact that we currently eagerly elaborate all projection predicates into the object, so if we had the following code: ``` trait Sub<Assoc = Self::SuperAssoc> {} trait Super { type SuperAssoc; } ``` Then given `dyn Sub<SuperAssoc = i32>` we would need to have a type that is substituted into itself an infinite number of times[^1], like `dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <... as Super>::SuperAssoc> as Super>::SuperAssoc> as Super>::SuperAssoc>`, i.e. the fixed-point of: `type T = dyn Sub<SuperAssoc = i32, Assoc = <T as Super>::SuperAssoc>`. Similarly for (B2), we restrict mentioning `Self::Assoc` in associated type item bounds, which is the cause for rust-lang#122798. However, there is **no reason** for us to do so, since item bounds never show up structurally in the `dyn Trait` object type. #### What? This PR relaxes the check for item bounds so that `Self` may be mentioned behind associated types in the same cases that they currently work for method signatures (A1) and where clauses (A2). #### Why? Fixes rust-lang#122798. Removes a subtle and confusing inconsistency for the code mentioned in that issue. This is sound because we only assemble alias bounds for rigid projections, and all projections coming from an object self type are not rigid, since all associated types should be specified by the type. This is also desirable because we can do this via supertraits already. In rust-lang#122789, it is noted that an item bound of `Eq` already works, just not `PartialEq` because of the default item bound. This is weird and should be fixed. #### Future work We could make the check for `Self` in super-predicates more sophisticated as well, only erroring if `Self` shows up in a projection super-predicate. [^1]: This could be fixed by some sort of structural replacement or eager normalization, but I don't think it's necessary currently.
…ference-self, r=BoxyUwU Item bounds can reference self projections and still be object safe ### Background Currently, we have some interesting rules about where `Self` is allowed to be mentioned in objects. Specifically, we allow mentioning `Self` behind associated types (e.g. `fn foo(&self) -> Self::Assoc`) only if that `Self` type comes from the trait we're defining or its supertraits: ``` trait Foo { fn good() -> Self::Assoc; // GOOD :) fn bad() -> <Self as OtherTrait>::Assoc; // BAD! } ``` And more specifically, these `Self::Assoc` projections are *only* allowed to show up in: * (A1) Method signatures * (A2) Where clauses on traits, GATs and methods But `Self::Assoc` projections are **not** allowed to show up in: * (B1) Supertrait bounds (specifically: all *super-predicates*, which includes the projections that come from elaboration, and not just the traits themselves). * (B2) Item bounds of associated types The reason for (B1) is interesting: specifically, it arises from the fact that we currently eagerly elaborate all projection predicates into the object, so if we had the following code: ``` trait Sub<Assoc = Self::SuperAssoc> {} trait Super { type SuperAssoc; } ``` Then given `dyn Sub<SuperAssoc = i32>` we would need to have a type that is substituted into itself an infinite number of times[^1], like `dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <... as Super>::SuperAssoc> as Super>::SuperAssoc> as Super>::SuperAssoc>`, i.e. the fixed-point of: `type T = dyn Sub<SuperAssoc = i32, Assoc = <T as Super>::SuperAssoc>`. Similarly for (B2), we restrict mentioning `Self::Assoc` in associated type item bounds, which is the cause for rust-lang#122798. However, there is **no reason** for us to do so, since item bounds never show up structurally in the `dyn Trait` object type. #### What? This PR relaxes the check for item bounds so that `Self` may be mentioned behind associated types in the same cases that they currently work for method signatures (A1) and where clauses (A2). #### Why? Fixes rust-lang#122798. Removes a subtle and confusing inconsistency for the code mentioned in that issue. This is sound because we only assemble alias bounds for rigid projections, and all projections coming from an object self type are not rigid, since all associated types should be specified by the type. This is also desirable because we can do this via supertraits already. In rust-lang#122789, it is noted that an item bound of `Eq` already works, just not `PartialEq` because of the default item bound. This is weird and should be fixed. #### Future work We could make the check for `Self` in super-predicates more sophisticated as well, only erroring if `Self` shows up in a projection super-predicate. [^1]: This could be fixed by some sort of structural replacement or eager normalization, but I don't think it's necessary currently.
Rollup of 9 pull requests Successful merges: - rust-lang#122804 (Item bounds can reference self projections and still be object safe) - rust-lang#124486 (Add tracking issue and unstable book page for `"vectorcall"` ABI) - rust-lang#125504 (Change pedantically incorrect OnceCell/OnceLock wording) - rust-lang#125608 (Avoid follow-up errors if the number of generic parameters already doesn't match) - rust-lang#125690 (ARM Target Docs Update) - rust-lang#125750 (Align `Term` methods with `GenericArg` methods, add `Term::expect_*`) - rust-lang#125818 (Handle no values cfgs with `--print=check-cfg`) - rust-lang#125909 (rustdoc: add a regression test for a former blanket impl synthesis ICE) - rust-lang#125919 (Remove stray "this") r? `@ghost` `@rustbot` modify labels: rollup
Failed in #125956 (comment) |
hehe, race condition, I was writing the same rollup failure message that this PR needed a rebase |
1dcf764
to
1b58a7f
Compare
…unsoundness, r=<try> Fix supertrait associated type unsoundness This is built on top of rust-lang#122804 though that's really not related, it's just easier to make this modification with the changes to the object safety code that I did in that PR. The only thing is that PR may make this unsoundness slightly easier to abuse, since there are more positions that allow self-associated-types. This PR only looks for supertrait associated types *without* normalizing. I'm gonna crater this initially -- preferably we don't need to normalize unless there's a lot of breakage. I assume that most people are writing `Self::Assoc` so they don't really care about the trait ref being normalized, so somewhat tempted to believe that this will just work out and we can extend it later if needed. r? lcnr
☔ The latest upstream changes (presumably #126374) made this pull request unmergeable. Please resolve the merge conflicts. |
@compiler-errors needs a rebase :-) |
1b58a7f
to
b7b1b3e
Compare
I would be perhaps a little worried about the other PR landing then causing more regressions than expected and it getting reverted. That'd mean this PR would wind up making the problem A Bit worse on a long term basis potentially although I imagine we'd wind up with a fcw in that scenario 🤷♀️. I think trying to hit that unsoundness is also pretty hard in practice, I remember trying to find an example that'd hit it a few times and didn't manage to. I don't think it matters to land that PR before this one, we should just go ahead and merge this imo |
Given that #126090 should be in FCP soon and feels quite straight forward, I think we should wait until it's through FCP and then merge both PRs at once (as that the fix builds on this one). |
…y-unsoundness, r=lcnr Fix supertrait associated type unsoundness ### What? Object safety allows us to name `Self::Assoc` associated types in certain positions if they come from our trait or one of our supertraits. When this check was implemented, I think it failed to consider that supertraits can have different args, and it was only checking def-id equality. This is problematic, since we can sneak different implementations in by implementing `Supertrait<NotActuallyTheSupertraitSubsts>` for a `dyn` type. This can be used to implement an unsound transmute function. See the committed test. ### How do we fix it? We consider the whole trait ref when checking for supertraits. Right now, this is implemented using equality *without* normalization. We erase regions since those don't affect trait selection. This is a limitation that could theoretically affect code that should be accepted, but doesn't matter in practice -- there are 0 crater regression. We could make this check stronger, but I would be worried about cycle issues. I assume that most people are writing `Self::Assoc` so they don't really care about the trait ref being normalized. --- ### What is up w the stacked commit This is built on top of rust-lang#122804 though that's really not related, it's just easier to make this modification with the changes to the object safety code that I did in that PR. The only thing is that PR may make this unsoundness slightly easier to abuse, since there are more positions that allow self-associated-types -- I am happy to stall that change until this PR merges. --- Fixes rust-lang#126079 r? lcnr
Rollup merge of rust-lang#126090 - compiler-errors:supertrait-assoc-ty-unsoundness, r=lcnr Fix supertrait associated type unsoundness ### What? Object safety allows us to name `Self::Assoc` associated types in certain positions if they come from our trait or one of our supertraits. When this check was implemented, I think it failed to consider that supertraits can have different args, and it was only checking def-id equality. This is problematic, since we can sneak different implementations in by implementing `Supertrait<NotActuallyTheSupertraitSubsts>` for a `dyn` type. This can be used to implement an unsound transmute function. See the committed test. ### How do we fix it? We consider the whole trait ref when checking for supertraits. Right now, this is implemented using equality *without* normalization. We erase regions since those don't affect trait selection. This is a limitation that could theoretically affect code that should be accepted, but doesn't matter in practice -- there are 0 crater regression. We could make this check stronger, but I would be worried about cycle issues. I assume that most people are writing `Self::Assoc` so they don't really care about the trait ref being normalized. --- ### What is up w the stacked commit This is built on top of rust-lang#122804 though that's really not related, it's just easier to make this modification with the changes to the object safety code that I did in that PR. The only thing is that PR may make this unsoundness slightly easier to abuse, since there are more positions that allow self-associated-types -- I am happy to stall that change until this PR merges. --- Fixes rust-lang#126079 r? lcnr
This was merged with #126090 |
…ness, r=lcnr Fix supertrait associated type unsoundness ### What? Object safety allows us to name `Self::Assoc` associated types in certain positions if they come from our trait or one of our supertraits. When this check was implemented, I think it failed to consider that supertraits can have different args, and it was only checking def-id equality. This is problematic, since we can sneak different implementations in by implementing `Supertrait<NotActuallyTheSupertraitSubsts>` for a `dyn` type. This can be used to implement an unsound transmute function. See the committed test. ### How do we fix it? We consider the whole trait ref when checking for supertraits. Right now, this is implemented using equality *without* normalization. We erase regions since those don't affect trait selection. This is a limitation that could theoretically affect code that should be accepted, but doesn't matter in practice -- there are 0 crater regression. We could make this check stronger, but I would be worried about cycle issues. I assume that most people are writing `Self::Assoc` so they don't really care about the trait ref being normalized. --- ### What is up w the stacked commit This is built on top of rust-lang/rust#122804 though that's really not related, it's just easier to make this modification with the changes to the object safety code that I did in that PR. The only thing is that PR may make this unsoundness slightly easier to abuse, since there are more positions that allow self-associated-types -- I am happy to stall that change until this PR merges. --- Fixes #126079 r? lcnr
Background
Currently, we have some interesting rules about where
Self
is allowed to be mentioned in objects. Specifically, we allow mentioningSelf
behind associated types (e.g.fn foo(&self) -> Self::Assoc
) only if thatSelf
type comes from the trait we're defining or its supertraits:And more specifically, these
Self::Assoc
projections are only allowed to show up in:But
Self::Assoc
projections are not allowed to show up in:The reason for (B1) is interesting: specifically, it arises from the fact that we currently eagerly elaborate all projection predicates into the object, so if we had the following code:
Then given
dyn Sub<SuperAssoc = i32>
we would need to have a type that is substituted into itself an infinite number of times1, likedyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <... as Super>::SuperAssoc> as Super>::SuperAssoc> as Super>::SuperAssoc>
, i.e. the fixed-point of:type T = dyn Sub<SuperAssoc = i32, Assoc = <T as Super>::SuperAssoc>
.Similarly for (B2), we restrict mentioning
Self::Assoc
in associated type item bounds, which is the cause for #122798. However, there is no reason for us to do so, since item bounds never show up structurally in thedyn Trait
object type.What?
This PR relaxes the check for item bounds so that
Self
may be mentioned behind associated types in the same cases that they currently work for method signatures (A1) and where clauses (A2).Why?
Fixes #122798. Removes a subtle and confusing inconsistency for the code mentioned in that issue.
This is sound because we only assemble alias bounds for rigid projections, and all projections coming from an object self type are not rigid, since all associated types should be specified by the type.
This is also desirable because we can do this via supertraits already. In #122789, it is noted that an item bound of
Eq
already works, just notPartialEq
because of the default item bound. This is weird and should be fixed.Future work
We could make the check for
Self
in super-predicates more sophisticated as well, only erroring ifSelf
shows up in a projection super-predicate.Footnotes
This could be fixed by some sort of structural replacement or eager normalization, but I don't think it's necessary currently. ↩