Skip to content

Commit 803d616

Browse files
authored
Rollup merge of rust-lang#80635 - sexxi-goose:use-place-instead-of-symbol, r=nikomatsakis`
Improve diagnostics when closure doesn't meet trait bound Improves the diagnostics when closure doesn't meet trait bound by modifying `TypeckResuts::closure_kind_origins` such that `hir::Place` is used instead of `Symbol`. Using `hir::Place` to describe which capture influenced the decision of selecting a trait a closure satisfies to (Fn/FnMut/FnOnce, Copy) allows us to show precise path in the diagnostics when `capture_disjoint_field` feature is enabled. Closes rust-lang/project-rfc-2229/issues/21 r? `@nikomatsakis`
2 parents c06ff92 + b498870 commit 803d616

16 files changed

+328
-23
lines changed

compiler/rustc_middle/src/ty/context.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::arena::Arena;
44
use crate::dep_graph::DepGraph;
55
use crate::hir::exports::ExportMap;
6+
use crate::hir::place::Place as HirPlace;
67
use crate::ich::{NodeIdHashingMode, StableHashingContext};
78
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
89
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
@@ -379,7 +380,7 @@ pub struct TypeckResults<'tcx> {
379380

380381
/// Records the reasons that we picked the kind of each closure;
381382
/// not all closures are present in the map.
382-
closure_kind_origins: ItemLocalMap<(Span, Symbol)>,
383+
closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>,
383384

384385
/// For each fn, records the "liberated" types of its arguments
385386
/// and return type. Liberated means that all bound regions
@@ -642,11 +643,13 @@ impl<'tcx> TypeckResults<'tcx> {
642643
self.upvar_capture_map[&upvar_id]
643644
}
644645

645-
pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> {
646+
pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> {
646647
LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins }
647648
}
648649

649-
pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> {
650+
pub fn closure_kind_origins_mut(
651+
&mut self,
652+
) -> LocalTableInContextMut<'_, (Span, HirPlace<'tcx>)> {
650653
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins }
651654
}
652655

compiler/rustc_middle/src/ty/mod.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ pub use self::IntVarValue::*;
1717
pub use self::Variance::*;
1818

1919
use crate::hir::exports::ExportMap;
20-
use crate::hir::place::Place as HirPlace;
20+
use crate::hir::place::{
21+
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
22+
};
2123
use crate::ich::StableHashingContext;
2224
use crate::middle::cstore::CrateStoreDyn;
2325
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
@@ -734,6 +736,43 @@ pub struct CapturedPlace<'tcx> {
734736
pub info: CaptureInfo<'tcx>,
735737
}
736738

739+
pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
740+
let name = match place.base {
741+
HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
742+
_ => bug!("Capture_information should only contain upvars"),
743+
};
744+
let mut curr_string = name;
745+
746+
for (i, proj) in place.projections.iter().enumerate() {
747+
match proj.kind {
748+
HirProjectionKind::Deref => {
749+
curr_string = format!("*{}", curr_string);
750+
}
751+
HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() {
752+
ty::Adt(def, ..) => {
753+
curr_string = format!(
754+
"{}.{}",
755+
curr_string,
756+
def.variants[variant].fields[idx as usize].ident.name.as_str()
757+
);
758+
}
759+
ty::Tuple(_) => {
760+
curr_string = format!("{}.{}", curr_string, idx);
761+
}
762+
_ => {
763+
bug!(
764+
"Field projection applied to a type other than Adt or Tuple: {:?}.",
765+
place.ty_before_projection(i).kind()
766+
)
767+
}
768+
},
769+
proj => bug!("{:?} unexpected because it isn't captured", proj),
770+
}
771+
}
772+
773+
curr_string.to_string()
774+
}
775+
737776
/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
738777
/// for a particular capture as well as identifying the part of the source code
739778
/// that triggered this capture to occur.

compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
103103
let did = did.expect_local();
104104
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
105105

