use crate::arena::Arena;
use crate::dep_graph::{self, DepGraph, DepKind, DepNode, DepNodeExt};
use crate::hir::exports::ExportMap;
+use crate::hir::place::Place as HirPlace;
use crate::ich::{NodeIdHashingMode, StableHashingContext};
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
/// Records the reasons that we picked the kind of each closure;
/// not all closures are present in the map.
- closure_kind_origins: ItemLocalMap<(Span, Symbol)>,
+ closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>,
/// For each fn, records the "liberated" types of its arguments
/// and return type. Liberated means that all bound regions
self.upvar_capture_map[&upvar_id]
}
- pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> {
+ pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins }
}
- pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> {
+ pub fn closure_kind_origins_mut(
+ &mut self,
+ ) -> LocalTableInContextMut<'_, (Span, HirPlace<'tcx>)> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins }
}
pub use self::Variance::*;
use crate::hir::exports::ExportMap;
-use crate::hir::place::Place as HirPlace;
+use crate::hir::place::{
+ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
+};
use crate::ich::StableHashingContext;
use crate::middle::cstore::CrateStoreDyn;
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
pub info: CaptureInfo<'tcx>,
}
+pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
+ let name = match place.base {
+ HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
+ _ => bug!("Capture_information should only contain upvars"),
+ };
+ let mut curr_string = name;
+
+ for (i, proj) in place.projections.iter().enumerate() {
+ match proj.kind {
+ HirProjectionKind::Deref => {
+ curr_string = format!("*{}", curr_string);
+ }
+ HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() {
+ ty::Adt(def, ..) => {
+ curr_string = format!(
+ "{}.{}",
+ curr_string,
+ def.variants[variant].fields[idx as usize].ident.name.as_str()
+ );
+ }
+ ty::Tuple(_) => {
+ curr_string = format!("{}.{}", curr_string, idx);
+ }
+ _ => {
+ bug!(
+ "Field projection applied to a type other than Adt or Tuple: {:?}.",
+ place.ty_before_projection(i).kind()
+ )
+ }
+ },
+ proj => bug!("{:?} unexpected because it isn't captured", proj),
+ }
+ }
+
+ curr_string.to_string()
+}
+
/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
/// for a particular capture as well as identifying the part of the source code
/// that triggered this capture to occur.
let did = did.expect_local();
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
- if let Some((span, name)) =
+ if let Some((span, hir_place)) =
self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
{
diag.span_note(
&format!(
"closure cannot be invoked more than once because it moves the \
variable `{}` out of its environment",
- name,
+ ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
),
);
return;
let did = did.expect_local();
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
- if let Some((span, name)) =
+ if let Some((span, hir_place)) =
self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id)
{
diag.span_note(
&format!(
"closure cannot be moved more than once as it is not `Copy` due to \
moving the variable `{}` out of its environment",
- name
+ ty::place_to_string_for_capture(self.infcx.tcx, hir_place)
),
);
}
if let Some(typeck_results) = self.in_progress_typeck_results {
let typeck_results = typeck_results.borrow();
match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
- (ty::ClosureKind::FnOnce, Some((span, name))) => {
+ (ty::ClosureKind::FnOnce, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnOnce` because it moves the \
variable `{}` out of its environment",
- name
+ ty::place_to_string_for_capture(tcx, place)
),
);
}
- (ty::ClosureKind::FnMut, Some((span, name))) => {
+ (ty::ClosureKind::FnMut, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnMut` because it mutates the \
variable `{}` here",
- name
+ ty::place_to_string_for_capture(tcx, place)
),
);
}
self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
// If we have an origin, store it.
- if let Some(origin) = delegate.current_origin {
+ if let Some(origin) = delegate.current_origin.clone() {
+ let origin = if self.tcx.features().capture_disjoint_fields {
+ origin
+ } else {
+ // FIXME(project-rfc-2229#26): Once rust-lang#80092 is merged, we should restrict the
+ // precision of origin as well. Otherwise, this will cause issues when project-rfc-2229#26
+ // is fixed as we might see Index projections in the origin, which we can't print because
+ // we don't store enough information.
+ (origin.0, Place { projections: vec![], ..origin.1 })
+ };
+
self.typeck_results
.borrow_mut()
.closure_kind_origins_mut()
// If we modified `current_closure_kind`, this field contains a `Some()` with the
// variable access that caused us to do so.
- current_origin: Option<(Span, Symbol)>,
+ current_origin: Option<(Span, Place<'tcx>)>,
/// For each Place that is captured by the closure, we track the minimal kind of
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
upvar_id.closure_expr_id,
ty::ClosureKind::FnOnce,
usage_span,
- var_name(tcx, upvar_id.var_path.hir_id),
+ place_with_id.place.clone(),
);
let capture_info = ty::CaptureInfo {
upvar_id.closure_expr_id,
ty::ClosureKind::FnMut,
tcx.hir().span(diag_expr_id),
- var_name(tcx, upvar_id.var_path.hir_id),
+ place_with_id.place.clone(),
);
}
}
closure_id: LocalDefId,
new_kind: ty::ClosureKind,
upvar_span: Span,
- var_name: Symbol,
+ place: Place<'tcx>,
) {
debug!(
- "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})",
- closure_id, new_kind, upvar_span, var_name
+ "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})",
+ closure_id, new_kind, upvar_span, place
);
// Is this the closure whose kind is currently being inferred?
| (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// new kind is stronger than the old kind
self.current_closure_kind = new_kind;
- self.current_origin = Some((upvar_span, var_name));
+ self.current_origin = Some((upvar_span, place));
}
}
}
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (&id, &origin) in fcx_typeck_results.closure_kind_origins().iter() {
- let hir_id = hir::HirId { owner: common_hir_owner, local_id: id };
- self.typeck_results.closure_kind_origins_mut().insert(hir_id, origin);
+ for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id };
+ let place_span = origin.0;
+ let place = self.resolve(origin.1.clone(), &place_span);
+ self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place));
}
}
--- /dev/null
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+
+// Check that precise paths are being reported back in the error message.
+
+
+enum MultiVariant {
+ Point(i32, i32),
+ Meta(i32)
+}
+
+fn main() {
+ let mut point = MultiVariant::Point(10, -10,);
+
+ let mut meta = MultiVariant::Meta(1);
+
+ let c = || {
+ if let MultiVariant::Point(ref mut x, _) = point {
+ *x += 1;
+ }
+
+ if let MultiVariant::Meta(ref mut v) = meta {
+ *v += 1;
+ }
+ };
+
+ let a = c;
+ let b = c; //~ ERROR use of moved value: `c` [E0382]
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/closure-origin-multi-variant-diagnostics.rs:1:12
+ |
+LL | #![feature(capture_disjoint_fields)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0382]: use of moved value: `c`
+ --> $DIR/closure-origin-multi-variant-diagnostics.rs:30:13
+ |
+LL | let a = c;
+ | - value moved here
+LL | let b = c;
+ | ^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+ --> $DIR/closure-origin-multi-variant-diagnostics.rs:20:52
+ |
+LL | if let MultiVariant::Point(ref mut x, _) = point {
+ | ^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+
+// Check that precise paths are being reported back in the error message.
+
+enum SingleVariant {
+ Point(i32, i32),
+}
+
+fn main() {
+ let mut point = SingleVariant::Point(10, -10);
+
+ let c = || {
+ // FIXME(project-rfc-2229#24): Change this to be a destructure pattern
+ // once this is fixed, to remove the warning.
+ if let SingleVariant::Point(ref mut x, _) = point {
+ //~^ WARNING: irrefutable if-let pattern
+ *x += 1;
+ }
+ };
+
+ let b = c;
+ let a = c; //~ ERROR use of moved value: `c` [E0382]
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:1:12
+ |
+LL | #![feature(capture_disjoint_fields)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: irrefutable if-let pattern
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:18:9
+ |
+LL | / if let SingleVariant::Point(ref mut x, _) = point {
+LL | |
+LL | | *x += 1;
+LL | | }
+ | |_________^
+ |
+ = note: `#[warn(irrefutable_let_patterns)]` on by default
+
+error[E0382]: use of moved value: `c`
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:25:13
+ |
+LL | let b = c;
+ | - value moved here
+LL | let a = c;
+ | ^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:18:53
+ |
+LL | if let SingleVariant::Point(ref mut x, _) = point {
+ | ^^^^^
+
+error: aborting due to previous error; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+
+// Check that precise paths are being reported back in the error message.
+
+struct Y {
+ y: X
+}
+
+struct X {
+ a: u32,
+ b: u32,
+}
+
+fn main() {
+ let mut x = Y { y: X { a: 5, b: 0 } };
+ let hello = || {
+ x.y.a += 1;
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/closure-origin-struct-diagnostics.rs:1:12
+ |
+LL | #![feature(capture_disjoint_fields)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0382]: use of moved value: `hello`
+ --> $DIR/closure-origin-struct-diagnostics.rs:24:13
+ |
+LL | let b = hello;
+ | ----- value moved here
+LL | let c = hello;
+ | ^^^^^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.y.a` out of its environment
+ --> $DIR/closure-origin-struct-diagnostics.rs:20:9
+ |
+LL | x.y.a += 1;
+ | ^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+
+// Check that precise paths are being reported back in the error message.
+
+fn main() {
+ let mut x = (5, 0);
+ let hello = || {
+ x.0 += 1;
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/closure-origin-tuple-diagnostics-1.rs:1:12
+ |
+LL | #![feature(capture_disjoint_fields)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0382]: use of moved value: `hello`
+ --> $DIR/closure-origin-tuple-diagnostics-1.rs:15:13
+ |
+LL | let b = hello;
+ | ----- value moved here
+LL | let c = hello;
+ | ^^^^^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.0` out of its environment
+ --> $DIR/closure-origin-tuple-diagnostics-1.rs:11:9
+ |
+LL | x.0 += 1;
+ | ^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0382`.
--- /dev/null
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+struct S(String, String);
+
+fn expect_fn<F: Fn()>(_f: F) {}
+
+fn main() {
+ let s = S(format!("s"), format!("s"));
+ let c = || { //~ ERROR expected a closure that implements the `Fn`
+ let s = s.1;
+ };
+ expect_fn(c);
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/closure-origin-tuple-diagnostics.rs:1:12
+ |
+LL | #![feature(capture_disjoint_fields)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+ --> $DIR/closure-origin-tuple-diagnostics.rs:11:13
+ |
+LL | let c = || {
+ | ^^ this closure implements `FnOnce`, not `Fn`
+LL | let s = s.1;
+ | --- closure is `FnOnce` because it moves the variable `s.1` out of its environment
+LL | };
+LL | expect_fn(c);
+ | --------- the requirement to implement `Fn` derives from here
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0525`.