Skip to content

Commit 4a2b4be

Browse files
authored
Unrolled build for rust-lang#120261
Rollup merge of rust-lang#120261 - estebank:issue-102629, r=wesleywiser Provide structured suggestion to use trait objects in some cases of `if` arm type divergence ``` error[E0308]: `if` and `else` have incompatible types --> $DIR/suggest-box-on-divergent-if-else-arms.rs:15:9 | LL | let _ = if true { | _____________- LL | | Struct | | ------ expected because of this LL | | } else { LL | | foo() | | ^^^^^ expected `Struct`, found `Box<dyn Trait>` LL | | }; | |_____- `if` and `else` have incompatible types | = note: expected struct `Struct` found struct `Box<dyn Trait>` help: `Struct` implements `Trait` so you can box it to coerce to the trait object `Box<dyn Trait>` | LL | Box::new(Struct) | +++++++++ + error[E0308]: `if` and `else` have incompatible types --> $DIR/suggest-box-on-divergent-if-else-arms.rs:20:9 | LL | let _ = if true { | _____________- LL | | foo() | | ----- expected because of this LL | | } else { LL | | Struct | | ^^^^^^ expected `Box<dyn Trait>`, found `Struct` LL | | }; | |_____- `if` and `else` have incompatible types | = note: expected struct `Box<dyn Trait>` found struct `Struct` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html help: store this in the heap by calling `Box::new` | LL | Box::new(Struct) | +++++++++ + error[E0308]: `if` and `else` have incompatible types --> $DIR/suggest-box-on-divergent-if-else-arms.rs:25:9 | LL | fn bar() -> impl Trait { | ---------- the found opaque type ... LL | let _ = if true { | _____________- LL | | Struct | | ------ expected because of this LL | | } else { LL | | bar() | | ^^^^^ expected `Struct`, found opaque type LL | | }; | |_____- `if` and `else` have incompatible types | = note: expected struct `Struct` found opaque type `impl Trait` help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>` | LL ~ Box::new(Struct) as Box<dyn Trait> LL | } else { LL ~ Box::new(bar()) | error[E0308]: `if` and `else` have incompatible types --> $DIR/suggest-box-on-divergent-if-else-arms.rs:30:9 | LL | fn bar() -> impl Trait { | ---------- the expected opaque type ... LL | let _ = if true { | _____________- LL | | bar() | | ----- expected because of this LL | | } else { LL | | Struct | | ^^^^^^ expected opaque type, found `Struct` LL | | }; | |_____- `if` and `else` have incompatible types | = note: expected opaque type `impl Trait` found struct `Struct` help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>` | LL ~ Box::new(bar()) as Box<dyn Trait> LL | } else { LL ~ Box::new(Struct) | ``` Partially address rust-lang#102629.
2 parents 5d3d347 + 34f4f3d commit 4a2b4be

File tree

5 files changed

+267
-17
lines changed

5 files changed

+267
-17
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4444
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
4545
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
4646
|| self.suggest_no_capture_closure(err, expected, expr_ty)
47-
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
47+
|| self.suggest_boxing_when_appropriate(
48+
err,
49+
expr.peel_blocks().span,
50+
expr.hir_id,
51+
expected,
52+
expr_ty,
53+
)
4854
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
4955
|| self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected)
5056
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)

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

