1
Fork 0

Group move errors before reporting, add suggestions

This commit is contained in:
Matthew Jasper 2018-06-27 22:06:54 +01:00
parent 43fce075d3
commit 0193d1f736
9 changed files with 502 additions and 66 deletions

View file

@ -497,8 +497,8 @@ pub enum LocalKind {
ReturnPointer, ReturnPointer,
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] #[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct VarBindingForm { pub struct VarBindingForm<'tcx> {
/// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`?
pub binding_mode: ty::BindingMode, pub binding_mode: ty::BindingMode,
/// If an explicit type was provided for this variable binding, /// If an explicit type was provided for this variable binding,
@ -508,21 +508,49 @@ pub struct VarBindingForm {
/// doing so breaks incremental compilation (as of this writing), /// doing so breaks incremental compilation (as of this writing),
/// while a `Span` does not cause our tests to fail. /// while a `Span` does not cause our tests to fail.
pub opt_ty_info: Option<Span>, pub opt_ty_info: Option<Span>,
/// Place of the RHS of the =, or the subject of the `match` where this
/// variable is initialized. None in the case of `let PATTERN;`.
/// Some((None, ..)) in the case of and `let [mut] x = ...` because
/// (a) the right-hand side isn't evaluated as a place expression.
/// (b) it gives a way to separate this case from the remaining cases
/// for diagnostics.
pub opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] #[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum BindingForm { pub enum BindingForm<'tcx> {
/// This is a binding for a non-`self` binding, or a `self` that has an explicit type. /// This is a binding for a non-`self` binding, or a `self` that has an explicit type.
Var(VarBindingForm), Var(VarBindingForm<'tcx>),
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
ImplicitSelf, ImplicitSelf,
} }
CloneTypeFoldableAndLiftImpls! { BindingForm, } CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
impl_stable_hash_for!(struct self::VarBindingForm { binding_mode, opt_ty_info }); impl_stable_hash_for!(struct self::VarBindingForm<'tcx> {
binding_mode,
opt_ty_info,
opt_match_place
});
impl_stable_hash_for!(enum self::BindingForm { Var(binding), ImplicitSelf, }); mod binding_form_impl {
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use ich::StableHashingContext;
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for super::BindingForm<'tcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
use super::BindingForm::*;
::std::mem::discriminant(self).hash_stable(hcx, hasher);
match self {
Var(binding) => binding.hash_stable(hcx, hasher),
ImplicitSelf => (),
}
}
}
}
/// A MIR local. /// A MIR local.
/// ///
@ -542,7 +570,7 @@ pub struct LocalDecl<'tcx> {
/// therefore it need not be visible across crates. pnkfelix /// therefore it need not be visible across crates. pnkfelix
/// currently hypothesized we *need* to wrap this in a /// currently hypothesized we *need* to wrap this in a
/// `ClearCrossCrate` as long as it carries as `HirId`. /// `ClearCrossCrate` as long as it carries as `HirId`.
pub is_user_variable: Option<ClearCrossCrate<BindingForm>>, pub is_user_variable: Option<ClearCrossCrate<BindingForm<'tcx>>>,
/// True if this is an internal local /// True if this is an internal local
/// ///
@ -670,6 +698,7 @@ impl<'tcx> LocalDecl<'tcx> {
Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_), binding_mode: ty::BindingMode::BindByValue(_),
opt_ty_info: _, opt_ty_info: _,
opt_match_place: _,
}))) => true, }))) => true,
// FIXME: might be able to thread the distinction between // FIXME: might be able to thread the distinction between
@ -688,6 +717,7 @@ impl<'tcx> LocalDecl<'tcx> {
Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_), binding_mode: ty::BindingMode::BindByValue(_),
opt_ty_info: _, opt_ty_info: _,
opt_match_place: _,
}))) => true, }))) => true,
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true, Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true,

View file