106-
if let Some((span, name)) =
106+
if let Some((span, hir_place)) =
107107
self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
108108
{
109109
diag.span_note(
110110
*span,
111111
&format!(
112112
"closure cannot be invoked more than once because it moves the \
113113
variable `{}` out of its environment",
114-
name,
114+
ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
115115
),
116116
);
117117
return;
@@ -127,15 +127,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
127127
let did = did.expect_local();
128128
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
129129

130-
if let Some((span, name)) =
130+
if let Some((span, hir_place)) =
131131
self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
132132
{
133133
diag.span_note(
134134
*span,
135135
&format!(
136136
"closure cannot be moved more than once as it is not `Copy` due to \
137137
moving the variable `{}` out of its environment",
138-
name
138+
ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
139139
),
140140
);
141141
}

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -589,23 +589,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
589589
if let Some(typeck_results) = self.in_progress_typeck_results {
590590
let typeck_results = typeck_results.borrow();
591591
match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
592-
(ty::ClosureKind::FnOnce, Some((span, name))) => {
592+
(ty::ClosureKind::FnOnce, Some((span, place))) => {
593593
err.span_label(
594594
*span,
595595
format!(
596596
"closure is `FnOnce` because it moves the \
597597
variable `{}` out of its environment",
598-
name
598+
ty::place_to_string_for_capture(tcx, place)
599599
),
600600
);
601601
}
602-
(ty::ClosureKind::FnMut, Some((span, name))) => {
602+
(ty::ClosureKind::FnMut, Some((span, place))) => {
603603
err.span_label(
604604
*span,
605605
format!(
606606
"closure is `FnMut` because it mutates the \
607607
variable `{}` here",
608-
name
608+
ty::place_to_string_for_capture(tcx, place)
609609
),
610610
);
611611
}

compiler/rustc_typeck/src/check/upvar.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
176176
self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
177177

178178
// If we have an origin, store it.
179-
if let Some(origin) = delegate.current_origin {
179+
if let Some(origin) = delegate.current_origin.clone() {
180+
let origin = if self.tcx.features().capture_disjoint_fields {
181+
origin
182+
} else {
183+
// FIXME(project-rfc-2229#26): Once rust-lang#80092 is merged, we should restrict the
184+
// precision of origin as well. Otherwise, this will cause issues when project-rfc-2229#26
185+
// is fixed as we might see Index projections in the origin, which we can't print because
186+
// we don't store enough information.
187+
(origin.0, Place { projections: vec![], ..origin.1 })
188+
};
189+
180190
self.typeck_results
181191
.borrow_mut()
182192
.closure_kind_origins_mut()
@@ -563,7 +573,7 @@ struct InferBorrowKind<'a, 'tcx> {
563573

564574
// If we modified `current_closure_kind`, this field contains a `Some()` with the
565575
// variable access that caused us to do so.
566-
current_origin: Option<(Span, Symbol)>,
576+
current_origin: Option<(Span, Place<'tcx>)>,
567577

568578
/// For each Place that is captured by the closure, we track the minimal kind of
569579
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
@@ -628,7 +638,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
628638
upvar_id.closure_expr_id,
629639
ty::ClosureKind::FnOnce,
630640
usage_span,
631-
var_name(tcx, upvar_id.var_path.hir_id),
641+
place_with_id.place.clone(),
632642
);
633643

634644
let capture_info = ty::CaptureInfo {
@@ -720,7 +730,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
720730
upvar_id.closure_expr_id,
721731
ty::ClosureKind::FnMut,
722732
tcx.hir().span(diag_expr_id),
723-
var_name(tcx, upvar_id.var_path.hir_id),
733+
place_with_id.place.clone(),
724734
);
725735
}
726736
}
@@ -765,11 +775,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
765775
closure_id: LocalDefId,
766776
new_kind: ty::ClosureKind,
767777
upvar_span: Span,
768-
var_name: Symbol,
778+
place: Place<'tcx>,
769779
) {
770780
debug!(
771-
"adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})",
772-
closure_id, new_kind, upvar_span, var_name
781+
"adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})",
782+
closure_id, new_kind, upvar_span, place
773783
);
774784

775785
// Is this the closure whose kind is currently being inferred?
@@ -797,7 +807,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
797807
| (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
798808
// new kind is stronger than the old kind
799809
self.current_closure_kind = new_kind;
800-
self.current_origin = Some((upvar_span, var_name));
810+
self.current_origin = Some((upvar_span, place));
801811
}
802812
}
803813
}

compiler/rustc_typeck/src/check/writeback.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
384384
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
385385
let common_hir_owner = fcx_typeck_results.hir_owner;
386386

387-
for (&id, &origin) in fcx_typeck_results.closure_kind_origins().iter() {
388-
let hir_id = hir::HirId { owner: common_hir_owner, local_id: id };
389-
self.typeck_results.closure_kind_origins_mut().insert(hir_id, origin);
387+
for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() {
388+
let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id };
389+
let place_span = origin.0;
390+
let place = self.resolve(origin.1.clone(), &place_span);
391+
self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place));
390392
}
391393
}
392394

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![feature(capture_disjoint_fields)]
2+
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
3+
//~| `#[warn(incomplete_features)]` on by default
4+
//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
5+
6+
// Check that precise paths are being reported back in the error message.
7+
8+
9+
enum MultiVariant {
10+
Point(i32, i32),
11+
Meta(i32)
12+
}
13+
14+
fn main() {
15+
let mut point = MultiVariant::Point(10, -10,);
16+
17+
let mut meta = MultiVariant::Meta(1);
18+
19+
let c = || {
20+
if let MultiVariant::Point(ref mut x, _) = point {
21+
*x += 1;
22+
}
23+
24+
if let MultiVariant::Meta(ref mut v) = meta {
25+
*v += 1;
26+
}
27+
};
28+
29+
let a = c;
30+
let b = c; //~ ERROR use of moved value: `c` [E0382]
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/closure-origin-multi-variant-diagnostics.rs:1:12
3+
|
4+
LL | #![feature(capture_disjoint_fields)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
9+
10+
error[E0382]: use of moved value: `c`
11+
--> $DIR/closure-origin-multi-variant-diagnostics.rs:30:13
12+
|
13+
LL | let a = c;
14+
| - value moved here
15+
LL | let b = c;
16+
| ^ value used here after move
17+
|
18+
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
19+
--> $DIR/closure-origin-multi-variant-diagnostics.rs:20:52
20+
|
21+
LL | if let MultiVariant::Point(ref mut x, _) = point {
22+
| ^^^^^
23+
24+
error: aborting due to previous error; 1 warning emitted
25+
26+
For more information about this error, try `rustc --explain E0382`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![feature(capture_disjoint_fields)]
2+
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
3+
//~| `#[warn(incomplete_features)]` on by default
4+
//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
5+
6+
// Check that precise paths are being reported back in the error message.
7+
8+
enum SingleVariant {
9+
Point(i32, i32),
10+
}
11+
12+
fn main() {
13+
let mut point = SingleVariant::Point(10, -10);
14+
15+
let c = || {
16+
// FIXME(project-rfc-2229#24): Change this to be a destructure pattern
17+
// once this is fixed, to remove the warning.
18+
if let SingleVariant::Point(ref mut x, _) = point {
19+
//~^ WARNING: irrefutable if-let pattern
20+
*x += 1;
21+
}
22+
};
23+
24+
let b = c;
25+
let a = c; //~ ERROR use of moved value: `c` [E0382]
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/closure-origin-single-variant-diagnostics.rs:1:12
3+
|
4+
LL | #![feature(capture_disjoint_fields)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
9+
10+
warning: irrefutable if-let pattern
11+
--> $DIR/closure-origin-single-variant-diagnostics.rs:18:9
12+
|
13+
LL | / if let SingleVariant::Point(ref mut x, _) = point {
14+
LL | |
15+
LL | | *x += 1;
16+
LL | | }
17+
| |_________^
18+
|
19+
= note: `#[warn(irrefutable_let_patterns)]` on by default
20+
21+
error[E0382]: use of moved value: `c`
22+
--> $DIR/closure-origin-single-variant-diagnostics.rs:25:13
23+
|
24+
LL | let b = c;
25+
| - value moved here
26+
LL | let a = c;
27+
| ^ value used here after move
28+
|
29+
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
30+
--> $DIR/closure-origin-single-variant-diagnostics.rs:18:53
31+
|
32+
LL | if let SingleVariant::Point(ref mut x, _) = point {
33+
| ^^^^^
34+
35+
error: aborting due to previous error; 2 warnings emitted
36+
37+
For more information about this error, try `rustc --explain E0382`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![feature(capture_disjoint_fields)]
2+
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
3+
//~| `#[warn(incomplete_features)]` on by default
4+
//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
5+
6+
// Check that precise paths are being reported back in the error message.
7+
8+
struct Y {
9+
y: X
10+
}
11+
12+
struct X {
13+
a: u32,
14+
b: u32,
15+
}
16+
17+
fn main() {
18+
let mut x = Y { y: X { a: 5, b: 0 } };
19+
let hello = || {
20+
x.y.a += 1;
21+
};
22+
23+
let b = hello;
24+
let c = hello; //~ ERROR use of moved value: `hello` [E0382]
25+
}

0 commit comments

Comments
 (0)