Skip to content

Commit dc1e10d

Browse files
committed
Account for tuples and structs in unsized locals errors for more accurate spans
``` error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized6.rs:26:19 | LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { | - this type parameter needs to be `Sized` ... LL | let (y, z) = (*x3, 4); | ^^^ doesn't have a size known at compile-time | = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature 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>) { | ``` ``` error[E0277]: the size for values of type `dyn T` cannot be known at compilation time --> $DIR/unsized-binding.rs:12:28 | LL | let Foo(a, b) = Foo(1, bar()); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `dyn T` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature ```
1 parent 8d5096b commit dc1e10d

File tree

6 files changed

+145
-33
lines changed

6 files changed

+145
-33
lines changed

compiler/rustc_hir/src/hir.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1985,6 +1985,14 @@ impl<'hir> QPath<'hir> {
19851985
QPath::LangItem(_, span) => span,
19861986
}
19871987
}
1988+
1989+
pub fn res(&self) -> Option<Res> {
1990+
match self {
1991+
QPath::LangItem(..) => None,
1992+
QPath::Resolved(_, path) => Some(path.res),
1993+
QPath::TypeRelative(_, path_segment) => Some(path_segment.res),
1994+
}
1995+
}
19881996
}
19891997

19901998
/// Hints at the original code for a let statement.

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
578578
self.require_type_is_sized_deferred(
579579
input,
580580
span,
581-
traits::SizedArgumentType(None),
581+
traits::SizedArgumentType(args.get(i).map(|e| e.hir_id)),
582582
);
583583
}
584584
}

compiler/rustc_hir_typeck/src/gather_locals.rs

+79-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::FnCtxt;
2+
use rustc_data_structures::fx::FxHashMap;
23
use rustc_hir as hir;
34
use rustc_hir::intravisit::{self, Visitor};
45
use rustc_hir::PatKind;
@@ -61,12 +62,12 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
6162
// *distinct* cases. so track when we are hitting a pattern *within* an fn
6263
// parameter.
6364
outermost_fn_param_pat: Option<(Span, hir::HirId)>,
64-
let_binding_init: Option<Span>,
65+
pat_expr_map: FxHashMap<hir::HirId, Span>,
6566
}
6667

6768
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
6869
pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
69-
Self { fcx, outermost_fn_param_pat: None, let_binding_init: None }
70+
Self { fcx, outermost_fn_param_pat: None, pat_expr_map: Default::default() }
7071
}
7172

7273
fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
@@ -155,14 +156,69 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
155156
self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&decl.hir_id).unwrap())
156157
);
157158
}
159+
}
160+
161+
/// Builds a correspondence mapping between the bindings in a pattern and the expression that
162+
/// originates the value. This is then used on unsized locals errors to point at the sub expression
163+
/// That corresponds to the sub-pattern with the `?Sized` type, instead of the binding.
164+
///
165+
/// This is somewhat limited, as it only supports bindings, tuples and structs for now, falling back
166+
/// on pointing at the binding otherwise.
167+
struct JointVisitorExpr<'hir> {
168+
pat: &'hir hir::Pat<'hir>,
169+
expr: &'hir hir::Expr<'hir>,
170+
map: FxHashMap<hir::HirId, Span>,
171+
}
158172