@ -35,7 +35,6 @@ use syntax_pos::Span;
use dataflow::indexes::BorrowIndex; use dataflow::indexes::BorrowIndex;
use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
use dataflow::Borrows; use dataflow::Borrows;
use dataflow::DataflowResultsConsumer; use dataflow::DataflowResultsConsumer;
use dataflow::FlowAtLocation; use dataflow::FlowAtLocation;
@ -62,6 +61,7 @@ mod path_utils;
crate mod place_ext; crate mod place_ext;
mod prefixes; mod prefixes;
mod used_muts; mod used_muts;
mod move_errors;
pub(crate) mod nll; pub(crate) mod nll;
@ -117,40 +117,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
Ok(move_data) => move_data, Ok(move_data) => move_data,
Err((move_data, move_errors)) => { Err((move_data, move_errors)) => {
for move_error in move_errors { move_errors::report_move_errors(&mir, tcx, move_errors, &move_data);
let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
MoveError::UnionMove { .. } => {
unimplemented!("don't know how to report union move errors yet.")
}
MoveError::IllegalMove {
cannot_move_out_of: o,
} => (o.span, o.kind),
};
let origin = Origin::Mir;
let mut err = match kind {
IllegalMoveOriginKind::Static => {
tcx.cannot_move_out_of(span, "static item", origin)
}
IllegalMoveOriginKind::BorrowedContent { target_ty: ty } => {
// Inspect the type of the content behind the
// borrow to provide feedback about why this
// was a move rather than a copy.
match ty.sty {
ty::TyArray(..) | ty::TySlice(..) => {
tcx.cannot_move_out_of_interior_noncopy(span, ty, None, origin)
}
_ => tcx.cannot_move_out_of(span, "borrowed content", origin),
}
}
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
tcx.cannot_move_out_of_interior_of_drop(span, ty, origin)
}
IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
tcx.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index), origin)
}
};
err.emit();
}
move_data move_data
} }
}; };

View file

