diff --git a/Cargo.lock b/Cargo.lock index c72c7b8481b5b..f02ae7ef6cfa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,13 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" +[[package]] +name = "coverage_test_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", +] + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -3922,6 +3929,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "coverage_test_macros", "either", "itertools 0.9.0", "polonius-engine", diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a566200c33896..958219cbebf3f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2327,11 +2327,12 @@ impl<'a> State<'a> { self.print_path(path, false, depth); } self.s.word(">"); - self.s.word("::"); - let item_segment = path.segments.last().unwrap(); - self.print_ident(item_segment.ident); - if let Some(ref args) = item_segment.args { - self.print_generic_args(args, colons_before_params) + for item_segment in &path.segments[qself.position..] { + self.s.word("::"); + self.print_ident(item_segment.ident); + if let Some(ref args) = item_segment.args { + self.print_generic_args(args, colons_before_params) + } } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index e6acb6860be9e..6237a4c0020eb 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -925,9 +925,7 @@ unsafe fn embed_bitcode( || cgcx.opts.target_triple.triple().starts_with("asmjs") { // nothing to do here - } else if cgcx.opts.target_triple.triple().contains("windows") - || cgcx.opts.target_triple.triple().contains("uefi") - { + } else if cgcx.is_pe_coff { let asm = " .section .llvmbc,\"n\" .section .llvmcmd,\"n\" diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b34bee3358b40..7f2bb7b5bcdaf 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -307,6 +307,7 @@ pub struct CodegenContext { pub allocator_module_config: Arc, pub tm_factory: TargetMachineFactory, pub msvc_imps_needed: bool, + pub is_pe_coff: bool, pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, @@ -1022,6 +1023,7 @@ fn start_executing_work( tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), + is_pe_coff: tcx.sess.target.is_like_windows, target_pointer_width: tcx.sess.target.pointer_width, target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0df67b63eba58..a035507924794 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -616,6 +616,9 @@ declare_features! ( /// Enables `#[cfg(panic = "...")]` config key. (active, cfg_panic, "1.49.0", Some(77443), None), + /// Allows capturing disjoint fields in a closure/generator (RFC 2229). + (active, capture_disjoint_fields, "1.49.0", Some(53488), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -639,6 +642,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::inline_const, sym::repr128, sym::unsized_locals, + sym::capture_disjoint_fields, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5c5cf609ac33c..fa8edba629e92 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -547,6 +547,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), + rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 3838e1b006f70..407e07354d174 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,6 +415,10 @@ pub struct TypeckResults<'tcx> { /// entire variable. pub closure_captures: ty::UpvarListMap, + /// Tracks the minimum captures required for a closure; + /// see `MinCaptureInformationMap` for more details. + pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, + /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). pub generator_interior_types: Vec>, @@ -442,6 +446,7 @@ impl<'tcx> TypeckResults<'tcx> { tainted_by_errors: None, concrete_opaque_types: Default::default(), closure_captures: Default::default(), + closure_min_captures: Default::default(), generator_interior_types: Default::default(), } } @@ -676,6 +681,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors, ref concrete_opaque_types, ref closure_captures, + ref closure_min_captures, ref generator_interior_types, } = *self; @@ -709,6 +715,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher); closure_captures.hash_stable(hcx, hasher); + closure_min_captures.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 06e69a0009b1f..1eb7c3b56edd2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -6,6 +6,7 @@ pub use self::IntVarValue::*; pub use self::Variance::*; use crate::hir::exports::ExportMap; +use crate::hir::place::Place as HirPlace; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStoreDyn; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; @@ -611,6 +612,18 @@ pub struct TyS<'tcx> { outer_exclusive_binder: ty::DebruijnIndex, } +impl<'tcx> TyS<'tcx> { + /// A constructor used only for internal testing. + #[allow(rustc::usage_of_ty_tykind)] + pub fn make_for_test( + kind: TyKind<'tcx>, + flags: TypeFlags, + outer_exclusive_binder: ty::DebruijnIndex, + ) -> TyS<'tcx> { + TyS { kind, flags, outer_exclusive_binder } + } +} + // `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] static_assert_size!(TyS<'_>, 32); @@ -674,6 +687,12 @@ pub struct UpvarId { pub closure_expr_id: LocalDefId, } +impl UpvarId { + pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId { + UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id } + } +} + #[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. @@ -756,6 +775,56 @@ pub struct UpvarBorrow<'tcx> { pub region: ty::Region<'tcx>, } +/// Given the closure DefId this map provides a map of root variables to minimum +/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure. +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; + +/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. +/// Used to track the minimum set of `Place`s that need to be captured to support all +/// Places captured by the closure starting at a given root variable. +/// +/// This provides a convenient and quick way of checking if a variable being used within +/// a closure is a capture of a local variable. +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; + +/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. +pub type MinCaptureList<'tcx> = Vec>; + +/// A `Place` and the corresponding `CaptureInfo`. +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct CapturedPlace<'tcx> { + pub place: HirPlace<'tcx>, + pub info: CaptureInfo<'tcx>, +} + +/// 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. +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +pub struct CaptureInfo<'tcx> { + /// Expr Id pointing to use that resulted in selecting the current capture kind + /// + /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is + /// possible that we don't see the use of a particular place resulting in expr_id being + /// None. In such case we fallback on uvpars_mentioned for span. + /// + /// Eg: + /// ```rust + /// let x = ...; + /// + /// let c = || { + /// let _ = x + /// } + /// ``` + /// + /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, + /// but we won't see it being used during capture analysis, since it's essentially a discard. + pub expr_id: Option, + + /// Capture mode that was selected + pub capture_kind: UpvarCapture<'tcx>, +} + pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 487668cfa1109..9bfd1da039120 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -31,3 +31,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } rustc_apfloat = { path = "../rustc_apfloat" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } + +[dev-dependencies] +coverage_test_macros = { path = "src/transform/coverage/test_macros" } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 4256f6e39d5e8..41f3edaa41380 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -383,16 +383,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field_from_ty(&ty, field, variant_index) } ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { - // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case - // the closure comes from another crate. But in that case we wouldn't - // be borrowck'ing it, so we can just unwrap: - let (&var_id, _) = self - .infcx - .tcx - .upvars_mentioned(def_id) - .unwrap() - .get_index(field.index()) - .unwrap(); + // We won't be borrowck'ing here if the closure came from another crate, + // so it's safe to call `expect_local`. + // + // We know the field exists so it's safe to call operator[] and `unwrap` here. + let (&var_id, _) = + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .get_index(field.index()) + .unwrap(); self.infcx.tcx.hir().name(var_id).to_string() } @@ -967,9 +965,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { - for ((upvar_hir_id, upvar), place) in - self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places) + for (upvar_hir_id, place) in + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .keys() + .zip(places) { + let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span; match place { Operand::Copy(place) | Operand::Move(place) if target_place == place.as_ref() => @@ -991,7 +992,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let usage_span = match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { ty::UpvarCapture::ByValue(Some(span)) => span, - _ => upvar.span, + _ => span, }; return Some((*args_span, generator_kind, usage_span)); } diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index d6c2f7f7aaf1d..20f6a16e0f757 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::coverage::*; /// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR /// `Coverage` statements. -pub(crate) struct CoverageCounters { +pub(super) struct CoverageCounters { function_source_hash: u64, next_counter_id: u32, num_expressions: u32, @@ -37,7 +37,7 @@ impl CoverageCounters { self.debug_counters.enable(); } - /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s /// representing intermediate values. pub fn make_bcb_counters( @@ -120,7 +120,6 @@ struct BcbCounters<'a> { basic_coverage_blocks: &'a mut CoverageGraph, } -// FIXME(richkadel): Add unit tests for `BcbCounters` functions/algorithms. impl<'a> BcbCounters<'a> { fn new( coverage_counters: &'a mut CoverageCounters, diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index ffa795134e257..7f1dc3844b21d 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -127,7 +127,7 @@ pub const NESTED_INDENT: &str = " "; const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; -pub(crate) fn debug_options<'a>() -> &'a DebugOptions { +pub(super) fn debug_options<'a>() -> &'a DebugOptions { static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) @@ -136,7 +136,7 @@ pub(crate) fn debug_options<'a>() -> &'a DebugOptions { /// Parses and maintains coverage-specific debug options captured from the environment variable /// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. #[derive(Debug, Clone)] -pub(crate) struct DebugOptions { +pub(super) struct DebugOptions { pub allow_unused_expressions: bool, counter_format: ExpressionFormat, } @@ -250,7 +250,7 @@ impl Default for ExpressionFormat { /// /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. -pub(crate) struct DebugCounters { +pub(super) struct DebugCounters { some_counters: Option>, } @@ -386,7 +386,7 @@ impl DebugCounter { /// If enabled, this data structure captures additional debugging information used when generating /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. -pub(crate) struct GraphvizData { +pub(super) struct GraphvizData { some_bcb_to_coverage_spans_with_counters: Option>>, some_bcb_to_dependency_counters: Option>>, @@ -496,7 +496,7 @@ impl GraphvizData { /// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// and/or a `CoverageGraph` graphviz output). -pub(crate) struct UsedExpressions { +pub(super) struct UsedExpressions { some_used_expression_operands: Option>>, some_unused_expressions: @@ -626,7 +626,7 @@ impl UsedExpressions { } /// Generates the MIR pass `CoverageSpan`-specific spanview dump file. -pub(crate) fn dump_coverage_spanview( +pub(super) fn dump_coverage_spanview( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, basic_coverage_blocks: &CoverageGraph, @@ -666,7 +666,7 @@ fn span_viewables( } /// Generates the MIR pass coverage-specific graphviz dump file. -pub(crate) fn dump_coverage_graphviz( +pub(super) fn dump_coverage_graphviz( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, pass_name: &str, @@ -815,7 +815,7 @@ fn bcb_to_string_sections( /// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any /// values it might hold. -pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { +pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { TerminatorKind::Goto { .. } => "Goto", TerminatorKind::SwitchInt { .. } => "SwitchInt", diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index c2ed2cbb10002..9d375633dcf51 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -17,7 +17,8 @@ const ID_SEPARATOR: &str = ","; /// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional /// set of additional counters--if needed--to count incoming edges, if there are more than one. /// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) -pub(crate) struct CoverageGraph { +#[derive(Debug)] +pub(super) struct CoverageGraph { bcbs: IndexVec, bb_to_bcb: IndexVec>, pub successors: IndexVec>, @@ -275,7 +276,7 @@ impl graph::WithPredecessors for CoverageGraph { rustc_index::newtype_index! { /// A node in the [control-flow graph][CFG] of CoverageGraph. - pub(crate) struct BasicCoverageBlock { + pub(super) struct BasicCoverageBlock { DEBUG_FORMAT = "bcb{}", } } @@ -305,7 +306,7 @@ rustc_index::newtype_index! { /// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) /// significance. #[derive(Debug, Clone)] -pub(crate) struct BasicCoverageBlockData { +pub(super) struct BasicCoverageBlockData { pub basic_blocks: Vec, pub counter_kind: Option, edge_from_bcbs: Option>, @@ -431,7 +432,7 @@ impl BasicCoverageBlockData { /// the specific branching BCB, representing the edge between the two. The latter case /// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. #[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) struct BcbBranch { +pub(super) struct BcbBranch { pub edge_from_bcb: Option, pub target_bcb: BasicCoverageBlock, } @@ -498,9 +499,8 @@ fn bcb_filtered_successors<'a, 'tcx>( /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the /// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that /// ensures a loop is completely traversed before processing Blocks after the end of the loop. -// FIXME(richkadel): Add unit tests for TraversalContext. #[derive(Debug)] -pub(crate) struct TraversalContext { +pub(super) struct TraversalContext { /// From one or more backedges returning to a loop header. pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, @@ -510,7 +510,7 @@ pub(crate) struct TraversalContext { pub worklist: Vec, } -pub(crate) struct TraverseCoverageGraphWithLoops { +pub(super) struct TraverseCoverageGraphWithLoops { pub backedges: IndexVec>, pub context_stack: Vec, visited: BitSet, @@ -642,7 +642,7 @@ impl TraverseCoverageGraphWithLoops { } } -fn find_loop_backedges( +pub(super) fn find_loop_backedges( basic_coverage_blocks: &CoverageGraph, ) -> IndexVec> { let num_bcbs = basic_coverage_blocks.num_nodes(); diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index c55349239b034..192bb6680e420 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -5,6 +5,9 @@ mod debug; mod graph; mod spans; +#[cfg(test)] +mod tests; + use counters::CoverageCounters; use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use spans::{CoverageSpan, CoverageSpans}; @@ -31,7 +34,7 @@ use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. #[derive(Debug)] -pub(crate) struct Error { +struct Error { message: String, } diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index cda4fc125442f..95c49922262f6 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -17,7 +17,7 @@ use rustc_span::{BytePos, Span, SyntaxContext}; use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] -pub(crate) enum CoverageStatement { +pub(super) enum CoverageStatement { Statement(BasicBlock, Span, usize), Terminator(BasicBlock, Span), } @@ -66,7 +66,7 @@ impl CoverageStatement { /// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` /// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] -pub(crate) struct CoverageSpan { +pub(super) struct CoverageSpan { pub span: Span, pub bcb: BasicCoverageBlock, pub coverage_statements: Vec, @@ -214,7 +214,7 @@ pub struct CoverageSpans<'a, 'tcx> { } impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { - pub(crate) fn generate_coverage_spans( + pub(super) fn generate_coverage_spans( mir_body: &'a mir::Body<'tcx>, body_span: Span, basic_coverage_blocks: &'a CoverageGraph, @@ -645,7 +645,10 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { } } -fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_statement_span( + statement: &'a Statement<'tcx>, + body_span: Span, +) -> Option { match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. @@ -686,7 +689,10 @@ fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O } } -fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_terminator_span( + terminator: &'a Terminator<'tcx>, + body_span: Span, +) -> Option { match terminator.kind { // These terminators have spans that don't positively contribute to computing a reasonable // span of actually executed source code. (For example, SwitchInt terminators extracted from diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml new file mode 100644 index 0000000000000..a9d6f0c803d2e --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["The Rust Project Developers"] +name = "coverage_test_macros" +version = "0.0.0" +edition = "2018" + +[lib] +proc-macro = true +doctest = false + +[dependencies] +proc-macro2 = "1" diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs new file mode 100644 index 0000000000000..3d6095d2738cb --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs @@ -0,0 +1,6 @@ +use proc_macro::TokenStream; + +#[proc_macro] +pub fn let_bcb(item: TokenStream) -> TokenStream { + format!("let bcb{} = graph::BasicCoverageBlock::from_usize({});", item, item).parse().unwrap() +} diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs new file mode 100644 index 0000000000000..d36f1b8e5f670 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -0,0 +1,714 @@ +//! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR +//! pass. +//! +//! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage` +//! functions and algorithms. Mocked objects include instances of `mir::Body`; including +//! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on +//! real, runtime versions of these mocked-up objects have constraints (such as cross-thread +//! limitations) and deep dependencies on other elements of the full Rust compiler (which is +//! *not* constructed or mocked for these tests). +//! +//! Of particular note, attempting to simply print elements of the `mir::Body` with default +//! `Debug` formatting can fail because some `Debug` format implementations require the +//! `TyCtxt`, obtained via a static global variable that is *not* set for these tests. +//! Initializing the global type context is prohibitively complex for the scope and scale of these +//! tests (essentially requiring initializing the entire compiler). +//! +//! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which +//! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some +//! basic, coverage-specific features would be impossible to test, but thankfully initializing these +//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument +//! to: `rustc_span::with_default_session_globals(|| { test_here(); })`. + +use super::counters; +use super::debug; +use super::graph; +use super::spans; + +use coverage_test_macros::let_bcb; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::graph::WithSuccessors; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::coverage::CoverageKind; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; +use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; + +// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`. +const TEMP_BLOCK: BasicBlock = BasicBlock::MAX; + +fn dummy_ty() -> &'static TyS<'static> { + thread_local! { + static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test( + ty::Bool, + TypeFlags::empty(), + DebruijnIndex::from_usize(0), + )); + } + + &DUMMY_TYS.with(|tys| *tys) +} + +struct MockBlocks<'tcx> { + blocks: IndexVec>, + dummy_place: Place<'tcx>, + next_local: usize, +} + +impl<'tcx> MockBlocks<'tcx> { + fn new() -> Self { + Self { + blocks: IndexVec::new(), + dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() }, + next_local: 0, + } + } + + fn new_temp(&mut self) -> Local { + let index = self.next_local; + self.next_local += 1; + Local::new(index) + } + + fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock { + let next_lo = if let Some(last) = self.blocks.last() { + self.blocks[last].terminator().source_info.span.hi() + } else { + BytePos(1) + }; + let next_hi = next_lo + BytePos(1); + self.blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), + kind, + }), + is_cleanup: false, + }) + } + + fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) { + match self.blocks[from_block].terminator_mut().kind { + TerminatorKind::Assert { ref mut target, .. } + | TerminatorKind::Call { destination: Some((_, ref mut target)), .. } + | TerminatorKind::Drop { ref mut target, .. } + | TerminatorKind::DropAndReplace { ref mut target, .. } + | TerminatorKind::FalseEdge { real_target: ref mut target, .. } + | TerminatorKind::FalseUnwind { real_target: ref mut target, .. } + | TerminatorKind::Goto { ref mut target } + | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. } + | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block, + ref invalid => bug!("Invalid from_block: {:?}", invalid), + } + } + + fn add_block_from( + &mut self, + some_from_block: Option, + to_kind: TerminatorKind<'tcx>, + ) -> BasicBlock { + let new_block = self.push(to_kind); + if let Some(from_block) = some_from_block { + self.link(from_block, new_block); + } + new_block + } + + fn set_branch(&mut self, switchint: BasicBlock, branch_index: usize, to_block: BasicBlock) { + match self.blocks[switchint].terminator_mut().kind { + TerminatorKind::SwitchInt { ref mut targets, .. } => { + let mut branches = targets.iter().collect::>(); + let otherwise = if branch_index == branches.len() { + to_block + } else { + let old_otherwise = targets.otherwise(); + if branch_index > branches.len() { + branches.push((branches.len() as u128, old_otherwise)); + while branches.len() < branch_index { + branches.push((branches.len() as u128, TEMP_BLOCK)); + } + to_block + } else { + branches[branch_index] = (branch_index as u128, to_block); + old_otherwise + } + }; + *targets = SwitchTargets::new(branches.into_iter(), otherwise); + } + ref invalid => bug!("Invalid BasicBlock kind or no to_block: {:?}", invalid), + } + } + + fn call(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from( + some_from_block, + TerminatorKind::Call { + func: Operand::Copy(self.dummy_place.clone()), + args: vec![], + destination: Some((self.dummy_place.clone(), TEMP_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ) + } + + fn goto(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Goto { target: TEMP_BLOCK }) + } + + fn switchint(&mut self, some_from_block: Option) -> BasicBlock { + let switchint_kind = TerminatorKind::SwitchInt { + discr: Operand::Move(Place::from(self.new_temp())), + switch_ty: dummy_ty(), + targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK), + }; + self.add_block_from(some_from_block, switchint_kind) + } + + fn return_(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Return) + } + + fn to_body(self) -> Body<'tcx> { + Body::new_cfg_only(self.blocks) + } +} + +fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { + format!( + "{:?}", + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + let term = &data.terminator(); + let kind = &term.kind; + let span = term.source_info.span; + let sp = format!("(span:{},{})", span.lo().to_u32(), span.hi().to_u32()); + match kind { + TerminatorKind::Assert { target, .. } + | TerminatorKind::Call { destination: Some((_, target)), .. } + | TerminatorKind::Drop { target, .. } + | TerminatorKind::DropAndReplace { target, .. } + | TerminatorKind::FalseEdge { real_target: target, .. } + | TerminatorKind::FalseUnwind { real_target: target, .. } + | TerminatorKind::Goto { target } + | TerminatorKind::InlineAsm { destination: Some(target), .. } + | TerminatorKind::Yield { resume: target, .. } => { + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target) + } + TerminatorKind::SwitchInt { targets, .. } => { + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets) + } + _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)), + } + }) + .collect::>() + ) +} + +static PRINT_GRAPHS: bool = false; + +fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bb, + bb, + debug::term_type(&data.terminator().kind), + mir_body + .successors(bb) + .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +fn print_coverage_graphviz( + name: &str, + mir_body: &Body<'_>, + basic_coverage_blocks: &graph::CoverageGraph, +) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + basic_coverage_blocks + .iter_enumerated() + .map(|(bcb, bcb_data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bcb, + bcb, + debug::term_type(&bcb_data.terminator(mir_body).kind), + basic_coverage_blocks + .successors(bcb) + .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +/// Create a mock `Body` with a simple flow. +fn goto_switchint() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let goto = blocks.goto(Some(start)); + let switchint = blocks.switchint(Some(goto)); + let then_call = blocks.call(None); + let else_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + blocks.set_branch(switchint, 1, else_call); + blocks.return_(Some(then_call)); + blocks.return_(Some(else_call)); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_goto_switchint", &mir_body); + /* Graphviz character plots created using: `graph-easy --as=boxart`: + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb1: Goto │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Call │ ◀── │ bb2: SwitchInt │ + └─────────────┘ └────────────────┘ + │ │ + │ │ + ▼ ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb6: Return │ │ bb3: Call │ + └─────────────┘ └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb5: Return │ + └────────────────┘ + */ + mir_body +} + +macro_rules! assert_successors { + ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { + let mut successors = $basic_coverage_blocks.successors[$i].clone(); + successors.sort_unstable(); + assert_eq!(successors, vec![$($successor),*]); + } +} + +#[test] +fn test_covgraph_goto_switchint() { + let mir_body = goto_switchint(); + if false { + println!("basic_blocks = {}", debug_basic_blocks(&mir_body)); + } + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks); + /* + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ + └──────────────┘ └─────────────────┘ + │ + │ + ▼ + ┌─────────────────┐ + │ bcb1: Return │ + └─────────────────┘ + */ + assert_eq!( + basic_coverage_blocks.num_nodes(), + 3, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); + assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb2, []); +} + +/// Create a mock `Body` with a loop. +fn switchint_then_loop_else_return() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let backedge_goto = blocks.goto(Some(then_call)); + blocks.link(backedge_goto, switchint); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb3: Goto │ ─┘ + └────────────────┘ + */ + mir_body +} + +#[test] +fn test_covgraph_switchint_then_loop_else_return() { + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_then_loop_else_return", + &mir_body, + &basic_coverage_blocks, + ); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌────────────┐ ┌─────────────────┐ + │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐ + └────────────┘ └─────────────────┘ │ + │ │ │ + │ │ │ + │ ▼ │ + │ ┌─────────────────┐ │ + │ │ bcb2: Return │ │ + │ └─────────────────┘ │ + │ │ + └─────────────────────────────────────┘ + */ + assert_eq!( + basic_coverage_blocks.num_nodes(), + 4, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); +} + +/// Create a mock `Body` with nested loops. +fn switchint_loop_then_inner_loop_else_break() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let inner_start = blocks.call(Some(then_call)); + let inner_switchint = blocks.switchint(Some(inner_start)); + let inner_then_call = blocks.call(None); + blocks.set_branch(inner_switchint, 0, inner_then_call); + let inner_backedge_goto = blocks.goto(Some(inner_then_call)); + blocks.link(inner_backedge_goto, inner_switchint); + let inner_else_break_goto = blocks.goto(None); + blocks.set_branch(inner_switchint, 1, inner_else_break_goto); + + let backedge_goto = blocks.goto(Some(inner_else_break_goto)); + blocks.link(backedge_goto, switchint); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb4: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────┐ ┌────────────────┐ │ + │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │ + └─────────────┘ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + ▼ ▼ │ │ + ┌─────────────┐ ┌────────────────┐ │ │ + │ bb9: Goto │ ─┐ │ bb6: Call │ │ │ + └─────────────┘ │ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────────────┐ │ │ + │ │ bb7: Goto │ ─┘ │ + │ └────────────────┘ │ + │ │ + └───────────────────────────┘ + */ + mir_body +} + +#[test] +fn test_covgraph_switchint_loop_then_inner_loop_else_break() { + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_loop_then_inner_loop_else_break", + &mir_body, + &basic_coverage_blocks, + ); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐ + └──────────────┘ └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────────┐ │ + │ bcb3: Call │ │ + └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌──────────────┐ ┌─────────────────┐ │ + │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐ + └──────────────┘ └─────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌─────────────────┐ │ │ + │ │ bcb5: Goto │ ─┘ │ + │ └─────────────────┘ │ + │ │ + └────────────────────────────────────────────┘ + */ + assert_eq!( + basic_coverage_blocks.num_nodes(), + 7, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); + assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); + assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); +} + +#[test] +fn test_find_loop_backedges_none() { + let mir_body = goto_switchint(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + if false { + println!( + "basic_coverage_blocks = {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + println!("successors = {:?}", basic_coverage_blocks.successors); + } + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 0, + "backedges: {:?}", + backedges + ); +} + +#[test] +fn test_find_loop_backedges_one() { + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 1, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(3); + + assert_eq!(backedges[bcb1], vec![bcb3]); +} + +#[test] +fn test_find_loop_backedges_two() { + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 2, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_eq!(backedges[bcb1], vec![bcb5]); + assert_eq!(backedges[bcb4], vec![bcb6]); +} + +#[test] +fn test_traverse_coverage_with_loops() { + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let mut traversed_in_order = Vec::new(); + let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); + while let Some(bcb) = traversal.next(&basic_coverage_blocks) { + traversed_in_order.push(bcb); + } + + let_bcb!(6); + + // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except* + // bcb6 are inside the first loop. + assert_eq!( + *traversed_in_order.last().expect("should have elements"), + bcb6, + "bcb6 should not be visited until all nodes inside the first loop have been visited" + ); +} + +fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span { + let mut some_span: Option = None; + for (_, data) in mir_body.basic_blocks().iter_enumerated() { + let term_span = data.terminator().source_info.span; + if let Some(span) = some_span.as_mut() { + *span = span.to(term_span); + } else { + some_span = Some(term_span) + } + } + some_span.expect("body must have at least one BasicBlock") +} + +#[test] +fn test_make_bcb_counters() { + rustc_span::with_default_session_globals(|| { + let mir_body = goto_switchint(); + let body_span = synthesize_body_span_from_terminators(&mir_body); + let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let mut coverage_spans = Vec::new(); + for (bcb, data) in basic_coverage_blocks.iter_enumerated() { + if let Some(span) = + spans::filtered_terminator_span(data.terminator(&mir_body), body_span) + { + coverage_spans.push(spans::CoverageSpan::for_terminator(span, bcb, data.last_bb())); + } + } + let mut coverage_counters = counters::CoverageCounters::new(0); + let intermediate_expressions = coverage_counters + .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) + .expect("should be Ok"); + assert_eq!(intermediate_expressions.len(), 0); + + let_bcb!(1); + assert_eq!( + 1, // coincidentally, bcb1 has a `Counter` with id = 1 + match basic_coverage_blocks[bcb1].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + + let_bcb!(2); + assert_eq!( + 2, // coincidentally, bcb2 has a `Counter` with id = 2 + match basic_coverage_blocks[bcb2].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + }); +} diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 7737672dbde66..aae98f5b6d8d6 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -7,6 +7,7 @@ use rustc_index::vec::Idx; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_span::{hygiene::ExpnKind, ExpnData, Span}; use rustc_target::spec::abi::Abi; @@ -28,6 +29,7 @@ pub struct Inline; #[derive(Copy, Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, block: BasicBlock, target: Option, source_info: SourceInfo, @@ -173,22 +175,23 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func: ref op, ref destination, .. } = terminator.kind { - if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { - // To resolve an instance its substs have to be fully normalized, so - // we do this here. - let normalized_substs = self.tcx.normalize_erasing_regions(self.param_env, substs); + if let TerminatorKind::Call { ref func, ref destination, .. } = terminator.kind { + let func_ty = func.ty(caller_body, self.tcx); + if let ty::FnDef(def_id, substs) = *func_ty.kind() { + // To resolve an instance its substs have to be fully normalized. + let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); let callee = - Instance::resolve(self.tcx, self.param_env, callee_def_id, normalized_substs) - .ok() - .flatten()?; + Instance::resolve(self.tcx, self.param_env, def_id, substs).ok().flatten()?; if let InstanceDef::Virtual(..) | InstanceDef::Intrinsic(_) = callee.def { return None; } + let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs); + return Some(CallSite { callee, + fn_sig, block: bb, target: destination.map(|(_, target)| target), source_info: terminator.source_info, @@ -203,9 +206,8 @@ impl Inliner<'tcx> { debug!("should_inline({:?})", callsite); let tcx = self.tcx; - // Cannot inline generators which haven't been transformed yet - if callee_body.yield_ty.is_some() { - debug!(" yield ty present - not inlining"); + if callsite.fn_sig.c_variadic() { + debug!("callee is variadic - not inlining"); return false; } @@ -218,11 +220,7 @@ impl Inliner<'tcx> { return false; } - let self_no_sanitize = - self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - let callee_no_sanitize = - codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - if self_no_sanitize != callee_no_sanitize { + if self.codegen_fn_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } @@ -256,9 +254,9 @@ impl Inliner<'tcx> { self.tcx.sess.opts.debugging_opts.inline_mir_threshold }; - // Significantly lower the threshold for inlining cold functions if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { - threshold /= 5; + debug!("#[cold] present - not inlining"); + return false; } // Give a bonus functions with a small number of blocks, @@ -447,7 +445,7 @@ impl Inliner<'tcx> { }; // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body); + let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body); let mut integrator = Integrator { args: &args, @@ -528,6 +526,7 @@ impl Inliner<'tcx> { args: Vec>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + callee_body: &Body<'tcx>, ) -> Vec { let tcx = self.tcx; @@ -554,9 +553,7 @@ impl Inliner<'tcx> { // tmp2 = tuple_tmp.2 // // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - // FIXME(eddyb) make this check for `"rust-call"` ABI combined with - // `callee_body.spread_arg == None`, instead of special-casing closures. - if tcx.is_closure(callsite.callee.def_id()) { + if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() { let mut args = args.into_iter(); let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index e1e6e71acb5a8..876ecee80c6a1 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -38,7 +38,9 @@ pub struct Validator { impl<'tcx> MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let param_env = tcx.param_env(def_id); + // We need to param_env_reveal_all_normalized, as some optimizations + // change types in ways that require unfolding opaque types. + let param_env = tcx.param_env_reveal_all_normalized(def_id); let mir_phase = self.mir_phase; let always_live_locals = AlwaysLiveLocals::new(body); @@ -79,7 +81,6 @@ pub fn equal_up_to_regions( } // Normalize lifetimes away on both sides, then compare. - let param_env = param_env.with_reveal_all_normalized(tcx); let normalize = |ty: Ty<'tcx>| { tcx.normalize_erasing_regions( param_env, @@ -167,17 +168,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } // Normalize projections and things like that. - // FIXME: We need to reveal_all, as some optimizations change types in ways - // that require unfolding opaque types. - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); - let src = self.tcx.normalize_erasing_regions(param_env, src); - let dest = self.tcx.normalize_erasing_regions(param_env, dest); + let src = self.tcx.normalize_erasing_regions(self.param_env, src); + let dest = self.tcx.normalize_erasing_regions(self.param_env, dest); // Type-changing assignments can happen when subtyping is used. While // all normal lifetimes are erased, higher-ranked types with their // late-bound lifetimes are still around and can lead to type // differences. So we compare ignoring lifetimes. - equal_up_to_regions(self.tcx, param_env, src, dest) + equal_up_to_regions(self.tcx, self.param_env, src, dest) } } @@ -358,6 +356,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } TerminatorKind::Call { func, args, destination, cleanup, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); + let func_ty = self.tcx.normalize_erasing_regions(self.param_env, func_ty); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 6ed7ed575fcb5..47c0400533bd8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -387,8 +387,9 @@ fn make_mirror_unadjusted<'a, 'tcx>( } }; let upvars = cx - .tcx - .upvars_mentioned(def_id) + .typeck_results() + .closure_captures + .get(&def_id) .iter() .flat_map(|upvars| upvars.iter()) .zip(substs.upvar_tys()) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index da1c54e88b5e2..40aa2db58c720 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1180,8 +1180,7 @@ impl<'a> Parser<'a> { /// Records all tokens consumed by the provided callback, /// including the current token. These tokens are collected /// into a `LazyTokenStream`, and returned along with the result - /// of the callback. The returned `LazyTokenStream` will be `None` - /// if not tokens were captured. + /// of the callback. /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` @@ -1203,17 +1202,14 @@ impl<'a> Parser<'a> { let ret = f(self)?; - // We didn't capture any tokens - let num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; - if num_calls == 0 { - return Ok((ret, None)); - } - // Produces a `TokenStream` on-demand. Using `cursor_snapshot` // and `num_calls`, we can reconstruct the `TokenStream` seen // by the callback. This allows us to avoid producing a `TokenStream` // if it is never needed - for example, a captured `macro_rules!` // argument that is never passed to a proc macro. + // In practice token stream creation happens rarely compared to + // calls to `collect_tokens` (see some statistics in #78736), + // so we are doing as little up-front work as possible. // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. @@ -1247,8 +1243,8 @@ impl<'a> Parser<'a> { let lazy_impl = LazyTokenStreamImpl { start_token, + num_calls: self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls, cursor_snapshot, - num_calls, desugar_doc_comments: self.desugar_doc_comments, }; Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 7288015e17029..debb873beb93b 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -317,10 +317,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // swap in a new set of IR maps for this body let mut maps = IrMaps::new(self.tcx); let hir_id = maps.tcx.hir().body_owner(body.id()); - let def_id = maps.tcx.hir().local_def_id(hir_id); + let local_def_id = maps.tcx.hir().local_def_id(hir_id); + let def_id = local_def_id.to_def_id(); // Don't run unused pass for #[derive()] - if let Some(parent) = self.tcx.parent(def_id.to_def_id()) { + if let Some(parent) = self.tcx.parent(def_id) { if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) { if self.tcx.has_attr(parent, sym::automatically_derived) { return; @@ -328,8 +329,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } - if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { - for (&var_hir_id, _upvar) in upvars { + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_captures.get(&def_id) { + for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); maps.add_variable(Upvar(var_hir_id, var_name)); } @@ -340,7 +341,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { intravisit::walk_body(&mut maps, body); // compute liveness - let mut lsets = Liveness::new(&mut maps, def_id); + let mut lsets = Liveness::new(&mut maps, local_def_id); let entry_ln = lsets.compute(&body, hir_id); lsets.log_liveness(entry_ln, body.id().hir_id); @@ -397,10 +398,18 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // construction site. let mut call_caps = Vec::new(); let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { + if let Some(captures) = self + .tcx + .typeck(closure_def_id) + .closure_captures + .get(&closure_def_id.to_def_id()) + { + // If closure captures is Some, upvars_mentioned must also be Some + let upvars = self.tcx.upvars_mentioned(closure_def_id).unwrap(); + call_caps.extend(captures.keys().map(|var_id| { + let upvar = upvars[var_id]; let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); - CaptureInfo { ln: upvar_ln, var_hid: var_id } + CaptureInfo { ln: upvar_ln, var_hid: *var_id } })); } self.set_captures(expr.hir_id, call_caps); @@ -564,6 +573,7 @@ struct Liveness<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, upvars: Option<&'tcx FxIndexMap>, + closure_captures: Option<&'tcx FxIndexMap>, successors: IndexVec>, rwu_table: RWUTable, @@ -587,6 +597,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); let param_env = ir.tcx.param_env(body_owner); let upvars = ir.tcx.upvars_mentioned(body_owner); + let closure_captures = typeck_results.closure_captures.get(&body_owner.to_def_id()); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -600,6 +611,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { typeck_results, param_env, upvars, + closure_captures, successors: IndexVec::from_elem_n(None, num_live_nodes), rwu_table: RWUTable::new(num_live_nodes * num_vars), closure_ln, @@ -850,14 +862,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(upvars) = self.upvars { + if let Some(closure_captures) = self.closure_captures { // Mark upvars captured by reference as used after closure exits. - for (&var_hir_id, upvar) in upvars.iter().rev() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.body_owner, - }; - match self.typeck_results.upvar_capture(upvar_id) { + // Since closure_captures is Some, upvars must exists too. + let upvars = self.upvars.unwrap(); + for (&var_hir_id, upvar_id) in closure_captures { + let upvar = upvars[&var_hir_id]; + match self.typeck_results.upvar_capture(*upvar_id) { ty::UpvarCapture::ByRef(_) => { let var = self.variable(var_hir_id, upvar.span); self.acc(self.exit_ln, var, ACC_READ | ACC_USE); @@ -869,7 +880,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let succ = self.propagate_through_expr(&body.value, self.exit_ln); - if self.upvars.is_none() { + if self.closure_captures.is_none() { // Either not a closure, or closure without any captured variables. // No need to determine liveness of captured variables, since there // are none. @@ -1341,7 +1352,21 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { acc: u32, ) -> LiveNode { match path.res { - Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), + Res::Local(hid) => { + let in_upvars = self.upvars.map_or(false, |u| u.contains_key(&hid)); + let in_captures = self.closure_captures.map_or(false, |c| c.contains_key(&hid)); + + match (in_upvars, in_captures) { + (false, _) | (true, true) => self.access_var(hir_id, hid, succ, acc, path.span), + (true, false) => { + // This case is possible when with RFC-2229, a wild pattern + // is used within a closure. + // eg: `let _ = x`. The closure doesn't capture x here, + // even though it's mentioned in the closure. + succ + } + } + } _ => succ, } } @@ -1531,11 +1556,15 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let upvars = match self.upvars { + let closure_captures = match self.closure_captures { None => return, - Some(upvars) => upvars, + Some(closure_captures) => closure_captures, }; - for (&var_hir_id, upvar) in upvars.iter() { + + // If closure_captures is Some(), upvars must be Some() too. + let upvars = self.upvars.unwrap(); + for &var_hir_id in closure_captures.keys() { + let upvar = upvars[&var_hir_id]; let var = self.variable(var_hir_id, upvar.span); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 1bf8160e4c384..2f82d0546ba98 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -21,7 +21,7 @@ // references. // // Signatures do not include visibility info. I'm not sure if this is a feature -// or an ommission (FIXME). +// or an omission (FIXME). // // FIXME where clauses need implementing, defs/refs in generics are mostly missing. @@ -677,7 +677,7 @@ impl<'hir> Sig for hir::Variant<'hir> { let mut text = self.ident.to_string(); match self.data { hir::VariantData::Struct(fields, r) => { - let id = parent_id.unwrap(); + let id = parent_id.ok_or("Missing id for Variant's parent")?; let name_def = SigElement { id: id_from_hir_id(id, scx), start: offset, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ad58f89d87da7..3a2a3adce35c9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -318,6 +318,7 @@ symbols! { call_mut, call_once, caller_location, + capture_disjoint_fields, cdylib, ceilf32, ceilf64, @@ -909,6 +910,7 @@ symbols! { rustc_args_required_const, rustc_attrs, rustc_builtin_macro, + rustc_capture_analysis, rustc_clean, rustc_const_stable, rustc_const_unstable, diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index d0ad45153d677..c9f622820dea7 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -10,7 +10,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,+neon,+fp-armv8".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index 41bd2182905c9..0811871c993aa 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -10,7 +10,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,-neon,-fp-armv8".to_string(), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 3685630572371..c6586b79b87f8 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -12,7 +12,6 @@ pub fn target() -> Target { options: TargetOptions { endian: "big".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index 2ff3c8950c483..e3d4397f6123f 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -12,7 +12,6 @@ pub fn target() -> Target { options: TargetOptions { endian: "big".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs index 742b403cff90c..74deab0191652 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -10,9 +10,6 @@ // bare-metal binaries (the `gcc` linker has the advantage that it knows where C // libraries and crt*.o are but it's not much of an advantage here); LLD is also // faster -// - `os` set to `none`. rationale: matches `thumb` targets -// - `env` and `vendor` are set to an empty string. rationale: matches `thumb` -// targets // - `panic_strategy` set to `abort`. rationale: matches `thumb` targets // - `relocation-model` set to `static`; also no PIE, no relro and no dynamic // linking. rationale: matches `thumb` targets @@ -21,7 +18,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index b9cda18d6b46f..c5c720f5fbde4 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -9,7 +9,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 440c2434907be..3f49bd8786937 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index c1bf332a72ddf..9b2e8a8058fe7 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 9cc10032c71da..67a7684da2c70 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -11,7 +11,6 @@ pub fn target(target_cpu: String) -> Target { pointer_width: 16, options: TargetOptions { c_int_width: "16".to_string(), - os: "unknown".to_string(), cpu: target_cpu.clone(), exe_suffix: ".elf".to_string(), diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index e467c7c8f21e9..5c39773cbe381 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -21,7 +21,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "fuchsia".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), lld_flavor: LldFlavor::Ld, diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index a8005927a7beb..0f9d3c3de1543 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { arch: "mips".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float,+noabicalls".to_string(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f949bf95a502a..b37783c5820bf 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -713,11 +713,14 @@ pub struct TargetOptions { pub endian: String, /// Width of c_int type. Defaults to "32". pub c_int_width: String, - /// OS name to use for conditional compilation. Defaults to "none". + /// OS name to use for conditional compilation (`target_os`). Defaults to "none". + /// "none" implies a bare metal target without `std` library. + /// A couple of targets having `std` also use "unknown" as an `os` value, + /// but they are exceptions. pub os: String, - /// Environment name to use for conditional compilation. Defaults to "". + /// Environment name to use for conditional compilation (`target_env`). Defaults to "". pub env: String, - /// Vendor name to use for conditional compilation. Defaults to "unknown". + /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown". pub vendor: String, /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed /// on the command line. Defaults to `LinkerFlavor::Gcc`. @@ -819,10 +822,23 @@ pub struct TargetOptions { /// Only useful for compiling against Illumos/Solaris, /// as they have a different set of linker flags. Defaults to false. pub is_like_solaris: bool, - /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows, - /// only really used for figuring out how to find libraries, since Windows uses its own - /// library naming convention. Defaults to false. + /// Whether the target is like Windows. + /// This is a combination of several more specific properties represented as a single flag: + /// - The target uses a Windows ABI, + /// - uses PE/COFF as a format for object code, + /// - uses Windows-style dllexport/dllimport for shared libraries, + /// - uses import libraries and .def files for symbol exports, + /// - executables support setting a subsystem. pub is_like_windows: bool, + /// Whether the target is like MSVC. + /// This is a combination of several more specific properties represented as a single flag: + /// - The target has all the properties from `is_like_windows` + /// (for in-tree targets "is_like_msvc ⇒ is_like_windows" is ensured by a unit test), + /// - has some MSVC-specific Windows ABI properties, + /// - uses a link.exe-like linker, + /// - uses CodeView/PDB for debuginfo and natvis for its visualization, + /// - uses SEH-based unwinding, + /// - supports control flow guard mechanism. pub is_like_msvc: bool, /// Whether the target toolchain is like Emscripten's. Only useful for compiling with /// Emscripten toolchain. diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs index ef966cb702ec4..cc2578aa578e8 100644 --- a/compiler/rustc_target/src/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -9,7 +9,6 @@ pub fn target() -> Target { options: TargetOptions { c_int_width: "16".to_string(), - vendor: String::new(), executables: true, // The LLVM backend currently can't generate object files. To diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index f348df7d5a716..9ec8467e0ac44 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -8,6 +8,7 @@ pub(super) fn test_target(target: Target) { impl Target { fn check_consistency(&self) { + assert!(self.is_like_windows || !self.is_like_msvc); // Check that LLD with the given flavor is treated identically to the linker it emulates. // If your target really needs to deviate from the rules below, except it and document the // reasons. @@ -16,6 +17,7 @@ impl Target { || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), self.lld_flavor == LldFlavor::Link, ); + assert_eq!(self.is_like_msvc, self.lld_flavor == LldFlavor::Link); for args in &[ &self.pre_link_args, &self.late_link_args, @@ -36,5 +38,18 @@ impl Target { && self.post_link_objects_fallback.is_empty()) || self.crt_objects_fallback.is_some() ); + // Keep the default "unknown" vendor instead. + assert_ne!(self.vendor, ""); + if !self.can_use_os_unknown() { + // Keep the default "none" for bare metal targets instead. + assert_ne!(self.os, "unknown"); + } + } + + // Add your target to the whitelist if it has `std` library + // and you certainly want "unknown" for the OS name. + fn can_use_os_unknown(&self) -> bool { + self.llvm_target == "wasm32-unknown-unknown" + || (self.env == "sgx" && self.vendor == "fortanix") } } diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index e550467502754..ec24807fec4ea 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -32,7 +32,6 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOpti pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, // In most cases, LLD is good enough diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 79fe77495e731..322b6f530e9fd 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -46,15 +46,6 @@ pub fn opts() -> TargetOptions { stack_probes: true, singlethread: true, linker: Some("rust-lld".to_string()), - // FIXME: This should likely be `true` inherited from `msvc_base` - // because UEFI follows Windows ABI and uses PE/COFF. - // The `false` is probably causing ABI bugs right now. - is_like_windows: false, - // FIXME: This should likely be `true` inherited from `msvc_base` - // because UEFI follows Windows ABI and uses PE/COFF. - // The `false` is probably causing ABI bugs right now. - is_like_msvc: false, - ..base } } diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 9c697674f397a..3f44acdc36b2d 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -79,7 +79,6 @@ pub fn target() -> Target { let mut options = wasm32_base::options(); options.os = "wasi".to_string(); - options.vendor = String::new(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); options .pre_link_args diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index e9dfef718fde9..e0bbe3cb07917 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -39,10 +39,21 @@ use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; -use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; +use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_span::sym; use rustc_span::{Span, Symbol}; -use std::collections::hash_map::Entry; + +/// Describe the relationship between the paths of two places +/// eg: +/// - `foo` is ancestor of `foo.bar.baz` +/// - `foo.bar.baz` is an descendant of `foo.bar` +/// - `foo.bar` and `foo.baz` are divergent +enum PlaceAncestryRelation { + Ancestor, + Descendant, + Divergent, +} impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -111,40 +122,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - let mut closure_captures: FxIndexMap = - FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default()); - for (&var_hir_id, _) in upvars.iter() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id.expect_local(), - }; - debug!("seed upvar_id {:?}", upvar_id); - // Adding the upvar Id to the list of Upvars, which will be added - // to the map for the closure at the end of the for loop. - closure_captures.insert(var_hir_id, upvar_id); - - let capture_kind = match capture_clause { - hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), - hir::CaptureBy::Ref => { - let origin = UpvarRegion(upvar_id, span); - let upvar_region = self.next_region_var(origin); - let upvar_borrow = - ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - ty::UpvarCapture::ByRef(upvar_borrow) - } - }; + let local_def_id = closure_def_id.expect_local(); - self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind); - } - // Add the vector of upvars to the map keyed with the closure id. - // This gives us an easier access to them without having to call - // tcx.upvars again.. - if !closure_captures.is_empty() { - self.typeck_results - .borrow_mut() - .closure_captures - .insert(closure_def_id, closure_captures); + let mut capture_information: FxIndexMap, ty::CaptureInfo<'tcx>> = + Default::default(); + if !self.tcx.features().capture_disjoint_fields { + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + for (&var_hir_id, _) in upvars.iter() { + let place = self.place_for_root_variable(local_def_id, var_hir_id); + + debug!("seed place {:?}", place); + + let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id); + let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); + let info = ty::CaptureInfo { expr_id: None, capture_kind }; + + capture_information.insert(place, info); + } } } @@ -153,9 +147,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut delegate = InferBorrowKind { fcx: self, closure_def_id, + closure_span: span, + capture_clause, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_origin: None, - adjust_upvar_captures: ty::UpvarCaptureMap::default(), + capture_information, }; euv::ExprUseVisitor::new( &mut delegate, @@ -166,6 +162,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + debug!( + "For closure={:?}, capture_information={:#?}", + closure_def_id, delegate.capture_information + ); + self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); + if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. @@ -182,7 +184,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.typeck_results.borrow_mut().upvar_capture_map.extend(delegate.adjust_upvar_captures); + self.compute_min_captures(closure_def_id, delegate); + self.log_closure_min_capture_info(closure_def_id, span); + + self.min_captures_to_closure_captures_bridge(closure_def_id); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -226,15 +231,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let closure_def_id = tcx.hir().local_def_id(closure_id); - tcx.upvars_mentioned(closure_def_id) + self.typeck_results + .borrow() + .closure_captures + .get(&closure_def_id.to_def_id()) .iter() .flat_map(|upvars| { upvars.iter().map(|(&var_hir_id, _)| { let upvar_ty = self.node_ty(var_hir_id); - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id, - }; + let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); let capture = self.typeck_results.borrow().upvar_capture(upvar_id); debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture); @@ -250,6 +255,296 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect() } + + /// Bridge for closure analysis + /// ---------------------------- + /// + /// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229, + /// to structures currently used in the compiler for handling closure captures. + /// + /// For example the following structure will be converted: + /// + /// closure_min_captures + /// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ] + /// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ] + /// + /// to + /// + /// 1. closure_captures + /// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c) + /// + /// 2. upvar_capture_map + /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue + fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) { + let mut closure_captures: FxIndexMap = Default::default(); + let mut upvar_capture_map = ty::UpvarCaptureMap::default(); + + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; + + let upvar_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id, + base => bug!("Expected upvar, found={:?}", base), + }; + + assert_eq!(upvar_id.var_path.hir_id, *var_hir_id); + assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); + + closure_captures.insert(*var_hir_id, upvar_id); + + let new_capture_kind = if let Some(capture_kind) = + upvar_capture_map.get(&upvar_id) + { + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; + determine_capture_info(fake_capture_info, capture_info).capture_kind + } else { + capture_info.capture_kind + }; + upvar_capture_map.insert(upvar_id, new_capture_kind); + } + } + } + debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures); + debug!( + "For closure_def_id={:?}, upvar_capture_map={:#?}", + closure_def_id, upvar_capture_map + ); + + if !closure_captures.is_empty() { + self.typeck_results + .borrow_mut() + .closure_captures + .insert(closure_def_id, closure_captures); + + self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map); + } + } + + /// Analyzes the information collected by `InferBorrowKind` to compute the min number of + /// Places (and corresponding capture kind) that we need to keep track of to support all + /// the required captured paths. + /// + /// Eg: + /// ```rust + /// struct Point { x: i32, y: i32 } + /// + /// let s: String; // hir_id_s + /// let mut p: Point; // his_id_p + /// let c = || { + /// println!("{}", s); // L1 + /// p.x += 10; // L2 + /// println!("{}" , p.y) // L3 + /// println!("{}", p) // L4 + /// drop(s); // L5 + /// }; + /// ``` + /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on + /// the lines L1..5 respectively. + /// + /// InferBorrowKind results in a structure like this: + /// + /// ``` + /// { + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue), + /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow)) + /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow)) + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow)) + /// ``` + /// + /// After the min capture analysis, we get: + /// ``` + /// { + /// hir_id_s -> [ + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue) + /// ], + /// hir_id_p -> [ + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)), + /// ], + /// ``` + fn compute_min_captures( + &self, + closure_def_id: DefId, + inferred_info: InferBorrowKind<'_, 'tcx>, + ) { + let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); + + for (place, capture_info) in inferred_info.capture_information.into_iter() { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + 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 min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { + None => { + let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }]; + root_var_min_capture_list.insert(var_hir_id, min_cap_list); + continue; + } + Some(min_cap_list) => min_cap_list, + }; + + // Go through each entry in the current list of min_captures + // - if ancestor is found, update it's capture kind to account for current place's + // capture information. + // + // - if descendant is found, remove it from the list, and update the current place's + // capture information to account for the descendants's capture kind. + // + // We can never be in a case where the list contains both an ancestor and a descendant + // Also there can only be ancestor but in case of descendants there might be + // multiple. + + let mut descendant_found = false; + let mut updated_capture_info = capture_info; + min_cap_list.retain(|possible_descendant| { + match determine_place_ancestry_relation(&place, &possible_descendant.place) { + // current place is ancestor of possible_descendant + PlaceAncestryRelation::Ancestor => { + descendant_found = true; + updated_capture_info = + determine_capture_info(updated_capture_info, possible_descendant.info); + false + } + + _ => true, + } + }); + + let mut ancestor_found = false; + if !descendant_found { + for possible_ancestor in min_cap_list.iter_mut() { + match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + // current place is descendant of possible_ancestor + PlaceAncestryRelation::Descendant => { + ancestor_found = true; + possible_ancestor.info = + determine_capture_info(possible_ancestor.info, capture_info); + + // Only one ancestor of the current place will be in the list. + break; + } + _ => {} + } + } + } + + // Only need to insert when we don't have an ancestor in the existing min capture list + if !ancestor_found { + let captured_place = + ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + min_cap_list.push(captured_place); + } + } + + debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); + + if !root_var_min_capture_list.is_empty() { + self.typeck_results + .borrow_mut() + .closure_min_captures + .insert(closure_def_id, root_var_min_capture_list); + } + } + + fn init_capture_kind( + &self, + capture_clause: hir::CaptureBy, + upvar_id: ty::UpvarId, + closure_span: Span, + ) -> ty::UpvarCapture<'tcx> { + match capture_clause { + hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), + hir::CaptureBy::Ref => { + let origin = UpvarRegion(upvar_id, closure_span); + let upvar_region = self.next_region_var(origin); + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; + ty::UpvarCapture::ByRef(upvar_borrow) + } + } + } + + fn place_for_root_variable( + &self, + closure_def_id: LocalDefId, + var_hir_id: hir::HirId, + ) -> Place<'tcx> { + let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); + + Place { + base_ty: self.node_ty(var_hir_id), + base: PlaceBase::Upvar(upvar_id), + projections: Default::default(), + } + } + + fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { + self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + } + + fn log_capture_analysis_first_pass( + &self, + closure_def_id: rustc_hir::def_id::DefId, + capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, + closure_span: Span, + ) { + if self.should_log_capture_analysis(closure_def_id) { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:"); + for (place, capture_info) in capture_information { + let capture_str = construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Capturing {}", capture_str); + + let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + diag.span_note(span, &output_str); + } + diag.emit(); + } + } + + fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) { + if self.should_log_capture_analysis(closure_def_id) { + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:"); + + for (_, min_captures_for_var) in min_captures { + for capture in min_captures_for_var { + let place = &capture.place; + let capture_info = &capture.info; + + let capture_str = + construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Min Capture {}", capture_str); + + let span = + capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + diag.span_note(span, &output_str); + } + } + diag.emit(); + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -258,6 +553,10 @@ struct InferBorrowKind<'a, 'tcx> { // The def-id of the closure whose kind and upvar accesses are being inferred. closure_def_id: DefId, + closure_span: Span, + + capture_clause: hir::CaptureBy, + // The kind that we have inferred that the current closure // requires. Note that we *always* infer a minimal kind, even if // we don't always *use* that in the final result (i.e., sometimes @@ -270,9 +569,29 @@ struct InferBorrowKind<'a, 'tcx> { // variable access that caused us to do so. current_origin: Option<(Span, Symbol)>, - // For each upvar that we access, we track the minimal kind of - // access we need (ref, ref mut, move, etc). - adjust_upvar_captures: ty::UpvarCaptureMap<'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. + /// + /// Consider closure where s.str1 is captured via an ImmutableBorrow and + /// s.str2 via a MutableBorrow + /// + /// ```rust + /// // Assume that the HirId for the variable definition is `V1` + /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } + /// + /// let fix_s = |new_s2| { + /// // Assume that the HirId for the expression `s.str1` is `E1` + /// println!("Updating SomeStruct with str1=", s.str1); + /// // Assume that the HirId for the expression `*s.str2` is `E2` + /// s.str2 = new_s2; + /// } + /// ``` + /// + /// For closure `fix_s`, (at a high level) the map contains + /// + /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } + /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } + capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { @@ -314,26 +633,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { var_name(tcx, upvar_id.var_path.hir_id), ); - let new_capture = ty::UpvarCapture::ByValue(Some(usage_span)); - match self.adjust_upvar_captures.entry(upvar_id) { - Entry::Occupied(mut e) => { - match e.get() { - // We always overwrite `ByRef`, since we require - // that the upvar be available by value. - // - // If we had a previous by-value usage without a specific - // span, use ours instead. Otherwise, keep the first span - // we encountered, since there isn't an obviously better one. - ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => { - e.insert(new_capture); - } - _ => {} - } - } - Entry::Vacant(e) => { - e.insert(new_capture); - } - } + let capture_info = ty::CaptureInfo { + expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), + }; + + let curr_info = self.capture_information[&place_with_id.place]; + let updated_info = determine_capture_info(curr_info, capture_info); + + self.capture_information[&place_with_id.place] = updated_info; } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -349,7 +657,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id ); - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + if let PlaceBase::Upvar(_) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; for pointer_ty in place_with_id.place.deref_tys() { match pointer_ty.kind() { @@ -363,7 +671,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { _ => (), } } - self.adjust_upvar_deref(upvar_id, self.fcx.tcx.hir().span(diag_expr_id), borrow_kind); + self.adjust_upvar_deref(place_with_id, diag_expr_id, borrow_kind); } } @@ -377,24 +685,20 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id ); - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + if let PlaceBase::Upvar(_) = place_with_id.place.base { if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { // Raw pointers don't inherit mutability. return; } // for a borrowed pointer to be unique, its base must be unique - self.adjust_upvar_deref( - upvar_id, - self.fcx.tcx.hir().span(diag_expr_id), - ty::UniqueImmBorrow, - ); + self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow); } } fn adjust_upvar_deref( &mut self, - upvar_id: ty::UpvarId, - place_span: Span, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, borrow_kind: ty::BorrowKind, ) { assert!(match borrow_kind { @@ -411,15 +715,16 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { // upvar, then we need to modify the // borrow_kind of the upvar to make sure it // is inferred to mutable if necessary - self.adjust_upvar_borrow_kind(upvar_id, borrow_kind); + self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); - // also need to be in an FnMut closure since this is not an ImmBorrow - self.adjust_closure_kind( - upvar_id.closure_expr_id, - ty::ClosureKind::FnMut, - place_span, - var_name(tcx, upvar_id.var_path.hir_id), - ); + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + tcx.hir().span(diag_expr_id), + var_name(tcx, upvar_id.var_path.hir_id), + ); + } } /// We infer the borrow_kind with which to borrow upvars in a stack closure. @@ -427,37 +732,34 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { /// moving from left to right as needed (but never right to left). /// Here the argument `mutbl` is the borrow_kind that is required by /// some particular use. - fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) { - let upvar_capture = self - .adjust_upvar_captures - .get(&upvar_id) - .copied() - .unwrap_or_else(|| self.fcx.typeck_results.borrow().upvar_capture(upvar_id)); + fn adjust_upvar_borrow_kind( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + kind: ty::BorrowKind, + ) { + let curr_capture_info = self.capture_information[&place_with_id.place]; + debug!( - "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", - upvar_id, upvar_capture, kind + "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})", + place_with_id, diag_expr_id, curr_capture_info, kind ); - match upvar_capture { - ty::UpvarCapture::ByValue(_) => { - // Upvar is already by-value, the strongest criteria. - } - ty::UpvarCapture::ByRef(mut upvar_borrow) => { - match (upvar_borrow.kind, kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => { - upvar_borrow.kind = kind; - self.adjust_upvar_captures - .insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow)); - } - // Take LHS: - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) - | (ty::MutBorrow, _) => {} - } - } - } + if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { + // It's already captured by value, we don't need to do anything here + return; + } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { + // Use the same region as the current capture information + // Doesn't matter since only one of the UpvarBorrow will be used. + let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; + + let capture_info = ty::CaptureInfo { + expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), + }; + let updated_info = determine_capture_info(curr_capture_info, capture_info); + self.capture_information[&place_with_id.place] = updated_info; + }; } fn adjust_closure_kind( @@ -501,6 +803,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } } } + + fn init_capture_info_for_place( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + ) { + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); + + let capture_kind = + self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); + + let expr_id = Some(diag_expr_id); + let capture_info = ty::CaptureInfo { expr_id, capture_kind }; + + debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); + + self.capture_information.insert(place_with_id.place.clone(), capture_info); + } else { + debug!("Not upvar: {:?}", place_with_id); + } + } } impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { @@ -514,7 +838,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", place_with_id, diag_expr_id, mode ); - self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode); + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id, diag_expr_id); + } + + self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode); } fn borrow( @@ -528,6 +856,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, bk ); + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id, diag_expr_id); + } + match bk { ty::ImmBorrow => {} ty::UniqueImmBorrow => { @@ -541,10 +873,174 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); + + if !self.capture_information.contains_key(&assignee_place.place) { + self.init_capture_info_for_place(assignee_place, diag_expr_id); + } + self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id); } } +fn construct_capture_info_string( + tcx: TyCtxt<'_>, + place: &Place<'tcx>, + capture_info: &ty::CaptureInfo<'tcx>, +) -> String { + let variable_name = match place.base { + PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + + let mut projections_str = String::new(); + for (i, item) in place.projections.iter().enumerate() { + let proj = match item.kind { + ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), + ProjectionKind::Deref => String::from("Deref"), + ProjectionKind::Index => String::from("Index"), + ProjectionKind::Subslice => String::from("Subslice"), + }; + if i != 0 { + projections_str.push_str(","); + } + projections_str.push_str(proj.as_str()); + } + + let capture_kind_str = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => "ByValue".into(), + ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + }; + format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str) +} + fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } + +/// Helper function to determine if we need to escalate CaptureKind from +/// CaptureInfo A to B and returns the escalated CaptureInfo. +/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) +/// +/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based +/// on the `CaptureInfo` containing an associated expression id. +/// +/// If both the CaptureKind and Expression are considered to be equivalent, +/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize +/// expressions reported back to the user as part of diagnostics based on which appears earlier +/// in the closure. This can be acheived simply by calling +/// `determine_capture_info(existing_info, current_info)`. This works out because the +/// expressions that occur earlier in the closure body than the current expression are processed before. +/// Consider the following example +/// ```rust +/// let mut p: Point { x: 10, y: 10 }; +/// +/// let c = || { +/// p.x += 10; +/// // ^ E1 ^ +/// // ... +/// // More code +/// // ... +/// p.x += 10; // E2 +/// // ^ E2 ^ +/// } +/// ``` +/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), +/// and both have an expression associated, however for diagnostics we prefer reporting +/// `E1` since it appears earlier in the closure body. When `E2` is being processed we +/// would've already handled `E1`, and have an existing capture_information for it. +/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return +/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. +fn determine_capture_info( + capture_info_a: ty::CaptureInfo<'tcx>, + capture_info_b: ty::CaptureInfo<'tcx>, +) -> ty::CaptureInfo<'tcx> { + // If the capture kind is equivalent then, we don't need to escalate and can compare the + // expressions. + let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { + // We don't need to worry about the spans being ignored here. + // + // The expr_id in capture_info corresponds to the span that is stored within + // ByValue(span) and therefore it gets handled with priortizing based on + // expressions below. + true + } + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + ref_a.kind == ref_b.kind + } + (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, + }; + + if eq_capture_kind { + match (capture_info_a.expr_id, capture_info_b.expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, + } + } else { + // We select the CaptureKind which ranks higher based the following priority order: + // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow + match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), _) => capture_info_a, + (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + match (ref_a.kind, ref_b.kind) { + // Take LHS: + (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) + | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, + + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::MutBorrow, ty::MutBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } +} + +/// Determines the Ancestry relationship of Place A relative to Place B +/// +/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B +/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B +/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other. +fn determine_place_ancestry_relation( + place_a: &Place<'tcx>, + place_b: &Place<'tcx>, +) -> PlaceAncestryRelation { + // If Place A and Place B, don't start off from the same root variable, they are divergent. + if place_a.base != place_b.base { + return PlaceAncestryRelation::Divergent; + } + + // Assume of length of projections_a = n + let projections_a = &place_a.projections; + + // Assume of length of projections_b = m + let projections_b = &place_b.projections; + + let mut same_initial_projections = true; + + for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) { + if proj_a != proj_b { + same_initial_projections = false; + break; + } + } + + if same_initial_projections { + // First min(n, m) projections are the same + // Select Ancestor/Descendant + if projections_b.len() >= projections_a.len() { + PlaceAncestryRelation::Ancestor + } else { + PlaceAncestryRelation::Descendant + } + } else { + PlaceAncestryRelation::Divergent + } +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 57bd89b9d3da9..83cc1da69851f 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{self, adjustment, TyCtxt}; use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; -use rustc_span::Span; /////////////////////////////////////////////////////////////////////////// // The Delegate trait @@ -73,6 +72,7 @@ pub enum MutateMode { // This is the code that actually walks the tree. pub struct ExprUseVisitor<'a, 'tcx> { mc: mc::MemCategorizationContext<'a, 'tcx>, + body_owner: LocalDefId, delegate: &'a mut dyn Delegate<'tcx>, } @@ -110,6 +110,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ) -> Self { ExprUseVisitor { mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), + body_owner, delegate, } } @@ -329,8 +330,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_expr(base); } - hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => { - self.walk_captures(expr, fn_decl_span); + hir::ExprKind::Closure(..) => { + self.walk_captures(expr); } hir::ExprKind::Box(ref base) => { @@ -529,7 +530,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); let tcx = self.tcx(); - let ExprUseVisitor { ref mc, ref mut delegate } = *self; + let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); @@ -569,50 +570,78 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) { + /// Handle the case where the current body contains a closure. + /// + /// When the current body being handled is a closure, then we must make sure that + /// - The parent closure only captures Places from the nested closure that are not local to it. + /// + /// In the following example the closures `c` only captures `p.x`` even though `incr` + /// is a capture of the nested closure + /// + /// ```rust + /// let p = ..; + /// let c = || { + /// let incr = 10; + /// let nested = || p.x += incr; + /// } + /// ``` + /// + /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing + /// closure as the DefId. + fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { debug!("walk_captures({:?})", closure_expr); - let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id); - if let Some(upvars) = self.tcx().upvars_mentioned(closure_def_id) { - for &var_id in upvars.keys() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_def_id, - }; - let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id); - let captured_place = return_if_err!(self.cat_captured_var( - closure_expr.hir_id, - fn_decl_span, - var_id, - )); - match upvar_capture { - ty::UpvarCapture::ByValue(_) => { - let mode = copy_or_move(&self.mc, &captured_place); - self.delegate.consume(&captured_place, captured_place.hir_id, mode); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow( - &captured_place, - captured_place.hir_id, - upvar_borrow.kind, - ); + let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); + let upvars = self.tcx().upvars_mentioned(self.body_owner); + + // For purposes of this function, generator and closures are equivalent. + let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() { + ty::Closure(..) | ty::Generator(..) => true, + _ => false, + }; + + if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) { + // The nested closure might be capturing the current (enclosing) closure's local variables. + // We check if the root variable is ever mentioned within the enclosing closure, if not + // then for the current body (if it's a closure) these aren't captures, we will ignore them. + continue; + } + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; + + let upvar_id = if body_owner_is_closure { + // Mark the place to be captured by the enclosing closure + ty::UpvarId::new(*var_hir_id, self.body_owner) + } else { + ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local()) + }; + let place_with_id = PlaceWithHirId::new( + capture_info.expr_id.unwrap_or(closure_expr.hir_id), + place.base_ty, + PlaceBase::Upvar(upvar_id), + place.projections.clone(), + ); + match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => { + let mode = copy_or_move(&self.mc, &place_with_id); + self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.delegate.borrow( + &place_with_id, + place_with_id.hir_id, + upvar_borrow.kind, + ); + } } } } } } - - fn cat_captured_var( - &mut self, - closure_hir_id: hir::HirId, - closure_span: Span, - var_id: hir::HirId, - ) -> mc::McResult> { - // Create the place for the variable being borrowed, from the - // perspective of the creator (parent) of the closure. - let var_ty = self.mc.node_ty(var_id)?; - self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id)) - } } fn copy_or_move<'a, 'tcx>( diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 49122f53d33ad..7151d3763f01f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -863,6 +863,30 @@ impl BTreeMap { } } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x*10)).collect(); + /// // Keep only the elements with even-numbered keys. + /// map.retain(|&k, _| k % 2 == 0); + /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)])); + /// ``` + #[inline] + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.drain_filter(|k, v| !f(k, v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index dd3ebcccf76a5..11dbb584abdac 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -808,6 +808,17 @@ fn test_range_mut() { map.check(); } +#[test] +fn test_retain() { + let mut map: BTreeMap = (0..100).map(|x| (x, x * 10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); +} + mod test_drain_filter { use super::*; diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 684019f8f5f5e..1a807100653bc 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -798,6 +798,30 @@ impl BTreeSet { Recover::take(&mut self.map, value) } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeSet; + /// + /// let xs = [1, 2, 3, 4, 5, 6]; + /// let mut set: BTreeSet = xs.iter().cloned().collect(); + /// // Keep only the even numbers. + /// set.retain(|&k| k % 2 == 0); + /// assert!(set.iter().eq([2, 4, 6].iter())); + /// ``` + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.drain_filter(|v| !f(v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 52cde8299e418..ef40a048a382e 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -324,6 +324,17 @@ fn test_is_subset() { assert_eq!(is_subset(&[99, 100], &large), false); } +#[test] +fn test_retain() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut set: BTreeSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); +} + #[test] fn test_drain_filter() { let mut x: BTreeSet<_> = [1].iter().copied().collect(); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 9f037890483cf..ca140b9d27828 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -178,6 +178,7 @@ const LLVM_TOOLS: &[&str] = &[ "llvm-size", // used to prints the size of the linker sections of a program "llvm-strip", // used to discard symbols from binary files to reduce their size "llvm-ar", // used for creating and modifying archive files + "llvm-as", // used to convert LLVM assembly to LLVM bitcode "llvm-dis", // used to disassemble LLVM bitcode "llc", // used to compile LLVM bytecode "opt", // used to optimize LLVM bytecode diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs index ff9049edb4f2c..30aff0a64efb9 100644 --- a/src/test/mir-opt/inline/inline-compatibility.rs +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -1,12 +1,11 @@ // Checks that only functions with compatible attributes are inlined. // // only-x86_64 -// needs-sanitizer-address -// compile-flags: -Zsanitizer=address #![crate_type = "lib"] #![feature(no_sanitize)] #![feature(target_feature_11)] +#![feature(c_variadic)] // EMIT_MIR inline_compatibility.inlined_target_feature.Inline.diff #[target_feature(enable = "sse2")] @@ -35,5 +34,22 @@ pub unsafe fn not_inlined_no_sanitize() { pub unsafe fn target_feature() {} #[inline] -#[no_sanitize(address, memory)] +#[no_sanitize(address)] pub unsafe fn no_sanitize() {} + +// EMIT_MIR inline_compatibility.not_inlined_c_variadic.Inline.diff +pub unsafe fn not_inlined_c_variadic() { + let s = sum(4u32, 4u32, 30u32, 200u32, 1000u32); +} + +#[no_mangle] +#[inline(always)] +unsafe extern "C" fn sum(n: u32, mut vs: ...) -> u32 { + let mut s = 0; + let mut i = 0; + while i != n { + s += vs.arg::(); + i += 1; + } + s +} diff --git a/src/test/mir-opt/inline/inline-generator.rs b/src/test/mir-opt/inline/inline-generator.rs new file mode 100644 index 0000000000000..d11b3e548f721 --- /dev/null +++ b/src/test/mir-opt/inline/inline-generator.rs @@ -0,0 +1,16 @@ +// ignore-wasm32-bare compiled with panic=abort by default +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::pin::Pin; + +// EMIT_MIR inline_generator.main.Inline.diff +fn main() { + let _r = Pin::new(&mut g()).resume(false); +} + +#[inline(always)] +pub fn g() -> impl Generator { + #[inline(always)] + |a| { yield if a { 7 } else { 13 } } +} diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff index 451ec39422fc4..c95cf47695785 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_no_sanitize` after Inline fn inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:24:37: 24:37 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -+ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:25:5: 25:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:23:37: 23:37 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 ++ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:24:5: 24:18 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 +- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:25:5: 25:16 +- // + span: $DIR/inline-compatibility.rs:24:5: 24:16 - // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:25:5: 25:18 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:25:18: 25:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:24:37: 26:2 - return; // scope 0 at $DIR/inline-compatibility.rs:26:2: 26:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:24:5: 24:18 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:24:18: 24:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:23:37: 25:2 + return; // scope 0 at $DIR/inline-compatibility.rs:25:2: 25:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff index a59ddd344cb26..2bb928343229f 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_target_feature` after Inline fn inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:13:40: 13:40 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -+ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:14:5: 14:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:12:40: 12:40 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 ++ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:13:5: 13:21 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 +- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:14:5: 14:19 +- // + span: $DIR/inline-compatibility.rs:13:5: 13:19 - // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:14:5: 14:21 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:14:21: 14:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:13:40: 15:2 - return; // scope 0 at $DIR/inline-compatibility.rs:15:2: 15:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:13:5: 13:21 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:13:21: 13:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:12:40: 14:2 + return; // scope 0 at $DIR/inline-compatibility.rs:14:2: 14:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff new file mode 100644 index 0000000000000..09bca903c80e8 --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff @@ -0,0 +1,25 @@ +- // MIR for `not_inlined_c_variadic` before Inline ++ // MIR for `not_inlined_c_variadic` after Inline + + fn not_inlined_c_variadic() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:41:40: 41:40 + let _1: u32; // in scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + scope 1 { + debug s => _1; // in scope 1 at $DIR/inline-compatibility.rs:42:9: 42:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + _1 = sum(const 4_u32, const 4_u32, const 30_u32, const 200_u32, const 1000_u32) -> bb1; // scope 0 at $DIR/inline-compatibility.rs:42:13: 42:52 + // mir::Constant + // + span: $DIR/inline-compatibility.rs:42:13: 42:16 + // + literal: Const { ty: unsafe extern "C" fn(u32, ...) -> u32 {sum}, val: Value(Scalar()) } + } + + bb1: { + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:41:40: 43:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:43:1: 43:2 + return; // scope 0 at $DIR/inline-compatibility.rs:43:2: 43:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff index 651eadc1e849c..5af3946f2e501 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_no_sanitize` after Inline fn not_inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:29:41: 29:41 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:28:41: 28:41 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 - _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 + _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 // mir::Constant - // + span: $DIR/inline-compatibility.rs:30:5: 30:16 + // + span: $DIR/inline-compatibility.rs:29:5: 29:16 // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:30:18: 30:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:29:41: 31:2 - return; // scope 0 at $DIR/inline-compatibility.rs:31:2: 31:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:29:18: 29:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:28:41: 30:2 + return; // scope 0 at $DIR/inline-compatibility.rs:30:2: 30:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff index 55b9edf3adc1f..8c9fa573ce218 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_target_feature` after Inline fn not_inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:18:44: 18:44 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:17:44: 17:44 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 - _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 + _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 // mir::Constant - // + span: $DIR/inline-compatibility.rs:19:5: 19:19 + // + span: $DIR/inline-compatibility.rs:18:5: 18:19 // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:19:21: 19:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:18:44: 20:2 - return; // scope 0 at $DIR/inline-compatibility.rs:20:2: 20:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:18:21: 18:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:17:44: 19:2 + return; // scope 0 at $DIR/inline-compatibility.rs:19:2: 19:2 } } diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff new file mode 100644 index 0000000000000..aa32daa82dd51 --- /dev/null +++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff @@ -0,0 +1,123 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-generator.rs:8:11: 8:11 + let _1: std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return>; // in scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + let mut _2: std::pin::Pin<&mut impl std::ops::Generator>; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + let mut _3: &mut impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + let mut _4: impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:28: 9:31 ++ let mut _7: bool; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + scope 1 { + debug _r => _1; // in scope 1 at $DIR/inline-generator.rs:9:9: 9:11 + } ++ scope 2 (inlined g) { // at $DIR/inline-generator.rs:9:28: 9:31 ++ } ++ scope 3 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _3; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _5: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ scope 4 { ++ scope 5 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new_unchecked) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _5; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _6: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ } ++ } ++ } ++ scope 6 (inlined g::{closure#0}) { // at $DIR/inline-generator.rs:9:14: 9:46 ++ debug a => _8; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _8: bool; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _9: u32; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + StorageLive(_2); // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + StorageLive(_3); // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + StorageLive(_4); // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- _4 = g() -> bb1; // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:28: 9:29 +- // + literal: Const { ty: fn() -> impl std::ops::Generator {g}, val: Value(Scalar()) } +- } +- +- bb1: { ++ discriminant(_4) = 0; // scope 2 at $DIR/inline-generator.rs:9:28: 9:31 + _3 = &mut _4; // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 +- _2 = Pin::<&mut impl Generator>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:14: 9:22 +- // + user_ty: UserType(0) +- // + literal: Const { ty: fn(&mut impl std::ops::Generator) -> std::pin::Pin<&mut impl std::ops::Generator> {std::pin::Pin::<&mut impl std::ops::Generator>::new}, val: Value(Scalar()) } +- } +- +- bb2: { ++ StorageLive(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ _5 = move _3; // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageLive(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ _6 = move _5; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ (_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]) = move _6; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 + StorageDead(_3); // scope 0 at $DIR/inline-generator.rs:9:31: 9:32 +- _1 = as Generator>::resume(move _2, const false) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:33: 9:39 +- // + literal: Const { ty: for<'r> fn(std::pin::Pin<&'r mut impl std::ops::Generator>, bool) -> std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return> { as std::ops::Generator>::resume}, val: Value(Scalar()) } ++ StorageLive(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _7 = const false; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _9 = discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))); // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + +- bb3: { ++ bb1: { ++ StorageDead(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + StorageDead(_2); // scope 0 at $DIR/inline-generator.rs:9:45: 9:46 + StorageDead(_4); // scope 0 at $DIR/inline-generator.rs:9:46: 9:47 + _0 = const (); // scope 0 at $DIR/inline-generator.rs:8:11: 10:2 + StorageDead(_1); // scope 0 at $DIR/inline-generator.rs:10:1: 10:2 + return; // scope 0 at $DIR/inline-generator.rs:10:2: 10:2 + } + +- bb4 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/inline-generator.rs:8:1: 10:2 ++ } ++ ++ bb3: { ++ _8 = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(_8) -> [false: bb4, otherwise: bb5]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb4: { ++ ((_1 as Yielded).0: i32) = const 13_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb5: { ++ ((_1 as Yielded).0: i32) = const 7_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb6: { ++ discriminant(_1) = 0; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 3; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:11: 15:39 ++ } ++ ++ bb7: { ++ ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant(_1) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:41: 15:41 ++ } ++ ++ bb8: { ++ assert(const false, "generator resumed after completion") -> [success: bb8, unwind: bb2]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb9: { ++ unreachable; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + } + diff --git a/src/test/pretty/qpath-associated-type-bound.rs b/src/test/pretty/qpath-associated-type-bound.rs new file mode 100644 index 0000000000000..e06885e03882b --- /dev/null +++ b/src/test/pretty/qpath-associated-type-bound.rs @@ -0,0 +1,16 @@ +// pp-exact + + +mod m { + pub trait Tr { + type Ts: super::Tu; + } +} + +trait Tu { + fn dummy() { } +} + +fn foo() { ::Ts::dummy(); } + +fn main() { } diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs new file mode 100644 index 0000000000000..f895a4c2104ba --- /dev/null +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -0,0 +1,22 @@ +// ignore-tidy-linelength + +#![crate_type="lib"] + +pub mod internal { + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.mod.html' + pub struct r#mod; + + /// See [name], [other name] + /// + /// [name]: mod + /// [other name]: crate::internal::mod + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + pub struct B; +} + +/// See [name]. +/// +/// [name]: internal::mod +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +pub struct A; diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs new file mode 100644 index 0000000000000..131af6a10c898 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -0,0 +1,24 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 +#![feature(rustc_attrs)] + +// Ensure that capture analysis results in arrays being completely captured. +fn main() { + let mut m = [1, 2, 3, 4, 5]; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + m[0] += 10; + //~^ NOTE: Capturing m[] -> MutBorrow + //~| NOTE: Min Capture m[] -> MutBorrow + m[1] += 40; + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr new file mode 100644 index 0000000000000..2a350f3033192 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/arrays-completely-captured.rs:11:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/arrays-completely-captured.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 + | +LL | m[0] += 10; + | ^ + +error: Min Capture analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 + | +LL | m[0] += 10; + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs new file mode 100644 index 0000000000000..ba4955085372a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -0,0 +1,33 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + println!("{}", p.x); + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr new file mode 100644 index 0000000000000..5fac6963afd32 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-struct.rs:17:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-struct.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs new file mode 100644 index 0000000000000..c1693fbad7986 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,28 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + println!("{}", t.0); + //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr new file mode 100644 index 0000000000000..1bfd63f2ace8c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-tuple.rs:12:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-tuple.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs new file mode 100644 index 0000000000000..8fb2f7f16d69c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -0,0 +1,64 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +enum Info { + Point(i32, i32, String), + Meta(String, Vec<(i32, i32)>) +} + +fn multi_variant_enum() { + let point = Info::Point(10, -10, "1".into()); + + let vec = Vec::new(); + let meta = Info::Meta("meta".into(), vec); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + if let Info::Point(_, _, str) = point { + //~^ NOTE: Capturing point[] -> ImmBorrow + //~| NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[] -> ByValue + println!("{}", str); + } + + if let Info::Meta(_, v) = meta { + //~^ NOTE: Capturing meta[] -> ImmBorrow + //~| NOTE: Capturing meta[(1, 1)] -> ByValue + //~| NOTE: Min Capture meta[] -> ByValue + println!("{:?}", v); + } + }; + + c(); +} + +enum SingleVariant { + Point(i32, i32, String), +} + +fn single_variant_enum() { + let point = SingleVariant::Point(10, -10, "1".into()); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let SingleVariant::Point(_, _, str) = point; + //~^ NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[(2, 0)] -> ByValue + println!("{}", str); + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr new file mode 100644 index 0000000000000..ebe1dcb98848b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -0,0 +1,122 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:18:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:49:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-enums.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:24:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:24:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:31:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ +note: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:31:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:24:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:31:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: aborting due to 6 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs new file mode 100644 index 0000000000000..080ca0405b477 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -0,0 +1,77 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// The array should be moved in entirety, even though only some elements are used. +fn arrays() { + let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let [a, b, .., e] = arr; + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} + +struct Point { + x: i32, + y: i32, + id: String, +} + +fn structs() { + let mut p = Point { x: 10, y: 10, id: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let Point { x: ref mut x, y: _, id: moved_id } = p; + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Capturing p[(2, 0)] -> ByValue + //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(2, 0)] -> ByValue + + println!("{}, {}", x, moved_id); + }; + c(); +} + +fn tuples() { + let mut t = (10, String::new(), (String::new(), 42)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let (ref mut x, ref ref_str, (moved_s, _)) = t; + //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow + //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue + + println!("{}, {} {}", x, ref_str, moved_s); + }; + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr new file mode 100644 index 0000000000000..06ccc2d7a88b4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -0,0 +1,177 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:12:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:38:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:58:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/destructure_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ +note: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ +note: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: aborting due to 9 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs new file mode 100644 index 0000000000000..a3222635b626c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -0,0 +1,20 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +fn main() { + let s = format!("s"); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("This uses new capture analyysis to capture s={}", s); + //~^ NOTE: Capturing s[] -> ImmBorrow + //~| NOTE: Min Capture s[] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr new file mode 100644 index 0000000000000..a031360ed34e1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/feature-gate-capture_disjoint_fields.rs:10:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/feature-gate-capture_disjoint_fields.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: Min Capture analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs new file mode 100644 index 0000000000000..9466e103897fb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,45 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr new file mode 100644 index 0000000000000..e9420fe5a0c3a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,35 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/filter-on-struct-member.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs new file mode 100644 index 0000000000000..7d2d4c104d489 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -0,0 +1,41 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + // Only paths that appears within the closure that directly start off + // a variable defined outside the closure are captured. + // + // Therefore `w.p` is captured + // Note that `wp.x` doesn't start off a variable defined outside the closure. + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let wp = &w.p; + //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow + println!("{}", wp.x); + }; + + // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr new file mode 100644 index 0000000000000..1c8db7952afe7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-1.rs:24:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-1.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 + | +LL | let wp = &w.p; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 + | +LL | let wp = &w.p; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs new file mode 100644 index 0000000000000..540e70138e50e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -0,0 +1,37 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", w.p.x); + //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr new file mode 100644 index 0000000000000..37287f6b3bc74 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-2.rs:21:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-2.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs new file mode 100644 index 0000000000000..88620550f2e7c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,56 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +// This testcase ensures that nested closures are handles properly +// - The nested closure is analyzed first. +// - The capture kind of the nested closure is accounted for by the enclosing closure +// - Any captured path by the nested closure that starts off a local variable in the enclosing +// closure is not listed as a capture of the enclosing closure. + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + let mut c1 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", p.x); + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + let incr = 10; + let mut c2 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || p.y += incr; + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Capturing incr[] -> ImmBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture incr[] -> ImmBorrow + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + c2(); + println!("{}", p.y); + }; + + c1(); + + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr new file mode 100644 index 0000000000000..21147be3f1d08 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -0,0 +1,110 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:23:18 + | +LL | let mut c1 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:33:22 + | +LL | let mut c2 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested-closure.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 + | +LL | || p.y += incr; + | ^^^ +note: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 + | +LL | || p.y += incr; + | ^^^ +note: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 + | +LL | || p.y += incr; + | ^^^^ + +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 + | +LL | println!("{}", p.x); + | ^^^ +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 + | +LL | || p.y += incr; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 + | +LL | println!("{}", p.x); + | ^^^ +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 + | +LL | || p.y += incr; + | ^^^ + +error: aborting due to 6 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs new file mode 100644 index 0000000000000..16acd2f3206c9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,35 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +struct Point { + x: f32, + y: f32, +} + +struct Pentagon { + points: [Point; 5], +} + +fn main() { + let p1 = Point { x: 10.0, y: 10.0 }; + let p2 = Point { x: 7.5, y: 12.5 }; + let p3 = Point { x: 15.0, y: 15.0 }; + let p4 = Point { x: 12.5, y: 12.5 }; + let p5 = Point { x: 20.0, y: 10.0 }; + + let pent = Pentagon { points: [p1, p2, p3, p4, p5] }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", pent.points[5].x); + //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr new file mode 100644 index 0000000000000..3c8d07ed9ba67 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -0,0 +1,57 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/path-with-array-access.rs:25:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-with-array-access.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs new file mode 100644 index 0000000000000..aaff3531e5850 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -0,0 +1,41 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +// Test to ensure that min analysis meets capture kind for all paths captured. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // + // Requirements: + // p.x -> MutBoorrow + // p -> ImmBorrow + // + // Requirements met when p is captured via MutBorrow + // + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + p.x += 10; + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[] -> MutBorrow + println!("{:?}", p); + //~^ NOTE: Capturing p[] -> ImmBorrow + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr new file mode 100644 index 0000000000000..30d3d5f504eb9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -0,0 +1,62 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/simple-struct-min-capture.rs:27:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple-struct-min-capture.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 + | +LL | p.x += 10; + | ^^^ +note: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:36:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 + | +LL | p.x += 10; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs new file mode 100644 index 0000000000000..90b8033d074a1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -0,0 +1,75 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +// Test to ensure that we can handle cases where +// let statements create no bindings are intialized +// using a Place expression +// +// Note: Currently when feature `capture_disjoint_fields` is enabled +// we can't handle such cases. So the test current use `_x` instead of +// `_` until the issue is resolved. +// Check rust-lang/project-rfc-2229#24 for status. + +struct Point { + x: i32, + y: i32, +} + +fn wild_struct() { + let p = Point { x: 10, y: 20 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let Point { x: _x, y: _ } = p; + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn wild_tuple() { + let t = (String::new(), 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let (_x, _) = t; + //~^ NOTE: Capturing t[(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +fn wild_arr() { + let arr = [String::new(), String::new()]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let [_x, _] = arr; + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr new file mode 100644 index 0000000000000..36be8431be508 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -0,0 +1,147 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:24:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:42:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:60:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/wild_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 + | +LL | let (_x, _) = t; + | ^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 + | +LL | let (_x, _) = t; + | ^ + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:67:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:67:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: aborting due to 9 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generator/print/generator-print-verbose-2.stderr b/src/test/ui/generator/print/generator-print-verbose-2.stderr index f23949091d912..d590f876b8e77 100644 --- a/src/test/ui/generator/print/generator-print-verbose-2.stderr +++ b/src/test/ui/generator/print/generator-print-verbose-2.stderr @@ -8,8 +8,8 @@ LL | assert_send(|| { | ^^^^^^^^^^^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` - = note: required because of the requirements on the impl of `Send` for `&'_#3r Cell` - = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell) _#17t]` + = note: required because of the requirements on the impl of `Send` for `&'_#4r Cell` + = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#4r Cell) _#17t]` error: generator cannot be shared between threads safely --> $DIR/generator-print-verbose-2.rs:12:5 diff --git a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs index fb4bf2b8b44e7..f3a51b415faca 100644 --- a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs +++ b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs @@ -1,7 +1,3 @@ -// revisions: default miropt -//[miropt]compile-flags: -Z mir-opt-level=2 -// ~^ This flag is for #77668, it used to be ICE. - #![crate_type = "lib"] pub fn bar

( // Error won't happen if "bar" is not generic diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs index 4d083bf232155..ccb279f7fa212 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs @@ -1,17 +1,27 @@ -// run-pass +// Regression test for various issues related to normalization & inlining. +// * #68347, #77306, #77668 - missed normalization during inlining. +// * #78442 - missed normalization in validator after inlining. +// +// build-pass // compile-flags:-Zmir-opt-level=2 -// Previously ICEd because we did not normalize during inlining, -// see https://github.com/rust-lang/rust/pull/77306 for more discussion. - pub fn write() { create()() } +pub fn write_generic(_t: T) { + hide()(); +} + pub fn create() -> impl FnOnce() { || () } +pub fn hide() -> impl Fn() { + write +} + fn main() { write(); + write_generic(()); }