diff options
| author | bors <bors@rust-lang.org> | 2021-01-31 23:45:37 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-01-31 23:45:37 +0000 |
| commit | 941343e0871dd04ea774e8cee7755461b144ef29 (patch) | |
| tree | f1b0d9272f0e8021ea13bb21054700ce52406c2f /compiler | |
| parent | 0e63af5da3400ace48a0345117980473fd21ad73 (diff) | |
| parent | 9165676d91d3e458a57f4f4eacc3975d89be9f5a (diff) | |
| download | rust-941343e0871dd04ea774e8cee7755461b144ef29.tar.gz rust-941343e0871dd04ea774e8cee7755461b144ef29.zip | |
Auto merge of #81596 - jonas-schievink:rollup-utk14gr, r=jonas-schievink
Rollup of 11 pull requests Successful merges: - #80092 (2229: Fix issues with move closures and mutability) - #80404 (Remove const_in_array_repeat) - #81255 (Don't link with --export-dynamic on wasm32-wasi) - #81480 (Add suggestion for nested fields) - #81549 (Misc ip documentation fixes) - #81566 (Add a test for #71202) - #81568 (Fix an old FIXME in redundant paren lint) - #81571 (Fix typo in E0759) - #81572 (Edit multiple error code Markdown files) - #81589 (Fix small typo in string.rs) - #81590 (Stabilize int_bits_const) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
34 files changed, 375 insertions, 214 deletions
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 5880bbd3de4..36d261fb737 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -13,7 +13,6 @@ #![feature(unboxed_closures)] #![feature(generator_trait)] #![feature(fn_traits)] -#![feature(int_bits_const)] #![feature(min_specialization)] #![feature(auto_traits)] #![feature(nll)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0013.md b/compiler/rustc_error_codes/src/error_codes/E0013.md index 8de177590ec..5605302772f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0013.md +++ b/compiler/rustc_error_codes/src/error_codes/E0013.md @@ -8,7 +8,7 @@ static X: i32 = 42; const Y: i32 = X; ``` -In this example, `Y` cannot refer to `X` here. To fix this, the value can be +In this example, `Y` cannot refer to `X`. To fix this, the value can be extracted as a const and then used: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index b2cc2a2273a..019d54b6202 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -287,5 +287,5 @@ the method `get_a()` would return an object of unknown type when called on the function. `Self` type parameters let us make object safe traits no longer safe, so they are forbidden when specifying supertraits. -There's no easy fix for this, generally code will need to be refactored so that +There's no easy fix for this. Generally, code will need to be refactored so that you no longer need to derive from `Super<Self>`. diff --git a/compiler/rustc_error_codes/src/error_codes/E0107.md b/compiler/rustc_error_codes/src/error_codes/E0107.md index 4d22b17fe10..4e37695a529 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0107.md +++ b/compiler/rustc_error_codes/src/error_codes/E0107.md @@ -1,4 +1,4 @@ -An incorrect number of generic arguments were provided. +An incorrect number of generic arguments was provided. Erroneous code example: diff --git a/compiler/rustc_error_codes/src/error_codes/E0116.md b/compiler/rustc_error_codes/src/error_codes/E0116.md index ca849c2a128..653be602989 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0116.md +++ b/compiler/rustc_error_codes/src/error_codes/E0116.md @@ -10,7 +10,7 @@ You can only define an inherent implementation for a type in the same crate where the type was defined. For example, an `impl` block as above is not allowed since `Vec` is defined in the standard library. -To fix this problem, you can do either of these things: +To fix this problem, you can either: - define a trait that has the desired associated functions/types/constants and implement the trait for the type in question diff --git a/compiler/rustc_error_codes/src/error_codes/E0277.md b/compiler/rustc_error_codes/src/error_codes/E0277.md index 2e2cd5e01fb..9f6db6ed7a2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0277.md +++ b/compiler/rustc_error_codes/src/error_codes/E0277.md @@ -59,9 +59,9 @@ fn main() { } ``` -Note that the error here is in the definition of the generic function: Although +Note that the error here is in the definition of the generic function. Although we only call it with a parameter that does implement `Debug`, the compiler -still rejects the function: It must work with all possible input types. In +still rejects the function. It must work with all possible input types. In order to make this example compile, we need to restrict the generic type we're accepting: diff --git a/compiler/rustc_error_codes/src/error_codes/E0309.md b/compiler/rustc_error_codes/src/error_codes/E0309.md index e719ee590ab..c36a56b00ce 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0309.md +++ b/compiler/rustc_error_codes/src/error_codes/E0309.md @@ -25,7 +25,7 @@ where The type definition contains some field whose type requires an outlives annotation. Outlives annotations (e.g., `T: 'a`) are used to guarantee that all -the data in T is valid for at least the lifetime `'a`. This scenario most +the data in `T` is valid for at least the lifetime `'a`. This scenario most commonly arises when the type contains an associated type reference like `<T as SomeTrait<'a>>::Output`, as shown in the previous code. diff --git a/compiler/rustc_error_codes/src/error_codes/E0597.md b/compiler/rustc_error_codes/src/error_codes/E0597.md index 3340768fa82..f6e0b62e1b6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0597.md +++ b/compiler/rustc_error_codes/src/error_codes/E0597.md @@ -1,4 +1,4 @@ -This error occurs because a value was dropped while it was still borrowed +This error occurs because a value was dropped while it was still borrowed. Erroneous code example: @@ -15,7 +15,7 @@ let mut x = Foo { x: None }; println!("{:?}", x.x); ``` -In here, `y` is dropped at the end of the inner scope, but it is borrowed by +Here, `y` is dropped at the end of the inner scope, but it is borrowed by `x` until the `println`. To fix the previous example, just remove the scope so that `y` isn't dropped until after the println diff --git a/compiler/rustc_error_codes/src/error_codes/E0658.md b/compiler/rustc_error_codes/src/error_codes/E0658.md index d821b9027f1..24245a38ae0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0658.md +++ b/compiler/rustc_error_codes/src/error_codes/E0658.md @@ -11,7 +11,7 @@ enum Foo { If you're using a stable or a beta version of rustc, you won't be able to use any unstable features. In order to do so, please switch to a nightly version of -rustc (by using rustup). +rustc (by using [rustup]). If you're using a nightly version of rustc, just add the corresponding feature to be able to use it: @@ -24,3 +24,5 @@ enum Foo { Bar(u64), } ``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0754.md b/compiler/rustc_error_codes/src/error_codes/E0754.md index 57620bcd65c..9f4b19cfda6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0754.md +++ b/compiler/rustc_error_codes/src/error_codes/E0754.md @@ -1,4 +1,4 @@ -An non-ascii identifier was used in an invalid context. +A non-ASCII identifier was used in an invalid context. Erroneous code examples: @@ -13,7 +13,7 @@ fn řųśť() {} // error! fn main() {} ``` -Non-ascii can be used as module names if it is inlined or if a `#[path]` +Non-ASCII can be used as module names if it is inlined or if a `#[path]` attribute is specified. For example: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0759.md b/compiler/rustc_error_codes/src/error_codes/E0759.md index 6d525310f75..2fe5ada257f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0759.md +++ b/compiler/rustc_error_codes/src/error_codes/E0759.md @@ -27,7 +27,7 @@ fn bar(x: &i32) -> Box<dyn Debug + 'static> { // ok! } ``` -Both [`dyn Trait`] and [`impl Trait`] in return types have a an implicit +Both [`dyn Trait`] and [`impl Trait`] in return types have an implicit `'static` requirement, meaning that the value implementing them that is being returned has to be either a `'static` borrow or an owned value. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index cd3c8fded63..e12b533b110 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -485,9 +485,6 @@ declare_features! ( /// Allows `async || body` closures. (active, async_closure, "1.37.0", Some(62290), None), - /// Allows `[x; N]` where `x` is a constant (RFC 2203). - (active, const_in_array_repeat_expressions, "1.37.0", Some(49147), None), - /// Allows `impl Trait` to be used inside type aliases (RFC 2515). (active, type_alias_impl_trait, "1.38.0", Some(63063), None), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 07bd1602cda..38a3a4e3d44 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -97,6 +97,9 @@ declare_features! ( (removed, extern_in_paths, "1.33.0", Some(55600), None, Some("subsumed by `::foo::bar` paths")), (removed, quote, "1.33.0", Some(29601), None, None), + /// Allows `[x; N]` where `x` is a constant (RFC 2203). + (removed, const_in_array_repeat_expressions, "1.37.0", Some(49147), None, + Some("removed due to causing promotable bugs")), /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238). (removed, dropck_parametricity, "1.38.0", Some(28498), None, None), (removed, await_macro, "1.38.0", Some(50547), None, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index c1a3eecbbc7..b611aebad01 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -977,8 +977,6 @@ impl UnusedDelimLint for UnusedBraces { } } ast::ExprKind::Let(_, ref expr) => { - // FIXME(#60336): Properly handle `let true = (false && true)` - // actually needing the parenthesis. self.check_unused_delims_expr( cx, expr, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a663f793aa..163b400973b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -228,8 +228,7 @@ pub enum ObligationCauseCode<'tcx> { /// Inline asm operand type must be `Sized`. InlineAsmSized, /// `[T, ..n]` implies that `T` must be `Copy`. - /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. - RepeatVec(bool), + RepeatVec, /// Types of fields (other than the last, except for packed structs) in a struct must be sized. FieldSized { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8e8caa46c38..babab005edb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -661,11 +661,28 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>; -/// A `Place` and the corresponding `CaptureInfo`. +/// A composite describing a `Place` that is captured by a closure. #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CapturedPlace<'tcx> { + /// The `Place` that is captured. pub place: HirPlace<'tcx>, + + /// `CaptureKind` and expression(s) that resulted in such capture of `place`. pub info: CaptureInfo<'tcx>, + + /// Represents if `place` can be mutated or not. + pub mutability: hir::Mutability, +} + +impl CapturedPlace<'tcx> { + /// Returns the hir-id of the root variable for the captured place. + /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. + pub fn get_root_variable(&self) -> hir::HirId { + match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected upvar, found={:?}", base), + } + } } pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 6d98bf554f1..04ea3cbd8b6 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -215,6 +215,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { PlaceRef { local, projection: [proj_base @ .., elem] } => { match elem { ProjectionElem::Deref => { + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); @@ -259,6 +260,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ProjectionElem::Field(field, _ty) => { autoderef = true; + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index 350e0d045fa..fb7694b7d88 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -345,7 +345,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let upvar = &self.upvars[upvar_field.unwrap().index()]; - let upvar_hir_id = upvar.var_hir_id; + // FIXME(project-rfc-2229#8): Improve borrow-check diagnostics in case of precise + // capture. + let upvar_hir_id = upvar.place.get_root_variable(); let upvar_name = upvar.name; let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index 73196c732f5..74abe2d35ee 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -64,12 +64,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); - item_msg = format!("`{}`", access_place_desc.unwrap()); - if self.is_upvar_field_projection(access_place.as_ref()).is_some() { - reason = ", as it is not declared as mutable".to_string(); + let imm_borrow_derefed = self.upvars[upvar_index.index()] + .place + .place + .deref_tys() + .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not))); + + // If the place is immutable then: + // + // - Either we deref a immutable ref to get to our final place. + // - We don't capture derefs of raw ptrs + // - Or the final place is immut because the root variable of the capture + // isn't marked mut and we should suggest that to the user. + if imm_borrow_derefed { + // If we deref an immutable ref then the suggestion here doesn't help. + return; } else { - let name = self.upvars[upvar_index.index()].name; - reason = format!(", as `{}` is not declared as mutable", name); + item_msg = format!("`{}`", access_place_desc.unwrap()); + if self.is_upvar_field_projection(access_place.as_ref()).is_some() { + reason = ", as it is not declared as mutable".to_string(); + } else { + let name = self.upvars[upvar_index.index()].name; + reason = format!(", as `{}` is not declared as mutable", name); + } } } @@ -259,9 +276,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); + let captured_place = &self.upvars[upvar_index.index()].place; + err.span_label(span, format!("cannot {ACT}", ACT = act)); - let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id; + let upvar_hir_id = captured_place.get_root_variable(); + if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) { if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs index a850b85e9bb..4abc623fc5f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs @@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexVec<Local, Option<Symbol>>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], fr: RegionVid, ) -> Option<(Option<Symbol>, Span)> { debug!("get_var_name_and_span_for_region(fr={:?})", fr); @@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("get_var_name_and_span_for_region: attempting upvar"); self.get_upvar_index_for_region(tcx, fr) .map(|index| { + // FIXME(project-rfc-2229#8): Use place span for diagnostics let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index); (Some(name), span) }) @@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { crate fn get_upvar_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], upvar_index: usize, ) -> (Symbol, Span) { - let upvar_hir_id = upvars[upvar_index].var_hir_id; + let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id); let upvar_name = tcx.hir().name(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 7c7edfdb5fb..5db52db70ac 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -5,11 +5,10 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, Node}; +use rustc_hir::Node; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, @@ -18,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -73,16 +72,14 @@ crate use region_infer::RegionInferenceContext; // FIXME(eddyb) perhaps move this somewhere more centrally. #[derive(Debug)] -crate struct Upvar { +crate struct Upvar<'tcx> { + // FIXME(project-rfc_2229#36): print capture precisely here. name: Symbol, - // FIXME(project-rfc-2229#8): This should use Place or something similar - var_hir_id: HirId, + place: CapturedPlace<'tcx>, /// If true, the capture is behind a reference. by_ref: bool, - - mutability: Mutability, } const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; @@ -161,26 +158,13 @@ fn do_mir_borrowck<'a, 'tcx>( let upvars: Vec<_> = tables .closure_min_captures_flattened(def.did.to_def_id()) .map(|captured_place| { - let var_hir_id = match captured_place.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => bug!("Expected upvar"), - }; + let var_hir_id = captured_place.get_root_variable(); let capture = captured_place.info.capture_kind; let by_ref = match capture { ty::UpvarCapture::ByValue(_) => false, ty::UpvarCapture::ByRef(..) => true, }; - let mut upvar = Upvar { - name: tcx.hir().name(var_hir_id), - var_hir_id, - by_ref, - mutability: Mutability::Not, - }; - let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - if bm == ty::BindByValue(hir::Mutability::Mut) { - upvar.mutability = Mutability::Mut; - } - upvar + Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref } }) .collect(); @@ -549,7 +533,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> { dominators: Dominators<BasicBlock>, /// Information about upvars not necessarily preserved in types or MIR - upvars: Vec<Upvar>, + upvars: Vec<Upvar<'tcx>>, /// Names of local (user) variables (extracted from `var_debug_info`). local_names: IndexVec<Local, Option<Symbol>>, @@ -1374,13 +1358,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { - if !place.projection.is_empty() { - if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + // We have three possibilities here: + // a. We are modifying something through a mut-ref + // b. We are modifying something that is local to our parent + // c. Current body is a nested closure, and we are modifying path starting from + // a Place captured by our parent closure. + + // Handle (c), the path being modified is exactly the path captured by our parent + if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + this.used_mut_upvars.push(field); + return; + } + + for (place_ref, proj) in place.iter_projections().rev() { + // Handle (a) + if proj == ProjectionElem::Deref { + match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() { + // We aren't modifying a variable directly + ty::Ref(_, _, hir::Mutability::Mut) => return, + + _ => {} + } + } + + // Handle (c) + if let Some(field) = this.is_upvar_field_projection(place_ref) { this.used_mut_upvars.push(field); + return; } - } else { - this.used_mut.insert(place.local); } + + // Handle(b) + this.used_mut.insert(place.local); }; // This relies on the current way that by-value @@ -2146,6 +2155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> { + debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed); match place.last_projection() { None => { let local = &self.body.local_decls[place.local]; @@ -2227,11 +2237,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(field) = upvar_field_projection { let upvar = &self.upvars[field.index()]; debug!( - "upvar.mutability={:?} local_mutation_is_allowed={:?} \ - place={:?}", - upvar, is_local_mutation_allowed, place + "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \ + place={:?}, place_base={:?}", + upvar, is_local_mutation_allowed, place, place_base ); - match (upvar.mutability, is_local_mutation_allowed) { + match (upvar.place.mutability, is_local_mutation_allowed) { ( Mutability::Not, LocalMutationIsAllowed::No diff --git a/compiler/rustc_mir/src/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs index 359c5f261a4..a0265b20d12 100644 --- a/compiler/rustc_mir/src/borrow_check/nll.rs +++ b/compiler/rustc_mir/src/borrow_check/nll.rs @@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs index fa3ae2367e0..80de3b4e363 100644 --- a/compiler/rustc_mir/src/borrow_check/path_utils.rs +++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs @@ -143,7 +143,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { /// of a closure type. pub(crate) fn is_upvar_field_projection( tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option<Field> { diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index fb9820e853f..e689964b998 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -43,10 +43,6 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations} use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::MoveData; use crate::dataflow::ResultsCursor; -use crate::transform::{ - check_consts::ConstCx, - promote_consts::should_suggest_const_in_array_repeat_expressions_attribute, -}; use crate::borrow_check::{ borrow_set::BorrowSet, @@ -132,7 +128,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc<RegionValueElements>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { @@ -821,7 +817,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option<AllFacts>, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - upvars: &'a [Upvar], + upvars: &'a [Upvar<'tcx>], } crate struct MirTypeckResults<'tcx> { @@ -1997,22 +1993,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let span = body.source_info(location).span; let ty = operand.ty(body, tcx); if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { - let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); - // To determine if `const_in_array_repeat_expressions` feature gate should - // be mentioned, need to check if the rvalue is promotable. - let should_suggest = - should_suggest_const_in_array_repeat_expressions_attribute( - &ccx, operand, - ); - debug!("check_rvalue: should_suggest={:?}", should_suggest); - let def_id = body.source.def_id().expect_local(); self.infcx.report_selection_error( &traits::Obligation::new( ObligationCause::new( span, self.tcx().hir().local_def_id_to_hir_id(def_id), - traits::ObligationCauseCode::RepeatVec(should_suggest), + traits::ObligationCauseCode::RepeatVec, ), self.param_env, ty::Binder::bind(ty::TraitRef::new( @@ -2490,7 +2477,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { body, ); let category = if let Some(field) = field { - ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable(); + // FIXME(project-rfc-2229#8): Use Place for better diagnostics + ConstraintCategory::ClosureUpvar(var_hir_id) } else { ConstraintCategory::Boring }; diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index d8758e04544..b4504a0e223 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -102,9 +102,6 @@ pub enum Candidate { /// Borrow of a constant temporary, candidate for lifetime extension. Ref(Location), - /// Promotion of the `x` in `[x; 32]`. - Repeat(Location), - /// Currently applied to function calls where the callee has the unstable /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle /// intrinsic. The intrinsic requires the arguments are indeed constant and @@ -120,14 +117,14 @@ impl Candidate { /// Returns `true` if we should use the "explicit" rules for promotability for this `Candidate`. fn forces_explicit_promotion(&self) -> bool { match self { - Candidate::Ref(_) | Candidate::Repeat(_) => false, + Candidate::Ref(_) => false, Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true, } } fn source_info(&self, body: &Body<'_>) -> SourceInfo { match self { - Candidate::Ref(location) | Candidate::Repeat(location) => *body.source_info(*location), + Candidate::Ref(location) => *body.source_info(*location), Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => { *body.source_info(body.terminator_loc(*bb)) } @@ -213,11 +210,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { Rvalue::Ref(..) => { self.candidates.push(Candidate::Ref(location)); } - Rvalue::Repeat(..) if self.ccx.tcx.features().const_in_array_repeat_expressions => { - // FIXME(#49147) only promote the element when it isn't `Copy` - // (so that code that can copy it at runtime is unaffected). - self.candidates.push(Candidate::Repeat(location)); - } _ => {} } } @@ -334,21 +326,6 @@ impl<'tcx> Validator<'_, 'tcx> { _ => bug!(), } } - Candidate::Repeat(loc) => { - assert!(!self.explicit); - - let statement = &self.body[loc.block].statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, Rvalue::Repeat(ref operand, _))) => { - if !self.tcx.features().const_in_array_repeat_expressions { - return Err(Unpromotable); - } - - self.validate_operand(operand) - } - _ => bug!(), - } - } Candidate::Argument { bb, index } => { assert!(self.explicit); @@ -1090,18 +1067,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { _ => bug!(), } } - Candidate::Repeat(loc) => { - let statement = &mut blocks[loc.block].statements[loc.statement_index]; - match statement.kind { - StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => { - let ty = operand.ty(local_decls, self.tcx); - let span = statement.source_info.span; - - Rvalue::Use(mem::replace(operand, promoted_operand(ty, span))) - } - _ => bug!(), - } - } Candidate::Argument { bb, index } => { let terminator = blocks[bb].terminator_mut(); match terminator.kind { @@ -1182,8 +1147,7 @@ pub fn promote_candidates<'tcx>( let mut extra_statements = vec![]; for candidate in candidates.into_iter().rev() { match candidate { - Candidate::Repeat(Location { block, statement_index }) - | Candidate::Ref(Location { block, statement_index }) => { + Candidate::Ref(Location { block, statement_index }) => { if let StatementKind::Assign(box (place, _)) = &body[block].statements[statement_index].kind { @@ -1267,27 +1231,3 @@ pub fn promote_candidates<'tcx>( promotions } - -/// This function returns `true` if the `const_in_array_repeat_expressions` feature attribute should -/// be suggested. This function is probably quite expensive, it shouldn't be run in the happy path. -/// Feature attribute should be suggested if `operand` can be promoted and the feature is not -/// enabled. -crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>( - ccx: &ConstCx<'_, 'tcx>, - operand: &Operand<'tcx>, -) -> bool { - let mut rpo = traversal::reverse_postorder(&ccx.body); - let (temps, _) = collect_temps_and_candidates(&ccx, &mut rpo); - let validator = Validator { ccx, temps: &temps, explicit: false }; - - let should_promote = validator.validate_operand(operand).is_ok(); - let feature_flag = validator.ccx.tcx.features().const_in_array_repeat_expressions; - debug!( - "should_suggest_const_in_array_repeat_expressions_flag: def_id={:?} \ - should_promote={:?} feature_flag={:?}", - validator.ccx.def_id(), - should_promote, - feature_flag - ); - should_promote && !feature_flag -} diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 99661599525..e4891eb5a3c 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -851,22 +851,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => bug!("Expected an upvar") }; - let mut mutability = Mutability::Not; + let mutability = captured_place.mutability; // FIXME(project-rfc-2229#8): Store more precise information let mut name = kw::Empty; if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { name = ident.name; - match hir_typeck_results - .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - Some(ty::BindByValue(hir::Mutability::Mut)) => { - mutability = Mutability::Mut; - } - Some(_) => mutability = Mutability::Not, - _ => {} - } } } diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 53c3adcc20c..ea04e7bb44b 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -17,7 +17,6 @@ Core encoding and decoding interfaces. #![feature(min_specialization)] #![feature(vec_spare_capacity)] #![feature(core_intrinsics)] -#![feature(int_bits_const)] #![feature(maybe_uninit_slice)] #![feature(new_uninit)] #![cfg_attr(test, feature(test))] diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index a2bcf2c251d..3e2aab5125a 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,4 +1,3 @@ -#![feature(int_bits_const)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs index a7957d84cbe..bfef3d37228 100644 --- a/compiler/rustc_target/src/spec/wasm32_base.rs +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -55,15 +55,6 @@ pub fn options() -> TargetOptions { // to do so. arg("--no-demangle"); - // The symbol visibility story is a bit in flux right now with LLD. - // It's... not entirely clear to me what's going on, but this looks to - // make everything work when `export_symbols` isn't otherwise called for - // things like executables. - // - // This is really only here to get things working. If it can be removed and - // basic tests still work, then sounds like it should be removed! - arg("--export-dynamic"); - let mut pre_link_args = BTreeMap::new(); pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); pre_link_args.insert(LinkerFlavor::Gcc, clang_args); diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index c12757b8f98..9f69ce16c21 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -2,6 +2,17 @@ use super::wasm32_base; use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Target { + let mut options = wasm32_base::options(); + + let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + + // Rust really needs a way for users to specify exports and imports in + // the source code. --export-dynamic isn't the right tool for this job, + // however it does have the side effect of automatically exporting a lot + // of symbols, which approximates what people want when compiling for + // wasm32-unknown-unknown expect, so use it for now. + clang_args.push("--export-dynamic".to_string()); + let mut post_link_args = LinkArgs::new(); post_link_args.insert( LinkerFlavor::Em, @@ -28,7 +39,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Unwind, post_link_args, os_family: Some("unix".to_string()), - ..wasm32_base::options() + ..options }; Target { llvm_target: "wasm32-unknown-emscripten".to_string(), diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 6037aa5b430..5e89ba2520b 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -26,11 +26,18 @@ pub fn target() -> Target { // For now this target just never has an entry symbol no matter the output // type, so unconditionally pass this. clang_args.push("-Wl,--no-entry".to_string()); - options - .pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)) - .unwrap() - .push("--no-entry".to_string()); + + // Rust really needs a way for users to specify exports and imports in + // the source code. --export-dynamic isn't the right tool for this job, + // however it does have the side effect of automatically exporting a lot + // of symbols, which approximates what people want when compiling for + // wasm32-unknown-unknown expect, so use it for now. + clang_args.push("-Wl,--export-dynamic".to_string()); + + // Add the flags to wasm-ld's args too. + let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap(); + lld_args.push("--no-entry".to_string()); + lld_args.push("--export-dynamic".to_string()); Target { llvm_target: "wasm32-unknown-unknown".to_string(), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0724a9290e9..690591930de 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1881,23 +1881,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::Coercion { source: _, target } => { err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); } - ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => { + ObligationCauseCode::RepeatVec => { err.note( "the `Copy` trait is required because the repeated element will be copied", ); - if suggest_const_in_array_repeat_expressions { - err.note( - "this array initializer can be evaluated at compile-time, see issue \ - #49147 <https://github.com/rust-lang/rust/issues/49147> \ - for more information", - ); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help( - "add `#![feature(const_in_array_repeat_expressions)]` to the \ - crate attributes to enable", - ); - } - } } ObligationCauseCode::VariableType(hir_id) => { let parent_node = self.tcx.hir().get_parent_node(hir_id); diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index e37b4ff742b..04c83a7665c 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -36,6 +36,7 @@ use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; @@ -46,8 +47,6 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_trait_selection::traits::{self, ObligationCauseCode}; -use std::fmt::Display; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { let ty = self.check_expr_with_hint(expr, expected); @@ -1585,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, field: Ident, ) -> Ty<'tcx> { + debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); let expr_t = self.check_expr(base); let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, _)) = autoderef.next() { + debug!("base_t: {:?}", base_t); match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", base_t); @@ -1706,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", field, base, expr, expr_t ); - let mut err = self.no_such_field_err(field.span, field, expr_t); + let mut err = self.no_such_field_err(field, expr_t); match *expr_t.peel_refs().kind() { ty::Array(_, len) => { @@ -1880,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn no_such_field_err<T: Display>( + fn no_such_field_err( &self, - span: Span, - field: T, - expr_t: &ty::TyS<'_>, + field: Ident, + expr_t: &'tcx ty::TyS<'tcx>, ) -> DiagnosticBuilder<'_> { - type_error_struct!( + let span = field.span; + debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); + + let mut err = type_error_struct!( self.tcx().sess, - span, + field.span, expr_t, E0609, "no field `{}` on type `{}`", field, expr_t - ) + ); + + // try to add a suggestion in case the field is a nested field of a field of the Adt + if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) { + for candidate_field in fields.iter() { + if let Some(field_path) = + self.check_for_nested_field(span, field, candidate_field, substs, vec![]) + { + let field_path_str = field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::<Vec<String>>() + .join("."); + debug!("field_path_str: {:?}", field_path_str); + + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "one of the expressions' fields has a field of the same name", + format!("{}.", field_path_str), + Applicability::MaybeIncorrect, + ); + } + } + } + err + } + + fn get_field_candidates( + &self, + span: Span, + base_t: Ty<'tcx>, + ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> { + debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); + + let mut autoderef = self.autoderef(span, base_t); + while let Some((base_t, _)) = autoderef.next() { + match base_t.kind() { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + let fields = &base_def.non_enum_variant().fields; + // For compile-time reasons put a limit on number of fields we search + if fields.len() > 100 { + return None; + } + return Some((fields, substs)); + } + _ => {} + } + } + None + } + + /// This method is called after we have encountered a missing field error to recursively + /// search for the field + fn check_for_nested_field( + &self, + span: Span, + target_field: Ident, + candidate_field: &ty::FieldDef, + subst: SubstsRef<'tcx>, + mut field_path: Vec<Ident>, + ) -> Option<Vec<Ident>> { + debug!( + "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}", + span, candidate_field, field_path + ); + + if candidate_field.ident == target_field { + Some(field_path) + } else if field_path.len() > 3 { + // For compile-time reasons and to avoid infinite recursion we only check for fields + // up to a depth of three + None + } else { + // recursively search fields of `candidate_field` if it's a ty::Adt + + field_path.push(candidate_field.ident.normalize_to_macros_2_0()); + let field_ty = candidate_field.ty(self.tcx, subst); + if let Some((nested_fields, _)) = self.get_field_candidates(span, &field_ty) { + for field in nested_fields.iter() { + let ident = field.ident.normalize_to_macros_2_0(); + if ident == target_field { + return Some(field_path); + } else { + let field_path = field_path.clone(); + if let Some(path) = self.check_for_nested_field( + span, + target_field, + field, + subst, + field_path, + ) { + return Some(path); + } + } + } + } + None + } } fn check_expr_index( diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 6b2cba62fa6..f039445bf77 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -184,10 +184,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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. + // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are + // made, make sure we are selecting and restricting + // the origin correctly. (origin.0, Place { projections: vec![], ..origin.1 }) }; @@ -252,8 +251,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let capture = captured_place.info.capture_kind; debug!( - "place={:?} upvar_ty={:?} capture={:?}", - captured_place.place, upvar_ty, capture + "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", + captured_place.place, upvar_ty, capture, captured_place.mutability, ); match capture { @@ -419,19 +418,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base => bug!("Expected upvar, found={:?}", base), }; - // Arrays are captured in entirety, drop Index projections and projections - // after Index projections. - let first_index_projection = - place.projections.split(|proj| ProjectionKind::Index == proj.kind).next(); - let place = Place { - base_ty: place.base_ty, - base: place.base, - projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()), - }; + let place = restrict_capture_precision(place, capture_info.capture_kind); let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { - let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }]; + let mutability = self.determine_capture_mutability(&place); + let min_cap_list = + vec![ty::CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } @@ -494,8 +487,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { + let mutability = self.determine_capture_mutability(&place); let captured_place = - ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + ty::CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); } } @@ -615,6 +609,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + + /// A captured place is mutable if + /// 1. Projections don't include a Deref of an immut-borrow, **and** + /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. + fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => unreachable!(), + }; + + let bm = *self + .typeck_results + .borrow() + .pat_binding_modes() + .get(var_hir_id) + .expect("missing binding mode"); + + let mut is_mutbl = match bm { + ty::BindByValue(mutability) => mutability, + ty::BindByReference(_) => hir::Mutability::Not, + }; + + for pointer_ty in place.deref_tys() { + match pointer_ty.kind() { + // We don't capture derefs of raw ptrs + ty::RawPtr(_) => unreachable!(), + + // Derefencing a mut-ref allows us to mut the Place if we don't deref + // an immut-ref after on top of this. + ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut, + + // The place isn't mutable once we dereference a immutable reference. + ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not, + + // Dereferencing a box doesn't change mutability + ty::Adt(def, ..) if def.is_box() => {} + + unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), + } + } + + is_mutbl + } } struct InferBorrowKind<'a, 'tcx> { @@ -960,6 +997,66 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } +/// Truncate projections so that following rules are obeyed by the captured `place`: +/// +/// - No Derefs in move closure, this will result in value behind a reference getting moved. +/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture +/// them completely. +/// - No Index projections are captured, since arrays are captured completely. +fn restrict_capture_precision<'tcx>( + mut place: Place<'tcx>, + capture_kind: ty::UpvarCapture<'tcx>, +) -> Place<'tcx> { + if place.projections.is_empty() { + // Nothing to do here + return place; + } + + if place.base_ty.is_unsafe_ptr() { + place.projections.truncate(0); + return place; + } + + let mut truncated_length = usize::MAX; + let mut first_deref_projection = usize::MAX; + + for (i, proj) in place.projections.iter().enumerate() { + if proj.ty.is_unsafe_ptr() { + // Don't apply any projections on top of an unsafe ptr + truncated_length = truncated_length.min(i + 1); + break; + } + match proj.kind { + ProjectionKind::Index => { + // Arrays are completely captured, so we drop Index projections + truncated_length = truncated_length.min(i); + break; + } + ProjectionKind::Deref => { + // We only drop Derefs in case of move closures + // There might be an index projection or raw ptr ahead, so we don't stop here. + first_deref_projection = first_deref_projection.min(i); + } + ProjectionKind::Field(..) => {} // ignore + ProjectionKind::Subslice => {} // We never capture this + } + } + + let length = place + .projections + .len() + .min(truncated_length) + // In case of capture `ByValue` we want to not capture derefs + .min(match capture_kind { + ty::UpvarCapture::ByValue(..) => first_deref_projection, + ty::UpvarCapture::ByRef(..) => usize::MAX, + }); + + place.projections.truncate(length); + + place +} + fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { let variable_name = match place.base { PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), |