@ -0,0 +1,388 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, TyCtxt};
use rustc_errors::DiagnosticBuilder;
use syntax_pos::Span;
use dataflow::move_paths::{IllegalMoveOrigin, IllegalMoveOriginKind, MoveData};
use dataflow::move_paths::{LookupResult, MoveError, MovePathIndex};
use util::borrowck_errors::{BorrowckErrors, Origin};
pub(crate) fn report_move_errors<'gcx, 'tcx>(
mir: &Mir<'tcx>,
tcx: TyCtxt<'_, 'gcx, 'tcx>,
move_errors: Vec<MoveError<'tcx>>,
move_data: &MoveData<'tcx>,
) {
MoveErrorCtxt {
mir,
tcx,
move_data,
}.report_errors(move_errors);
}
#[derive(Copy, Clone)]
struct MoveErrorCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
move_data: &'a MoveData<'tcx>,
}
// Often when desugaring a pattern match we may have many individual moves in
// MIR that are all part of one operation from the user's point-of-view. For
// example:
//
// let (x, y) = foo()
//
// would move x from the 0 field of some temporary, and y from the 1 field. We
// group such errors together for cleaner error reporting.
//
// Errors are kept separate if they are from places with different parent move
// paths. For example, this generates two errors:
//
// let (&x, &y) = (&String::new(), &String::new());
#[derive(Debug)]
enum GroupedMoveError<'tcx> {
// Match place can't be moved from
// e.g. match x[0] { s => (), } where x: &[String]
MovesFromMatchPlace {
span: Span,
move_from: Place<'tcx>,
kind: IllegalMoveOriginKind<'tcx>,
binds_to: Vec<Local>,
},
// Part of a pattern can't be moved from,
// e.g. match &String::new() { &x => (), }
MovesFromPattern {
span: Span,
move_from: MovePathIndex,
kind: IllegalMoveOriginKind<'tcx>,
binds_to: Vec<Local>,
},
// Everything that isn't from pattern matching.
OtherIllegalMove {
span: Span,
kind: IllegalMoveOriginKind<'tcx>,
},
}
impl<'a, 'gcx, 'tcx> MoveErrorCtxt<'a, 'gcx, 'tcx> {
fn report_errors(self, move_errors: Vec<MoveError<'tcx>>) {
let grouped_errors = self.group_move_errors(move_errors);
for error in grouped_errors {
self.report(error);
}
}
fn group_move_errors(self, errors: Vec<MoveError<'tcx>>) -> Vec<GroupedMoveError<'tcx>> {
let mut grouped_errors = Vec::new();
for error in errors {
self.append_to_grouped_errors(&mut grouped_errors, error);
}
grouped_errors
}
fn append_to_grouped_errors(
self,
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
error: MoveError<'tcx>,
) {
match error {
MoveError::UnionMove { .. } => {
unimplemented!("don't know how to report union move errors yet.")
}
MoveError::IllegalMove {
cannot_move_out_of: IllegalMoveOrigin { location, kind },
} => {
let stmt_source_info = self.mir.source_info(location);
if let Some(StatementKind::Assign(
Place::Local(local),
Rvalue::Use(Operand::Move(move_from)),
)) = self.mir.basic_blocks()[location.block]
.statements
.get(location.statement_index)
.map(|stmt| &stmt.kind)
{
let local_decl = &self.mir.local_decls[*local];
if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
opt_match_place: Some((ref opt_match_place, match_span)),
binding_mode: _,
opt_ty_info: _,
}))) = local_decl.is_user_variable
{
// opt_match_place is the
// match_span is the span of the expression being matched on
// match *x.y { ... } match_place is Some(*x.y)
// ^^^^ match_span is the span of *x.y
// opt_match_place is None for let [mut] x = ... statements,
// whether or not the right-hand side is a place expression
// HACK use scopes to determine if this assignment is
// the initialization of a variable.
// FIXME(matthewjasper) This would probably be more
// reliable if it used the ever initialized dataflow
// but move errors are currently reported before the
// rest of borrowck has run.
if self
.mir
.is_sub_scope(local_decl.source_info.scope, stmt_source_info.scope)
{
self.append_binding_error(
grouped_errors,
kind,
move_from,
*local,
opt_match_place,
match_span,
);
}
return;
}
}
grouped_errors.push(GroupedMoveError::OtherIllegalMove {
span: stmt_source_info.span,
kind,
});
}
}
}
fn append_binding_error(
self,
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
kind: IllegalMoveOriginKind<'tcx>,
move_from: &Place<'tcx>,
bind_to: Local,
match_place: &Option<Place<'tcx>>,
match_span: Span,
) {
debug!(
"append_to_grouped_errors(match_place={:?}, match_span={:?})",
match_place, match_span
);
let from_simple_let = match_place.is_none();
let match_place = match_place.as_ref().unwrap_or(move_from);
match self.move_data.rev_lookup.find(match_place) {
// Error with the match place
LookupResult::Parent(_) => {
for ge in &mut *grouped_errors {
if let GroupedMoveError::MovesFromMatchPlace { span, binds_to, .. } = ge {
if match_span == *span {
debug!("appending local({:?}) to list", bind_to);
if !binds_to.is_empty() {
binds_to.push(bind_to);
}
return;
}
}
}
debug!("found a new move error location");
// Don't need to point to x in let x = ... .
let binds_to = if from_simple_let {
vec![]
} else {
vec![bind_to]
};
grouped_errors.push(GroupedMoveError::MovesFromMatchPlace {
span: match_span,
move_from: match_place.clone(),
kind,
binds_to,
});
}
// Error with the pattern
LookupResult::Exact(_) => {
let mpi = match self.move_data.rev_lookup.find(move_from) {
LookupResult::Parent(Some(mpi)) => mpi,
// move_from should be a projection from match_place.
_ => unreachable!("Probably not unreachable..."),
};
for ge in &mut *grouped_errors {
if let GroupedMoveError::MovesFromPattern {
span,
move_from: other_mpi,
binds_to,
..
} = ge
{
if match_span == *span && mpi == *other_mpi {
debug!("appending local({:?}) to list", bind_to);
binds_to.push(bind_to);
return;
}
}
}
debug!("found a new move error location");
grouped_errors.push(GroupedMoveError::MovesFromPattern {
span: match_span,
move_from: mpi,
kind,
binds_to: vec![bind_to],
});
}
};
}
fn report(self, error: GroupedMoveError<'tcx>) {
let (mut err, err_span) = {
let (span, kind): (Span, &IllegalMoveOriginKind) = match error {
GroupedMoveError::MovesFromMatchPlace { span, ref kind, .. }
| GroupedMoveError::MovesFromPattern { span, ref kind, .. }
| GroupedMoveError::OtherIllegalMove { span, ref kind } => (span, kind),
};
let origin = Origin::Mir;
(
match kind {
IllegalMoveOriginKind::Static => {
self.tcx.cannot_move_out_of(span, "static item", origin)
}
IllegalMoveOriginKind::BorrowedContent { target_ty: ty } => {
// Inspect the type of the content behind the
// borrow to provide feedback about why this
// was a move rather than a copy.
match ty.sty {
ty::TyArray(..) | ty::TySlice(..) => self
.tcx
.cannot_move_out_of_interior_noncopy(span, ty, None, origin),
_ => self
.tcx
.cannot_move_out_of(span, "borrowed content", origin),
}
}
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
self.tcx
.cannot_move_out_of_interior_of_drop(span, ty, origin)
}
IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => self
.tcx
.cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index), origin),
},
span,
)
};
self.add_move_hints(error, &mut err, err_span);
err.emit();
}
fn add_move_hints(
self,
error: GroupedMoveError<'tcx>,
err: &mut DiagnosticBuilder<'a>,
span: Span,
) {
match error {
GroupedMoveError::MovesFromMatchPlace {
mut binds_to,
move_from,
..
} => {
// Ok to suggest a borrow, since the target can't be moved from
// anyway.
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
match move_from {
Place::Projection(ref proj)
if self.suitable_to_remove_deref(proj, &snippet) =>
{
err.span_suggestion(
span,
"consider removing this dereference operator",
format!("{}", &snippet[1..]),
);
}
_ => {
err.span_suggestion(
span,
"consider using a reference instead",
format!("&{}", snippet),
);
}
}
binds_to.sort();
binds_to.dedup();
for local in binds_to {
let bind_to = &self.mir.local_decls[local];
let binding_span = bind_to.source_info.span;
err.span_label(
binding_span,
format!(
"move occurs because {} has type `{}`, \
which does not implement the `Copy` trait",
bind_to.name.unwrap(),
bind_to.ty
),
);
}
}
}
GroupedMoveError::MovesFromPattern { mut binds_to, .. } => {
// Suggest ref, since there might be a move in
// another match arm
binds_to.sort();
binds_to.dedup();
for local in binds_to {
let bind_to = &self.mir.local_decls[local];
let binding_span = bind_to.source_info.span;
// Suggest ref mut when the user has already written mut.
let ref_kind = match bind_to.mutability {
Mutability::Not => "ref",
Mutability::Mut => "ref mut",
};
match bind_to.name {
Some(name) => {
err.span_suggestion(
binding_span,
"to prevent move, use ref or ref mut",
format!("{} {:?}", ref_kind, name),
);
}
None => {
err.span_label(
span,
format!("Local {:?} is not suitable for ref", bind_to),
);
}
}
}
}
// Nothing to suggest.
GroupedMoveError::OtherIllegalMove { .. } => (),
}
}
fn suitable_to_remove_deref(self, proj: &PlaceProjection<'tcx>, snippet: &str) -> bool {
let is_shared_ref = |ty: ty::Ty| match ty.sty {
ty::TypeVariants::TyRef(.., hir::Mutability::MutImmutable) => true,
_ => false,
};
proj.elem == ProjectionElem::Deref && snippet.starts_with('*') && match proj.base {
Place::Local(local) => {
let local_decl = &self.mir.local_decls[local];
// If this is a temporary, then this could be from an
// overloaded * operator.
local_decl.is_user_variable.is_some() && is_shared_ref(local_decl.ty)
}
Place::Static(ref st) => is_shared_ref(st.ty),
Place::Projection(ref proj) => match proj.elem {
ProjectionElem::Field(_, ty) => is_shared_ref(ty),
_ => false,
},
}
}
}

