Skip to content

Fix some suggestions where a Box<T> is expected. #111056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns

hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new

hir_typeck_suggest_boxing_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

hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `Box::new`
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,31 @@ pub struct ArgMismatchIndeterminate {
#[primary_span]
pub span: Span,
}

#[derive(Subdiagnostic)]
pub enum SuggestBoxing {
#[note(hir_typeck_suggest_boxing_note)]
#[multipart_suggestion(
hir_typeck_suggest_boxing_when_appropriate,
applicability = "machine-applicable"
)]
Unit {
#[suggestion_part(code = "Box::new(())")]
start: Span,
#[suggestion_part(code = "")]
end: Span,
},
#[note(hir_typeck_suggest_boxing_note)]
AsyncBody,
#[note(hir_typeck_suggest_boxing_note)]
#[multipart_suggestion(
hir_typeck_suggest_boxing_when_appropriate,
applicability = "machine-applicable"
)]
Other {
#[suggestion_part(code = "Box::new(")]
start: Span,
#[suggestion_part(code = ")")]
end: Span,
},
}
20 changes: 14 additions & 6 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// case we can ignore the tail expression (e.g., `'a: {
// break 'a 22; }` would not force the type of the block
// to be `()`).
let tail_expr = blk.expr.as_ref();
let coerce_to_ty = expected.coercion_target_type(self, blk.span);
let coerce = if blk.targeted_by_break {
CoerceMany::new(coerce_to_ty)
Expand All @@ -1532,13 +1531,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// check the tail expression **without** holding the
// `enclosing_breakables` lock below.
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
let tail_expr_ty =
blk.expr.map(|expr| (expr, self.check_expr_with_expectation(expr, expected)));

let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
let coerce = ctxt.coerce.as_mut().unwrap();
if let Some(tail_expr_ty) = tail_expr_ty {
let tail_expr = tail_expr.unwrap();
if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty {
let span = self.get_expr_coercion_span(tail_expr);
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
let ty_for_diagnostic = coerce.merged_ty();
Expand Down Expand Up @@ -1591,6 +1590,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self.misc(sp),
&mut |err| {
if let Some(expected_ty) = expected.only_has_type(self) {
if blk.stmts.is_empty() && blk.expr.is_none() {
self.suggest_boxing_when_appropriate(
err,
blk.span,
blk.hir_id,
expected_ty,
self.tcx.mk_unit(),
);
}
if !self.consider_removing_semicolon(blk, expected_ty, err) {
self.err_ctxt().consider_returning_binding(
blk,
Expand All @@ -1603,7 +1611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// silence this redundant error, as we already emit E0070.

// Our block must be a `assign desugar local; assignment`
if let Some(hir::Node::Block(hir::Block {
if let hir::Block {
stmts:
[
hir::Stmt {
Expand All @@ -1625,7 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
],
..
})) = self.tcx.hir().find(blk.hir_id)
} = blk
{
self.comes_from_while_condition(blk.hir_id, |_| {
err.downgrade_to_delayed_bug();
Expand Down
48 changes: 24 additions & 24 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::FnCtxt;

use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
use crate::fluent_generated as fluent;
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
Expand All @@ -9,7 +9,8 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
AsyncGeneratorKind, Expr, ExprKind, GeneratorKind, GenericBound, HirId, Node, Path, QPath,
Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::traits::{self, StatementAsExpression};
Expand Down Expand Up @@ -438,33 +439,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn suggest_boxing_when_appropriate(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
span: Span,
hir_id: HirId,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> bool {
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
// Do not suggest `Box::new` in const context.
return false;
}
if !expected.is_box() || found.is_box() {
// Do not suggest `Box::new` in const context.
if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
return false;
}
let boxed_found = self.tcx.mk_box(found);
if self.can_coerce(boxed_found, expected) {
err.multipart_suggestion(
"store this in the heap by calling `Box::new`",
vec![
(expr.span.shrink_to_lo(), "Box::new(".to_string()),
(expr.span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
);
err.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",
);
if self.can_coerce(self.tcx.mk_box(found), expected) {
let suggest_boxing = match found.kind() {
ty::Tuple(tuple) if tuple.is_empty() => {
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
}
ty::Generator(def_id, ..)
if matches!(
self.tcx.generator_kind(def_id),
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure))
) =>
{
SuggestBoxing::AsyncBody
}
_ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() },
};
err.subdiagnostic(suggest_boxing);

true
} else {
false
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/issues/auxiliary/issue-111011.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(async_closure)]

// edition:2021

fn foo<X>(x: impl FnOnce() -> Box<X>) {}
// just to make sure async closures can still be suggested for boxing.
fn bar<X>(x: Box<dyn FnOnce() -> X>) {}

fn main() {
foo(async move || {}); //~ ERROR mismatched types
bar(async move || {}); //~ ERROR mismatched types
}
34 changes: 34 additions & 0 deletions tests/ui/issues/auxiliary/issue-111011.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
error[E0308]: mismatched types
--> $DIR/issue-111011.rs:10:23
|
LL | foo(async move || {});
| ^^ expected `Box<_>`, found `async` closure body
|
= note: expected struct `Box<_>`
found `async` closure body `[async closure body@$DIR/issue-111011.rs:10:23: 10:25]`
= 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

error[E0308]: mismatched types
--> $DIR/issue-111011.rs:11:9
|
LL | bar(async move || {});
| --- ^^^^^^^^^^^^^^^^ expected `Box<dyn FnOnce() -> _>`, found closure
| |
| arguments to this function are incorrect
|
= note: expected struct `Box<(dyn FnOnce() -> _ + 'static)>`
found closure `[closure@$DIR/issue-111011.rs:11:9: 11:22]`
= 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
note: function defined here
--> $DIR/issue-111011.rs:7:4
|
LL | fn bar<X>(x: Box<dyn FnOnce() -> X>) {}
| ^^^ -------------------------
help: store this in the heap by calling `Box::new`
|
LL | bar(Box::new(async move || {}));
| +++++++++ +

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
12 changes: 12 additions & 0 deletions tests/ui/suggestions/suggest-boxed-empty-block.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(async_closure)]

// edition:2021
// run-rustfix

fn foo<T>(_: Box<T>) {}
fn bar<T>(_: impl Fn() -> Box<T>) {}

fn main() {
foo(Box::new(())); //~ ERROR mismatched types
bar(|| Box::new(())); //~ ERROR mismatched types
}
12 changes: 12 additions & 0 deletions tests/ui/suggestions/suggest-boxed-empty-block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(async_closure)]

// edition:2021
// run-rustfix

fn foo<T>(_: Box<T>) {}
fn bar<T>(_: impl Fn() -> Box<T>) {}

fn main() {
foo({}); //~ ERROR mismatched types
bar(|| {}); //~ ERROR mismatched types
}
33 changes: 33 additions & 0 deletions tests/ui/suggestions/suggest-boxed-empty-block.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0308]: mismatched types
--> $DIR/suggest-boxed-empty-block.rs:10:9
|
LL | foo({});
| ^^ expected `Box<_>`, found `()`
|
= note: expected struct `Box<_>`
found unit type `()`
= 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 - foo({});
LL + foo(Box::new(()));
|

error[E0308]: mismatched types
--> $DIR/suggest-boxed-empty-block.rs:11:12
|
LL | bar(|| {});
| ^^ expected `Box<_>`, found `()`
|
= note: expected struct `Box<_>`
found unit type `()`
= 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 - bar(|| {});
LL + bar(|| Box::new(()));
|

error: aborting due to 2 previous errors

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