-
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
Actually use the #[do_not_recommend]
attribute if present
#124708
Conversation
compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
Outdated
Show resolved
Hide resolved
ee93d22
to
a6ceae9
Compare
The tests seem to pass locally with |
Can you open a separate PR to move |
I'll put up a PR to implement |
Also, this line should fix your ICEs: I believe you should enable debug assertions in your local compiler: (
|
@@ -464,6 +464,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | |||
root_obligation, | |||
) | |||
} else { | |||
let mut trait_predicate = trait_predicate; | |||
while matches!( |
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.
This doesn't take into account obligation stacks that are like A -requires> B -requires> C
where A
and B
come from impls marked with #[do_not_recommend]
but C
does not.
I believe we need to basically need to pop #[do_not_recommend]
s until there are none left in the stack anywhere, even if it isn't the last obligation in the stack.
For example, consider we put #[do_not_recommend]
on this IntoIterator
blanket, and then consider the following code:
trait OtherTrait {}
struct It<T>(T);
impl<T> Iterator for It<T> where T: OtherTrait {
type Item = ();
fn next(&mut self) -> Option<Self::Item> { todo!() }
}
fn main() {
for x in It(()) {}
}
I would not expect the compiler to mention (): OtherTrait
, but it would in this case, right? IMO it should just mention It<()>: IntoIterator
.
From another perspective, considering the full tree of solving a goal like It<()>: IntoIterator
, we simply wouldn't walk into the obligations that come from considering the blanket implementation marked with #[do_not_recommend]
.
I know that such a tree is kind of inverted from our perspective here, but that's only a consequence of how we record derived obligations currently, and shouldn't limit our implementation IMO.
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.
Doesn't that loop not already do that?
We reset the oblication.cause.code()
field in line 475, so we should pop these obligations as long as we encounter ones that are marked with #[do_not_recommend]
, right?
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.
Not if the top obligation is not a ImplDerivedObligation, right? For example, a BuiltinDerivedObligation
that is stacked onto a ImplDerivedObligation
(e.g. via a T: Send
where clause).
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.
Thanks for the clarification. That sounds like I should first do some reading on what's actually represented by the Obligation
struct. Is there any resource on this other than the source code?
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.
Hopefully it's now closer to what you imagined, although I'm still not sure about the details of Obligation
.
a6ceae9
to
4b50243
Compare
This comment has been minimized.
This comment has been minimized.
4b50243
to
240f678
Compare
I will open a PR for that next week. That PR should keep the
I rebased the PR to include your change. Hopefully that fixes the issue. |
This comment has been minimized.
This comment has been minimized.
Seems like the CI is currently failing for unrelated reasons (Network error while downloading LLVM?) |
|
Actually, sorry, now that I look on this on my own web browser (not phone) I see there was a force-push to the master branch. Please do still rebase to fix that, though. |
240f678
to
1917f3a
Compare
Whatever I managed to do there… |
1917f3a
to
eca373a
Compare
(trait_predicate, &obligation) | ||
let mut temp_trait_predicate = trait_predicate; | ||
let mut loop_code = obligation.cause.code().clone(); | ||
loop{ |
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.
Can you run ./x.py fmt
? This might not be the only thing that CI will complain about.
loop{ | |
loop { |
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's probably that rustfmt bailed in this method because of something else, like a string that's too long or a where clause somewhere that it can't handle, because otherwise tidy would have failed.
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.
Probably the matches!
let mut temp_trait_predicate = trait_predicate; | ||
let mut loop_code = obligation.cause.code().clone(); | ||
loop{ | ||
let (code, base) = loop_code.peel_derives_with_predicate(); | ||
if matches!(loop_code, ObligationCauseCode::ImplDerivedObligation(ref c) if tcx.has_attr(c.impl_or_alias_def_id, sym::do_not_recommend)) { | ||
if let Some(base) = base { | ||
let code = code.clone(); | ||
obligation.cause.map_code(|_| code); | ||
obligation.predicate = ty::PredicateKind::Clause(ty::ClauseKind::Trait(base.skip_binder().clone())).to_predicate(tcx); | ||
temp_trait_predicate = base.clone(); | ||
trait_predicate = base; | ||
} | ||
} | ||
if loop_code == *code { | ||
break; | ||
} else { | ||
loop_code = code.clone(); | ||
} | ||
} |
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.
Can we add a comment in front of this? When skimming this code it will be really hard to figure out what's going on without paying a lot of attention. What, and more importantly, why.
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 really should be extracted into a helper function, I think.
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've fixed the formatting and extracted it into a helper function in https://github.com/rust-lang/rust/compare/eca373a073222883772da9c30616b3c746e3d6f2..f0279abd052e1ec4fa39ab71edbd7484dbc84c7b
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'd like to take another look at this later before it lands.
let mut temp_trait_predicate = trait_predicate; | ||
let mut loop_code = obligation.cause.code().clone(); | ||
loop{ | ||
let (code, base) = loop_code.peel_derives_with_predicate(); |
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 don't think it's correct to use peel_derives_with_predicate
here. We need to be peeling these one at a time.
Consider a where clause where the first impl we have to go through isn't marked with #[do_not_recommend]
but the second is.
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.
Hopefully it's more correct with https://github.com/rust-lang/rust/compare/eca373a073222883772da9c30616b3c746e3d6f2..f0279abd052e1ec4fa39ab71edbd7484dbc84c7b now. If that's not the case I would appreciate bit more guidance of how to address that.
compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
Outdated
Show resolved
Hide resolved
eca373a
to
f0279ab
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.
I'd like to see more tests that exercise the cases that I've alluded to previously in review, since I'm not totally certain if the loop you've implemented is right 🤔. For example, here's one test that I'd like to see works correctly:
#![feature(do_not_recommend)]
trait Root {}
trait DontRecommend {}
trait Other {}
#[do_not_recommend]
impl<T> Root for T where T: DontRecommend {}
impl<T> DontRecommend for T where T: Other {}
fn needs_root<T: Root>() {}
fn main() {
needs_root::<()>();
// Should not mention `(): Other` or `(): DontRecommend`
}
I'd also like to see if you can think of any other tests...
You should also be able to compare the tests against the implementation in the new solver by using revisions, tagging your tests with:
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
They should look the same.
use std::ops::Deref; | ||
|
||
let code = c.derived.parent_code.deref().clone(); |
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.
nit:
use std::ops::Deref; | |
let code = c.derived.parent_code.deref().clone(); | |
let code = (*c.derived.parent_code).clone(); |
@@ -1000,6 +1001,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | |||
err.emit() | |||
} | |||
|
|||
fn apply_do_not_recommend<'b>( | |||
&self, | |||
trait_predicate: &'_ mut ty::Binder<'b, ty::TraitPredicate<'tcx>>, |
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.
This 'b
lifetime is unnecessary. It should always be 'tcx
. If not, then there's almost certainly some other lifetime in the caller that needs to be turned into a 'tcx
.
} | ||
} | ||
|
||
// later we need to walk the parent tree |
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.
Why does this need to be a separate loop? The logic that applies above (we need to peel until we find the obligation preceding the first1 do_not_recommend
in the stack, if it exists) should also apply below -- should be able to just have one loop.
Footnotes
-
first from the root obligation, or last, if we're going down from the leaf like we're doing here. ↩
f0279ab
to
ec1dd21
Compare
… r=compiler-errors,estebank Actually use the `#[do_not_recommend]` attribute if present This change tweaks the error message generation to actually use the `#[do_not_recommend]` attribute if present by just skipping the marked trait impl in favour of the parent impl. It also adds a compile test for this behaviour. Without this change the test would output the following error: ``` error[E0277]: the trait bound `&str: Expression` is not satisfied --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:53:15 | LL | SelectInt.check("bar"); | ^^^^^ the trait `Expression` is not implemented for `&str`, which is required by `&str: AsExpression<Integer>` | = help: the following other types implement trait `Expression`: Bound<T> SelectInt note: required for `&str` to implement `AsExpression<Integer>` --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:26:13 | LL | impl<T, ST> AsExpression<ST> for T | ^^^^^^^^^^^^^^^^ ^ LL | where LL | T: Expression<SqlType = ST>, | ------------------------ unsatisfied trait bound introduced here ``` Note how that mentions `&str: Expression` before and now mentions `&str: AsExpression<Integer>` instead which is much more helpful for users. Open points for further changes before stabilization: * We likely want to move the attribute to the `#[diagnostic]` namespace to relax the guarantees given? * How does it interact with the new trait solver? r? `@estebank`
This comment has been minimized.
This comment has been minimized.
💔 Test failed - checks-actions |
@weiznich: |
023ba29
to
a08e09f
Compare
@bors r=compiler-errors,estebank |
This comment has been minimized.
This comment has been minimized.
Ah, tests need blessing too. I'll leave this up to you then. r=me when rebased. @bors r- |
This change tweaks the error message generation to actually use the `#[do_not_recommend]` attribute if present by just skipping the marked trait impl in favour of the parent impl. It also adds a compile test for this behaviour. Without this change the test would output the following error: ``` error[E0277]: the trait bound `&str: Expression` is not satisfied --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:53:15 | LL | SelectInt.check("bar"); | ^^^^^ the trait `Expression` is not implemented for `&str`, which is required by `&str: AsExpression<Integer>` | = help: the following other types implement trait `Expression`: Bound<T> SelectInt note: required for `&str` to implement `AsExpression<Integer>` --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:26:13 | LL | impl<T, ST> AsExpression<ST> for T | ^^^^^^^^^^^^^^^^ ^ LL | where LL | T: Expression<SqlType = ST>, | ------------------------ unsatisfied trait bound introduced here ``` Note how that mentions `&str: Expression` before and now mentions `&str: AsExpression<Integer>` instead which is much more helpful for users. Open points for further changes before stabilization: * We likely want to move the attribute to the `#[diagnostic]` namespace to relax the guarantees given? * How does it interact with the new trait solver?
a08e09f
to
9b45cfd
Compare
I've adjusted the tests. It should work now. I will try to open a PR for moving the attribute to the |
@bors r=compiler-errors,estebank |
…mpiler-errors Rollup of 7 pull requests Successful merges: - rust-lang#123709 (Update documentation related to the recent cmd.exe fix) - rust-lang#124304 (revise the interpretation of ReadDir for HermitOS) - rust-lang#124708 (Actually use the `#[do_not_recommend]` attribute if present) - rust-lang#125252 (Add `#[inline]` to float `Debug` fallback used by `cfg(no_fp_fmt_parse)`) - rust-lang#125261 (crashes: add more) - rust-lang#125270 (Followup fixes from rust-lang#123344) - rust-lang#125275 (Migrate `run-make/rustdoc-scrape-examples-test` to new `rmake.rs`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#124708 - weiznich:implement_do_not_recommend, r=compiler-errors,estebank Actually use the `#[do_not_recommend]` attribute if present This change tweaks the error message generation to actually use the `#[do_not_recommend]` attribute if present by just skipping the marked trait impl in favour of the parent impl. It also adds a compile test for this behaviour. Without this change the test would output the following error: ``` error[E0277]: the trait bound `&str: Expression` is not satisfied --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:53:15 | LL | SelectInt.check("bar"); | ^^^^^ the trait `Expression` is not implemented for `&str`, which is required by `&str: AsExpression<Integer>` | = help: the following other types implement trait `Expression`: Bound<T> SelectInt note: required for `&str` to implement `AsExpression<Integer>` --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:26:13 | LL | impl<T, ST> AsExpression<ST> for T | ^^^^^^^^^^^^^^^^ ^ LL | where LL | T: Expression<SqlType = ST>, | ------------------------ unsatisfied trait bound introduced here ``` Note how that mentions `&str: Expression` before and now mentions `&str: AsExpression<Integer>` instead which is much more helpful for users. Open points for further changes before stabilization: * We likely want to move the attribute to the `#[diagnostic]` namespace to relax the guarantees given? * How does it interact with the new trait solver? r? `@estebank`
This change tweaks the error message generation to actually use the
#[do_not_recommend]
attribute if present by just skipping the marked trait impl in favour of the parent impl. It also adds a compile test for this behaviour. Without this change the test would output the following error:Note how that mentions
&str: Expression
before and now mentions&str: AsExpression<Integer>
instead which is much more helpful for users.Open points for further changes before stabilization:
#[diagnostic]
namespace to relax the guarantees given?r? @estebank