View file

@ -115,11 +115,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Declare the bindings, which may create a source scope. // Declare the bindings, which may create a source scope.
let remainder_span = remainder_scope.span(this.hir.tcx(), let remainder_span = remainder_scope.span(this.hir.tcx(),
&this.hir.region_scope_tree); &this.hir.region_scope_tree);
let scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
ArmHasGuard(false)); let scope;
// Evaluate the initializer, if present. // Evaluate the initializer, if present.
if let Some(init) = initializer { if let Some(init) = initializer {
let initializer_span = init.span();
scope = this.declare_bindings(
None,
remainder_span,
lint_level,
&pattern,
ArmHasGuard(false),
Some((None, initializer_span)),
);
unpack!(block = this.in_opt_scope( unpack!(block = this.in_opt_scope(
opt_destruction_scope.map(|de|(de, source_info)), block, |this| { opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
let scope = (init_scope, source_info); let scope = (init_scope, source_info);
@ -128,6 +138,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}) })
})); }));
} else { } else {
scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
ArmHasGuard(false), None);
// FIXME(#47184): We currently only insert `UserAssertTy` statements for // FIXME(#47184): We currently only insert `UserAssertTy` statements for
// patterns that are bindings, this is as we do not want to deconstruct // patterns that are bindings, this is as we do not want to deconstruct
// the type being assertion to match the pattern. // the type being assertion to match the pattern.

