Skip to content

Commit 82f2c08

Browse files
committed
fix wrong suggestions for boxed trait objects instead of impl trait
1 parent fc43bd6 commit 82f2c08

7 files changed

+122
-31
lines changed

compiler/rustc_typeck/src/check/_match.rs

+33-26
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9494
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
9595
all_arms_diverge &= self.diverges.get();
9696

97-
let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
97+
let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| {
98+
self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected)
99+
});
98100

99101
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
100102
(Some(blk.hir_id), self.find_block_span(blk))
@@ -473,43 +475,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
473475
// provide a structured suggestion in that case.
474476
pub(crate) fn opt_suggest_box_span(
475477
&self,
476-
outer_ty: Ty<'tcx>,
478+
first_ty: Ty<'tcx>,
479+
second_ty: Ty<'tcx>,
477480
orig_expected: Expectation<'tcx>,
478481
) -> Option<Span> {
479482
match orig_expected {
480483
Expectation::ExpectHasType(expected)
481484
if self.in_tail_expr
482485
&& self.return_type_has_opaque
483-
&& self.can_coerce(outer_ty, expected) =>
486+
&& self.can_coerce(first_ty, expected)
487+
&& self.can_coerce(second_ty, expected) =>
484488
{
485489
let obligations = self.fulfillment_cx.borrow().pending_obligations();
486490
let mut suggest_box = !obligations.is_empty();
487-
for o in obligations {
488-
match o.predicate.kind().skip_binder() {
489-
ty::PredicateKind::Trait(t) => {
490-
let pred =
491-
ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
492-
trait_ref: ty::TraitRef {
493-
def_id: t.def_id(),
494-
substs: self.tcx.mk_substs_trait(outer_ty, &[]),
491+
'outer: for o in obligations {
492+
for outer_ty in &[first_ty, second_ty] {
493+
match o.predicate.kind().skip_binder() {
494+
ty::PredicateKind::Trait(t) => {
495+
let pred = ty::Binder::dummy(ty::PredicateKind::Trait(
496+
ty::TraitPredicate {
497+
trait_ref: ty::TraitRef {
498+
def_id: t.def_id(),
499+
substs: self.tcx.mk_substs_trait(*outer_ty, &[]),
500+
},
501+
constness: t.constness,
502+
polarity: t.polarity,
495503
},
496-
constness: t.constness,
497-
polarity: t.polarity,
498-
}));
499-
let obl = Obligation::new(
500-
o.cause.clone(),
501-
self.param_env,
502-
pred.to_predicate(self.tcx),
503-
);
504-
suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
505-
if !suggest_box {
506-
// We've encountered some obligation that didn't hold, so the
507-
// return expression can't just be boxed. We don't need to
508-
// evaluate the rest of the obligations.
509-
break;
504+
));
505+
let obl = Obligation::new(
506+
o.cause.clone(),
507+
self.param_env,
508+
pred.to_predicate(self.tcx),
509+
);
510+
suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
511+
if !suggest_box {
512+
// We've encountered some obligation that didn't hold, so the
513+
// return expression can't just be boxed. We don't need to
514+
// evaluate the rest of the obligations.
515+
break 'outer;
516+
}
510517
}
518+
_ => {}
511519
}
512-
_ => {}
513520
}
514521
}
515522
// If all the obligations hold (or there are no obligations) the tail expression

compiler/rustc_typeck/src/check/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10021002
let else_ty = self.check_expr_with_expectation(else_expr, expected);
10031003
let else_diverges = self.diverges.get();
10041004

1005-
let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected);
1005+
let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected);
10061006
let if_cause = self.if_cause(
10071007
sp,
10081008
cond_expr.span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
struct S;
2+
struct Y;
3+
4+
trait Trait {}
5+
6+
impl Trait for Y {}
7+
8+
fn foo() -> impl Trait {
9+
if true {
10+
S
11+
} else {
12+
Y //~ ERROR `if` and `else` have incompatible types
13+
}
14+
}
15+
16+
fn bar() -> impl Trait {
17+
match true {
18+
true => S,
19+
false => Y, //~ ERROR `match` arms have incompatible types
20+
}
21+
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0308]: `if` and `else` have incompatible types
2+
--> $DIR/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs:12:9
3+
|
4+
LL | / if true {
5+
LL | | S
6+
| | - expected because of this
7+
LL | | } else {
8+
LL | | Y
9+
| | ^ expected struct `S`, found struct `Y`
10+
LL | | }
11+
| |_____- `if` and `else` have incompatible types
12+
13+
error[E0308]: `match` arms have incompatible types
14+
--> $DIR/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs:19:18
15+
|
16+
LL | / match true {
17+
LL | | true => S,
18+
| | - this is found to be of type `S`
19+
LL | | false => Y,
20+
| | ^ expected struct `S`, found struct `Y`
21+
LL | | }
22+
| |_____- `match` arms have incompatible types
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.fixed

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ trait Trait {}
1010
impl Trait for S {}
1111
impl Trait for Y {}
1212

13-
fn baz() -> Box<dyn Trait> {
13+
fn foo() -> Box<dyn Trait> {
1414
if true {
1515
Box::new(S)
1616
} else {
1717
Box::new(Y) //~ ERROR `if` and `else` have incompatible types
1818
}
1919
}
2020

21+
fn bar() -> Box<dyn Trait> {
22+
match true {
23+
true => Box::new(S),
24+
false => Box::new(Y), //~ ERROR `match` arms have incompatible types
25+
}
26+
}
27+
2128
fn main() {}

src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ trait Trait {}
1010
impl Trait for S {}
1111
impl Trait for Y {}
1212

13-
fn baz() -> impl Trait {
13+
fn foo() -> impl Trait {
1414
if true {
1515
S
1616
} else {
1717
Y //~ ERROR `if` and `else` have incompatible types
1818
}
1919
}
2020

21+
fn bar() -> impl Trait {
22+
match true {
23+
true => S,
24+
false => Y, //~ ERROR `match` arms have incompatible types
25+
}
26+
}
27+
2128
fn main() {}

src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr

+23-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | | }
1212
|
1313
help: you could change the return type to be a boxed trait object
1414
|
15-
LL | fn baz() -> Box<dyn Trait> {
15+
LL | fn foo() -> Box<dyn Trait> {
1616
| ~~~~~~~ +
1717
help: if you change the return type to expect trait objects, box the returned expressions
1818
|
@@ -21,6 +21,27 @@ LL | } else {
2121
LL ~ Box::new(Y)
2222
|
2323

24-
error: aborting due to previous error
24+
error[E0308]: `match` arms have incompatible types
25+
--> $DIR/suggest-boxed-trait-objects-instead-of-impl-trait.rs:24:18
26+
|
27+
LL | / match true {
28+
LL | | true => S,
29+
| | - this is found to be of type `S`
30+
LL | | false => Y,
31+
| | ^ expected struct `S`, found struct `Y`
32+
LL | | }
33+
| |_____- `match` arms have incompatible types
34+
|
35+
help: you could change the return type to be a boxed trait object
36+
|
37+
LL | fn bar() -> Box<dyn Trait> {
38+
| ~~~~~~~ +
39+
help: if you change the return type to expect trait objects, box the returned expressions
40+
|
41+
LL ~ true => Box::new(S),
42+
LL ~ false => Box::new(Y),
43+
|
44+
45+
error: aborting due to 2 previous errors
2546

2647
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)