Skip to content

Commit

Permalink
Rollup merge of rust-lang#128467 - estebank:unsized-args, r=cjgillot
Browse files Browse the repository at this point in the history
Detect `*` operator on `!Sized` expression

The suggestion is new:

```
error[E0277]: the size for values of type `str` cannot be known at compilation time
  --> $DIR/unsized-str-in-return-expr-arg-and-local.rs:15:9
   |
LL |     let x = *"";
   |         ^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `str`
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature
help: references to `!Sized` types like `&str` are `Sized`; consider not dereferencing the expression
   |
LL -     let x = *"";
LL +     let x = "";
   |
```

Fix rust-lang#128199.
  • Loading branch information
matthiaskrgr authored Aug 24, 2024
2 parents 54797fa + f6767f7 commit 5dbb40d
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 9 deletions.
6 changes: 5 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,13 +841,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ret_ty = ret_coercion.borrow().expected_ty();
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
let mut span = return_expr.span;
let mut hir_id = return_expr.hir_id;
// Use the span of the trailing expression for our cause,
// not the span of the entire function
if !explicit_return
&& let ExprKind::Block(body, _) = return_expr.kind
&& let Some(last_expr) = body.expr
{
span = last_expr.span;
hir_id = last_expr.hir_id;
}
ret_coercion.borrow_mut().coerce(
self,
Expand All @@ -864,6 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.select_obligations_where_possible(|errors| {
self.point_at_return_for_opaque_ty_error(
errors,
hir_id,
span,
return_expr_ty,
return_expr.span,
Expand Down Expand Up @@ -921,6 +924,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn point_at_return_for_opaque_ty_error(
&self,
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
hir_id: HirId,
span: Span,
return_expr_ty: Ty<'tcx>,
return_span: Span,
Expand All @@ -935,7 +939,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let new_cause = ObligationCause::new(
cause.span,
cause.body_id,
ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))),
ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, hir_id))),
);
*cause = new_cause;
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ pub enum ObligationCauseCode<'tcx> {
ReturnValue(HirId),