View file

@ -44,6 +44,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
arms: Vec<Arm<'tcx>>) arms: Vec<Arm<'tcx>>)
-> BlockAnd<()> { -> BlockAnd<()> {
let tcx = self.hir.tcx(); let tcx = self.hir.tcx();
let discriminant_span = discriminant.span();
let discriminant_place = unpack!(block = self.as_place(block, discriminant)); let discriminant_place = unpack!(block = self.as_place(block, discriminant));
// Matching on a `discriminant_place` with an uninhabited type doesn't // Matching on a `discriminant_place` with an uninhabited type doesn't
@ -96,7 +97,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let scope = self.declare_bindings(None, body.span, let scope = self.declare_bindings(None, body.span,
LintLevel::Inherited, LintLevel::Inherited,
&arm.patterns[0], &arm.patterns[0],
ArmHasGuard(arm.guard.is_some())); ArmHasGuard(arm.guard.is_some()),
Some((Some(&discriminant_place), discriminant_span)));
(body, scope.unwrap_or(self.source_scope)) (body, scope.unwrap_or(self.source_scope))
}).collect(); }).collect();
@ -254,7 +256,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
_ => { _ => {
let place = unpack!(block = self.as_place(block, initializer)); let place = unpack!(block = self.as_place(block, initializer));
self.place_into_pattern(block, irrefutable_pat, &place) self.place_into_pattern(block, irrefutable_pat, &place, true)
} }
} }
} }
@ -262,7 +264,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
pub fn place_into_pattern(&mut self, pub fn place_into_pattern(&mut self,
mut block: BasicBlock, mut block: BasicBlock,
irrefutable_pat: Pattern<'tcx>, irrefutable_pat: Pattern<'tcx>,
initializer: &Place<'tcx>) initializer: &Place<'tcx>,
set_match_place: bool)
-> BlockAnd<()> { -> BlockAnd<()> {
// create a dummy candidate // create a dummy candidate
let mut candidate = Candidate { let mut candidate = Candidate {
@ -288,6 +291,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
candidate.match_pairs); candidate.match_pairs);
} }
// for matches and function arguments, the place that is being matched
// can be set when creating the variables. But the place for
// let PATTERN = ... might not even exist until we do the assignment.
// so we set it here instead
if set_match_place {
for binding in &candidate.bindings {
let local = self.var_local_id(binding.var_id, OutsideGuard);
if let Some(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm {opt_match_place: Some((ref mut match_place, _)), .. }
))) = self.local_decls[local].is_user_variable
{
*match_place = Some(initializer.clone());
} else {
bug!("Let binding to non-user variable.")
}
}
}
// now apply the bindings, which will also declare the variables // now apply the bindings, which will also declare the variables
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
@ -302,7 +324,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
scope_span: Span, scope_span: Span,
lint_level: LintLevel, lint_level: LintLevel,
pattern: &Pattern<'tcx>, pattern: &Pattern<'tcx>,
has_guard: ArmHasGuard) has_guard: ArmHasGuard,
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>)
-> Option<SourceScope> { -> Option<SourceScope> {
assert!(!(visibility_scope.is_some() && lint_level.is_explicit()), assert!(!(visibility_scope.is_some() && lint_level.is_explicit()),
"can't have both a visibility and a lint scope at the same time"); "can't have both a visibility and a lint scope at the same time");
@ -326,7 +349,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}; };
let visibility_scope = visibility_scope.unwrap(); let visibility_scope = visibility_scope.unwrap();
this.declare_binding(source_info, visibility_scope, mutability, name, mode, var, this.declare_binding(source_info, visibility_scope, mutability, name, mode, var,
ty, has_guard); ty, has_guard, opt_match_place.map(|(x, y)| (x.cloned(), y)));
}); });
visibility_scope visibility_scope
} }
@ -1121,7 +1144,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
mode: BindingMode, mode: BindingMode,
var_id: NodeId, var_id: NodeId,
var_ty: Ty<'tcx>, var_ty: Ty<'tcx>,
has_guard: ArmHasGuard) has_guard: ArmHasGuard,
opt_match_place: Option<(Option<Place<'tcx>>, Span)>)
{ {
debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
visibility_scope={:?}, source_info={:?})", visibility_scope={:?}, source_info={:?})",
@ -1146,6 +1170,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// idents in pat; but complex w/ unclear UI payoff. // idents in pat; but complex w/ unclear UI payoff.
// Instead, just abandon providing diagnostic info. // Instead, just abandon providing diagnostic info.
opt_ty_info: None, opt_ty_info: None,
opt_match_place,
}))), }))),
}; };
let for_arm_body = self.local_decls.push(local.clone()); let for_arm_body = self.local_decls.push(local.clone());

