-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #106477 - Nathan-Fenner:nathanf/refined-error-span-tr…
…ait-impl, r=compiler-errors Refine error spans for "The trait bound `T: Trait` is not satisfied" when passing literal structs/tuples This PR adds a new heuristic which refines the error span reported for "`T: Trait` is not satisfied" errors, by "drilling down" into individual fields of structs/enums/tuples to point to the "problematic" value. Here's a self-contained example of the difference in error span: ```rs struct Burrito<Filling> { filling: Filling, } impl <Filling: Delicious> Delicious for Burrito<Filling> {} fn eat_delicious_food<Food: Delicious>(food: Food) {} fn will_type_error() { eat_delicious_food(Burrito { filling: Kale }); // ^~~~~~~~~~~~~~~~~~~~~~~~~ (before) The trait bound `Kale: Delicious` is not satisfied // ^~~~ (after) The trait bound `Kale: Delicious` is not satisfied } ``` (kale is fine, this is just a silly food-based example) Before this PR, the error span is identified as the entire argument to the generic function `eat_delicious_food`. However, since only `Kale` is the "problematic" part, we can point at it specifically. In particular, the primary error message itself mentions the missing `Kale: Delicious` trait bound, so it's much clearer if this part is called out explicitly. --- The _existing_ heuristic tries to label the right function argument in `point_at_arg_if_possible`. It goes something like this: - Look at the broken base trait `Food: Delicious` and find which generics it mentions (in this case, only `Food`) - Look at the parameter type definitions and find which of them mention `Filling` (in this case, only `food`) - If there is exactly one relevant parameter, label the corresponding argument with the error span, instead of the entire call This PR extends this heuristic by further refining the resulting expression span in the new `point_at_specific_expr_if_possible` function. For each `impl` in the (broken) chain, we apply the following strategy: The strategy to determine this span involves connecting information about our generic `impl` with information about our (struct) type and the (struct) literal expression: - Find the `impl` (`impl <Filling: Delicious> Delicious for Burrito<Filling>`) that links our obligation (`Kale: Delicious`) with the parent obligation (`Burrito<Kale>: Delicious`) - Find the "original" predicate constraint in the impl (`Filling: Delicious`) which produced our obligation. - Find all of the generics that are mentioned in the predicate (`Filling`). - Examine the `Self` type in the `impl`, and see which of its type argument(s) mention any of those generics. - Examing the definition for the `Self` type, and identify (for each of its variants) if there's a unique field which uses those generic arguments. - If there is a unique field mentioning the "blameable" arguments, use that field for the error span. Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize. This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be only a partial success - but it cannot be refined even further. --- I added a new test file which exercises this new behavior. A few existing tests were affected, since their error spans are now different. In one case, this leads to a different code suggestion for the autofix - although the new suggestion isn't _wrong_, it is different from what used to be. This change doesn't create any new errors or remove any existing ones, it just adjusts the spans where they're presented. --- Some considerations: right now, this check occurs in addition to some similar logic in `adjust_fulfillment_error_for_expr_obligation` function, which tidies up various kinds of error spans (not just trait-fulfillment error). It's possible that this new code would be better integrated into that function (or another one) - but I haven't looked into this yet. Although this code only occurs when there's a type error, it's definitely not as efficient as possible. In particular, there are definitely some cases where it degrades to quadratic performance (e.g. for a trait `impl` with 100+ generic parameters or 100 levels deep nesting of generic types). I'm not sure if these are realistic enough to worry about optimizing yet. There's also still a lot of repetition in some of the logic, where the behavior for different types (namely, `struct` vs `enum` variant) is _similar_ but not the same. --- I think the biggest win here is better targeting for tuples; in particular, if you're using tuples + traits to express variadic-like functions, the compiler can't tell you which part of a tuple has the wrong type, since the span will cover the entire argument. This change allows the individual field in the tuple to be highlighted, as in this example: ``` // NEW LL | want(Wrapper { value: (3, q) }); | ---- ^ the trait `T3` is not implemented for `Q` // OLD LL | want(Wrapper { value: (3, q) }); | ---- ^~~~~~~~~~~~~~~~~~~~~~~~~ the trait `T3` is not implemented for `Q` ``` Especially with large tuples, the existing error spans are not very effective at quickly narrowing down the source of the problem.
- Loading branch information
Showing
14 changed files
with
1,120 additions
and
81 deletions.
There are no files selected for viewing
457 changes: 457 additions & 0 deletions
457
compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod _impl; | ||
mod adjust_fulfillment_errors; | ||
mod arg_matrix; | ||
mod checks; | ||
mod suggestions; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
trait T1 {} | ||
trait T2 {} | ||
trait T3 {} | ||
trait T4 {} | ||
|
||
impl<B: T2> T1 for Wrapper<B> {} | ||
|
||
impl T2 for i32 {} | ||
impl T3 for i32 {} | ||
|
||
impl<A: T3> T2 for Burrito<A> {} | ||
|
||
struct Wrapper<W> { | ||
value: W, | ||
} | ||
|
||
struct Burrito<F> { | ||
filling: F, | ||
} | ||
|
||
fn want<V: T1>(_x: V) {} | ||
|
||
fn example<Q>(q: Q) { | ||
want(Wrapper { value: Burrito { filling: q } }); | ||
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277] | ||
} | ||
|
||
fn main() {} |
35 changes: 35 additions & 0 deletions
35
tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
error[E0277]: the trait bound `Q: T3` is not satisfied | ||
--> $DIR/blame-trait-error.rs:24:46 | ||
| | ||
LL | want(Wrapper { value: Burrito { filling: q } }); | ||
| ---- ^ the trait `T3` is not implemented for `Q` | ||
| | | ||
| required by a bound introduced by this call | ||
| | ||
note: required for `Burrito<Q>` to implement `T2` | ||
--> $DIR/blame-trait-error.rs:11:13 | ||
| | ||
LL | impl<A: T3> T2 for Burrito<A> {} | ||
| -- ^^ ^^^^^^^^^^ | ||
| | | ||
| unsatisfied trait bound introduced here | ||
note: required for `Wrapper<Burrito<Q>>` to implement `T1` | ||
--> $DIR/blame-trait-error.rs:6:13 | ||
| | ||
LL | impl<B: T2> T1 for Wrapper<B> {} | ||
| -- ^^ ^^^^^^^^^^ | ||
| | | ||
| unsatisfied trait bound introduced here | ||
note: required by a bound in `want` | ||
--> $DIR/blame-trait-error.rs:21:12 | ||
| | ||
LL | fn want<V: T1>(_x: V) {} | ||
| ^^ required by this bound in `want` | ||
help: consider restricting type parameter `Q` | ||
| | ||
LL | fn example<Q: T3>(q: Q) { | ||
| ++++ | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0277`. |
Oops, something went wrong.