Skip to content

Commit ac56a2b

Browse files
committed
Suggest boxing if then expr if that solves divergent arms
When encountering ```rust let _ = if true { Struct } else { foo() // -> Box<dyn Trait> }; ``` if `Struct` implements `Trait`, suggest boxing the then arm tail expression. Part of rust-lang#102629.
1 parent 390ef9b commit ac56a2b

File tree

4 files changed

+63
-1
lines changed

4 files changed

+63
-1
lines changed

compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs

+32
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,38 @@ impl<T> Trait<T> for X {
330330
);
331331
}
332332
}
333+
(ty::Adt(_, _), ty::Adt(def, args))
334+
if let ObligationCauseCode::IfExpression(cause) = cause.code()
335+
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
336+
&& let Some(then) = blk.expr
337+
&& def.is_box()
338+
&& let boxed_ty = args.type_at(0)
339+
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
340+
&& let Some(def_id) = t.principal_def_id()
341+
&& let mut impl_def_ids = vec![]
342+
&& let _ =
343+
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
344+
impl_def_ids.push(did)
345+
})
346+
&& let [_] = &impl_def_ids[..] =>
347+
{
348+
// We have divergent if/else arms where the expected value is a type that
349+
// implements the trait of the found boxed trait object.
350+
diag.multipart_suggestion(
351+
format!(
352+
"`{}` implements `{}` so you can box it to coerce to the trait \
353+
object `{}`",
354+
values.expected,
355+
tcx.item_name(def_id),
356+
values.found,
357+
),
358+
vec![
359+
(then.span.shrink_to_lo(), "Box::new(".to_string()),
360+
(then.span.shrink_to_hi(), ")".to_string()),
361+
],
362+
MachineApplicable,
363+
);
364+
}
333365
_ => {}
334366
}
335367
debug!(

tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ fn foo() -> Box<dyn Trait> {
66
Box::new(Struct)
77
}
88
fn main() {
9+
let _ = if true {
10+
Box::new(Struct)
11+
} else {
12+
foo() //~ ERROR E0308
13+
};
914
let _ = if true {
1015
foo()
1116
} else {

tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ fn foo() -> Box<dyn Trait> {
66
Box::new(Struct)
77
}
88
fn main() {
9+
let _ = if true {
10+
Struct
11+
} else {
12+
foo() //~ ERROR E0308
13+
};
914
let _ = if true {
1015
foo()
1116
} else {

tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr

+21-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@ error[E0308]: `if` and `else` have incompatible types
33
|
44
LL | let _ = if true {
55
| _____________-
6+
LL | | Struct
7+
| | ------ expected because of this
8+
LL | | } else {
9+
LL | | foo()
10+
| | ^^^^^ expected `Struct`, found `Box<dyn Trait>`
11+
LL | | };
12+
| |_____- `if` and `else` have incompatible types
13+
|
14+
= note: expected struct `Struct`
15+
found struct `Box<dyn Trait>`
16+
help: `Struct` implements `Trait` so you can box it to coerce to the trait object `Box<dyn Trait>`
17+
|
18+
LL | Box::new(Struct)
19+
| +++++++++ +
20+
21+
error[E0308]: `if` and `else` have incompatible types
22+
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:17:9
23+
|
24+
LL | let _ = if true {
25+
| _____________-
626
LL | | foo()
727
| | ----- expected because of this
828
LL | | } else {
@@ -19,6 +39,6 @@ help: store this in the heap by calling `Box::new`
1939
LL | Box::new(Struct)
2040
| +++++++++ +
2141

22-
error: aborting due to 1 previous error
42+
error: aborting due to 2 previous errors
2343

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

0 commit comments

Comments
 (0)