View file

@ -705,6 +705,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(pattern) = pattern { if let Some(pattern) = pattern {
let pattern = self.hir.pattern_from_hir(pattern); let pattern = self.hir.pattern_from_hir(pattern);
let span = pattern.span;
match *pattern.kind { match *pattern.kind {
// Don't introduce extra copies for simple bindings // Don't introduce extra copies for simple bindings
@ -716,15 +717,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} else { } else {
let binding_mode = ty::BindingMode::BindByValue(mutability.into()); let binding_mode = ty::BindingMode::BindByValue(mutability.into());
Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
binding_mode, opt_ty_info }))) binding_mode,
opt_ty_info,
opt_match_place: Some((Some(place.clone()), span)),
})))
}; };
self.var_indices.insert(var, LocalsForNode::One(local)); self.var_indices.insert(var, LocalsForNode::One(local));
} }
_ => { _ => {
scope = self.declare_bindings(scope, ast_body.span, scope = self.declare_bindings(scope, ast_body.span,
LintLevel::Inherited, &pattern, LintLevel::Inherited, &pattern,
matches::ArmHasGuard(false)); matches::ArmHasGuard(false),
unpack!(block = self.place_into_pattern(block, pattern, &place)); Some((Some(&place), span)));
unpack!(block = self.place_into_pattern(block, pattern, &place, false));
} }
} }
} }

View file

