diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index d7ea8e1bcc2cc..2bd067d416142 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -124,6 +124,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( borrow_set, move_data, &universal_region_relations, + &constraints, ); let mut regioncx = RegionInferenceContext::new( diff --git a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs new file mode 100644 index 0000000000000..9c4aa8ea1edb7 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs @@ -0,0 +1,85 @@ +use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location, Place}; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::move_paths::{LookupResult, MoveData}; +use tracing::debug; + +use crate::def_use::{self, DefUse}; +use crate::facts::AllFacts; +use crate::location::{LocationIndex, LocationTable}; +use crate::universal_regions::UniversalRegions; + +/// Emit polonius facts for variable defs, uses, drops, and path accesses. +pub(crate) fn emit_access_facts<'tcx>( + tcx: TyCtxt<'tcx>, + facts: &mut AllFacts, + body: &Body<'tcx>, + location_table: &LocationTable, + move_data: &MoveData<'tcx>, + universal_regions: &UniversalRegions<'tcx>, +) { + let mut extractor = AccessFactsExtractor { facts, move_data, location_table }; + extractor.visit_body(body); + + for (local, local_decl) in body.local_decls.iter_enumerated() { + debug!("add use_of_var_derefs_origin facts - local={:?}, type={:?}", local, local_decl.ty); + tcx.for_each_free_region(&local_decl.ty, |region| { + let region_vid = universal_regions.to_region_vid(region); + facts.use_of_var_derefs_origin.push((local, region_vid.into())); + }); + } +} + +/// MIR visitor extracting point-wise facts about accesses. +struct AccessFactsExtractor<'a, 'tcx> { + facts: &'a mut AllFacts, + move_data: &'a MoveData<'tcx>, + location_table: &'a LocationTable, +} + +impl<'tcx> AccessFactsExtractor<'_, 'tcx> { + fn location_to_index(&self, location: Location) -> LocationIndex { + self.location_table.mid_index(location) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> { + fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { + match def_use::categorize(context) { + Some(DefUse::Def) => { + debug!("AccessFactsExtractor - emit def"); + self.facts.var_defined_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Use) => { + debug!("AccessFactsExtractor - emit use"); + self.facts.var_used_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Drop) => { + debug!("AccessFactsExtractor - emit drop"); + self.facts.var_dropped_at.push((local, self.location_to_index(location))); + } + _ => (), + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + + match context { + PlaceContext::NonMutatingUse(_) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + let path = match self.move_data.rev_lookup.find(place.as_ref()) { + LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path, + _ => { + // There's no path access to emit. + return; + } + }; + debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})"); + self.facts.path_accessed_at_base.push((path, self.location_to_index(location))); + } + + _ => {} + } + } +} diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index f646beeecf7b3..0d5b6f3a2c8c2 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -21,22 +21,22 @@ use crate::{ /// Emit `loan_invalidated_at` facts. pub(super) fn emit_loan_invalidations<'tcx>( tcx: TyCtxt<'tcx>, - all_facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut AllFacts, body: &Body<'tcx>, + location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, ) { let dominators = body.basic_blocks.dominators(); let mut visitor = - LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators }; + LoanInvalidationsGenerator { facts, borrow_set, tcx, location_table, body, dominators }; visitor.visit_body(body); } struct LoanInvalidationsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - all_facts: &'a mut AllFacts, - location_table: &'a LocationTable, + facts: &'a mut AllFacts, body: &'a Body<'tcx>, + location_table: &'a LocationTable, dominators: &'a Dominators, borrow_set: &'a BorrowSet<'tcx>, } @@ -151,7 +151,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { let resume = self.location_table.start_index(resume.start_location()); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((resume, i)); + self.facts.loan_invalidated_at.push((resume, i)); } } @@ -165,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { let start = self.location_table.start_index(location); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((start, i)); + self.facts.loan_invalidated_at.push((start, i)); } } } @@ -409,7 +409,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { /// Generates a new `loan_invalidated_at(L, B)` fact. fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) { let lidx = self.location_table.start_index(l); - self.all_facts.loan_invalidated_at.push((lidx, b)); + self.facts.loan_invalidated_at.push((lidx, b)); } fn check_activations(&mut self, location: Location) { diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs index 68e0865ab82c9..fdde9fa047621 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs @@ -14,12 +14,12 @@ use crate::places_conflict; /// Emit `loan_killed_at` and `cfg_edge` facts at the same time. pub(super) fn emit_loan_kills<'tcx>( tcx: TyCtxt<'tcx>, - all_facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut AllFacts, body: &Body<'tcx>, + location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, ) { - let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body }; + let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -27,7 +27,7 @@ pub(super) fn emit_loan_kills<'tcx>( struct LoanKillsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - all_facts: &'a mut AllFacts, + facts: &'a mut AllFacts, location_table: &'a LocationTable, borrow_set: &'a BorrowSet<'tcx>, body: &'a Body<'tcx>, @@ -36,12 +36,12 @@ struct LoanKillsGenerator<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { // Also record CFG facts here. - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), )); - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.mid_index(location), self.location_table.start_index(location.successor_within_block()), )); @@ -63,15 +63,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { // Also record CFG facts here. - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), )); let successor_blocks = terminator.successors(); - self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0); + self.facts.cfg_edge.reserve(successor_blocks.size_hint().0); for successor_block in successor_blocks { - self.all_facts.cfg_edge.push(( + self.facts.cfg_edge.push(( self.location_table.mid_index(location), self.location_table.start_index(successor_block.start_location()), )); @@ -128,7 +128,7 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> { if places_conflict { let location_index = self.location_table.mid_index(location); - self.all_facts.loan_killed_at.push((borrow_index, location_index)); + self.facts.loan_killed_at.push((borrow_index, location_index)); } } } @@ -140,9 +140,9 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> { fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) { if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { let location_index = self.location_table.mid_index(location); - self.all_facts.loan_killed_at.reserve(borrow_indices.len()); + self.facts.loan_killed_at.reserve(borrow_indices.len()); for &borrow_index in borrow_indices { - self.all_facts.loan_killed_at.push((borrow_index, location_index)); + self.facts.loan_killed_at.push((borrow_index, location_index)); } } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 9fccc00bdaf0e..60fd2afe63e1c 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -3,16 +3,23 @@ //! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature //! parity. -use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; -use rustc_middle::ty::TyCtxt; +use std::iter; + +use either::Either; +use rustc_middle::mir::{Body, Local, LocalKind, Location, START_BLOCK}; +use rustc_middle::ty::{GenericArg, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; use tracing::debug; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::facts::{AllFacts, PoloniusRegionVid}; use crate::location::LocationTable; +use crate::type_check::MirTypeckRegionConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; +mod accesses; mod loan_invalidations; mod loan_kills; @@ -22,6 +29,8 @@ mod loan_kills; /// - CFG points and edges /// - loan kills /// - loan invalidations +/// - access facts such as variable definitions, uses, drops, and path accesses +/// - outlives constraints /// /// The rest of the facts are emitted during typeck and liveness. pub(crate) fn emit_facts<'tcx>( @@ -30,34 +39,42 @@ pub(crate) fn emit_facts<'tcx>( location_table: &LocationTable, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, - move_data: &MoveData<'_>, - universal_region_relations: &UniversalRegionRelations<'_>, + move_data: &MoveData<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, ) { - let Some(all_facts) = all_facts else { + let Some(facts) = all_facts else { // We don't do anything if there are no facts to fill. return; }; let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - emit_move_facts(all_facts, move_data, location_table, body); - emit_universal_region_facts(all_facts, borrow_set, universal_region_relations); - emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); - emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); + emit_move_facts(facts, body, location_table, move_data); + emit_universal_region_facts(facts, borrow_set, universal_region_relations); + loan_kills::emit_loan_kills(tcx, facts, body, location_table, borrow_set); + loan_invalidations::emit_loan_invalidations(tcx, facts, body, location_table, borrow_set); + accesses::emit_access_facts( + tcx, + facts, + body, + location_table, + move_data, + &universal_region_relations.universal_regions, + ); + emit_outlives_facts(facts, location_table, constraints); } /// Emit facts needed for move/init analysis: moves and assignments. fn emit_move_facts( - all_facts: &mut AllFacts, - move_data: &MoveData<'_>, - location_table: &LocationTable, + facts: &mut AllFacts, body: &Body<'_>, + location_table: &LocationTable, + move_data: &MoveData<'_>, ) { - all_facts - .path_is_var - .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); + facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); for (child, move_path) in move_data.move_paths.iter_enumerated() { if let Some(parent) = move_path.parent { - all_facts.child_path.push((child, parent)); + facts.child_path.push((child, parent)); } } @@ -83,14 +100,14 @@ fn emit_move_facts( // The initialization happened in (or rather, when arriving at) // the successors, but not in the unwind block. let first_statement = Location { block: successor, statement_index: 0 }; - all_facts + facts .path_assigned_at_base .push((init.path, location_table.start_index(first_statement))); } } else { // In all other cases, the initialization just happens at the // midpoint, like any other effect. - all_facts + facts .path_assigned_at_base .push((init.path, location_table.mid_index(location))); } @@ -98,7 +115,7 @@ fn emit_move_facts( // Arguments are initialized on function entry InitLocation::Argument(local) => { assert!(body.local_kind(local) == LocalKind::Arg); - all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); + facts.path_assigned_at_base.push((init.path, fn_entry_start)); } } } @@ -107,20 +124,20 @@ fn emit_move_facts( if body.local_kind(local) != LocalKind::Arg { // Non-arguments start out deinitialised; we simulate this with an // initial move: - all_facts.path_moved_at_base.push((path, fn_entry_start)); + facts.path_moved_at_base.push((path, fn_entry_start)); } } // moved_out_at // deinitialisation is assumed to always happen! - all_facts + facts .path_moved_at_base .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); } /// Emit universal regions facts, and their relations. fn emit_universal_region_facts( - all_facts: &mut AllFacts, + facts: &mut AllFacts, borrow_set: &BorrowSet<'_>, universal_region_relations: &UniversalRegionRelations<'_>, ) { @@ -131,7 +148,7 @@ fn emit_universal_region_facts( // added to the existing number of loans, as if they succeeded them in the set. // let universal_regions = &universal_region_relations.universal_regions; - all_facts + facts .universal_region .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from)); let borrow_count = borrow_set.len(); @@ -144,7 +161,7 @@ fn emit_universal_region_facts( for universal_region in universal_regions.universal_regions_iter() { let universal_region_idx = universal_region.index(); let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); + facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); } // 2: the universal region relations `outlives` constraints are emitted as @@ -156,29 +173,51 @@ fn emit_universal_region_facts( fr1={:?}, fr2={:?}", fr1, fr2 ); - all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); + facts.known_placeholder_subset.push((fr1.into(), fr2.into())); } } } -/// Emit facts about loan invalidations. -fn emit_loan_invalidations_facts<'tcx>( - all_facts: &mut AllFacts, +/// For every potentially drop()-touched region `region` in `local`'s type +/// (`kind`), emit a `drop_of_var_derefs_origin(local, origin)` fact. +pub(crate) fn emit_drop_facts<'tcx>( tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, + local: Local, + kind: &GenericArg<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + all_facts: &mut Option, ) { - loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set); + debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); + let Some(facts) = all_facts.as_mut() else { return }; + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); + tcx.for_each_free_region(kind, |drop_live_region| { + let region_vid = universal_regions.to_region_vid(drop_live_region); + facts.drop_of_var_derefs_origin.push((local, region_vid.into())); + }); } -/// Emit facts about CFG points and edges, as well as locations where loans are killed. -fn emit_cfg_and_loan_kills_facts<'tcx>( - all_facts: &mut AllFacts, - tcx: TyCtxt<'tcx>, +/// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive +/// closure. +fn emit_outlives_facts<'tcx>( + facts: &mut AllFacts, location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, ) { - loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); + facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map( + |constraint: &OutlivesConstraint<'_>| { + if let Some(from_location) = constraint.locations.from_location() { + Either::Left(iter::once(( + constraint.sup.into(), + constraint.sub.into(), + location_table.mid_index(from_location), + ))) + } else { + Either::Right( + location_table.all_points().map(move |location| { + (constraint.sup.into(), constraint.sub.into(), location) + }), + ) + } + }, + )); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 05e4a176a6d19..683293bf82863 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -17,7 +17,6 @@ use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; mod local_use_map; -mod polonius; mod trace; /// Combines liveness analysis with initialization analysis to @@ -45,8 +44,6 @@ pub(super) fn generate<'a, 'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - polonius::emit_access_facts(typeck, body, move_data); - trace::trace( typeck, body, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs deleted file mode 100644 index 5ffba94ee6821..0000000000000 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ /dev/null @@ -1,123 +0,0 @@ -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{Body, Local, Location, Place}; -use rustc_middle::ty::GenericArg; -use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; -use tracing::debug; - -use super::TypeChecker; -use crate::def_use::{self, DefUse}; -use crate::location::{LocationIndex, LocationTable}; - -type VarPointRelation = Vec<(Local, LocationIndex)>; -type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>; - -/// Emit polonius facts for variable defs, uses, drops, and path accesses. -pub(super) fn emit_access_facts<'a, 'tcx>( - typeck: &mut TypeChecker<'a, 'tcx>, - body: &Body<'tcx>, - move_data: &MoveData<'tcx>, -) { - if let Some(facts) = typeck.all_facts.as_mut() { - debug!("emit_access_facts()"); - - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let location_table = typeck.location_table; - - let mut extractor = AccessFactsExtractor { - var_defined_at: &mut facts.var_defined_at, - var_used_at: &mut facts.var_used_at, - var_dropped_at: &mut facts.var_dropped_at, - path_accessed_at_base: &mut facts.path_accessed_at_base, - location_table, - move_data, - }; - extractor.visit_body(body); - - for (local, local_decl) in body.local_decls.iter_enumerated() { - debug!( - "add use_of_var_derefs_origin facts - local={:?}, type={:?}", - local, local_decl.ty - ); - let universal_regions = &typeck.universal_regions; - typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { - let region_vid = universal_regions.to_region_vid(region); - facts.use_of_var_derefs_origin.push((local, region_vid.into())); - }); - } - } -} - -/// For every potentially drop()-touched region `region` in `local`'s type -/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact. -pub(super) fn emit_drop_facts<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, - local: Local, - kind: &GenericArg<'tcx>, -) { - debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); - if let Some(facts) = typeck.all_facts.as_mut() { - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let universal_regions = &typeck.universal_regions; - typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { - let region_vid = universal_regions.to_region_vid(drop_live_region); - facts.drop_of_var_derefs_origin.push((local, region_vid.into())); - }); - } -} - -/// MIR visitor extracting point-wise facts about accesses. -struct AccessFactsExtractor<'a, 'tcx> { - var_defined_at: &'a mut VarPointRelation, - var_used_at: &'a mut VarPointRelation, - location_table: &'a LocationTable, - var_dropped_at: &'a mut VarPointRelation, - move_data: &'a MoveData<'tcx>, - path_accessed_at_base: &'a mut PathPointRelation, -} - -impl<'tcx> AccessFactsExtractor<'_, 'tcx> { - fn location_to_index(&self, location: Location) -> LocationIndex { - self.location_table.mid_index(location) - } -} - -impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> { - fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { - match def_use::categorize(context) { - Some(DefUse::Def) => { - debug!("AccessFactsExtractor - emit def"); - self.var_defined_at.push((local, self.location_to_index(location))); - } - Some(DefUse::Use) => { - debug!("AccessFactsExtractor - emit use"); - self.var_used_at.push((local, self.location_to_index(location))); - } - Some(DefUse::Drop) => { - debug!("AccessFactsExtractor - emit drop"); - self.var_dropped_at.push((local, self.location_to_index(location))); - } - _ => (), - } - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.super_place(place, context, location); - - match context { - PlaceContext::NonMutatingUse(_) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - let path = match self.move_data.rev_lookup.find(place.as_ref()) { - LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path, - _ => { - // There's no path access to emit. - return; - } - }; - debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})"); - self.path_accessed_at_base.push((path, self.location_to_index(location))); - } - - _ => {} - } - } -} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 539d3f97a6387..f510d193dd9f1 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,9 +15,9 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type use tracing::debug; use crate::location::RichLocation; +use crate::polonius; use crate::region_infer::values::{self, LiveLoans}; use crate::type_check::liveness::local_use_map::LocalUseMap; -use crate::type_check::liveness::polonius; use crate::type_check::{NormalizeLocation, TypeChecker}; /// This is the heart of the liveness computation. For each variable X @@ -590,7 +590,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); - polonius::emit_drop_facts(self.typeck, dropped_local, &kind); + polonius::legacy::emit_drop_facts( + self.typeck.tcx(), + dropped_local, + &kind, + self.typeck.universal_regions, + self.typeck.all_facts, + ); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 90d327b0ad20d..de0804b01895a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use std::{fmt, iter, mem}; -use either::Either; use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -107,7 +106,6 @@ mod relate_tys; /// # Parameters /// /// - `infcx` -- inference context to use -/// - `param_env` -- parameter environment to use for trait solving /// - `body` -- MIR body to type-check /// - `promoted` -- map of promoted constants within `body` /// - `universal_regions` -- the universal regions from `body`s function signature @@ -155,7 +153,7 @@ pub(crate) fn type_check<'a, 'tcx>( debug!(?normalized_inputs_and_output); - let mut checker = TypeChecker { + let mut typeck = TypeChecker { infcx, last_span: body.span, body, @@ -171,24 +169,22 @@ pub(crate) fn type_check<'a, 'tcx>( constraints: &mut constraints, }; - checker.check_user_type_annotations(); + typeck.check_user_type_annotations(); - let mut verifier = TypeVerifier { cx: &mut checker, promoted, last_span: body.span }; + let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span }; verifier.visit_body(body); - checker.typeck_mir(body); - checker.equate_inputs_and_outputs(body, &normalized_inputs_and_output); - checker.check_signature_annotation(body); + typeck.typeck_mir(body); + typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); + typeck.check_signature_annotation(body); - liveness::generate(&mut checker, body, &elements, flow_inits, move_data); + liveness::generate(&mut typeck, body, &elements, flow_inits, move_data); - translate_outlives_facts(&mut checker); - let opaque_type_values = infcx.take_opaque_types(); - - let opaque_type_values = opaque_type_values + let opaque_type_values = infcx + .take_opaque_types() .into_iter() .map(|(opaque_type_key, decl)| { - let _: Result<_, ErrorGuaranteed> = checker.fully_perform_op( + let _: Result<_, ErrorGuaranteed> = typeck.fully_perform_op( Locations::All(body.span), ConstraintCategory::OpaqueType, CustomTypeOp::new( @@ -218,11 +214,11 @@ pub(crate) fn type_check<'a, 'tcx>( match region.kind() { ty::ReVar(_) => region, ty::RePlaceholder(placeholder) => { - checker.constraints.placeholder_region(infcx, placeholder) + typeck.constraints.placeholder_region(infcx, placeholder) } _ => ty::Region::new_var( infcx.tcx, - checker.universal_regions.to_region_vid(region), + typeck.universal_regions.to_region_vid(region), ), } }); @@ -234,30 +230,6 @@ pub(crate) fn type_check<'a, 'tcx>( MirTypeckResults { constraints, universal_region_relations, opaque_type_values } } -fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { - if let Some(facts) = typeck.all_facts { - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let location_table = typeck.location_table; - facts.subset_base.extend( - typeck.constraints.outlives_constraints.outlives().iter().flat_map( - |constraint: &OutlivesConstraint<'_>| { - if let Some(from_location) = constraint.locations.from_location() { - Either::Left(iter::once(( - constraint.sup.into(), - constraint.sub.into(), - location_table.mid_index(from_location), - ))) - } else { - Either::Right(location_table.all_points().map(move |location| { - (constraint.sup.into(), constraint.sub.into(), location) - })) - } - }, - ), - ); - } -} - #[track_caller] fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) { // We sometimes see MIR failures (notably predicate failures) due to @@ -276,7 +248,7 @@ enum FieldAccessError { /// type, calling `span_mirbug` and returning an error type if there /// is a problem. struct TypeVerifier<'a, 'b, 'tcx> { - cx: &'a mut TypeChecker<'b, 'tcx>, + typeck: &'a mut TypeChecker<'b, 'tcx>, promoted: &'b IndexSlice>, last_span: Span, } @@ -298,9 +270,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.super_const_operand(constant, location); let ty = self.sanitize_type(constant, constant.const_.ty()); - self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { - let live_region_vid = self.cx.universal_regions.to_region_vid(live_region); - self.cx.constraints.liveness_constraints.add_location(live_region_vid, location); + self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| { + let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region); + self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location); }); // HACK(compiler-errors): Constants that are gathered into Body.required_consts @@ -312,14 +284,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { }; if let Some(annotation_index) = constant.user_ty { - if let Err(terr) = self.cx.relate_type_and_user_type( + if let Err(terr) = self.typeck.relate_type_and_user_type( constant.const_.ty(), ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, ConstraintCategory::Boring, ) { - let annotation = &self.cx.user_type_annotations[annotation_index]; + let annotation = &self.typeck.user_type_annotations[annotation_index]; span_mirbug!( self, constant, @@ -348,9 +320,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { promoted: &Body<'tcx>, ty, san_ty| { - if let Err(terr) = - verifier.cx.eq_types(ty, san_ty, locations, ConstraintCategory::Boring) - { + if let Err(terr) = verifier.typeck.eq_types( + ty, + san_ty, + locations, + ConstraintCategory::Boring, + ) { span_mirbug!( verifier, promoted, @@ -368,21 +343,21 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let promoted_ty = promoted_body.return_ty(); check_err(self, promoted_body, ty, promoted_ty); } else { - self.cx.ascribe_user_type( + self.typeck.ascribe_user_type( constant.const_.ty(), ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None, })), - locations.span(self.cx.body), + locations.span(self.typeck.body), ); } } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); - let normalized_ty = self.cx.normalize(unnormalized_ty, locations); + let normalized_ty = self.typeck.normalize(unnormalized_ty, locations); let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); - if let Err(terr) = self.cx.eq_types( + if let Err(terr) = self.typeck.eq_types( literal_ty, normalized_ty, locations, @@ -394,7 +369,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); - self.cx.normalize_and_prove_instantiated_predicates( + self.typeck.normalize_and_prove_instantiated_predicates( def_id, instantiated_predicates, locations, @@ -404,7 +379,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), Some(DefKind::Impl { of_trait: true }) )); - self.cx.prove_predicates( + self.typeck.prove_predicates( args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), locations, ConstraintCategory::Boring, @@ -438,7 +413,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { local_decl.ty }; - if let Err(terr) = self.cx.relate_type_and_user_type( + if let Err(terr) = self.typeck.relate_type_and_user_type( ty, ty::Invariant, user_ty, @@ -468,11 +443,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn body(&self) -> &Body<'tcx> { - self.cx.body + self.typeck.body } fn tcx(&self) -> TyCtxt<'tcx> { - self.cx.infcx.tcx + self.typeck.infcx.tcx } fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -522,7 +497,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // whether the bounds fully apply: in effect, the rule is // that if a value of some type could implement `Copy`, then // it must. - self.cx.prove_trait_ref( + self.typeck.prove_trait_ref( trait_ref, location.to_locations(), ConstraintCategory::CopyBound, @@ -537,7 +512,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // checker on the promoted MIR, then transfer the constraints back to // the main MIR, changing the locations to the provided location. - let parent_body = mem::replace(&mut self.cx.body, promoted_body); + let parent_body = mem::replace(&mut self.typeck.body, promoted_body); // Use new sets of constraints and closure bounds so that we can // modify their locations. @@ -548,18 +523,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { - mem::swap(this.cx.all_facts, all_facts); - mem::swap(&mut this.cx.constraints.outlives_constraints, &mut constraints); - mem::swap(&mut this.cx.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.typeck.all_facts, all_facts); + mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); + mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); }; swap_constraints(self); self.visit_body(promoted_body); - self.cx.typeck_mir(promoted_body); + self.typeck.typeck_mir(promoted_body); - self.cx.body = parent_body; + self.typeck.body = parent_body; // Merge the outlives constraints back in, at the given location. swap_constraints(self); @@ -575,7 +550,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // temporary from the user's point of view. constraint.category = ConstraintCategory::Boring; } - self.cx.constraints.outlives_constraints.push(constraint) + self.typeck.constraints.outlives_constraints.push(constraint) } // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region @@ -585,7 +560,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // unordered. #[allow(rustc::potential_query_instability)] for region in liveness_constraints.live_regions_unordered() { - self.cx.constraints.liveness_constraints.add_location(region, location); + self.typeck.constraints.liveness_constraints.add_location(region, location); } } @@ -669,13 +644,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(place, fty); - let fty = self.cx.normalize(fty, location); + let fty = self.typeck.normalize(fty, location); match self.field_ty(place, base, field, location) { Ok(ty) => { - let ty = self.cx.normalize(ty, location); + let ty = self.typeck.normalize(ty, location); debug!(?fty, ?ty); - if let Err(terr) = self.cx.relate_types( + if let Err(terr) = self.typeck.relate_types( ty, self.get_ambient_variance(context), fty, @@ -707,8 +682,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } ProjectionElem::OpaqueCast(ty) => { let ty = self.sanitize_type(place, ty); - let ty = self.cx.normalize(ty, location); - self.cx + let ty = self.typeck.normalize(ty, location); + self.typeck .relate_types( ty, self.get_ambient_variance(context), @@ -817,7 +792,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }; if let Some(field) = variant.fields.get(field) { - Ok(self.cx.normalize(field.ty(tcx, args), location)) + Ok(self.typeck.normalize(field.ty(tcx, args), location)) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs deleted file mode 100644 index c5d1ebdfe7c5f..0000000000000 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rustc_data_structures::captures::Captures; -use rustc_middle::mir::coverage::{ - CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, - SourceRegion, -}; - -use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; - -pub(crate) struct FunctionCoverage<'tcx> { - pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, - /// If `None`, the corresponding function is unused. - ids_info: Option<&'tcx CoverageIdsInfo>, -} - -impl<'tcx> FunctionCoverage<'tcx> { - pub(crate) fn new_used( - function_coverage_info: &'tcx FunctionCoverageInfo, - ids_info: &'tcx CoverageIdsInfo, - ) -> Self { - Self { function_coverage_info, ids_info: Some(ids_info) } - } - - pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self { - Self { function_coverage_info, ids_info: None } - } - - /// Returns true for a used (called) function, and false for an unused function. - pub(crate) fn is_used(&self) -> bool { - self.ids_info.is_some() - } - - /// Return the source hash, generated from the HIR node structure, and used to indicate whether - /// or not the source code structure changed between different compilations. - pub(crate) fn source_hash(&self) -> u64 { - if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 } - } - - /// Convert this function's coverage expression data into a form that can be - /// passed through FFI to LLVM. - pub(crate) fn counter_expressions( - &self, - ) -> impl Iterator + ExactSizeIterator + Captures<'_> { - // We know that LLVM will optimize out any unused expressions before - // producing the final coverage map, so there's no need to do the same - // thing on the Rust side unless we're confident we can do much better. - // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) - - self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| { - CounterExpression { - lhs: self.counter_for_term(lhs), - kind: match op { - Op::Add => ExprKind::Add, - Op::Subtract => ExprKind::Subtract, - }, - rhs: self.counter_for_term(rhs), - } - }) - } - - /// Converts this function's coverage mappings into an intermediate form - /// that will be used by `mapgen` when preparing for FFI. - pub(crate) fn counter_regions( - &self, - ) -> impl Iterator + ExactSizeIterator { - self.function_coverage_info.mappings.iter().map(move |mapping| { - let Mapping { kind, source_region } = mapping; - let kind = - kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term }); - (kind, source_region) - }) - } - - fn counter_for_term(&self, term: CovTerm) -> Counter { - if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) } - } - - fn is_zero_term(&self, term: CovTerm) -> bool { - match self.ids_info { - Some(ids_info) => ids_info.is_zero_term(term), - // This function is unused, so all coverage counters/expressions are zero. - None => true, - } - } -} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 4f2af73252751..ca33428620052 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,6 +1,6 @@ use std::iter; -use itertools::Itertools as _; +use itertools::Itertools; use rustc_abi::Align; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, @@ -8,8 +8,8 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; +use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; -use rustc_middle::{bug, mir}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; use rustc_span::def_id::DefIdSet; @@ -18,7 +18,6 @@ use tracing::debug; use crate::common::CodegenCx; use crate::coverageinfo::llvm_cov; -use crate::coverageinfo::map_data::FunctionCoverage; use crate::coverageinfo::mapgen::covfun::prepare_covfun_record; use crate::llvm; @@ -49,46 +48,40 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); - // In order to show that unused functions have coverage counts of zero (0), LLVM requires the - // functions exist. Generate synthetic functions with a (required) single counter, and add the - // MIR `Coverage` code regions to the `function_coverage_map`, before calling - // `ctx.take_function_coverage_map()`. - if cx.codegen_unit.is_code_coverage_dead_code_cgu() { - add_unused_functions(cx); - } - // FIXME(#132395): Can this be none even when coverage is enabled? - let function_coverage_map = match cx.coverage_cx { - Some(ref cx) => cx.take_function_coverage_map(), + let instances_used = match cx.coverage_cx { + Some(ref cx) => cx.instances_used.borrow(), None => return, }; - if function_coverage_map.is_empty() { - // This CGU has no functions with coverage instrumentation. - return; - } - let all_file_names = function_coverage_map - .iter() - .map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span) - .map(|span| span_file_name(tcx, span)); - let global_file_table = GlobalFileTable::new(all_file_names); + // The order of entries in this global file table needs to be deterministic, + // and ideally should also be independent of the details of stable-hashing, + // because coverage tests snapshots (`.cov-map`) can observe the order and + // would need to be re-blessed if it changes. As long as those requirements + // are satisfied, the order can be arbitrary. + let mut global_file_table = GlobalFileTable::new(); - // Encode all filenames referenced by coverage mappings in this CGU. - let filenames_buffer = global_file_table.make_filenames_buffer(tcx); - // The `llvm-cov` tool uses this hash to associate each covfun record with - // its corresponding filenames table, since the final binary will typically - // contain multiple covmap records from different compilation units. - let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); - - let mut unused_function_names = Vec::new(); - - let covfun_records = function_coverage_map - .into_iter() - .filter_map(|(instance, function_coverage)| { - prepare_covfun_record(tcx, &global_file_table, instance, &function_coverage) - }) + let mut covfun_records = instances_used + .iter() + .copied() + // Sort by symbol name, so that the global file table is built in an + // order that doesn't depend on the stable-hash-based order in which + // instances were visited during codegen. + .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) + .filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true)) .collect::>(); + // In a single designated CGU, also prepare covfun records for functions + // in this crate that were instrumented for coverage, but are unused. + if cx.codegen_unit.is_code_coverage_dead_code_cgu() { + let mut unused_instances = gather_unused_function_instances(cx); + // Sort the unused instances by symbol name, for the same reason as the used ones. + unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name); + covfun_records.extend(unused_instances.into_iter().filter_map(|instance| { + prepare_covfun_record(tcx, &mut global_file_table, instance, false) + })); + } + // If there are no covfun records for this CGU, don't generate a covmap record. // Emitting a covmap record without any covfun records causes `llvm-cov` to // fail when generating coverage reports, and if there are no covfun records @@ -98,6 +91,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { return; } + // Encode all filenames referenced by coverage mappings in this CGU. + let filenames_buffer = global_file_table.make_filenames_buffer(tcx); + // The `llvm-cov` tool uses this hash to associate each covfun record with + // its corresponding filenames table, since the final binary will typically + // contain multiple covmap records from different compilation units. + let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); + + let mut unused_function_names = vec![]; + for covfun in &covfun_records { unused_function_names.extend(covfun.mangled_function_name_if_unused()); @@ -137,22 +139,13 @@ struct GlobalFileTable { } impl GlobalFileTable { - fn new(all_file_names: impl IntoIterator) -> Self { - // Collect all of the filenames into a set. Filenames usually come in - // contiguous runs, so we can dedup adjacent ones to save work. - let mut raw_file_table = all_file_names.into_iter().dedup().collect::>(); - - // Sort the file table by its actual string values, not the arbitrary - // ordering of its symbols. - raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str())); - - Self { raw_file_table } + fn new() -> Self { + Self { raw_file_table: FxIndexSet::default() } } - fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId { - let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| { - bug!("file name not found in prepared global file table: {file_name}"); - }); + fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> GlobalFileId { + // Ensure the given file has a table entry, and get its index. + let (raw_id, _) = self.raw_file_table.insert_full(file_name); // The raw file table doesn't include an entry for the working dir // (which has ID 0), so add 1 to get the correct ID. GlobalFileId::from_usize(raw_id + 1) @@ -264,39 +257,35 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_ /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. /// We also end up adding their symbol names to a special global array that LLVM will include in /// its embedded coverage data. -fn add_unused_functions(cx: &CodegenCx<'_, '_>) { +fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec> { assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); let tcx = cx.tcx; let usage = prepare_usage_sets(tcx); let is_unused_fn = |def_id: LocalDefId| -> bool { - let def_id = def_id.to_def_id(); - - // To be eligible for "unused function" mappings, a definition must: - // - Be function-like + // Usage sets expect `DefId`, so convert from `LocalDefId`. + let d: DefId = LocalDefId::to_def_id(def_id); + // To be potentially eligible for "unused function" mappings, a definition must: + // - Be eligible for coverage instrumentation // - Not participate directly in codegen (or have lost all its coverage statements) // - Not have any coverage statements inlined into codegenned functions - tcx.def_kind(def_id).is_fn_like() - && (!usage.all_mono_items.contains(&def_id) - || usage.missing_own_coverage.contains(&def_id)) - && !usage.used_via_inlining.contains(&def_id) + tcx.is_eligible_for_coverage(def_id) + && (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d)) + && !usage.used_via_inlining.contains(&d) }; - // Scan for unused functions that were instrumented for coverage. - for def_id in tcx.mir_keys(()).iter().copied().filter(|&def_id| is_unused_fn(def_id)) { - // Get the coverage info from MIR, skipping functions that were never instrumented. - let body = tcx.optimized_mir(def_id); - let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue }; + // FIXME(#79651): Consider trying to filter out dummy instantiations of + // unused generic functions from library crates, because they can produce + // "unused instantiation" in coverage reports even when they are actually + // used by some downstream crate in the same binary. - // FIXME(79651): Consider trying to filter out dummy instantiations of - // unused generic functions from library crates, because they can produce - // "unused instantiation" in coverage reports even when they are actually - // used by some downstream crate in the same binary. - - debug!("generating unused fn: {def_id:?}"); - add_unused_function_coverage(cx, def_id, function_coverage_info); - } + tcx.mir_keys(()) + .iter() + .copied() + .filter(|&def_id| is_unused_fn(def_id)) + .map(|def_id| make_dummy_instance(tcx, def_id)) + .collect::>() } struct UsageSets<'tcx> { @@ -361,16 +350,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { UsageSets { all_mono_items, used_via_inlining, missing_own_coverage } } -fn add_unused_function_coverage<'tcx>( - cx: &CodegenCx<'_, 'tcx>, - def_id: LocalDefId, - function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo, -) { - let tcx = cx.tcx; - let def_id = def_id.to_def_id(); +fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> { + let def_id = local_def_id.to_def_id(); // Make a dummy instance that fills in all generics with placeholders. - let instance = ty::Instance::new( + ty::Instance::new( def_id, ty::GenericArgs::for_item(tcx, def_id, |param, _| { if let ty::GenericParamDefKind::Lifetime = param.kind { @@ -379,9 +363,5 @@ fn add_unused_function_coverage<'tcx>( tcx.mk_param_from_def(param) } }), - ); - - // An unused function's mappings will all be rewritten to map to zero. - let function_coverage = FunctionCoverage::new_unused(function_coverage_info); - cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage); + ) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 33e7a0f2f201b..8e853f057bedf 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -11,13 +11,14 @@ use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; use rustc_middle::bug; -use rustc_middle::mir::coverage::MappingKind; +use rustc_middle::mir::coverage::{ + CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, +}; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_target::spec::HasTargetSpec; use tracing::debug; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::FunctionCoverage; use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name}; use crate::coverageinfo::{ffi, llvm_cov}; use crate::llvm; @@ -45,20 +46,25 @@ impl<'tcx> CovfunRecord<'tcx> { pub(crate) fn prepare_covfun_record<'tcx>( tcx: TyCtxt<'tcx>, - global_file_table: &GlobalFileTable, + global_file_table: &mut GlobalFileTable, instance: Instance<'tcx>, - function_coverage: &FunctionCoverage<'tcx>, + is_used: bool, ) -> Option> { + let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?; + let ids_info = tcx.coverage_ids_info(instance.def); + + let expressions = prepare_expressions(fn_cov_info, ids_info, is_used); + let mut covfun = CovfunRecord { mangled_function_name: tcx.symbol_name(instance).name, - source_hash: function_coverage.source_hash(), - is_used: function_coverage.is_used(), + source_hash: if is_used { fn_cov_info.function_source_hash } else { 0 }, + is_used, virtual_file_mapping: VirtualFileMapping::default(), - expressions: function_coverage.counter_expressions().collect::>(), + expressions, regions: ffi::Regions::default(), }; - fill_region_tables(tcx, global_file_table, function_coverage, &mut covfun); + fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun); if covfun.regions.has_no_regions() { if covfun.is_used { @@ -72,20 +78,50 @@ pub(crate) fn prepare_covfun_record<'tcx>( Some(covfun) } +/// Convert the function's coverage-counter expressions into a form suitable for FFI. +fn prepare_expressions( + fn_cov_info: &FunctionCoverageInfo, + ids_info: &CoverageIdsInfo, + is_used: bool, +) -> Vec { + // If any counters or expressions were removed by MIR opts, replace their + // terms with zero. + let counter_for_term = |term| { + if !is_used || ids_info.is_zero_term(term) { + ffi::Counter::ZERO + } else { + ffi::Counter::from_term(term) + } + }; + + // We know that LLVM will optimize out any unused expressions before + // producing the final coverage map, so there's no need to do the same + // thing on the Rust side unless we're confident we can do much better. + // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) + fn_cov_info + .expressions + .iter() + .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression { + lhs: counter_for_term(lhs), + kind: match op { + Op::Add => ffi::ExprKind::Add, + Op::Subtract => ffi::ExprKind::Subtract, + }, + rhs: counter_for_term(rhs), + }) + .collect::>() +} + /// Populates the mapping region tables in the current function's covfun record. fn fill_region_tables<'tcx>( tcx: TyCtxt<'tcx>, - global_file_table: &GlobalFileTable, - function_coverage: &FunctionCoverage<'tcx>, + global_file_table: &mut GlobalFileTable, + fn_cov_info: &'tcx FunctionCoverageInfo, + ids_info: &'tcx CoverageIdsInfo, covfun: &mut CovfunRecord<'tcx>, ) { - let counter_regions = function_coverage.counter_regions(); - if counter_regions.is_empty() { - return; - } - // Currently a function's mappings must all be in the same file as its body span. - let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span); + let file_name = span_file_name(tcx, fn_cov_info.body_span); // Look up the global file ID for that filename. let global_file_id = global_file_table.global_file_id_for_file_name(file_name); @@ -99,10 +135,14 @@ fn fill_region_tables<'tcx>( // For each counter/region pair in this function+file, convert it to a // form suitable for FFI. - for (mapping_kind, region) in counter_regions { - debug!("Adding counter {mapping_kind:?} to map for {region:?}"); - let span = ffi::CoverageSpan::from_source_region(local_file_id, region); - match mapping_kind { + let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term); + for Mapping { kind, ref source_region } in &fn_cov_info.mappings { + // If the mapping refers to counters/expressions that were removed by + // MIR opts, replace those occurrences with zero. + let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term }); + + let span = ffi::CoverageSpan::from_source_region(local_file_id, source_region); + match kind { MappingKind::Code(term) => { code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 82b6677e2038f..7311cd9d230c6 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -5,7 +5,7 @@ use rustc_abi::Size; use rustc_codegen_ssa::traits::{ BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::HasTyCtxt; @@ -13,18 +13,16 @@ use tracing::{debug, instrument}; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::FunctionCoverage; use crate::llvm; pub(crate) mod ffi; mod llvm_cov; -pub(crate) mod map_data; mod mapgen; /// Extra per-CGU context/state needed for coverage instrumentation. pub(crate) struct CguCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. - pub(crate) function_coverage_map: RefCell, FunctionCoverage<'tcx>>>, + pub(crate) instances_used: RefCell>>, pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, pub(crate) mcdc_condition_bitmap_map: RefCell, Vec<&'ll llvm::Value>>>, @@ -34,17 +32,13 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> { impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { pub(crate) fn new() -> Self { Self { - function_coverage_map: Default::default(), + instances_used: RefCell::>::default(), pgo_func_name_var_map: Default::default(), mcdc_condition_bitmap_map: Default::default(), covfun_section_name: Default::default(), } } - fn take_function_coverage_map(&self) -> FxIndexMap, FunctionCoverage<'tcx>> { - self.function_coverage_map.replace(FxIndexMap::default()) - } - /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is /// called condition bitmap. In order to handle nested decisions, several condition bitmaps can /// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit @@ -157,12 +151,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { // Mark the instance as used in this CGU, for coverage purposes. // This includes functions that were not partitioned into this CGU, // but were MIR-inlined into one of this CGU's functions. - coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| { - FunctionCoverage::new_used( - function_coverage_info, - bx.tcx.coverage_ids_info(instance.def), - ) - }); + coverage_cx.instances_used.borrow_mut().insert(instance); match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 25d4146255647..58fe3ec4b8580 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1569,18 +1569,18 @@ impl DiagCtxtInner { debug!(?diagnostic); debug!(?self.emitted_diagnostics); - let already_emitted_sub = |sub: &mut Subdiag| { + let not_yet_emitted = |sub: &mut Subdiag| { debug!(?sub); if sub.level != OnceNote && sub.level != OnceHelp { - return false; + return true; } let mut hasher = StableHasher::new(); sub.hash(&mut hasher); let diagnostic_hash = hasher.finish(); debug!(?diagnostic_hash); - !self.emitted_diagnostics.insert(diagnostic_hash) + self.emitted_diagnostics.insert(diagnostic_hash) }; - diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {}); + diagnostic.children.retain_mut(not_yet_emitted); if already_emitted { let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; diagnostic.sub(Note, msg, MultiSpan::new()); diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 9b495c19990c1..50c64a9c94757 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -205,7 +205,7 @@ impl EarlyLintPass for NonAsciiIdents { (IdentifierType::Not_NFKC, "Not_NFKC"), ] { let codepoints: Vec<_> = - chars.extract_if(|(_, ty)| *ty == Some(id_ty)).collect(); + chars.extract_if(.., |(_, ty)| *ty == Some(id_ty)).collect(); if codepoints.is_empty() { continue; } @@ -217,7 +217,7 @@ impl EarlyLintPass for NonAsciiIdents { } let remaining = chars - .extract_if(|(c, _)| !GeneralSecurityProfile::identifier_allowed(*c)) + .extract_if(.., |(c, _)| !GeneralSecurityProfile::identifier_allowed(*c)) .collect::>(); if !remaining.is_empty() { cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index fe3bdb3a7b904..2ccc0d0dc022b 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -544,7 +544,7 @@ impl<'tcx> Collector<'tcx> { // can move them to the end of the list below. let mut existing = self .libs - .extract_if(|lib| { + .extract_if(.., |lib| { if lib.name.as_str() == passed_lib.name { // FIXME: This whole logic is questionable, whether modifiers are // involved or not, library reordering and kind overriding without diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 604f1da26c648..406e732744bbb 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -309,7 +309,7 @@ pub fn suggest_constraining_type_params<'a>( let Some(param) = param else { return false }; { - let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| { + let mut sized_constraints = constraints.extract_if(.., |(_, def_id, _)| { def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)) }); if let Some((_, def_id, _)) = sized_constraints.next() { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 368eb3c26c7b3..7cd56a8fb950e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2817,11 +2817,11 @@ fn show_candidates( path_strings.sort_by(|a, b| a.0.cmp(&b.0)); path_strings.dedup_by(|a, b| a.0 == b.0); let core_path_strings = - path_strings.extract_if(|p| p.0.starts_with("core::")).collect::>(); + path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); let std_path_strings = - path_strings.extract_if(|p| p.0.starts_with("std::")).collect::>(); + path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); let foreign_crate_path_strings = - path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::>(); + path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); // We list the `crate` local paths first. // Then we list the `std`/`core` paths. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 85ea6a74d3cfc..1625f69158fbf 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -629,7 +629,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Try to filter out intrinsics candidates, as long as we have // some other candidates to suggest. let intrinsic_candidates: Vec<_> = candidates - .extract_if(|sugg| { + .extract_if(.., |sugg| { let path = path_names_to_string(&sugg.path); path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::") }) diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 952a00c52a258..f763c37f535e3 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -13,8 +13,8 @@ pub(crate) fn target() -> Target { llvm_target: "powerpc64le-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()), - tier: Some(3), - host_tools: Some(false), + tier: Some(2), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 069fab6a6e6b9..069d42d4018c0 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -447,7 +447,7 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .extract_if(|predicate| { + .extract_if(.., |predicate| { matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..)) }) .collect(); diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index ca0ea1ec8b2ba..13168b7a39fe4 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1939,9 +1939,7 @@ pub struct ExtractIf< T: 'a, F: 'a, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ +> { list: &'a mut LinkedList, it: Option>>, pred: F, @@ -1979,10 +1977,7 @@ where } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl fmt::Debug for ExtractIf<'_, T, F> -where - F: FnMut(&mut T) -> bool, -{ +impl fmt::Debug for ExtractIf<'_, T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ExtractIf").field(&self.list).finish() } diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index 72d51e8904488..4db13981596bc 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -1,3 +1,4 @@ +use core::ops::{Range, RangeBounds}; use core::{ptr, slice}; use super::Vec; @@ -14,7 +15,7 @@ use crate::alloc::{Allocator, Global}; /// #![feature(extract_if)] /// /// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0); +/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] #[derive(Debug)] @@ -24,24 +25,32 @@ pub struct ExtractIf< T, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ - pub(super) vec: &'a mut Vec, +> { + vec: &'a mut Vec, /// The index of the item that will be inspected by the next call to `next`. - pub(super) idx: usize, + idx: usize, + /// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`. + end: usize, /// The number of items that have been drained (removed) thus far. - pub(super) del: usize, + del: usize, /// The original length of `vec` prior to draining. - pub(super) old_len: usize, + old_len: usize, /// The filter test predicate. - pub(super) pred: F, + pred: F, } -impl ExtractIf<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ +impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { + pub(super) fn new>(vec: &'a mut Vec, pred: F, range: R) -> Self { + let old_len = vec.len(); + let Range { start, end } = slice::range(range, ..old_len); + + // Guard against the vec getting leaked (leak amplification) + unsafe { + vec.set_len(0); + } + ExtractIf { vec, idx: start, del: 0, end, old_len, pred } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] @@ -59,7 +68,7 @@ where fn next(&mut self) -> Option { unsafe { - while self.idx < self.old_len { + while self.idx < self.end { let i = self.idx; let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); let drained = (self.pred)(&mut v[i]); @@ -82,24 +91,15 @@ where } fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) + (0, Some(self.end - self.idx)) } } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl Drop for ExtractIf<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ +impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { unsafe { if self.idx < self.old_len && self.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The backshift - // is required to prevent a double-drop of the last successfully - // drained item prior to a panic in the predicate. let ptr = self.vec.as_mut_ptr(); let src = ptr.add(self.idx); let dst = src.sub(self.del); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7e7a8ff72c7b1..5f4b85b58a98c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3615,12 +3615,15 @@ impl Vec { Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } } - /// Creates an iterator which uses a closure to determine if an element should be removed. + /// Creates an iterator which uses a closure to determine if element in the range should be removed. /// /// If the closure returns true, then the element is removed and yielded. /// If the closure returns false, the element will remain in the vector and will not be yielded /// by the iterator. /// + /// Only elements that fall in the provided range are considered for extraction, but any elements + /// after the range will still have to be moved if any element has been extracted. + /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. /// Use [`retain`] with a negated predicate if you do not need the returned iterator. @@ -3630,10 +3633,12 @@ impl Vec { /// Using this method is equivalent to the following code: /// /// ``` + /// # use std::cmp::min; /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; - /// let mut i = 0; - /// while i < vec.len() { + /// # let range = 1..4; + /// let mut i = range.start; + /// while i < min(vec.len(), range.end) { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); /// // your code here @@ -3648,8 +3653,12 @@ impl Vec { /// But `extract_if` is easier to use. `extract_if` is also more efficient, /// because it can backshift the elements of the array in bulk. /// - /// Note that `extract_if` also lets you mutate every element in the filter closure, - /// regardless of whether you choose to keep or remove it. + /// Note that `extract_if` also lets you mutate the elements passed to the filter closure, + /// regardless of whether you choose to keep or remove them. + /// + /// # Panics + /// + /// If `range` is out of bounds. /// /// # Examples /// @@ -3659,25 +3668,29 @@ impl Vec { /// #![feature(extract_if)] /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; /// - /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::>(); /// let odds = numbers; /// /// assert_eq!(evens, vec![2, 4, 6, 8, 14]); /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]); /// ``` + /// + /// Using the range argument to only process a part of the vector: + /// + /// ``` + /// #![feature(extract_if)] + /// let mut items = vec![0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]; + /// let ones = items.extract_if(7.., |x| *x == 1).collect::>(); + /// assert_eq!(items, vec![0, 0, 0, 0, 0, 0, 0, 2, 2, 2]); + /// assert_eq!(ones.len(), 3); + /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] - pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + pub fn extract_if(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, + R: RangeBounds, { - let old_len = self.len(); - - // Guard against us getting leaked (leak amplification) - unsafe { - self.set_len(0); - } - - ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter } + ExtractIf::new(self, filter, range) } } diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 0f27fdff3e182..84679827ba1c0 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1414,7 +1414,7 @@ fn extract_if_empty() { let mut vec: Vec = vec![]; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); @@ -1431,7 +1431,7 @@ fn extract_if_zst() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1454,7 +1454,7 @@ fn extract_if_false() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| false); + let mut iter = vec.extract_if(.., |_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; @@ -1476,7 +1476,7 @@ fn extract_if_true() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1492,6 +1492,31 @@ fn extract_if_true() { assert_eq!(vec, vec![]); } +#[test] +fn extract_if_ranges() { + let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut count = 0; + let it = vec.extract_if(1..=3, |_| { + count += 1; + true + }); + assert_eq!(it.collect::>(), vec![1, 2, 3]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(count, 3); + + let it = vec.extract_if(1..=3, |_| false); + assert_eq!(it.collect::>(), vec![]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +#[should_panic] +fn extract_if_out_of_bounds() { + let mut vec = vec![0, 1]; + let _ = vec.extract_if(5.., |_| true).for_each(drop); +} + #[test] fn extract_if_complex() { { @@ -1501,7 +1526,7 @@ fn extract_if_complex() { 39, ]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1515,7 +1540,7 @@ fn extract_if_complex() { 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, ]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1528,7 +1553,7 @@ fn extract_if_complex() { let mut vec = vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1540,7 +1565,7 @@ fn extract_if_complex() { // [xxxxxxxxxx+++++++++++] let mut vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1552,7 +1577,7 @@ fn extract_if_complex() { // [+++++++++++xxxxxxxxxx] let mut vec = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1600,7 +1625,7 @@ fn extract_if_consumed_panic() { } c.index < 6 }; - let drain = data.extract_if(filter); + let drain = data.extract_if(.., filter); // NOTE: The ExtractIf is explicitly consumed drain.for_each(drop); @@ -1653,7 +1678,7 @@ fn extract_if_unconsumed_panic() { } c.index < 6 }; - let _drain = data.extract_if(filter); + let _drain = data.extract_if(.., filter); // NOTE: The ExtractIf is dropped without being consumed }); @@ -1669,7 +1694,7 @@ fn extract_if_unconsumed_panic() { #[test] fn extract_if_unconsumed() { let mut vec = vec![1, 2, 3, 4]; - let drain = vec.extract_if(|&mut x| x % 2 != 0); + let drain = vec.extract_if(.., |&mut x| x % 2 != 0); drop(drain); assert_eq!(vec, [1, 2, 3, 4]); } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c79b2f7ad8ed3..151d879fabeed 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -4,7 +4,7 @@ macro_rules! uint_impl { ActualT = $ActualT:ident, SignedT = $SignedT:ident, - // There are all for use *only* in doc comments. + // These are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 717500221454a..a86c20d46bda5 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -245,6 +245,11 @@ def v(*args): "target.mips64el-unknown-linux-muslabi64.musl-root", "mips64el-unknown-linux-muslabi64 install directory", ) +v( + "musl-root-powerpc64le", + "target.powerpc64le-unknown-linux-musl.musl-root", + "powerpc64le-unknown-linux-musl install directory", +) v( "musl-root-riscv32gc", "target.riscv32gc-unknown-linux-musl.musl-root", diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index ffb7d9a9e0e74..be5b405705162 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -217,6 +217,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { ("powerpc-unknown-linux-gnu", false), ("powerpc64-unknown-linux-gnu", false), ("powerpc64le-unknown-linux-gnu", false), + ("powerpc64le-unknown-linux-musl", false), ("riscv64gc-unknown-linux-gnu", false), ("s390x-unknown-linux-gnu", false), ("x86_64-unknown-freebsd", false), diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b2ffbd9c70f6a..819a552093be4 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -91,7 +91,7 @@ macro_rules! rustc { #[test] fn test_valid() { // make sure multi suite paths are accepted - check_cli(["test", "tests/ui/attr-start.rs", "tests/ui/attr-shebang.rs"]); + check_cli(["test", "tests/ui/bootstrap/self-test/a.rs", "tests/ui/bootstrap/self-test/b.rs"]); } #[test] diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile index 5dc282403be9c..9ef391892497f 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile @@ -3,23 +3,46 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh +COPY scripts/crosstool-ng-git.sh /scripts/ +RUN sh /scripts/crosstool-ng-git.sh + COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh + WORKDIR /tmp +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +WORKDIR /build + RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /tmp/ +COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +ENV PATH=$PATH:/x-tools/powerpc64le-unknown-linux-musl/bin + ENV \ AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \ CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \ - CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ + CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ \ + AR_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-ar \ + CC_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-gcc \ + CXX_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-g++ + +ENV HOSTS=powerpc64le-unknown-linux-gnu,powerpc64le-unknown-linux-musl -ENV HOSTS=powerpc64le-unknown-linux-gnu +ENV RUST_CONFIGURE_ARGS \ + --enable-extended \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --disable-docs \ + --set target.powerpc64le-unknown-linux-musl.crt-static=false \ + --musl-root-powerpc64le=/x-tools/powerpc64le-unknown-linux-musl/powerpc64le-unknown-linux-musl/sysroot/usr -ENV RUST_CONFIGURE_ARGS --enable-extended --enable-profiler --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig new file mode 100644 index 0000000000000..c6cde30b2a4e5 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig @@ -0,0 +1,16 @@ +CT_CONFIG_VERSION="4" +CT_EXPERIMENTAL=y +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_POWERPC=y +CT_ARCH_LE=y +CT_ARCH_64=y +# CT_DEMULTILIB is not set +CT_ARCH_ARCH="powerpc64le" +CT_KERNEL_LINUX=y +CT_LINUX_V_4_19=y +CT_LIBC_MUSL=y +CT_MUSL_V_1_2_3=y +CT_CC_LANG_CXX=y +CT_GETTEXT_NEEDED=y diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index fe0fdde39d0ff..24a9f843f3368 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -190,7 +190,7 @@ auto: <<: *job-linux-4c - image: dist-powerpc64le-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-riscv64-linux <<: *job-linux-4c diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 2341d25ff096c..1e9f5a33fc792 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -66,6 +66,7 @@ - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) + - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - [riscv32e*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - [riscv32i*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c4efe27efc230..db6612f9fff27 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -97,6 +97,7 @@ target | notes `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) `powerpc64le-unknown-linux-gnu` | PPC64LE Linux (kernel 3.10, glibc 2.17) +[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19, musl 1.2.3) [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) [`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2, glibc 2.17) @@ -348,7 +349,6 @@ target | std | host | notes `powerpc-unknown-freebsd` | ? | | PowerPC FreeBSD `powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | -`powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) `riscv32gc-unknown-linux-gnu` | ✓ | | RISC-V Linux (kernel 5.4, glibc 2.33) diff --git a/src/doc/rustc/src/platform-support/powerpc64le-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/powerpc64le-unknown-linux-musl.md new file mode 100644 index 0000000000000..3bd3f5d8b7f78 --- /dev/null +++ b/src/doc/rustc/src/platform-support/powerpc64le-unknown-linux-musl.md @@ -0,0 +1,48 @@ +# powerpc64le-unknown-linux-musl + +**Tier: 2** + +Target for 64-bit little endian PowerPC Linux programs using musl libc. + +## Target maintainers + +- [@Gelbpunkt](https://github.com/Gelbpunkt) +- [@famfo](https://github.com/famfo) +- [@neuschaefer](https://github.com/neuschaefer) + +## Requirements + +Building the target itself requires a 64-bit little endian PowerPC compiler that is supported by `cc-rs`. + +## Building the target + +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["powerpc64le-unknown-linux-musl"] +``` + +Make sure your C compiler is included in `$PATH`, then add it to the `config.toml`: + +```toml +[target.powerpc64le-unknown-linux-musl] +cc = "powerpc64le-linux-musl-gcc" +cxx = "powerpc64le-linux-musl-g++" +ar = "powerpc64le-linux-musl-ar" +linker = "powerpc64le-linux-musl-gcc" +``` + +## Building Rust programs + +This target are distributed through `rustup`, and otherwise require no +special configuration. + +## Cross-compilation + +This target can be cross-compiled from any host. + +## Testing + +This target can be tested as normal with `x.py` on a 64-bit little endian +PowerPC host or via QEMU emulation. diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 39d9158a1ffa7..561b611148a20 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -37,6 +37,7 @@ static HOSTS: &[&str] = &[ "powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", + "powerpc64le-unknown-linux-musl", "riscv64gc-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-apple-darwin", @@ -131,6 +132,7 @@ static TARGETS: &[&str] = &[ "powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", + "powerpc64le-unknown-linux-musl", "riscv32i-unknown-none-elf", "riscv32im-risc0-zkvm-elf", "riscv32im-unknown-none-elf", diff --git a/tests/coverage/unused_mod.cov-map b/tests/coverage/unused_mod.cov-map index af10906fa3c54..5e8b69fcdba9f 100644 --- a/tests/coverage/unused_mod.cov-map +++ b/tests/coverage/unused_mod.cov-map @@ -1,16 +1,16 @@ Function name: unused_mod::main -Raw bytes (9): 0x[01, 02, 00, 01, 01, 04, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 04, 01, 02, 02] Number of files: 1 -- file 0 => global file 2 +- file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 4, 1) to (start + 2, 2) Highest counter ID seen: c0 Function name: unused_mod::unused_module::never_called_function (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 02, 01, 02, 02] +Raw bytes (9): 0x[01, 02, 00, 01, 00, 02, 01, 02, 02] Number of files: 1 -- file 0 => global file 1 +- file 0 => global file 2 Number of expressions: 0 Number of file 0 mappings: 1 - Code(Zero) at (prev + 2, 1) to (start + 2, 2) diff --git a/tests/ui/bootstrap/self-test/a.rs b/tests/ui/bootstrap/self-test/a.rs new file mode 100644 index 0000000000000..64d2d6f11bbdd --- /dev/null +++ b/tests/ui/bootstrap/self-test/a.rs @@ -0,0 +1,2 @@ +//! Not used by compiler, this is used by bootstrap cli self-test. +//@ ignore-test diff --git a/tests/ui/bootstrap/self-test/b.rs b/tests/ui/bootstrap/self-test/b.rs new file mode 100644 index 0000000000000..91f92f67910b7 --- /dev/null +++ b/tests/ui/bootstrap/self-test/b.rs @@ -0,0 +1,2 @@ +//! Not used by compiler, used by bootstrap cli self-test. +//@ ignore-test diff --git a/tests/ui/impl-trait/rpit/inherits-lifetime.rs b/tests/ui/impl-trait/rpit/inherits-lifetime.rs new file mode 100644 index 0000000000000..60c2a96c8737b --- /dev/null +++ b/tests/ui/impl-trait/rpit/inherits-lifetime.rs @@ -0,0 +1,24 @@ +//! Check that lifetimes are inherited in RPIT. +//! Previously, the hidden lifetime of T::Bar would be overlooked +//! and would instead end up as >::Bar. +//! +//! Regression test for . + +//@ check-pass + +trait Foo<'a> { + type Bar; +} + +impl<'a> Foo<'a> for u32 { + type Bar = &'a (); +} + +fn baz<'a, T>() -> impl IntoIterator +where + T: Foo<'a>, +{ + None +} + +fn main() {}