+102-16
Original file line numberDiff line numberDiff line change
@@ -294,30 +294,84 @@ impl<T> Trait<T> for X {
294294
);
295295
}
296296
}
297-
(ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias))
298-
if alias.def_id.is_local()
297+
(_, ty::Alias(ty::Opaque, opaque_ty))
298+
| (ty::Alias(ty::Opaque, opaque_ty), _) => {
299+
if opaque_ty.def_id.is_local()
299300
&& matches!(
300301
tcx.def_kind(body_owner_def_id),
301302
DefKind::Fn
302303
| DefKind::Static(_)
303304
| DefKind::Const
304305
| DefKind::AssocFn
305306
| DefKind::AssocConst
306-
) =>
307-
{
308-
if tcx.is_type_alias_impl_trait(alias.def_id) {
309-
if !tcx
307+
)
308+
&& tcx.is_type_alias_impl_trait(opaque_ty.def_id)
309+
&& !tcx
310310
.opaque_types_defined_by(body_owner_def_id.expect_local())
311-
.contains(&alias.def_id.expect_local())
312-
{
313-
let sp = tcx
314-
.def_ident_span(body_owner_def_id)
315-
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
316-
diag.span_note(
317-
sp,
318-
"\
319-
this item must have the opaque type in its signature \
320-
in order to be able to register hidden types",
311+
.contains(&opaque_ty.def_id.expect_local())
312+
{
313+
let sp = tcx
314+
.def_ident_span(body_owner_def_id)
315+
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
316+
diag.span_note(
317+
sp,
318+
"this item must have the opaque type in its signature in order to \
319+
be able to register hidden types",
320+
);
321+
}
322+
// If two if arms can be coerced to a trait object, provide a structured
323+
// suggestion.
324+
let ObligationCauseCode::IfExpression(cause) = cause.code() else {
325+
return;
326+
};
327+
let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
328+
return;
329+
};
330+
let Some(then) = blk.expr else {
331+
return;
332+
};
333+
let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
334+
return;
335+
};
336+
let Some(else_) = blk.expr else {
337+
return;
338+
};
339+
let expected = match values.found.kind() {
340+
ty::Alias(..) => values.expected,
341+
_ => values.found,
342+
};
343+
let preds = tcx.explicit_item_bounds(opaque_ty.def_id);
344+
for (pred, _span) in preds.skip_binder() {
345+
let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
346+
else {
347+
continue;
348+
};
349+
if trait_predicate.polarity != ty::ImplPolarity::Positive {
350+
continue;
351+
}
352+
let def_id = trait_predicate.def_id();
353+
let mut impl_def_ids = vec![];
354+
tcx.for_each_relevant_impl(def_id, expected, |did| {
355+
impl_def_ids.push(did)
356+
});
357+
if let [_] = &impl_def_ids[..] {
358+
let trait_name = tcx.item_name(def_id);
359+
diag.multipart_suggestion(
360+
format!(
361+
"`{expected}` implements `{trait_name}` so you can box \
362+
both arms and coerce to the trait object \
363+
`Box<dyn {trait_name}>`",
364+
),
365+
vec![
366+
(then.span.shrink_to_lo(), "Box::new(".to_string()),
367+
(
368+
then.span.shrink_to_hi(),
369+
format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
370+
),
371+
(else_.span.shrink_to_lo(), "Box::new(".to_string()),
372+
(else_.span.shrink_to_hi(), ")".to_string()),
373+
],
374+
MachineApplicable,
321375
);
322376
}
323377
}
@@ -330,6 +384,38 @@ impl<T> Trait<T> for X {
330384
);
331385
}
332386
}
387+
(ty::Adt(_, _), ty::Adt(def, args))
388+
if let ObligationCauseCode::IfExpression(cause) = cause.code()
389+
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
390+
&& let Some(then) = blk.expr
391+
&& def.is_box()
392+
&& let boxed_ty = args.type_at(0)
393+
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
394+
&& let Some(def_id) = t.principal_def_id()
395+
&& let mut impl_def_ids = vec![]
396+
&& let _ =
397+
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
398+
impl_def_ids.push(did)
399+
})
400+
&& let [_] = &impl_def_ids[..] =>
401+
{
402+
// We have divergent if/else arms where the expected value is a type that
403+
// implements the trait of the found boxed trait object.
404+
diag.multipart_suggestion(
405+
format!(
406+
"`{}` implements `{}` so you can box it to coerce to the trait \
407+
object `{}`",
408+
values.expected,
409+
tcx.item_name(def_id),
410+
values.found,
411+
),
412+
vec![
413+
(then.span.shrink_to_lo(), "Box::new(".to_string()),
414+
(then.span.shrink_to_hi(), ")".to_string()),
415+
],
416+
MachineApplicable,
417+
);
418+
}
333419
_ => {}
334420
}
335421
debug!(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// run-rustfix
2+
trait Trait {}
3+
struct Struct;
4+
impl Trait for Struct {}
5+
fn foo() -> Box<dyn Trait> {
6+
Box::new(Struct)
7+
}
8+
fn bar() -> impl Trait {
9+
Struct
10+
}
11+
fn main() {
12+
let _ = if true {
13+
Box::new(Struct)
14+
} else {
15+
foo() //~ ERROR E0308
16+
};
17+
let _ = if true {
18+
foo()
19+
} else {
20+
Box::new(Struct) //~ ERROR E0308
21+
};
22+
let _ = if true {
23+
Box::new(Struct) as Box<dyn Trait>
24+
} else {
25+
Box::new(bar()) //~ ERROR E0308
26+
};
27+
let _ = if true {
28+
Box::new(bar()) as Box<dyn Trait>
29+
} else {
30+
Box::new(Struct) //~ ERROR E0308
31+
};
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// run-rustfix
2+
trait Trait {}
3+
struct Struct;
4+
impl Trait for Struct {}
5+
fn foo() -> Box<dyn Trait> {
6+
Box::new(Struct)
7+
}
8+
fn bar() -> impl Trait {
9+
Struct
10+
}
11+
fn main() {
12+
let _ = if true {
13+
Struct
14+
} else {
15+
foo() //~ ERROR E0308
16+
};
17+
let _ = if true {
18+
foo()
19+
} else {
20+
Struct //~ ERROR E0308
21+
};
22+
let _ = if true {
23+
Struct
24+
} else {
25+
bar() //~ ERROR E0308
26+
};
27+
let _ = if true {
28+
bar()
29+
} else {
30+
Struct //~ ERROR E0308
31+
};
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
error[E0308]: `if` and `else` have incompatible types
2+
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:15:9
3+
|
4+
LL | let _ = if true {
5+
| _____________-
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:20:9
23+
|
24+
LL | let _ = if true {
25+
| _____________-
26+
LL | | foo()
27+
| | ----- expected because of this
28+
LL | | } else {
29+
LL | | Struct
30+
| | ^^^^^^ expected `Box<dyn Trait>`, found `Struct`
31+
LL | | };
32+
| |_____- `if` and `else` have incompatible types
33+
|
34+
= note: expected struct `Box<dyn Trait>`
35+
found struct `Struct`
36+
= note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html
37+
help: store this in the heap by calling `Box::new`
38+
|
39+
LL | Box::new(Struct)
40+
| +++++++++ +
41+
42+
error[E0308]: `if` and `else` have incompatible types
43+
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:25:9
44+
|
45+
LL | fn bar() -> impl Trait {
46+
| ---------- the found opaque type
47+
...
48+
LL | let _ = if true {
49+
| _____________-
50+
LL | | Struct
51+
| | ------ expected because of this
52+
LL | | } else {
53+
LL | | bar()
54+
| | ^^^^^ expected `Struct`, found opaque type
55+
LL | | };
56+
| |_____- `if` and `else` have incompatible types
57+
|
58+
= note: expected struct `Struct`
59+
found opaque type `impl Trait`
60+
help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>`
61+
|
62+
LL ~ Box::new(Struct) as Box<dyn Trait>
63+
LL | } else {
64+
LL ~ Box::new(bar())
65+
|
66+
67+
error[E0308]: `if` and `else` have incompatible types
68+
--> $DIR/suggest-box-on-divergent-if-else-arms.rs:30:9
69+
|
70+
LL | fn bar() -> impl Trait {
71+
| ---------- the expected opaque type
72+
...
73+
LL | let _ = if true {
74+
| _____________-
75+
LL | | bar()
76+
| | ----- expected because of this
77+
LL | | } else {
78+
LL | | Struct
79+
| | ^^^^^^ expected opaque type, found `Struct`
80+
LL | | };
81+
| |_____- `if` and `else` have incompatible types
82+
|
83+
= note: expected opaque type `impl Trait`
84+
found struct `Struct`
85+
help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box<dyn Trait>`
86+
|
87+
LL ~ Box::new(bar()) as Box<dyn Trait>
88+
LL | } else {
89+
LL ~ Box::new(Struct)
90+
|
91+
92+
error: aborting due to 4 previous errors
93+
94+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)