159-
fn span_for_init_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Span {
160-
// In other parts of the compiler, when emitting a bound error on a method call we point at
161-
// the method call path. We mimic that here so that error deduplication will work as
162-
// expected.
163-
match expr.kind {
164-
hir::ExprKind::MethodCall(path, ..) => path.ident.span,
165-
_ => expr.span,
173+
impl<'hir> JointVisitorExpr<'hir> {
174+
fn walk(&mut self) {
175+
match (self.pat.kind, self.expr.kind) {
176+
(hir::PatKind::Tuple(pat_fields, pos), hir::ExprKind::Tup(expr_fields))
177+
if pat_fields.len() == expr_fields.len() && pos.as_opt_usize() == None =>
178+
{
179+
for (pat, expr) in pat_fields.iter().zip(expr_fields.iter()) {
180+
self.map.insert(pat.hir_id, expr.span);
181+
let mut v = JointVisitorExpr { pat, expr, map: Default::default() };
182+
v.walk();
183+
self.map.extend(v.map);
184+
}
185+
}
186+
(hir::PatKind::Binding(..), hir::ExprKind::MethodCall(path, ..)) => {
187+
self.map.insert(self.pat.hir_id, path.ident.span);
188+
}
189+
(hir::PatKind::Binding(..), _) => {
190+
self.map.insert(self.pat.hir_id, self.expr.span);
191+
}
192+
(
193+
hir::PatKind::Struct(pat_path, pat_fields, _),
194+
hir::ExprKind::Struct(call_path, expr_fields, _),
195+
) if pat_path.res() == call_path.res() && pat_path.res().is_some() => {
196+
for (pat_field, expr_field) in pat_fields.iter().zip(expr_fields.iter()) {
197+
self.map.insert(pat_field.hir_id, expr_field.span);
198+
let mut v = JointVisitorExpr {
199+
pat: pat_field.pat,
200+
expr: expr_field.expr,
201+
map: Default::default(),
202+
};
203+
v.walk();
204+
self.map.extend(v.map);
205+
}
206+
}
207+
(
208+
hir::PatKind::TupleStruct(pat_path, pat_fields, _),
209+
hir::ExprKind::Call(
210+
hir::Expr { kind: hir::ExprKind::Path(expr_path), .. },
211+
expr_fields,
212+
),
213+
) if pat_path.res() == expr_path.res() && pat_path.res().is_some() => {
214+
for (pat, expr) in pat_fields.iter().zip(expr_fields.iter()) {
215+
self.map.insert(pat.hir_id, expr.span);
216+
let mut v = JointVisitorExpr { pat: pat, expr: expr, map: Default::default() };
217+
v.walk();
218+
self.map.extend(v.map);
219+
}
220+
}
221+
_ => {}
166222
}
167223
}
168224
}
@@ -171,20 +227,26 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
171227
// Add explicitly-declared locals.
172228
fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) {
173229
self.declare(local.into());
174-
let sp = self.let_binding_init.take();
175-
self.let_binding_init = local.init.map(|e| self.span_for_init_expr(e));
230+
if let Some(init) = local.init {
231+
let mut v = JointVisitorExpr { pat: &local.pat, expr: &init, map: Default::default() };
232+
v.walk();
233+
self.pat_expr_map.extend(v.map);
234+
}
176235
intravisit::walk_local(self, local);
177-
self.let_binding_init = sp;
178236
}
179237

180238
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
181-
let sp = self.let_binding_init.take();
182239
if let hir::ExprKind::Let(let_expr) = expr.kind {
183-
self.let_binding_init = Some(self.span_for_init_expr(&let_expr.init));
240+
let mut v = JointVisitorExpr {
241+
pat: &let_expr.pat,
242+
expr: &let_expr.init,
243+
map: Default::default(),
244+
};
245+
v.walk();
246+
self.pat_expr_map.extend(v.map);
184247
self.declare((let_expr, expr.hir_id).into());
185248
}
186249
intravisit::walk_expr(self, expr);
187-
self.let_binding_init = sp;
188250
}
189251

190252
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
@@ -219,11 +281,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
219281
}
220282
} else {
221283
if !self.fcx.tcx.features().unsized_locals {
222-
self.fcx.require_type_is_sized(
223-
var_ty,
224-
self.let_binding_init.unwrap_or(p.span),
225-
traits::VariableType(p.hir_id),
226-
);
284+
let span = *self.pat_expr_map.get(&p.hir_id).unwrap_or(&p.span);
285+
self.fcx.require_type_is_sized(var_ty, span, traits::VariableType(p.hir_id));
227286
}
228287
}
229288

@@ -234,11 +293,6 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
234293
var_ty
235294
);
236295
}
237-
// We only point at the init expression if this is the top level pattern, otherwise we point
238-
// at the specific binding, because we might not be able to tie the binding to the,
239-
// expression, like `let (a, b) = foo();`. FIXME: We could specialize
240-
// `let (a, b) = (unsized(), bar());` to point at `unsized()` instead of `a`.
241-
self.let_binding_init.take();
242296
let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
243297
intravisit::walk_pat(self, p);
244298
self.outermost_fn_param_pat = old_outermost_fn_param_pat;

