Skip to content

Commit 316479a

Browse files
committed
find the right error source when we can't unsize
1 parent cb9d064 commit 316479a

File tree

7 files changed

+77
-45
lines changed

7 files changed

+77
-45
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,9 +2035,7 @@ impl AsCoercionSite for hir::Arm<'_> {
20352035
}
20362036
}
20372037

2038-
/// Recursively visit goals to decide whether an unsizing is possible.
2039-
/// `Break`s when it isn't, and an error should be raised.
2040-
/// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
2038+
/// Visitor that checks whether an unsizing coercion is
20412039
struct CoerceVisitor<'a, 'tcx> {
20422040
fcx: &'a FnCtxt<'a, 'tcx>,
20432041
span: Span,
@@ -2055,22 +2053,36 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> {
20552053
return ControlFlow::Continue(());
20562054
};
20572055

2056+
// Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
2057+
// Otherwise there's nothing to do.
20582058
if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
20592059
&& !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized)
20602060
{
20612061
return ControlFlow::Continue(());
20622062
}
20632063

20642064
match goal.result() {
2065+
// If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
20652066
Ok(Certainty::Yes) => ControlFlow::Continue(()),
2066-
Err(NoSolution) => ControlFlow::Break(()),
2067+
Err(NoSolution) => {
2068+
// Even if we find no solution, continue recursing if we find a single candidate
2069+
// for which we're shallowly certain it holds to get the right error source.
2070+
if let [only_candidate] = &goal.candidates()[..]
2071+
&& only_candidate.shallow_certainty() == Certainty::Yes
2072+
{
2073+
only_candidate.visit_nested_no_probe(self)
2074+
} else {
2075+
ControlFlow::Break(())
2076+
}
2077+
}
20672078
Ok(Certainty::Maybe { .. }) => {
20682079
// FIXME: structurally normalize?
20692080
if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)
20702081
&& let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()
20712082
&& let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind()
20722083
&& self.fcx.type_var_is_sized(vid)
20732084
{
2085+
// Here we detected that an unszing to `dyn Trait` is possible, yay!
20742086
ControlFlow::Continue(())
20752087
} else if let Some(cand) = goal.unique_applicable_candidate()
20762088
&& cand.shallow_certainty() == Certainty::Yes

tests/ui/dst/dst-object-from-unsized-type.current.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LL + fn test1<T: Foo>(t: &T) {
1414
|
1515

1616
error[E0277]: the size for values of type `T` cannot be known at compilation time
17-
--> $DIR/dst-object-from-unsized-type.rs:16:23
17+
--> $DIR/dst-object-from-unsized-type.rs:17:23
1818
|
1919
LL | fn test2<T: ?Sized + Foo>(t: &T) {
2020
| - this type parameter needs to be `Sized`
@@ -29,7 +29,7 @@ LL + fn test2<T: Foo>(t: &T) {
2929
|
3030

3131
error[E0277]: the size for values of type `str` cannot be known at compilation time
32-
--> $DIR/dst-object-from-unsized-type.rs:21:28
32+
--> $DIR/dst-object-from-unsized-type.rs:23:28
3333
|
3434
LL | let _: &[&dyn Foo] = &["hi"];
3535
| ^^^^ doesn't have a size known at compile-time
@@ -38,7 +38,7 @@ LL | let _: &[&dyn Foo] = &["hi"];
3838
= note: required for the cast from `&'static str` to `&dyn Foo`
3939

4040
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
41-
--> $DIR/dst-object-from-unsized-type.rs:26:23
41+
--> $DIR/dst-object-from-unsized-type.rs:29:23
4242
|
4343
LL | let _: &dyn Foo = x as &dyn Foo;
4444
| ^ doesn't have a size known at compile-time
Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,57 @@
1-
error[E0308]: mismatched types
1+
error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
22
--> $DIR/dst-object-from-unsized-type.rs:11:23
33
|
44
LL | fn test1<T: ?Sized + Foo>(t: &T) {
5-
| - found this type parameter
5+
| - this type parameter needs to be `Sized`
66
LL | let u: &dyn Foo = t;
7-
| -------- ^ expected `&dyn Foo`, found `&T`
8-
| |
9-
| expected due to this
10-
|
11-
= note: expected reference `&dyn Foo`
12-
found reference `&T`
13-
= help: type parameters must be constrained to match other types
14-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
7+
| ^ within `T`, the trait `Sized` is not implemented for `T`
8+
|
9+
= note: required because it appears within the type `T`
10+
= note: required for `&T` to implement `CoerceUnsized<&dyn Foo>`
11+
= note: required for the cast from `&T` to `&dyn Foo`
12+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
13+
|
14+
LL - fn test1<T: ?Sized + Foo>(t: &T) {
15+
LL + fn test1<T: Foo>(t: &T) {
16+
|
1517

16-
error[E0605]: non-primitive cast: `&T` as `&dyn Foo`
17-
--> $DIR/dst-object-from-unsized-type.rs:16:23
18+
error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
19+
--> $DIR/dst-object-from-unsized-type.rs:17:23
1820
|
21+
LL | fn test2<T: ?Sized + Foo>(t: &T) {
22+
| - this type parameter needs to be `Sized`
1923
LL | let v: &dyn Foo = t as &dyn Foo;
20-
| ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
24+
| ^ within `T`, the trait `Sized` is not implemented for `T`
25+
|
26+
= note: required because it appears within the type `T`
27+
= note: required for `&T` to implement `CoerceUnsized<&dyn Foo>`
28+
= note: required for the cast from `&T` to `&dyn Foo`
29+
help: consider removing the `?Sized` bound to make the type parameter `Sized`
30+
|
31+
LL - fn test2<T: ?Sized + Foo>(t: &T) {
32+
LL + fn test2<T: Foo>(t: &T) {
33+
|
2134

22-
error[E0308]: mismatched types
23-
--> $DIR/dst-object-from-unsized-type.rs:21:28
35+
error[E0277]: the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str`
36+
--> $DIR/dst-object-from-unsized-type.rs:23:28
2437
|
2538
LL | let _: &[&dyn Foo] = &["hi"];
26-
| ^^^^ expected `&dyn Foo`, found `&str`
39+
| ^^^^ within `str`, the trait `Sized` is not implemented for `str`
2740
|
28-
= note: expected reference `&dyn Foo`
29-
found reference `&'static str`
30-
= help: `str` implements `Foo` so you could box the found value and coerce it to the trait object `Box<dyn Foo>`, you will have to change the expected type as well
41+
= note: `str` is considered to contain a `[u8]` slice for auto trait purposes
42+
= note: required for `&str` to implement `CoerceUnsized<&dyn Foo>`
43+
= note: required for the cast from `&'static str` to `&dyn Foo`
3144

32-
error[E0605]: non-primitive cast: `&[u8]` as `&dyn Foo`
33-
--> $DIR/dst-object-from-unsized-type.rs:26:23
45+
error[E0277]: the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]`
46+
--> $DIR/dst-object-from-unsized-type.rs:29:23
3447
|
3548
LL | let _: &dyn Foo = x as &dyn Foo;
36-
| ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
49+
| ^ within `[u8]`, the trait `Sized` is not implemented for `[u8]`
50+
|
51+
= note: required because it appears within the type `[u8]`
52+
= note: required for `&[u8]` to implement `CoerceUnsized<&dyn Foo>`
53+
= note: required for the cast from `&[u8]` to `&dyn Foo`
3754

3855
error: aborting due to 4 previous errors
3956

40-
Some errors have detailed explanations: E0308, E0605.
41-
For more information about an error, try `rustc --explain E0308`.
57+
For more information about this error, try `rustc --explain E0277`.

tests/ui/dst/dst-object-from-unsized-type.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,26 @@ impl Foo for [u8] {}
99

1010
fn test1<T: ?Sized + Foo>(t: &T) {
1111
let u: &dyn Foo = t;
12-
//~^ ERROR the size for values of type
12+
//[current]~^ ERROR the size for values of type
13+
//[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
1314
}
1415

1516
fn test2<T: ?Sized + Foo>(t: &T) {
1617
let v: &dyn Foo = t as &dyn Foo;
17-
//~^ ERROR the size for values of type
18+
//[current]~^ ERROR the size for values of type
19+
//[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T`
1820
}
1921

2022
fn test3() {
2123
let _: &[&dyn Foo] = &["hi"];
22-
//~^ ERROR the size for values of type
24+
//[current]~^ ERROR the size for values of type
25+
//[next]~^^ ERROR the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str`
2326
}
2427

2528
fn test4(x: &[u8]) {
2629
let _: &dyn Foo = x as &dyn Foo;
27-
//~^ ERROR the size for values of type
30+
//[current]~^ ERROR the size for values of type
31+
//[next]~^^ ERROR the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]`
2832
}
2933

3034
fn main() { }

tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
//@[current] check-pass
55
// Test from trait-system-refactor-initiative#241:
66
// Used to ICE in mir typeck because of ambiguity in the new solver.
7-
// The wrong (first) trait bound was selected.
8-
// This is fixed with new logic for unsizing coercions that's independent from that of the old solver, which this test verifies.
7+
// In the old solver, the first super trait was
98

109
trait Super<T> {}
1110
trait Trait<T>: Super<T> + for<'hr> Super<&'hr ()> {}
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
error[E0308]: mismatched types
1+
error[E0277]: the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied
22
--> $DIR/higher-ranked-upcasting-ub.rs:22:5
33
|
4-
LL | fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> {
5-
| ----------------------------------- expected `&dyn for<'a, 'b> Supertrait<'a, 'b>` because of return type
64
LL | x
7-
| ^ expected trait `Supertrait`, found trait `Subtrait`
5+
| ^ the trait `Unsize<dyn for<'a, 'b> Supertrait<'a, 'b>>` is not implemented for `dyn for<'a> Subtrait<'a, 'a>`
86
|
9-
= note: expected reference `&dyn for<'a, 'b> Supertrait<'a, 'b>`
10-
found reference `&dyn for<'a> Subtrait<'a, 'a>`
7+
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
8+
= note: required for `&dyn for<'a> Subtrait<'a, 'a>` to implement `CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>`
9+
= note: required for the cast from `&dyn for<'a> Subtrait<'a, 'a>` to `&dyn for<'a, 'b> Supertrait<'a, 'b>`
1110

1211
error: aborting due to 1 previous error
1312

14-
For more information about this error, try `rustc --explain E0308`.
13+
For more information about this error, try `rustc --explain E0277`.

tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ impl<'a> Supertrait<'a, 'a> for () {
1919
}
2020
impl<'a> Subtrait<'a, 'a> for () {}
2121
fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> {
22-
x //~ ERROR mismatched types
22+
x
2323
//[current]~^ ERROR mismatched types
24+
//[current]~| ERROR mismatched types
25+
//[next]~^^^ ERROR the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied
2426
}
2527

2628
fn transmute<'a, 'b>(x: &'a str) -> &'b str {

0 commit comments

Comments
 (0)