@ -109,8 +109,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
match *place { match *place {
Place::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), Place::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]),
Place::Static(..) => { Place::Static(..) => {
let span = self.builder.mir.source_info(self.loc).span; Err(MoveError::cannot_move_out_of(self.loc, Static))
Err(MoveError::cannot_move_out_of(span, Static))
} }
Place::Projection(ref proj) => { Place::Projection(ref proj) => {
self.move_path_for_projection(place, proj) self.move_path_for_projection(place, proj)
@ -133,13 +132,13 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
let mir = self.builder.mir; let mir = self.builder.mir;
let tcx = self.builder.tcx; let tcx = self.builder.tcx;
let place_ty = proj.base.ty(mir, tcx).to_ty(tcx); let place_ty = proj.base.ty(mir, tcx).to_ty(tcx);
match place_ty.sty { match place_ty.sty {
ty::TyRef(..) | ty::TyRawPtr(..) => ty::TyRef(..) | ty::TyRawPtr(..) =>
return Err(MoveError::cannot_move_out_of( return Err(MoveError::cannot_move_out_of(
mir.source_info(self.loc).span, self.loc,
BorrowedContent { target_ty: place.ty(mir, tcx).to_ty(tcx) })), BorrowedContent { target_ty: place.ty(mir, tcx).to_ty(tcx) })),
ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() =>
return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span, return Err(MoveError::cannot_move_out_of(self.loc,
InteriorOfTypeWithDestructor { InteriorOfTypeWithDestructor {
container_ty: place_ty container_ty: place_ty
})), })),
@ -148,7 +147,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
return Err(MoveError::UnionMove { path: base }), return Err(MoveError::UnionMove { path: base }),
ty::TySlice(_) => ty::TySlice(_) =>
return Err(MoveError::cannot_move_out_of( return Err(MoveError::cannot_move_out_of(
mir.source_info(self.loc).span, self.loc,
InteriorOfSliceOrArray { InteriorOfSliceOrArray {
ty: place_ty, is_index: match proj.elem { ty: place_ty, is_index: match proj.elem {
ProjectionElem::Index(..) => true, ProjectionElem::Index(..) => true,
@ -158,7 +157,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
ty::TyArray(..) => match proj.elem { ty::TyArray(..) => match proj.elem {
ProjectionElem::Index(..) => ProjectionElem::Index(..) =>
return Err(MoveError::cannot_move_out_of( return Err(MoveError::cannot_move_out_of(
mir.source_info(self.loc).span, self.loc,
InteriorOfSliceOrArray { InteriorOfSliceOrArray {
ty: place_ty, is_index: true ty: place_ty, is_index: true
})), })),

View file

@ -209,7 +209,7 @@ pub enum InitKind {
Deep, Deep,
/// Only does a shallow init /// Only does a shallow init
Shallow, Shallow,
/// This doesn't initialize the variabe on panic (and a panic is possible). /// This doesn't initialize the variable on panic (and a panic is possible).
NonPanicPathOnly, NonPanicPathOnly,
} }
@ -271,7 +271,7 @@ impl<'tcx> MovePathLookup<'tcx> {
#[derive(Debug)] #[derive(Debug)]
pub struct IllegalMoveOrigin<'tcx> { pub struct IllegalMoveOrigin<'tcx> {
pub(crate) span: Span, pub(crate) location: Location,
pub(crate) kind: IllegalMoveOriginKind<'tcx>, pub(crate) kind: IllegalMoveOriginKind<'tcx>,
} }
@ -304,8 +304,8 @@ pub enum MoveError<'tcx> {
} }
impl<'tcx> MoveError<'tcx> { impl<'tcx> MoveError<'tcx> {
fn cannot_move_out_of(span: Span, kind: IllegalMoveOriginKind<'tcx>) -> Self { fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
let origin = IllegalMoveOrigin { span, kind }; let origin = IllegalMoveOrigin { location, kind };
MoveError::IllegalMove { cannot_move_out_of: origin } MoveError::IllegalMove { cannot_move_out_of: origin }
} }
} }

View file

@ -315,6 +315,15 @@ pub enum LogicalOp {
Or, Or,
} }
impl<'tcx> ExprRef<'tcx> {
pub fn span(&self) -> Span {
match self {
ExprRef::Hair(expr) => expr.span,
ExprRef::Mirror(expr) => expr.span,
}
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// The Mirror trait // The Mirror trait