tests/ui/sized/unsized-binding.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1+
struct Foo<T: ?Sized>(i32, T);
2+
trait T {}
3+
4+
fn bar() -> dyn T { //~ ERROR E0746
5+
panic!()
6+
}
7+
18
fn main() {
29
let x = *""; //~ ERROR E0277
310
println!("{}", x);
411
println!("{}", x);
12+
let Foo(a, b) = Foo(1, bar());
13+
//~^ ERROR E0277
14+
//~| ERROR E0277
15+
// The second error above could be deduplicated if we remove or unify the additional `note`
16+
// explaining unsized locals and fn arguments.
517
}

tests/ui/sized/unsized-binding.stderr

+41-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1+
error[E0746]: return type cannot have an unboxed trait object
2+
--> $DIR/unsized-binding.rs:4:13
3+
|
4+
LL | fn bar() -> dyn T {
5+
| ^^^^^ doesn't have a size known at compile-time
6+
|
7+
= help: if the returned value came from a borrowed argument that implements the trait, then you could return `&dyn Trait`
8+
help: return an `impl Trait` instead of a `dyn Trait`
9+
|
10+
LL | fn bar() -> impl T {
11+
| ~~~~
12+
help: alternatively, box the return type to make a boxed trait object, and wrap all of the returned values in `Box::new`
13+
|
14+
LL ~ fn bar() -> Box<dyn T> {
15+
LL ~ Box::new(panic!())
16+
|
17+
118
error[E0277]: the size for values of type `str` cannot be known at compilation time
2-
--> $DIR/unsized-binding.rs:2:13
19+
--> $DIR/unsized-binding.rs:9:13
320
|
421
LL | let x = *"";
522
| ^^^ doesn't have a size known at compile-time
@@ -8,6 +25,27 @@ LL | let x = *"";
825
= note: all local variables must have a statically known size
926
= help: unsized locals are gated as an unstable feature
1027

11-
error: aborting due to 1 previous error
28+
error[E0277]: the size for values of type `dyn T` cannot be known at compilation time
29+
--> $DIR/unsized-binding.rs:12:28
30+
|
31+
LL | let Foo(a, b) = Foo(1, bar());
32+
| ^^^^^ doesn't have a size known at compile-time
33+
|
34+
= help: the trait `Sized` is not implemented for `dyn T`
35+
= note: all local variables must have a statically known size
36+
= help: unsized locals are gated as an unstable feature
37+
38+
error[E0277]: the size for values of type `dyn T` cannot be known at compilation time
39+
--> $DIR/unsized-binding.rs:12:28
40+
|
41+
LL | let Foo(a, b) = Foo(1, bar());
42+
| ^^^^^ doesn't have a size known at compile-time
43+
|
44+
= help: the trait `Sized` is not implemented for `dyn T`
45+
= note: all function arguments must have a statically known size
46+
= help: unsized fn params are gated as an unstable feature
47+
48+
error: aborting due to 4 previous errors
1249

13-
For more information about this error, try `rustc --explain E0277`.
50+
Some errors have detailed explanations: E0277, E0746.
51+
For more information about an error, try `rustc --explain E0277`.

tests/ui/unsized/unsized6.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ LL + fn f3<X>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
125125
|
126126

127127
error[E0277]: the size for values of type `X` cannot be known at compilation time
128-
--> $DIR/unsized6.rs:26:10
128+
--> $DIR/unsized6.rs:26:19
129129
|
130130
LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
131131
| - this type parameter needs to be `Sized`
132132
...
133133
LL | let (y, z) = (*x3, 4);
134-
| ^ doesn't have a size known at compile-time
134+
| ^^^ doesn't have a size known at compile-time
135135
|
136136
= note: all local variables must have a statically known size
137137
= help: unsized locals are gated as an unstable feature
@@ -179,13 +179,13 @@ LL + fn f4<X: T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
179179
|
180180

181181
error[E0277]: the size for values of type `X` cannot be known at compilation time
182-
--> $DIR/unsized6.rs:34:10
182+
--> $DIR/unsized6.rs:34:19
183183
|
184184
LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) {
185185
| - this type parameter needs to be `Sized`
186186
...
187187
LL | let (y, z) = (*x3, 4);
188-
| ^ doesn't have a size known at compile-time
188+
| ^^^ doesn't have a size known at compile-time
189189
|
190190
= note: all local variables must have a statically known size
191191
= help: unsized locals are gated as an unstable feature

0 commit comments

Comments
 (0)