/// Opaque return type of this function
OpaqueReturnType(Option<(Ty<'tcx>, Span)>),
OpaqueReturnType(Option<(Ty<'tcx>, HirId)>),

/// Block implicit return
BlockTailExpression(HirId, hir::MatchSource),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {

let tcx = self.tcx;
let predicate = predicate.upcast(tcx);
let suggest_remove_deref = |err: &mut Diag<'_, G>, expr: &hir::Expr<'_>| {
if let Some(pred) = predicate.as_trait_clause()
&& tcx.is_lang_item(pred.def_id(), LangItem::Sized)
&& let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
{
err.span_suggestion_verbose(
expr.span.until(inner.span),
"references are always `Sized`, even if they point to unsized data; consider \
not dereferencing the expression",
String::new(),
Applicability::MaybeIncorrect,
);
}
};
match *cause_code {
ObligationCauseCode::ExprAssignable
| ObligationCauseCode::MatchExpressionArm { .. }
Expand Down Expand Up @@ -2773,6 +2787,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
| ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)
if !span.is_dummy() =>
{
if let ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, pos) = &cause_code {
if let Node::Expr(expr) = tcx.parent_hir_node(*hir_id)
&& let hir::ExprKind::Call(_, args) = expr.kind
&& let Some(expr) = args.get(*pos)
{
suggest_remove_deref(err, &expr);
} else if let Node::Expr(expr) = self.tcx.hir_node(*hir_id)
&& let hir::ExprKind::MethodCall(_, _, args, _) = expr.kind
&& let Some(expr) = args.get(*pos)
{
suggest_remove_deref(err, &expr);
}
}
let item_name = tcx.def_path_str(item_def_id);
let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
let mut multispan = MultiSpan::from(span);
Expand Down Expand Up @@ -2970,6 +2997,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
));
err.downgrade_to_delayed_bug();
}
let mut local = true;
match tcx.parent_hir_node(hir_id) {
Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => {
err.span_suggestion_verbose(
Expand All @@ -2978,7 +3006,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"&",
Applicability::MachineApplicable,
);
err.note("all local variables must have a statically known size");
}
Node::LetStmt(hir::LetStmt {
init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }),
Expand All @@ -2993,7 +3020,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"&",
Applicability::MachineApplicable,
);
err.note("all local variables must have a statically known size");
}
Node::LetStmt(hir::LetStmt { init: Some(expr), .. }) => {
// When encountering an assignment of an unsized trait, like `let x = *"";`,
// we check if the RHS is a deref operation, to suggest removing it.
suggest_remove_deref(err, &expr);
}
Node::Param(param) => {
err.span_suggestion_verbose(
Expand All @@ -3003,10 +3034,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"&",
Applicability::MachineApplicable,
);
local = false;
}
_ => {
err.note("all local variables must have a statically known size");
}
_ => {}
}
if local {
err.note("all local variables must have a statically known size");
}
if !tcx.features().unsized_locals {
err.help("unsized locals are gated as an unstable feature");
Expand Down Expand Up @@ -3529,14 +3562,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
ObligationCauseCode::OpaqueReturnType(expr_info) => {
if let Some((expr_ty, expr_span)) = expr_info {
if let Some((expr_ty, hir_id)) = expr_info {
let expr_ty = self.tcx.short_ty_string(expr_ty, &mut long_ty_file);
let expr = self.infcx.tcx.hir().expect_expr(hir_id);
err.span_label(
expr_span,
expr.span,
with_forced_trimmed_paths!(format!(
"return type was inferred to be `{expr_ty}` here",
)),
);
suggest_remove_deref(err, &expr);
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/dst/dst-rvalue.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ LL | let _x: Box<str> = Box::new(*"hello world");
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `Box::<T>::new`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let _x: Box<str> = Box::new(*"hello world");
LL + let _x: Box<str> = Box::new("hello world");
|

error[E0277]: the size for values of type `[isize]` cannot be known at compilation time
--> $DIR/dst-rvalue.rs:8:37
Expand All @@ -21,6 +26,11 @@ LL | let _x: Box<[isize]> = Box::new(*array);
= help: the trait `Sized` is not implemented for `[isize]`
note: required by a bound in `Box::<T>::new`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let _x: Box<[isize]> = Box::new(*array);
LL + let _x: Box<[isize]> = Box::new(array);
|

error: aborting due to 2 previous errors

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/issues/issue-17651.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ LL | (|| Box::new(*(&[0][..])))();
= help: the trait `Sized` is not implemented for `[{integer}]`
note: required by a bound in `Box::<T>::new`
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - (|| Box::new(*(&[0][..])))();
LL + (|| Box::new((&[0][..])))();
|

error: aborting due to 1 previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/sized/unsized-binding.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ LL | let x = *"";
= help: the trait `Sized` is not implemented for `str`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let x = *"";
LL + let x = "";
|

error: aborting due to 1 previous error

Expand Down
30 changes: 30 additions & 0 deletions tests/ui/sized/unsized-str-in-return-expr-arg-and-local.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
fn foo() -> impl Sized {
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
*"" //~ HELP consider not dereferencing the expression
}
fn bar(_: impl Sized) {}
struct S;

impl S {
fn baz(&self, _: impl Sized) {}
}

fn main() {
let _ = foo();
let x = *"";
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP consider not dereferencing the expression
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized locals are gated as an unstable feature
bar(x);
S.baz(x);
bar(*"");
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP consider not dereferencing the expression
//~| HELP the trait `Sized` is not implemented for `str`
S.baz(*"");
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP consider not dereferencing the expression
//~| HELP the trait `Sized` is not implemented for `str`
}
74 changes: 74 additions & 0 deletions tests/ui/sized/unsized-str-in-return-expr-arg-and-local.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:1:13
|
LL | fn foo() -> impl Sized {
| ^^^^^^^^^^ doesn't have a size known at compile-time
...
LL | *""
| --- return type was inferred to be `str` here
|
= help: the trait `Sized` is not implemented for `str`
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - *""
LL + ""
|

error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:15:9
|
LL | let x = *"";
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let x = *"";
LL + let x = "";
|

error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:22:9
|
LL | bar(*"");
| --- ^^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `bar`
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:6:16
|
LL | fn bar(_: impl Sized) {}
| ^^^^^ required by this bound in `bar`
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - bar(*"");
LL + bar("");
|

error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:26:11
|
LL | S.baz(*"");
| --- ^^^ doesn't have a size known at compile-time
| |
| required by a bound introduced by this call
|
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `S::baz`
--> $DIR/unsized-str-in-return-expr-arg-and-local.rs:10:27
|
LL | fn baz(&self, _: impl Sized) {}
| ^^^^^ required by this bound in `S::baz`
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - S.baz(*"");
LL + S.baz("");
|

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0277`.
5 changes: 5 additions & 0 deletions tests/ui/suggestions/issue-84973-blacklist.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ note: required by a bound in `f_sized`
|
LL | fn f_sized<T: Sized>(t: T) {}
| ^^^^^ required by this bound in `f_sized`
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - f_sized(*ref_cl);
LL + f_sized(ref_cl);
|

error[E0277]: `Rc<{integer}>` cannot be sent between threads safely
--> $DIR/issue-84973-blacklist.rs:27:12
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/unsized/unsized6.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized`
LL - fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
LL + fn f3<X>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
|
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let y = *x2;
LL + let y = x2;
|

error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:26:10
Expand Down Expand Up @@ -177,6 +182,11 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized`
LL - fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
LL + fn f4<X: T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
|
help: references are always `Sized`, even if they point to unsized data; consider not dereferencing the expression
|
LL - let y = *x2;
LL + let y = x2;
|

error[E0277]: the size for values of type `X` cannot be known at compilation time
--> $DIR/unsized6.rs:34:10
Expand Down

0 comments on commit 5dbb40d

Please sign in to comment.