Skip to content

Commit ec1dd21

Browse files
committed
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?
1 parent 80420a6 commit ec1dd21

File tree

7 files changed

+192
-1
lines changed

7 files changed

+192
-1
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
418418
match bound_predicate.skip_binder() {
419419
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
420420
let trait_predicate = bound_predicate.rebind(trait_predicate);
421-
let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
421+
let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);
422422

423423
// Let's use the root obligation as the main message, when we care about the
424424
// most general case ("X doesn't implement Pattern<'_>") over the case that
@@ -464,6 +464,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
464464
root_obligation,
465465
)
466466
} else {
467+
let trait_predicate = self.apply_do_not_recommend(&mut trait_predicate, &mut obligation);
467468
(trait_predicate, &obligation)
468469
};
469470
let trait_ref = main_trait_predicate.to_poly_trait_ref();
@@ -1000,6 +1001,35 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
10001001
err.emit()
10011002
}
10021003

1004+
fn apply_do_not_recommend(
1005+
&self,
1006+
trait_predicate: &'_ mut ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
1007+
obligation: &'_ mut PredicateObligation<'tcx>,
1008+
) -> ty::Binder<'tcx, ty::TraitPredicate<'tcx>> {
1009+
let mut temp_trait_predicate = trait_predicate.clone();
1010+
let mut base_cause = obligation.cause.code().clone();
1011+
loop {
1012+
// we first need to check whether the current impl
1013+
// is marked as `#[do_not_recommend]`
1014+
if let ObligationCauseCode::ImplDerivedObligation(ref c) = base_cause {
1015+
if self.tcx.has_attr(c.impl_or_alias_def_id, sym::do_not_recommend) {
1016+
let code = (*c.derived.parent_code).clone();
1017+
obligation.cause.map_code(|_| code);
1018+
obligation.predicate = c.derived.parent_trait_pred.to_predicate(self.tcx);
1019+
temp_trait_predicate = c.derived.parent_trait_pred.clone();
1020+
*trait_predicate = c.derived.parent_trait_pred;
1021+
}
1022+
}
1023+
if let Some((parent_cause, _parent_pred)) = base_cause.parent() {
1024+
base_cause = parent_cause.clone();
1025+
} else {
1026+
break;
1027+
}
1028+
}
1029+
1030+
temp_trait_predicate
1031+
}
1032+
10031033
fn emit_specialized_closure_kind_error(
10041034
&self,
10051035
obligation: &PredicateObligation<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
2+
--> $DIR/as_expression.rs:57:15
3+
|
4+
LL | SelectInt.check("bar");
5+
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
6+
|
7+
= help: the trait `AsExpression<Text>` is implemented for `&str`
8+
= help: for that trait implementation, expected `Text`, found `Integer`
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0277]: the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
2+
--> $DIR/as_expression.rs:57:21
3+
|
4+
LL | SelectInt.check("bar");
5+
| ----- ^^^^^ the trait `AsExpression<<SelectInt as Expression>::SqlType>` is not implemented for `&str`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `AsExpression<Text>` is implemented for `&str`
10+
note: required by a bound in `Foo::check`
11+
--> $DIR/as_expression.rs:48:12
12+
|
13+
LL | fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
14+
| ----- required by a bound in this associated function
15+
LL | where
16+
LL | T: AsExpression<Self::SqlType>,
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
18+
19+
error: the type `fn(&SelectInt, &str) -> <&str as AsExpression<<SelectInt as Expression>::SqlType>>::Expression` is not well-formed
20+
--> $DIR/as_expression.rs:57:15
21+
|
22+
LL | SelectInt.check("bar");
23+
| ^^^^^
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
5+
#![feature(do_not_recommend)]
6+
7+
pub trait Expression {
8+
type SqlType;
9+
}
10+
11+
pub trait AsExpression<ST> {
12+
type Expression: Expression<SqlType = ST>;
13+
}
14+
15+
pub struct Text;
16+
pub struct Integer;
17+
18+
pub struct Bound<T>(T);
19+
pub struct SelectInt;
20+
21+
impl Expression for SelectInt {
22+
type SqlType = Integer;
23+
}
24+
25+
impl<T> Expression for Bound<T> {
26+
type SqlType = T;
27+
}
28+
29+
#[do_not_recommend]
30+
impl<T, ST> AsExpression<ST> for T
31+
where
32+
T: Expression<SqlType = ST>,
33+
{
34+
type Expression = T;
35+
}
36+
37+
impl AsExpression<Integer> for i32 {
38+
type Expression = Bound<Integer>;
39+
}
40+
41+
impl AsExpression<Text> for &'_ str {
42+
type Expression = Bound<Text>;
43+
}
44+
45+
trait Foo: Expression + Sized {
46+
fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
47+
where
48+
T: AsExpression<Self::SqlType>,
49+
{
50+
todo!()
51+
}
52+
}
53+
54+
impl<T> Foo for T where T: Expression {}
55+
56+
fn main() {
57+
SelectInt.check("bar");
58+
//[next]~^ ERROR the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
59+
//[next]~| ERROR the type `fn(&SelectInt, &str) -> <&str as AsExpression<<SelectInt as Expression>::SqlType>>::Expression` is not well-formed
60+
//[current]~^^^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: the trait bound `(): Root` is not satisfied
2+
--> $DIR/stacked.rs:19:18
3+
|
4+
LL | needs_root::<()>();
5+
| ^^ the trait `Other` is not implemented for `()`, which is required by `(): Root`
6+
|
7+
note: required for `()` to implement `DontRecommend`
8+
--> $DIR/stacked.rs:14:9
9+
|
10+
LL | impl<T> DontRecommend for T where T: Other {}
11+
| ^^^^^^^^^^^^^ ^ ----- unsatisfied trait bound introduced here
12+
note: required for `()` to implement `Root`
13+
--> $DIR/stacked.rs:12:9
14+
|
15+
LL | impl<T> Root for T where T: DontRecommend {}
16+
| ^^^^ ^ ------------- unsatisfied trait bound introduced here
17+
note: required by a bound in `needs_root`
18+
--> $DIR/stacked.rs:16:18
19+
|
20+
LL | fn needs_root<T: Root>() {}
21+
| ^^^^ required by this bound in `needs_root`
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `(): Root` is not satisfied
2+
--> $DIR/stacked.rs:19:18
3+
|
4+
LL | needs_root::<()>();
5+
| ^^ the trait `Root` is not implemented for `()`
6+
|
7+
note: required by a bound in `needs_root`
8+
--> $DIR/stacked.rs:16:18
9+
|
10+
LL | fn needs_root<T: Root>() {}
11+
| ^^^^ required by this bound in `needs_root`
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
5+
#![feature(do_not_recommend)]
6+
7+
trait Root {}
8+
trait DontRecommend {}
9+
trait Other {}
10+
11+
#[do_not_recommend]
12+
impl<T> Root for T where T: DontRecommend {}
13+
14+
impl<T> DontRecommend for T where T: Other {}
15+
16+
fn needs_root<T: Root>() {}
17+
18+
fn main() {
19+
needs_root::<()>();
20+
//~^ ERROR the trait bound `(): Root` is not satisfied
21+
}

0 commit comments

Comments
 (0)