1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use rustc_data_structures
::indexed_vec
::Idx
;
15 use rustc_errors
::DiagnosticBuilder
;
18 use borrow_check
::MirBorrowckCtxt
;
19 use dataflow
::move_paths
::{IllegalMoveOrigin, IllegalMoveOriginKind}
;
20 use dataflow
::move_paths
::{LookupResult, MoveError, MovePathIndex}
;
21 use util
::borrowck_errors
::{BorrowckErrors, Origin}
;
23 // Often when desugaring a pattern match we may have many individual moves in
24 // MIR that are all part of one operation from the user's point-of-view. For
29 // would move x from the 0 field of some temporary, and y from the 1 field. We
30 // group such errors together for cleaner error reporting.
32 // Errors are kept separate if they are from places with different parent move
33 // paths. For example, this generates two errors:
35 // let (&x, &y) = (&String::new(), &String::new());
37 enum GroupedMoveError
<'tcx
> {
38 // Match place can't be moved from
39 // e.g. match x[0] { s => (), } where x: &[String]
42 move_from
: Place
<'tcx
>,
43 kind
: IllegalMoveOriginKind
<'tcx
>,
46 // Part of a pattern can't be moved from,
47 // e.g. match &String::new() { &x => (), }
50 move_from
: MovePathIndex
,
51 kind
: IllegalMoveOriginKind
<'tcx
>,
54 // Everything that isn't from pattern matching.
57 kind
: IllegalMoveOriginKind
<'tcx
>,
61 impl<'a
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'a
, 'gcx
, 'tcx
> {
62 pub(crate) fn report_move_errors(&mut self, move_errors
: Vec
<MoveError
<'tcx
>>) {
63 let grouped_errors
= self.group_move_errors(move_errors
);
64 for error
in grouped_errors
{
69 fn group_move_errors(&self, errors
: Vec
<MoveError
<'tcx
>>) -> Vec
<GroupedMoveError
<'tcx
>> {
70 let mut grouped_errors
= Vec
::new();
72 self.append_to_grouped_errors(&mut grouped_errors
, error
);
77 fn append_to_grouped_errors(
79 grouped_errors
: &mut Vec
<GroupedMoveError
<'tcx
>>,
80 error
: MoveError
<'tcx
>,
83 MoveError
::UnionMove { .. }
=> {
84 unimplemented
!("don't know how to report union move errors yet.")
86 MoveError
::IllegalMove
{
87 cannot_move_out_of
: IllegalMoveOrigin { location, kind }
,
89 let stmt_source_info
= self.mir
.source_info(location
);
90 // Note: that the only time we assign a place isn't a temporary
91 // to a user variable is when initializing it.
92 // If that ever stops being the case, then the ever initialized
93 // flow could be used.
94 if let Some(StatementKind
::Assign(
96 Rvalue
::Use(Operand
::Move(move_from
)),
97 )) = self.mir
.basic_blocks()[location
.block
]
99 .get(location
.statement_index
)
100 .map(|stmt
| &stmt
.kind
)
102 let local_decl
= &self.mir
.local_decls
[*local
];
103 // opt_match_place is the
104 // match_span is the span of the expression being matched on
105 // match *x.y { ... } match_place is Some(*x.y)
106 // ^^^^ match_span is the span of *x.y
108 // opt_match_place is None for let [mut] x = ... statements,
109 // whether or not the right-hand side is a place expression
110 if let Some(ClearCrossCrate
::Set(BindingForm
::Var(VarBindingForm
{
111 opt_match_place
: Some((ref opt_match_place
, match_span
)),
114 }))) = local_decl
.is_user_variable
116 self.append_binding_error(
123 stmt_source_info
.span
,
128 grouped_errors
.push(GroupedMoveError
::OtherIllegalMove
{
129 span
: stmt_source_info
.span
,
136 fn append_binding_error(
138 grouped_errors
: &mut Vec
<GroupedMoveError
<'tcx
>>,
139 kind
: IllegalMoveOriginKind
<'tcx
>,
140 move_from
: &Place
<'tcx
>,
142 match_place
: &Option
<Place
<'tcx
>>,
144 statement_span
: Span
,
147 "append_to_grouped_errors(match_place={:?}, match_span={:?})",
148 match_place
, match_span
151 let from_simple_let
= match_place
.is_none();
152 let match_place
= match_place
.as_ref().unwrap_or(move_from
);
154 match self.move_data
.rev_lookup
.find(match_place
) {
155 // Error with the match place
156 LookupResult
::Parent(_
) => {
157 for ge
in &mut *grouped_errors
{
158 if let GroupedMoveError
::MovesFromMatchPlace { span, binds_to, .. }
= ge
{
159 if match_span
== *span
{
160 debug
!("appending local({:?}) to list", bind_to
);
161 if !binds_to
.is_empty() {
162 binds_to
.push(bind_to
);
168 debug
!("found a new move error location");
170 // Don't need to point to x in let x = ... .
171 let (binds_to
, span
) = if from_simple_let
{
172 (vec
![], statement_span
)
174 (vec
![bind_to
], match_span
)
176 grouped_errors
.push(GroupedMoveError
::MovesFromMatchPlace
{
178 move_from
: match_place
.clone(),
183 // Error with the pattern
184 LookupResult
::Exact(_
) => {
185 let mpi
= match self.move_data
.rev_lookup
.find(move_from
) {
186 LookupResult
::Parent(Some(mpi
)) => mpi
,
187 // move_from should be a projection from match_place.
188 _
=> unreachable
!("Probably not unreachable..."),
190 for ge
in &mut *grouped_errors
{
191 if let GroupedMoveError
::MovesFromPattern
{
193 move_from
: other_mpi
,
198 if match_span
== *span
&& mpi
== *other_mpi
{
199 debug
!("appending local({:?}) to list", bind_to
);
200 binds_to
.push(bind_to
);
205 debug
!("found a new move error location");
206 grouped_errors
.push(GroupedMoveError
::MovesFromPattern
{
210 binds_to
: vec
![bind_to
],
216 fn report(&mut self, error
: GroupedMoveError
<'tcx
>) {
217 let (mut err
, err_span
) = {
218 let (span
, kind
): (Span
, &IllegalMoveOriginKind
) = match error
{
219 GroupedMoveError
::MovesFromMatchPlace { span, ref kind, .. }
220 | GroupedMoveError
::MovesFromPattern { span, ref kind, .. }
221 | GroupedMoveError
::OtherIllegalMove { span, ref kind }
=> (span
, kind
),
223 let origin
= Origin
::Mir
;
226 IllegalMoveOriginKind
::Static
=> {
227 self.tcx
.cannot_move_out_of(span
, "static item", origin
)
229 IllegalMoveOriginKind
::BorrowedContent { target_place: place }
=> {
230 // Inspect the type of the content behind the
231 // borrow to provide feedback about why this
232 // was a move rather than a copy.
233 let ty
= place
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
235 ty
::TyArray(..) | ty
::TySlice(..) => self
237 .cannot_move_out_of_interior_noncopy(span
, ty
, None
, origin
),
238 ty
::TyClosure(def_id
, closure_substs
)
239 if !self.mir
.upvar_decls
.is_empty()
242 Place
::Projection(ref proj
) => {
243 proj
.base
== Place
::Local(Local
::new(1))
246 Place
::Local(_
) | Place
::Static(_
) => unreachable
!(),
250 let closure_kind_ty
=
251 closure_substs
.closure_kind_ty(def_id
, self.tcx
);
252 let closure_kind
= closure_kind_ty
.to_opt_closure_kind();
253 let place_description
= match closure_kind
{
254 Some(ty
::ClosureKind
::Fn
) => {
255 "captured variable in an `Fn` closure"
257 Some(ty
::ClosureKind
::FnMut
) => {
258 "captured variable in an `FnMut` closure"
260 Some(ty
::ClosureKind
::FnOnce
) => {
261 bug
!("closure kind does not match first argument type")
263 None
=> bug
!("closure kind not inferred by borrowck"),
265 self.tcx
.cannot_move_out_of(span
, place_description
, origin
)
269 .cannot_move_out_of(span
, "borrowed content", origin
),
272 IllegalMoveOriginKind
::InteriorOfTypeWithDestructor { container_ty: ty }
=> {
274 .cannot_move_out_of_interior_of_drop(span
, ty
, origin
)
276 IllegalMoveOriginKind
::InteriorOfSliceOrArray { ty, is_index }
=> self
278 .cannot_move_out_of_interior_noncopy(span
, ty
, Some(*is_index
), origin
),
284 self.add_move_hints(error
, &mut err
, err_span
);
285 err
.buffer(&mut self.errors_buffer
);
290 error
: GroupedMoveError
<'tcx
>,
291 err
: &mut DiagnosticBuilder
<'a
>,
295 GroupedMoveError
::MovesFromMatchPlace
{
300 // Ok to suggest a borrow, since the target can't be moved from
302 if let Ok(snippet
) = self.tcx
.sess
.codemap().span_to_snippet(span
) {
304 Place
::Projection(ref proj
)
305 if self.suitable_to_remove_deref(proj
, &snippet
) =>
309 "consider removing this dereference operator",
310 (&snippet
[1..]).to_owned(),
316 "consider using a reference instead",
317 format
!("&{}", snippet
),
324 for local
in binds_to
{
325 let bind_to
= &self.mir
.local_decls
[local
];
326 let binding_span
= bind_to
.source_info
.span
;
330 "move occurs because {} has type `{}`, \
331 which does not implement the `Copy` trait",
332 bind_to
.name
.unwrap(),
339 GroupedMoveError
::MovesFromPattern { mut binds_to, .. }
=> {
340 // Suggest ref, since there might be a move in
344 for local
in binds_to
{
345 let bind_to
= &self.mir
.local_decls
[local
];
346 let binding_span
= bind_to
.source_info
.span
;
348 // Suggest ref mut when the user has already written mut.
349 let ref_kind
= match bind_to
.mutability
{
350 Mutability
::Not
=> "ref",
351 Mutability
::Mut
=> "ref mut",
357 "to prevent move, use ref or ref mut",
358 format
!("{} {:?}", ref_kind
, name
),
364 format
!("Local {:?} is not suitable for ref", bind_to
),
370 // Nothing to suggest.
371 GroupedMoveError
::OtherIllegalMove { .. }
=> (),
375 fn suitable_to_remove_deref(&self, proj
: &PlaceProjection
<'tcx
>, snippet
: &str) -> bool
{
376 let is_shared_ref
= |ty
: ty
::Ty
| match ty
.sty
{
377 ty
::TypeVariants
::TyRef(.., hir
::Mutability
::MutImmutable
) => true,
381 proj
.elem
== ProjectionElem
::Deref
&& snippet
.starts_with('
*'
) && match proj
.base
{
382 Place
::Local(local
) => {
383 let local_decl
= &self.mir
.local_decls
[local
];
384 // If this is a temporary, then this could be from an
385 // overloaded * operator.
386 local_decl
.is_user_variable
.is_some() && is_shared_ref(local_decl
.ty
)
388 Place
::Promoted(_
) => true,
389 Place
::Static(ref st
) => is_shared_ref(st
.ty
),
390 Place
::Projection(ref proj
) => match proj
.elem
{
391 ProjectionElem
::Field(_
, ty
) => is_shared_ref(ty
),