Skip to content

Commit 7be359e

Browse files
authored
Rollup merge of #100019 - TaKO8Ki:suggest-boxed-trait-objects-instead-of-impl-trait, r=compiler-errors
Revive suggestions for boxed trait objects instead of impl Trait The suggestion implemented in #75608 was not working properly, so I fixed it.
2 parents 9ee22ff + 82f2c08 commit 7be359e

8 files changed

+217
-29
lines changed

compiler/rustc_typeck/src/check/_match.rs

+35-28
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_errors::{Applicability, MultiSpan};
44
use rustc_hir::{self as hir, ExprKind};
55
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
66
use rustc_infer::traits::Obligation;
7-
use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
7+
use rustc_middle::ty::{self, ToPredicate, Ty};
88
use rustc_span::Span;
99
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1010
use rustc_trait_selection::traits::{
@@ -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
482-
&& self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types()
483-
&& self.can_coerce(outer_ty, expected) =>
485+
&& self.return_type_has_opaque
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,

src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr

+29
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ LL | | 1u32
5252
LL | | }
5353
| |_____- `if` and `else` have incompatible types
5454
|
55+
help: you could change the return type to be a boxed trait object
56+
|
57+
LL | fn qux() -> Box<dyn std::fmt::Display> {
58+
| ~~~~~~~ +
59+
help: if you change the return type to expect trait objects, box the returned expressions
60+
|
61+
LL ~ Box::new(0i32)
62+
LL | } else {
63+
LL ~ Box::new(1u32)
64+
|
5565
help: change the type of the numeric literal from `u32` to `i32`
5666
|
5767
LL | 1i32
@@ -114,6 +124,15 @@ LL | | _ => 2u32,
114124
LL | | }
115125
| |_____- `match` arms have incompatible types
116126
|
127+
help: you could change the return type to be a boxed trait object
128+
|
129+
LL | fn dog() -> Box<dyn std::fmt::Display> {
130+
| ~~~~~~~ +
131+
help: if you change the return type to expect trait objects, box the returned expressions
132+
|
133+
LL ~ 0 => Box::new(0i32),
134+
LL ~ 1 => Box::new(1u32),
135+
|
117136
help: change the type of the numeric literal from `u32` to `i32`
118137
|
119138
LL | 1 => 1i32,
@@ -131,6 +150,16 @@ LL | | 1u32
131150
LL | | }
132151
| |_____- `if` and `else` have incompatible types
133152
|
153+
help: you could change the return type to be a boxed trait object
154+
|
155+
LL | fn apt() -> Box<dyn std::fmt::Display> {
156+
| ~~~~~~~ +
157+
help: if you change the return type to expect trait objects, box the returned expressions
158+
|
159+
LL ~ Box::new(0i32)
160+
LL | } else {
161+
LL ~ Box::new(1u32)
162+
|
134163
help: change the type of the numeric literal from `u32` to `i32`
135164
|
136165
LL | 1i32
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`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
struct S;
6+
struct Y;
7+
8+
trait Trait {}
9+
10+
impl Trait for S {}
11+
impl Trait for Y {}
12+
13+
fn foo() -> Box<dyn Trait> {
14+
if true {
15+
Box::new(S)
16+
} else {
17+
Box::new(Y) //~ ERROR `if` and `else` have incompatible types
18+
}
19+
}
20+
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+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
struct S;
6+
struct Y;
7+
8+
trait Trait {}
9+
10+
impl Trait for S {}
11+
impl Trait for Y {}
12+
13+
fn foo() -> impl Trait {
14+
if true {
15+
S
16+
} else {
17+
Y //~ ERROR `if` and `else` have incompatible types
18+
}
19+
}
20+
21+
fn bar() -> impl Trait {
22+
match true {
23+
true => S,
24+
false => Y, //~ ERROR `match` arms have incompatible types
25+
}
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0308]: `if` and `else` have incompatible types
2+
--> $DIR/suggest-boxed-trait-objects-instead-of-impl-trait.rs:17: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+
help: you could change the return type to be a boxed trait object
14+
|
15+
LL | fn foo() -> Box<dyn Trait> {
16+
| ~~~~~~~ +
17+
help: if you change the return type to expect trait objects, box the returned expressions
18+
|
19+
LL ~ Box::new(S)
20+
LL | } else {
21+
LL ~ Box::new(Y)
22+
|
23+
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
46+
47+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)