diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 31fa9dd2ee64e..7a7a89bea5710 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -26,7 +26,7 @@ use crate::polonius::PoloniusContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; -use crate::region_infer::{RegionInferenceContext, UnsolvedRegionInferenceContext}; +use crate::region_infer::RegionInferenceContext; use crate::type_check::MirTypeckRegionConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -96,14 +96,15 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( &universal_region_relations, infcx, ); - let regioncx = UnsolvedRegionInferenceContext::new( + let (_, closure_region_requirements, _nll_errors) = RegionInferenceContext::solve( &infcx, lowered_constraints, universal_region_relations.clone(), location_map, + body, + None, ); - let (_, closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None); closure_region_requirements } @@ -156,7 +157,7 @@ pub(crate) fn compute_regions<'tcx>( &universal_region_relations.universal_regions, body, move_data, - Rc::clone(&location_map), + &location_map, borrow_set, ); } @@ -182,17 +183,16 @@ pub(crate) fn compute_regions<'tcx>( } }); - let regioncx = UnsolvedRegionInferenceContext::new( + // Solve the region constraints. + let (regioncx, closure_region_requirements, nll_errors) = RegionInferenceContext::solve( infcx, lowered_constraints, universal_region_relations, location_map, + body, + polonius_output.clone(), ); - // Solve the region constraints. - let (regioncx, closure_region_requirements, nll_errors) = - regioncx.solve(infcx, body, polonius_output.clone()); - NllOutput { regioncx, polonius_input: polonius_facts.map(Box::new), diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 68e851c05ace3..d445d2e8ef96d 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::IndexVec; use rustc_middle::mir::{Body, Location}; @@ -41,8 +39,6 @@ pub(super) struct LocalizedNode { /// The localized constraint graph indexes the physical and logical edges to lazily compute a given /// node's successors during traversal. pub(super) struct LocalizedConstraintGraph { - location_map: Rc, - /// The actual, physical, edges we have recorded for a given node. We localize them on-demand /// when traversing from the node to the successor region. edges: FxHashMap>, @@ -67,7 +63,7 @@ pub(super) trait LocalizedConstraintGraphVisitor { impl LocalizedConstraintGraph { /// Traverses the constraints and returns the indexed graph of edges per node. pub(super) fn new<'tcx>( - location_map: Rc, + location_map: &DenseLocationMap, outlives_constraints: impl Iterator>, ) -> Self { let mut edges: FxHashMap<_, SmallVec<[RegionVid; 4]>> = FxHashMap::default(); @@ -96,7 +92,7 @@ impl LocalizedConstraintGraph { } } - LocalizedConstraintGraph { location_map, edges, logical_edges } + LocalizedConstraintGraph { edges, logical_edges } } /// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered @@ -105,6 +101,7 @@ impl LocalizedConstraintGraph { &self, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, + location_map: &DenseLocationMap, liveness_source: &mut impl LivenessSource, visitor: &mut impl LocalizedConstraintGraphVisitor, ) { @@ -119,7 +116,7 @@ impl LocalizedConstraintGraph { let start_node = LocalizedNode { region: loan.region, - point: self.location_map.point_from_location(loan.reserve_location), + point: location_map.point_from_location(loan.reserve_location), }; visited.insert(start_node); stack.push(start_node); @@ -127,7 +124,7 @@ impl LocalizedConstraintGraph { while let Some(node) = stack.pop() { let liveness = liveness_source.liveness_for_region(node.region); // We've reached a node we haven't visited before. - let location = self.location_map.to_location(node.point); + let location = location_map.to_location(node.point); visitor.on_node_traversed(loan_idx, node, liveness.is_live_at(node.point)); // When we find a _new_ successor, we'd like to @@ -183,7 +180,7 @@ impl LocalizedConstraintGraph { for successor_block in body[location.block].terminator().successors() { let next_location = Location { block: successor_block, statement_index: 0 }; - let next_point = self.location_map.point_from_location(next_location); + let next_point = location_map.point_from_location(next_location); if liveness.is_live_at(next_point) { successor_found(LocalizedNode { region: node.region, @@ -226,7 +223,7 @@ impl LocalizedConstraintGraph { statement_index: body[pred_block].statements.len(), }; let previous_point = - self.location_map.point_from_location(previous_location); + location_map.point_from_location(previous_location); successor_found(LocalizedNode { region: node.region, point: previous_point, diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index fb4c6fbcc9da9..c32b118a9a1cc 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -5,7 +5,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions}; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; -use rustc_mir_dataflow::points::PointIndex; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; @@ -71,7 +71,8 @@ pub(crate) fn dump_polonius_mir<'tcx>( }; let mut collector = MirDumpCollector::default(); if let Some(graph) = &polonius_context.graph { - graph.traverse(body, borrow_set, &mut liveness_source, &mut collector); + let location_map = regioncx.liveness_constraints().location_map(); + graph.traverse(body, borrow_set, location_map, &mut liveness_source, &mut collector); } let extra_data = &|pass_where, out: &mut dyn io::Write| { @@ -172,7 +173,7 @@ fn emit_polonius_dump<'tcx>( "POLONIUS_CONSTRAINTS" => { edge_count = emit_mermaid_constraint_graph( borrow_set, - regioncx.liveness_constraints(), + regioncx.liveness_constraints().location_map(), &collector.constraints, out, )?; @@ -271,7 +272,7 @@ fn emit_polonius_mir<'tcx>( out, )?; - let liveness = regioncx.liveness_constraints(); + let location_map = regioncx.liveness_constraints().location_map(); // Add localized outlives constraints match pass_where { @@ -281,8 +282,8 @@ fn emit_polonius_mir<'tcx>( for constraint in localized_outlives_constraints { let LocalizedOutlivesConstraint { source, from, target, to } = constraint; - let from = liveness.location_from_point(*from); - let to = liveness.location_from_point(*to); + let from = location_map.to_location(*from); + let to = location_map.to_location(*to); writeln!(out, "| {source:?} at {from:?} -> {target:?} at {to:?}")?; } writeln!(out, "|")?; @@ -462,12 +463,12 @@ fn emit_mermaid_nll_sccs<'tcx>( /// region, and loan introductions. fn emit_mermaid_constraint_graph<'tcx>( borrow_set: &BorrowSet<'tcx>, - liveness: &LivenessValues, + location_map: &DenseLocationMap, localized_outlives_constraints: &[LocalizedOutlivesConstraint], out: &mut dyn io::Write, ) -> io::Result { let node_label = |region: RegionVid, point: PointIndex| { - let location = liveness.location_from_point(point); + let location = location_map.to_location(point); node_name(region, location) }; @@ -533,6 +534,7 @@ fn emit_loan_reachability( reachability: &FxIndexMap>, out: &mut dyn io::Write, ) -> io::Result<()> { + let location_map = liveness.location_map(); for (loan, _) in borrow_set.iter_enumerated() { let Some(reachability) = reachability.get(&loan) else { continue; @@ -553,7 +555,7 @@ fn emit_loan_reachability( for (idx, node) in reachability.iter().enumerate() { writeln!(out, "
  • ")?; - let location = liveness.location_from_point(node.point); + let location = location_map.to_location(node.point); let kind = if idx == 0 { "starts in" } else { "reaches" }; writeln!( out, diff --git a/compiler/rustc_borrowck/src/polonius/liveness.rs b/compiler/rustc_borrowck/src/polonius/liveness.rs index 9cbaa2d6ddc4d..346f3622ebdbb 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness.rs @@ -57,9 +57,11 @@ impl<'a> RegionLiveness<'a> { } /// The data needed to compute region liveness on-demand while traversing the localized outlives -/// constraint graph to compute loan liveness. +/// constraint graph to compute loan liveness. Each region comes from a local's type, so this holds +/// mostly the locals and drop-liveness specifics about their type, to be used as inputs to the +/// `LivenessComputation` giving us the region liveness. #[derive(Default)] -pub(crate) struct DeferredLocals<'tcx> { +pub(crate) struct DeferredRegionLiveness<'tcx> { /// For each region, the local whose liveness is deferred. /// /// Importantly, because of MIR renumbering, this will always be a 1:1 relationship. @@ -69,7 +71,9 @@ pub(crate) struct DeferredLocals<'tcx> { drop_args_by_local: IndexVec>>>, } -impl<'tcx> DeferredLocals<'tcx> { +impl<'tcx> DeferredRegionLiveness<'tcx> { + /// We want to defer computing the liveness of the region in the local's type, so we store the + /// necessary inputs to do that later. pub(crate) fn defer_local( &mut self, tcx: TyCtxt<'tcx>, @@ -86,7 +90,6 @@ impl<'tcx> DeferredLocals<'tcx> { // Then, we want to map all the regions contained within this local to // the local itself. Later, when asked for liveness of a given region, // we can trace liveness for the local containing it. - let by_region = &mut self.by_region; tcx.for_each_free_region(&local_ty, |region| { // See note in `VarianceExtractor::record_variance`. if region.is_bound() || region.is_erased() { @@ -95,7 +98,7 @@ impl<'tcx> DeferredLocals<'tcx> { let vid = universal_regions.to_region_vid(region); // Because of MIR renumbering, we should always have a 1:1 mapping // between a region and a local. - let previous = by_region.insert(vid, local); + let previous = self.by_region.insert(vid, local); debug_assert!( previous.is_none(), "{vid:?} is in the type of both {previous:?} and {local:?}, but \ @@ -106,7 +109,7 @@ impl<'tcx> DeferredLocals<'tcx> { /// For a given region, compute the liveness for the local containing it, if it is deferred. #[inline] - pub(crate) fn compute_deferred_local( + pub(crate) fn ensure_deferred_liveness( &mut self, region: RegionVid, universal_regions: &UniversalRegions<'tcx>, diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 73133b6ca0811..664bcedfc5ef2 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -39,8 +39,6 @@ pub(crate) mod legacy; mod liveness; mod liveness_constraints; -use std::rc::Rc; - use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; @@ -54,7 +52,7 @@ pub(crate) use self::dump::dump_polonius_mir; pub(crate) use self::liveness_constraints::record_live_region_variance; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; -pub(crate) use crate::polonius::liveness::DeferredLocals; +pub(crate) use crate::polonius::liveness::DeferredRegionLiveness; use crate::polonius::liveness::{LivenessSource, RegionLiveness}; use crate::region_infer::values::LivenessValues; use crate::type_check::liveness::{LivenessComputation, LocalUseMap}; @@ -105,7 +103,7 @@ pub(crate) struct PoloniusContext<'tcx> { /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics. pub(crate) boring_nll_locals: FxHashSet, - pub(crate) deferred_locals_for_liveness: DeferredLocals<'tcx>, + pub(crate) deferred_liveness: DeferredRegionLiveness<'tcx>, pub(crate) local_use_map: Option, } @@ -143,7 +141,7 @@ impl<'tcx> PoloniusContext<'tcx> { universal_regions: &UniversalRegions<'tcx>, body: &Body<'tcx>, move_data: &MoveData<'tcx>, - location_map: Rc, + location_map: &DenseLocationMap, borrow_set: &BorrowSet<'tcx>, ) { // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to @@ -152,27 +150,25 @@ impl<'tcx> PoloniusContext<'tcx> { // From the outlives constraints, liveness, and variances, we can compute reachability // on the lazy localized constraint graph to trace the liveness of loans, for the next // step in the chain (the NLL loan scope and active loans computations). - let graph = - LocalizedConstraintGraph::new(Rc::clone(&location_map), outlives_constraints); + let graph = LocalizedConstraintGraph::new(location_map, outlives_constraints); let local_use_map = self .local_use_map .as_ref() .expect("local use map should be computed before loan liveness"); - let deferred_locals_for_liveness = - std::mem::take(&mut self.deferred_locals_for_liveness); + let deferred_liveness = std::mem::take(&mut self.deferred_liveness); let mut live_loans = LiveLoans::new(location_map.num_points(), borrow_set.len()); let comp = - LivenessComputation::new(infcx, body, &location_map, move_data, &local_use_map); + LivenessComputation::new(infcx, body, location_map, move_data, &local_use_map); let mut liveness_source = DeferredLivenessSource { liveness, live_region_variances: &mut self.live_region_variances, universal_regions, - deferred_locals_for_liveness, + deferred_liveness, comp, }; let mut visitor = LoanLivenessVisitor { live_loans: &mut live_loans }; - graph.traverse(body, borrow_set, &mut liveness_source, &mut visitor); + graph.traverse(body, borrow_set, location_map, &mut liveness_source, &mut visitor); liveness.record_live_loans(live_loans); // The graph can be traversed again during MIR dumping, so we store it here. @@ -188,14 +184,14 @@ struct DeferredLivenessSource<'a, 'tcx> { liveness: &'a mut LivenessValues, live_region_variances: &'a mut LiveRegionVariances, universal_regions: &'a UniversalRegions<'tcx>, - deferred_locals_for_liveness: DeferredLocals<'tcx>, + deferred_liveness: DeferredRegionLiveness<'tcx>, comp: LivenessComputation<'a, 'tcx>, } impl<'a> LivenessSource for DeferredLivenessSource<'a, '_> { #[inline] fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_> { - self.deferred_locals_for_liveness.compute_deferred_local( + self.deferred_liveness.ensure_deferred_liveness( region, self.universal_regions, &mut self.liveness, diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index 68f822aac403a..72e16b4b86dfc 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -68,7 +68,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { with_msg: &mut dyn FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for region in self.definitions.indices() { - let value = self.liveness_constraints.pretty_print_live_points(region); + let value = self.liveness_constraints().pretty_print_live_points(region); if value != "{}" { with_msg(&format!("{region:?} live at {value}"))?; } diff --git a/compiler/rustc_borrowck/src/region_infer/region_context.rs b/compiler/rustc_borrowck/src/region_infer/region_context.rs index af0f48526b4fe..a6f7c19ff0c17 100644 --- a/compiler/rustc_borrowck/src/region_infer/region_context.rs +++ b/compiler/rustc_borrowck/src/region_infer/region_context.rs @@ -50,7 +50,7 @@ pub struct RegionInferenceContextInner<'tcx> { /// regions, these start out empty and steadily grow, though for /// each universally quantified region R they start out containing /// the entire CFG and `end(R)`. - pub(super) liveness_constraints: LivenessValues, + liveness_constraints: LivenessValues, /// The outlives constraints computed by the type-check. pub(super) constraints: Frozen>, @@ -58,17 +58,17 @@ pub struct RegionInferenceContextInner<'tcx> { /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct /// the SCC (see `constraint_sccs`) and for error reporting. - pub(super) constraint_graph: Frozen, + constraint_graph: Frozen, /// The SCC computed from `constraints` and the constraint /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. pub(super) constraint_sccs: ConstraintSccs, - pub(super) scc_annotations: IndexVec, + scc_annotations: IndexVec, /// Map universe indexes to information on why we created it. - pub(super) universe_causes: FxIndexMap>, + universe_causes: FxIndexMap>, /// The final inferred values of the region variables; we compute /// one value per SCC. To get the value for any given *region*, @@ -83,37 +83,212 @@ pub struct RegionInferenceContextInner<'tcx> { /// This contains data around region constraints and liveness, up to and after solving. /// All data is immutable. pub struct RegionInferenceContext<'tcx> { - data: Frozen>, + inner: Frozen>, } impl<'tcx> Deref for RegionInferenceContext<'tcx> { type Target = RegionInferenceContextInner<'tcx>; fn deref(&self) -> &Self::Target { - &self.data + &self.inner + } +} + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Performs region inference and report errors if we see any + /// unsatisfiable constraints. If this is a closure, returns the + /// region requirements to propagate to our creator, if any. + #[instrument( + skip(infcx, lowered_constraints, location_map, body, polonius_output), + level = "debug" + )] + pub(crate) fn solve( + infcx: &BorrowckInferCtxt<'tcx>, + lowered_constraints: LoweredConstraints<'tcx>, + universal_region_relations: Frozen>, + location_map: Rc, + body: &Body<'tcx>, + polonius_output: Option>, + ) -> (RegionInferenceContext<'tcx>, Option>, RegionErrors<'tcx>) + { + // 1. We first prepare the data needed for the `UnsolvedRegionInferenceContext` to do the + // solving work. + let universal_regions = &universal_region_relations.universal_regions; + + let LoweredConstraints { + constraint_sccs, + definitions, + outlives_constraints, + scc_annotations, + type_tests, + liveness_constraints, + universe_causes, + placeholder_indices, + } = lowered_constraints; + + debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); + debug!("outlives constraints: {:#?}", outlives_constraints); + debug!("placeholder_indices: {:#?}", placeholder_indices); + debug!("type tests: {:#?}", type_tests); + + let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); + + if cfg!(debug_assertions) { + sccs_info(infcx, &constraint_sccs); + } + + let mut scc_values = + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + + // Initializes the region variables with their initial live points. + for (region, definition) in definitions.iter_enumerated() { + let scc = constraint_sccs.scc(region); + + // For each universally quantified region (lifetime parameter). The + // first N variables always correspond to the regions appearing in the + // function signature (both named and anonymous) and in where-clauses. + match definition.origin { + // For each free, universally quantified region X: + NllRegionVariableOrigin::FreeRegion => { + // Add `end(X)` into the set for X. + scc_values.add_free_region(scc, region); + } + + NllRegionVariableOrigin::Placeholder(placeholder) => { + scc_values.add_placeholder(scc, placeholder); + } + + NllRegionVariableOrigin::Existential { .. } => { + // For existential, regions, nothing to do. + } + } + + // Initially copy the liveness constraints of any region that + // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. + // + // These values will later be propagated during + // [`UnsolvedRegionInferenceContext::propagate_constraints()`]. + // The values include any live-at-all-points constraints added previously in `liveness::generate`. + if let Some(liveness) = liveness_constraints.point_liveness(region) { + scc_values.merge_liveness(scc, liveness) + } + } + + let mut unsolved_regioncx = UnsolvedRegionInferenceContext { + inner: RegionInferenceContextInner { + definitions, + liveness_constraints, + constraints: outlives_constraints, + constraint_graph, + constraint_sccs, + scc_annotations, + universe_causes, + universal_region_relations, + scc_values, + }, + type_tests, + }; + + // 2. And now we can do the actual solving, the region inference. + let mir_def_id = body.source.def_id(); + unsolved_regioncx.propagate_constraints(); + + let mut errors_buffer = RegionErrors::new(infcx.tcx); + + // If this is a nested body, we propagate unsatisfied + // outlives constraints to the parent body instead of + // eagerly erroing. + let mut propagated_outlives_requirements = + infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); + + unsolved_regioncx.check_type_tests( + infcx, + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + ); + + debug!(?errors_buffer); + debug!(?propagated_outlives_requirements); + + // In Polonius mode, the errors about missing universal region relations are in the output + // and need to be emitted or propagated. Otherwise, we need to check whether the + // constraints were too strong, and if so, emit or propagate those errors. + if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() { + unsolved_regioncx.check_polonius_subset_errors( + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + polonius_output + .as_ref() + .expect("Polonius output is unavailable despite `-Z polonius`"), + ); + } else { + unsolved_regioncx.check_universal_regions( + propagated_outlives_requirements.as_mut(), + &mut errors_buffer, + ); + } + + debug!(?errors_buffer); + + let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default(); + if propagated_outlives_requirements.is_empty() { + ( + RegionInferenceContext { inner: Frozen::freeze(unsolved_regioncx.inner) }, + None, + errors_buffer, + ) + } else { + let num_external_vids = + unsolved_regioncx.universal_regions().num_global_and_external_regions(); + ( + RegionInferenceContext { inner: Frozen::freeze(unsolved_regioncx.inner) }, + Some(ClosureRegionRequirements { + num_external_vids, + outlives_requirements: propagated_outlives_requirements, + }), + errors_buffer, + ) + } + } + + /// Returns `true` if the region `r` contains the point `p`. + pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { + let scc = self.constraint_sccs.scc(r); + self.scc_values.contains_point(scc, p) + } + + /// Returns the lowest statement index in `start..=end` which is not contained by `r`. + pub(crate) fn first_non_contained_inclusive( + &self, + r: RegionVid, + block: BasicBlock, + start: usize, + end: usize, + ) -> Option { + let scc = self.constraint_sccs.scc(r); + self.scc_values.first_non_contained_inclusive(scc, block, start, end) } } /// This contains data around region constraints and liveness, up to solving. -/// Calling `solve` returns a new immutable `RegionInferenceContext`. -pub(crate) struct UnsolvedRegionInferenceContext<'tcx> { - pub(super) data: RegionInferenceContextInner<'tcx>, +struct UnsolvedRegionInferenceContext<'tcx> { + inner: RegionInferenceContextInner<'tcx>, /// Type constraints that we check after solving. - pub(super) type_tests: Vec>, + type_tests: Vec>, } impl<'tcx> Deref for UnsolvedRegionInferenceContext<'tcx> { type Target = RegionInferenceContextInner<'tcx>; fn deref(&self) -> &Self::Target { - &self.data + &self.inner } } impl<'tcx> DerefMut for UnsolvedRegionInferenceContext<'tcx> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data + &mut self.inner } } @@ -143,28 +318,6 @@ impl<'tcx> RegionInferenceContextInner<'tcx> { self.universal_regions().annotate(tcx, err) } - /// Returns `true` if the region `r` contains the point `p`. - /// - /// Panics if called before `solve()` executes, - pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { - let scc = self.constraint_sccs.scc(r); - self.scc_values.contains_point(scc, p) - } - - /// Returns the lowest statement index in `start..=end` which is not contained by `r`. - /// - /// Panics if called before `solve()` executes. - pub(crate) fn first_non_contained_inclusive( - &self, - r: RegionVid, - block: BasicBlock, - start: usize, - end: usize, - ) -> Option { - let scc = self.constraint_sccs.scc(r); - self.scc_values.first_non_contained_inclusive(scc, block, start, end) - } - /// Returns access to the value of `r` for debugging purposes. pub(crate) fn region_value_str(&self, r: RegionVid) -> String { let scc = self.constraint_sccs.scc(r); @@ -694,164 +847,12 @@ impl<'tcx> RegionInferenceContextInner<'tcx> { /// region is contained within the type of a variable that is live at this point. /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { - let point = self.liveness_constraints.point_from_location(location); + let point = self.liveness_constraints.location_map().point_from_location(location); self.liveness_constraints.is_loan_live_at(loan_idx, point) } } impl<'tcx> UnsolvedRegionInferenceContext<'tcx> { - /// Creates a new region inference context with a total of - /// `num_region_variables` valid inference variables; the first N - /// of those will be constant regions representing the free - /// regions defined in `universal_regions`. - /// - /// The `outlives_constraints` and `type_tests` are an initial set - /// of constraints produced by the MIR type check. - pub(crate) fn new( - infcx: &BorrowckInferCtxt<'tcx>, - lowered_constraints: LoweredConstraints<'tcx>, - universal_region_relations: Frozen>, - location_map: Rc, - ) -> Self { - let universal_regions = &universal_region_relations.universal_regions; - - let LoweredConstraints { - constraint_sccs, - definitions, - outlives_constraints, - scc_annotations, - type_tests, - liveness_constraints, - universe_causes, - placeholder_indices, - } = lowered_constraints; - - debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); - debug!("outlives constraints: {:#?}", outlives_constraints); - debug!("placeholder_indices: {:#?}", placeholder_indices); - debug!("type tests: {:#?}", type_tests); - - let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); - - if cfg!(debug_assertions) { - sccs_info(infcx, &constraint_sccs); - } - - let mut scc_values = - RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - - // Initializes the region variables with their initial live points. - for (region, definition) in definitions.iter_enumerated() { - let scc = constraint_sccs.scc(region); - - // For each universally quantified region (lifetime parameter). The - // first N variables always correspond to the regions appearing in the - // function signature (both named and anonymous) and in where-clauses. - match definition.origin { - // For each free, universally quantified region X: - NllRegionVariableOrigin::FreeRegion => { - // Add `end(X)` into the set for X. - scc_values.add_free_region(scc, region); - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - scc_values.add_placeholder(scc, placeholder); - } - - NllRegionVariableOrigin::Existential { .. } => { - // For existential, regions, nothing to do. - } - } - - // Initially copy the liveness constraints of any region that - // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. - // - // These values will later be propagated during [`Self::propagate_constraints()`]. - // The values include any live-at-all-points constraints added previously in `liveness::generate`. - if let Some(liveness) = liveness_constraints.point_liveness(region) { - scc_values.merge_liveness(scc, liveness) - } - } - - Self { - data: RegionInferenceContextInner { - definitions, - liveness_constraints, - constraints: outlives_constraints, - constraint_graph, - constraint_sccs, - scc_annotations, - universe_causes, - universal_region_relations, - scc_values, - }, - type_tests, - } - } - - /// Performs region inference and report errors if we see any - /// unsatisfiable constraints. If this is a closure, returns the - /// region requirements to propagate to our creator, if any. - #[instrument(skip(self, infcx, body, polonius_output), level = "debug")] - pub(crate) fn solve( - mut self, - infcx: &InferCtxt<'tcx>, - body: &Body<'tcx>, - polonius_output: Option>, - ) -> (RegionInferenceContext<'tcx>, Option>, RegionErrors<'tcx>) - { - let mir_def_id = body.source.def_id(); - self.propagate_constraints(); - - let mut errors_buffer = RegionErrors::new(infcx.tcx); - - // If this is a nested body, we propagate unsatisfied - // outlives constraints to the parent body instead of - // eagerly erroing. - let mut propagated_outlives_requirements = - infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); - - self.check_type_tests(infcx, propagated_outlives_requirements.as_mut(), &mut errors_buffer); - - debug!(?errors_buffer); - debug!(?propagated_outlives_requirements); - - // In Polonius mode, the errors about missing universal region relations are in the output - // and need to be emitted or propagated. Otherwise, we need to check whether the - // constraints were too strong, and if so, emit or propagate those errors. - if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() { - self.check_polonius_subset_errors( - propagated_outlives_requirements.as_mut(), - &mut errors_buffer, - polonius_output - .as_ref() - .expect("Polonius output is unavailable despite `-Z polonius`"), - ); - } else { - self.check_universal_regions( - propagated_outlives_requirements.as_mut(), - &mut errors_buffer, - ); - } - - debug!(?errors_buffer); - - let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default(); - if propagated_outlives_requirements.is_empty() { - (RegionInferenceContext { data: Frozen::freeze(self.data) }, None, errors_buffer) - } else { - let num_external_vids = self.universal_regions().num_global_and_external_regions(); - ( - RegionInferenceContext { data: Frozen::freeze(self.data) }, - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements: propagated_outlives_requirements, - }), - errors_buffer, - ) - } - } - /// Propagate the region constraints: this will grow the values /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be @@ -874,9 +875,9 @@ impl<'tcx> UnsolvedRegionInferenceContext<'tcx> { // dependency order. I.e. a chain A: B: C will visit C, B, A. for scc_a in self.constraint_sccs.all_sccs() { // Walk each SCC `B` such that `A: B`... - for &scc_b in self.data.constraint_sccs.successors(scc_a) { + for &scc_b in self.inner.constraint_sccs.successors(scc_a) { debug!(?scc_b); - self.data.scc_values.add_region(scc_a, scc_b); + self.inner.scc_values.add_region(scc_a, scc_b); } } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 800f72275b052..709eddf6e985f 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -191,16 +191,6 @@ impl LivenessValues { ) } - #[inline] - pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { - self.location_map.point_from_location(location) - } - - #[inline] - pub(crate) fn location_from_point(&self, point: PointIndex) -> Location { - self.location_map.to_location(point) - } - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active /// loans dataflow computations. pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { @@ -214,6 +204,11 @@ impl LivenessValues { .expect("Accessing live loans requires `-Zpolonius=next`") .contains(point, loan_idx) } + + #[inline] + pub(crate) fn location_map(&self) -> &DenseLocationMap { + &self.location_map + } } /// Maps from `ty::PlaceholderRegion` values that are used in the rest of diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 905547c7dce51..735fd68a2b1d2 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -69,24 +69,42 @@ pub(super) fn generate<'tcx>( // for now. // FIXME: this NLL optimization idea, to reduce work to relevant locals only, still makes sense // for polonius, and should be investigated to improve liveness performance. - let deferred_locals = 'deferred: { + let deferred_locals = if typeck.polonius_context.is_none() || typeck.borrow_set.len() == 0 { // If we aren't going to be using the additional liveness information, // don't even bother computing the larger relevant set. // Similarly, since this liveness information is ultimately used for *loan* // liveness, we don't need to compute it when there are no loans. - if typeck.polonius_context.is_none() || typeck.borrow_set.len() == 0 { - break 'deferred FxIndexSet::default(); - } - - let free_regions = typeck.universal_regions.universal_regions_iter().collect(); - let (polonius_relevant, _) = - compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); + FxIndexSet::default() + } else { + // As described above, we can defer computing liveness for the regions that are + // NLL-boring-and-polonius-relevant. Let's find these. + // + // Note that this is basically a simplified version of `compute_relevant_live_locals` + // avoiding the allocations that are unnecessary in our more limited use-case. + let tcx = typeck.tcx(); + let universal_regions = typeck.universal_regions; + let boring_nll_locals: FxHashSet<_> = boring_locals.iter().copied().collect(); + let deferred_polonius_locals = typeck + .body + .local_decls + .iter_enumerated() + .filter_map(|(local, decl)| { + // The polonius-relevant locals are the ones whose types do not only contain + // universal regions. + if boring_nll_locals.contains(&local) + && !tcx.all_free_regions_meet(&decl.ty, |r| { + universal_regions.is_universal_region(r.as_var()) + }) + { + Some(local) + } else { + None + } + }) + .collect(); - let boring: FxHashSet<_> = boring_locals.iter().copied().collect(); - let deferred = - polonius_relevant.into_iter().filter(|local| boring.contains(local)).collect(); - typeck.polonius_context.as_mut().unwrap().boring_nll_locals = boring; - deferred + typeck.polonius_context.as_mut().unwrap().boring_nll_locals = boring_nll_locals; + deferred_polonius_locals }; trace::trace( diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 21b2e0f304b05..05bd139934e4d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -18,7 +18,7 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; -use crate::polonius::{DeferredLocals, LiveRegionVariances, record_live_region_variance}; +use crate::polonius::{DeferredRegionLiveness, LiveRegionVariances, record_live_region_variance}; use crate::region_infer::values::LivenessValues; use crate::type_check::liveness::local_use_map::LocalUseMap; use crate::type_check::liveness::make_all_regions_live; @@ -46,13 +46,13 @@ pub(super) fn trace<'tcx>( move_data: &MoveData<'tcx>, relevant_live_locals: &[Local], boring_locals: &[Local], - deferred: &FxIndexSet, + deferred_locals: &FxIndexSet, ) { let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace"); // The use map must also cover the deferred locals: their liveness is computed later, from // this same map, when the loan liveness traversal first reaches one of their regions. - let use_map_locals = relevant_live_locals.iter().chain(deferred).copied(); + let use_map_locals = relevant_live_locals.iter().chain(deferred_locals).copied(); let local_use_map = LocalUseMap::build(use_map_locals, location_map, typeck.body); let comp = LivenessComputation::new( typeck.infcx, @@ -64,16 +64,15 @@ pub(super) fn trace<'tcx>( let mut results = LivenessResults::new(typeck, comp); - let mut deferred_locals = DeferredLocals::default(); - - results.record_legacy_polonius_drop_facts(relevant_live_locals, &deferred); + results.record_legacy_polonius_drop_facts(relevant_live_locals, &deferred_locals); results.compute_for_all_locals(relevant_live_locals); - results.dropck_boring_locals(boring_locals, &deferred, &mut deferred_locals); + let mut deferred_liveness = DeferredRegionLiveness::default(); + results.dropck_boring_locals(boring_locals, &deferred_locals, &mut deferred_liveness); if let Some(polonius_context) = &mut typeck.polonius_context { - polonius_context.deferred_locals_for_liveness = deferred_locals; + polonius_context.deferred_liveness = deferred_liveness; polonius_context.local_use_map = Some(local_use_map); } } @@ -197,19 +196,20 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { fn dropck_boring_locals( &mut self, boring_locals: &[Local], - deferred: &FxIndexSet, - deferred_locals: &mut DeferredLocals<'tcx>, + deferred_locals: &FxIndexSet, + deferred_liveness: &mut DeferredRegionLiveness<'tcx>, ) { for &local in boring_locals { - self.dropck_boring_local(local, deferred, deferred_locals); + let is_local_deferred = deferred_locals.contains(&local); + self.dropck_boring_local(local, is_local_deferred, deferred_liveness); } } fn dropck_boring_local( &mut self, local: Local, - deferred: &FxIndexSet, - deferred_locals: &mut DeferredLocals<'tcx>, + is_local_deferred: bool, + deferred_liveness: &mut DeferredRegionLiveness<'tcx>, ) { let typeck = &mut *self.typeck; let local_ty = self.comp.body.local_decls[local].ty; @@ -219,8 +219,8 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { // in turn would have skipped calculating dropck *at all* for locals without drop-liveness. // Calculating drop-liveness is expensive, but we can skip it when we know that there // are *no* drops (which is relatively cheap). - if deferred.contains(&local) && self.comp.local_use_map.drops(local).next().is_none() { - deferred_locals.defer_local( + if is_local_deferred && self.comp.local_use_map.drops(local).next().is_none() { + deferred_liveness.defer_local( typeck.infcx.tcx, typeck.universal_regions, local, @@ -238,7 +238,7 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { let drop_data = dropck_local(&typeck.infcx, &mut self.drop_data, local_ty, local_span); // We are done with *truly* boring locals. - if !deferred.contains(&local) { + if !is_local_deferred { return; } @@ -264,7 +264,7 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } // Finally, we mark that this local is deferred, including the drop kinds. - deferred_locals.defer_local( + deferred_liveness.defer_local( typeck.infcx.tcx, typeck.universal_regions, local, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 42dbc801bfb5a..010462e5551e0 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -411,7 +411,7 @@ fn not_testable_error(cx: &ExtCtxt<'_>, is_bench: bool, attr_sp: Span, item: Opt let level = match item.map(|i| &i.kind) { // These were a warning before #92959 and need to continue being that to avoid breaking // stable user code (#94508). - Some(ast::ItemKind::MacCall(_)) => Level::Warning, + Some(ast::ItemKind::MacCall(_)) => Level::Warning(None), _ => Level::Error, }; let mut err = Diag::new(dcx, level, msg); diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 0628171e488b3..90d08d13a080a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -9,7 +9,9 @@ use rustc_codegen_ssa::target_features; use rustc_data_structures::smallvec::{SmallVec, smallvec}; use rustc_session::config::NATIVE_CPU; use rustc_session::{EarlySession, Session}; -use rustc_target::spec::{Arch, RelocModel, StackProbeType, StackProtector, Target}; +use rustc_target::spec::{ + Arch, MergeFunctions, RelocModel, StackProbeType, StackProtector, Target, +}; fn gcc_features_by_flags(sess: &EarlySession, features: &mut Vec) { target_features::retpoline_features_by_flags(sess, features); @@ -220,6 +222,16 @@ pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> { StackProtector::None => (), } + match sess.merge_functions() { + MergeFunctions::Disabled => { + context.add_command_line_option("-fno-ipa-icf-functions"); + } + // GCC always merges functions with trampolines rather than aliases, so we don't + // need to differentiate MergeFunctions::Trampolines and MergeFunctions::Aliases + // as trampolines are allowed either way. + MergeFunctions::Trampolines | MergeFunctions::Aliases => {} + } + match sess.target.stack_probes { StackProbeType::None => (), StackProbeType::Inline | StackProbeType::InlineOrCall { .. } => { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index dd2dbdff6b82c..deaa78fcda003 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -418,7 +418,7 @@ fn report_inline_asm( }; let level = match level { llvm::DiagnosticLevel::Error => Level::Error, - llvm::DiagnosticLevel::Warning => Level::Warning, + llvm::DiagnosticLevel::Warning => Level::Warning(None), llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; let msg = msg.trim_prefix("error: ").to_string(); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index b26798219a5fb..4db534b978330 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -120,7 +120,7 @@ unsafe fn configure_llvm(sess: &EarlySession) { add("-generate-arange-section", false); } - match sess.opts.unstable_opts.merge_functions.unwrap_or(sess.target.merge_functions) { + match sess.merge_functions() { MergeFunctions::Disabled | MergeFunctions::Trampolines => {} MergeFunctions::Aliases => { add("-mergefunc-use-aliases", false); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 147e4c43c7103..9f239bbb9ac98 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -244,12 +244,7 @@ impl ModuleConfig { // backends (again, NVPTX). Therefore, allow targets to opt out of // the MergeFunctions pass, but otherwise keep the pass enabled (at // O2 and O3) since it can be useful for reducing code size. - merge_functions: match sess - .opts - .unstable_opts - .merge_functions - .unwrap_or(sess.target.merge_functions) - { + merge_functions: match sess.merge_functions() { MergeFunctions::Disabled => false, MergeFunctions::Trampolines | MergeFunctions::Aliases => { use config::OptLevel::*; @@ -2060,7 +2055,7 @@ impl SharedEmitterMain { sess.dcx().abort_if_errors(); } Ok(SharedEmitterMessage::InlineAsmError(inner)) => { - assert_matches!(inner.level, Level::Error | Level::Warning | Level::Note); + assert_matches!(inner.level, Level::Error | Level::Warning(None) | Level::Note); let mut err = Diag::new(sess.dcx(), inner.level, inner.msg); if !inner.span.is_dummy() { err.span(inner.span.span()); diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index d7e38148806f7..15a435357544d 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -124,12 +124,10 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<' annotate_snippets::Level::ERROR.with_name("error: internal compiler error") } Level::Fatal | Level::Error => annotate_snippets::level::ERROR, - Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING, + Level::Warning(_) => annotate_snippets::Level::WARNING, Level::Note => annotate_snippets::Level::NOTE, Level::Help => annotate_snippets::Level::HELP, Level::FailureNote => annotate_snippets::Level::NOTE.no_name(), - Level::Allow => panic!("Should not call with Allow"), - Level::Expect => panic!("Should not call with Expect"), } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9688f99abfebb..efd5ead56b36f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -10,7 +10,7 @@ use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::stable_hash::StableHasher; use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg}; use rustc_hashes::Hash128; -use rustc_lint_defs::{Applicability, LintExpectationId}; +use rustc_lint_defs::Applicability; use rustc_macros::{Decodable, Encodable}; use rustc_span::{Span, Spanned, Symbol}; use tracing::debug; @@ -190,7 +190,6 @@ pub struct DiagInner { pub messages: Vec<(DiagMessage, Style)>, pub code: Option, - pub lint_id: Option, pub span: MultiSpan, pub children: Vec, pub suggestions: Suggestions, @@ -212,7 +211,6 @@ impl DiagInner { pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self { DiagInner { level, - lint_id: None, messages, code: None, span: MultiSpan::new(), @@ -234,13 +232,7 @@ impl DiagInner { match self.level { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true, - Level::ForceWarning - | Level::Warning - | Level::Note - | Level::Help - | Level::FailureNote - | Level::Allow - | Level::Expect => false, + Level::Warning(_) | Level::Note | Level::Help | Level::FailureNote => false, } } @@ -254,16 +246,6 @@ impl DiagInner { self.is_lint.as_ref().and_then(|is| is.rust_version) } - pub(crate) fn is_force_warn(&self) -> bool { - match self.level { - Level::ForceWarning => { - assert!(self.is_lint.is_some()); - true - } - _ => false, - } - } - pub(crate) fn sub( &mut self, level: Sublevel, @@ -307,7 +289,6 @@ impl DiagInner { level, messages, code, - lint_id: _, // ignore span, children, suggestions, @@ -317,8 +298,16 @@ impl DiagInner { emitted_at: _, // ignore } = self; - let hashed_parts = - (level, messages, code, span, children, suggestions, args.as_slice(), is_lint); + let hashed_parts = ( + std::mem::discriminant(level), // ignore the field within `Warning` + messages, + code, + span, + children, + suggestions, + args.as_slice(), + is_lint, + ); let mut hasher = StableHasher::new(); hashed_parts.hash(&mut hasher); @@ -1099,16 +1088,6 @@ impl<'a> Diag<'a> { self } } - with_fn! { with_lint_id, - /// Add an argument. - pub fn lint_id( - &mut self, - id: LintExpectationId, - ) -> &mut Self { - self.lint_id = Some(id); - self - } } - with_fn! { with_primary_message, /// Add a primary message. pub fn primary_message(&mut self, msg: impl Into) -> &mut Self { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 1f5a8c2fe94a6..59d79754314c1 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -136,20 +136,8 @@ impl Emitter for JsonEmitter { fn emit_future_breakage_report(&mut self, diags: Vec) { let data: Vec> = diags .into_iter() - .map(|mut diag| { - // Allowed or expected lints don't normally (by definition) emit a lint - // but future incompat lints are special and are emitted anyway. - // - // So to avoid ICEs and confused users we "upgrade" the lint level for - // those `FutureBreakageItem` to warn. - if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) { - diag.level = crate::Level::Warning; - } - FutureBreakageItem { - diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, - )), - } + .map(|diag| FutureBreakageItem { + diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(diag, self)), }) .collect(); let report = FutureIncompatReport { future_incompat_report: data }; diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 97b8557b95ef6..4cc5d5c230d15 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -350,9 +350,8 @@ struct DiagCtxtInner { future_breakage_diagnostics: Vec, - /// expected diagnostic will have the level `Expect` which additionally - /// carries the [`LintExpectationId`] of the expectation that can be - /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s + /// Any `expect` lint will carry the [`LintExpectationId`] of the expectation that can be + /// marked as fulfilled. This field is a collection of all [`LintExpectationId`]s /// that have been marked as fulfilled this way. /// /// Emitting expectations after having stolen this field can happen. In particular, an @@ -612,7 +611,7 @@ impl<'a> DiagCtxtHandle<'a> { DelayedBug => { return self.dcx.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors); } - ForceWarning | Warning | Note | Help | FailureNote | Allow | Expect => None, + Warning(_) | Note | Help | FailureNote => None, }; // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic @@ -787,12 +786,12 @@ impl<'a> DiagCtxtHandle<'a> { match (errors.len(), warnings.len()) { (0, 0) => return, (0, _) => { - // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a - // configuration like `--cap-lints allow --force-warn bare_trait_objects`. - inner.emit_diagnostic( - DiagInner::new(ForceWarning, DiagMessage::Str(warnings)), - None, + // Force emission so this message always prints. + let diag = DiagInner::new( + Warning(Some(EmissionOverride::Forced { lint_id: None })), + DiagMessage::Str(warnings), ); + inner.emit_diagnostic(diag, None); } (_, 0) => { inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors); @@ -918,6 +917,17 @@ impl<'a> DiagCtxtHandle<'a> { inner.emitter.emit_unused_externs(lint_level, unused_externs) } + /// We need to make sure that submitted expectation ids are correctly fulfilled, suppressed, + /// and stored between compilation sessions. To avoid doing these steps manually, we create a + /// dummy diagnostic and emit it as usual, which will be suppressed and stored like a normal + /// expected lint diagnostic. + #[track_caller] + pub fn fulfill_expectation(self, expectation: impl Into) { + let emission_override = Some(EmissionOverride::Expected { lint_id: expectation.into() }); + let msg = "this is a dummy diagnostic, to submit and store an expectation"; + Diag::new(self, Warning(emission_override), msg).emit() + } + /// This methods steals all [`LintExpectationId`]s that are stored inside /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled. #[must_use] @@ -1018,7 +1028,6 @@ impl<'a> DiagCtxtHandle<'a> { self.create_fatal(fatal).emit_fatal() } - // FIXME: This method should be removed (every error should have an associated error code). #[track_caller] pub fn struct_err(self, msg: impl Into) -> Diag<'a> { Diag::new(self, Error, msg) @@ -1078,7 +1087,7 @@ impl<'a> DiagCtxtHandle<'a> { #[track_caller] pub fn struct_warn(self, msg: impl Into) -> Diag<'a> { - Diag::new(self, Warning, msg) + Diag::new(self, Warning(None), msg) } #[track_caller] @@ -1102,7 +1111,7 @@ impl<'a> DiagCtxtHandle<'a> { #[track_caller] pub fn create_warn(self, warning: impl Diagnostic<'a>) -> Diag<'a> { - warning.into_diag(self, Warning) + warning.into_diag(self, Warning(None)) } #[track_caller] @@ -1143,16 +1152,6 @@ impl<'a> DiagCtxtHandle<'a> { pub fn emit_note(self, note: impl Diagnostic<'a>) { self.create_note(note).emit() } - - #[track_caller] - pub fn struct_allow(self, msg: impl Into) -> Diag<'a> { - Diag::new(self, Allow, msg) - } - - #[track_caller] - pub fn struct_expect(self, msg: impl Into, id: LintExpectationId) -> Diag<'a> { - Diag::new(self, Expect, msg).with_lint_id(id) - } } impl DiagCtxtInner { @@ -1186,13 +1185,13 @@ impl DiagCtxtInner { let has_errors = !self.err_guars.is_empty(); for (_, stashed_diagnostics) in mem::take(&mut self.stashed_diagnostics).into_iter() { for (_, (diag, _guar, _thread)) in stashed_diagnostics { - if !diag.is_error() { - // Unless they're forced, don't flush stashed warnings when - // there are errors, to avoid causing warning overload. The - // stash would've been stolen already if it were important. - if !diag.is_force_warn() && has_errors { - continue; - } + // When there are errors, skip flushing of stashed unforced warnings, to avoid + // warning overload. (They would have been stolen already if they were important.) + if has_errors + && let Warning(emission_override) = diag.level + && !matches!(emission_override, Some(EmissionOverride::Forced { .. })) + { + continue; } guar = guar.or(self.emit_diagnostic(diag, None)); } @@ -1207,10 +1206,10 @@ impl DiagCtxtInner { taint: Option<&Cell>>, ) -> Option { if diagnostic.has_future_breakage() { - // Future breakages aren't emitted if they're `Level::Allow` or - // `Level::Expect`, but they still need to be constructed and - // stashed below, so they'll trigger the must_produce_diag check. - assert_matches!(diagnostic.level, Error | ForceWarning | Warning | Allow | Expect); + // About the `allow`/`expect` lint sub-cases of `Warning`: future breakages aren't + // emitted for them, but they still need to be handled below so they'll trigger the + // `must_produce_diag` check. + assert_matches!(diagnostic.level, Error | Warning(_)); self.future_breakage_diagnostics.push(diagnostic.clone()); } @@ -1258,36 +1257,37 @@ impl DiagCtxtInner { }; } } - ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect` - Warning => { - if !self.flags.can_emit_warnings { - // We are not emitting warnings. - if diagnostic.has_future_breakage() { - // The side-effect is at the top of this method. + Warning(emission_override) => { + match emission_override { + None => { + if !self.flags.can_emit_warnings { + // We are not emitting warnings. + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + } + return None; + } + } + Some(EmissionOverride::Forced { lint_id: None }) => {} + Some(EmissionOverride::Forced { lint_id: Some(lint_id) }) => { + self.fulfilled_expectations.insert(lint_id); + } + Some(EmissionOverride::Allowed) => { + assert!(diagnostic.has_future_breakage()); + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; + } + Some(EmissionOverride::Expected { lint_id }) => { + self.fulfilled_expectations.insert(lint_id); TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; } - return None; } } Note | Help | FailureNote => {} - Allow => { - // Nothing emitted for allowed lints. - if diagnostic.has_future_breakage() { - // The side-effect is at the top of this method. - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); - self.suppressed_expected_diag = true; - } - return None; - } - Expect | ForceWarning => { - self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap()); - if let Expect = diagnostic.level { - // Nothing emitted here for expected lints. - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); - self.suppressed_expected_diag = true; - return None; - } - } } if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version()) @@ -1313,7 +1313,7 @@ impl DiagCtxtInner { ) && mem::replace(&mut self.emitted_recursion_depth_exceeding_limit, true) }); - // Only emit the diagnostic if we've been asked to deduplicate or + // Only emit the diagnostic if deduplication is disabled or we // haven't already emitted an equivalent diagnostic. if !silence_recursion_depth_exceeded_limit && !(self.flags.deduplicate_diagnostics && already_emitted) @@ -1343,7 +1343,7 @@ impl DiagCtxtInner { if is_error { self.deduplicated_err_count += 1; - } else if matches!(diagnostic.level, ForceWarning | Warning) { + } else if matches!(diagnostic.level, Warning(_)) { self.deduplicated_warn_count += 1; } self.has_printed = true; @@ -1544,19 +1544,45 @@ impl DelayedDiagInner { } } +/// Special emission behaviours on warning diagnostics. Combines with `DiagInner::is_lint` in the +/// following ways. +/// +/// | case | is_lint | `Option` field in `Warning` +/// | ---- | ------- | --------------------------------------------- +/// | ordinary non-lint diagnostic | None | None +/// | warning-count summary | None | Some(Forced { lint_id: None }) +/// | N/A | None | Some(Forced { lint_id: Some(lint_id) }) +/// | dummy expectation fulfillment | None | Some(Expected { lint_id }) +/// | N/A | None | Some(Allowed) +/// | ordinary `warn` lint | Some | None +/// | `warn` lint at `force-warn` | Some | Some(Forced { lint_id: None }) +/// | `expect` lint at `force-warn` | Some | Some(Forced { lint_id: Some(lint_id) }) +/// | `expect` lint | Some | Some(Expected { lint_id }) +/// | `allow` lint with future breakage | Some | Some(Allowed) +/// +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum EmissionOverride { + /// Cases where emission is forced. + Forced { lint_id: Option }, + + /// Used for `allow` lints with future breakage. (Allow lints without future breakage are + /// ignored and therefore never need an `EmissionOverride` value.) + Allowed, + + /// Used for `expect` lints. + Expected { lint_id: LintExpectationId }, +} + /// | Level | is_error | usable emit fns | Top-level | Used in lints? /// | ----- | -------- | --------------- | --------- | -------------- /// | Bug | yes | emit, emit_bug | yes | - /// | Fatal | yes | emit, emit_fatal | yes | - /// | Error | yes | emit, emit_err | yes | yes /// | DelayedBug | yes | emit, emit_err | yes | - -/// | ForceWarning | - | emit | yes | lint-only /// | Warning | - | emit | yes | yes /// | Note | - | emit | rare | - /// | Help | - | emit | don't use | - /// | FailureNote | - | emit | rare | - -/// | Allow | - | emit | yes | lint-only -/// | Expect | - | emit | yes | lint-only /// #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] pub enum Level { @@ -1577,16 +1603,9 @@ pub enum Level { /// that should only be reached when compiling erroneous code. DelayedBug, - /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation - /// from finishing. - /// - /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this - /// should be `None`. - ForceWarning, - /// A warning about the code being compiled. Does not prevent compilation from finishing. - /// Will be skipped if `can_emit_warnings` is false. - Warning, + /// Might not be emitted, depending on the value of `EmissionOverride` and `can_emit_warnings`. + Warning(Option), /// A rarely-used level for output that isn't an error or a warning. Note, @@ -1601,12 +1620,6 @@ pub enum Level { /// Similar to `Note`, but even rarer. Lacks the a trailing blank line that all other /// diagnostics have. Also, when printed for human consumption it doesn't have a `note:` label. FailureNote, - - /// Only used for lints. - Allow, - - /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints. - Expect, } impl fmt::Display for Level { @@ -1620,16 +1633,15 @@ impl Level { match self { Bug | DelayedBug => "error: internal compiler error", Fatal | Error => "error", - ForceWarning | Warning => "warning", + Warning(_) => "warning", Note => "note", Help => "help", FailureNote => "failure-note", - Allow | Expect => unreachable!(), } } - pub fn is_failure_note(&self) -> bool { - matches!(*self, FailureNote) + pub fn is_failure_note(self) -> bool { + matches!(self, FailureNote) } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1ecf808f78839..ce6a0d7548be1 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -407,7 +407,7 @@ impl ToInternal for Level { fn to_internal(self) -> rustc_errors::Level { match self { Level::Error => rustc_errors::Level::Error, - Level::Warning => rustc_errors::Level::Warning, + Level::Warning => rustc_errors::Level::Warning(None), Level::Note => rustc_errors::Level::Note, Level::Help => rustc_errors::Level::Help, _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), diff --git a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs index 6717d11d0f3de..f2b166e0b2a01 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs @@ -3,7 +3,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, Diagnostic, MultiSpan, pluralize}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItem, AssocItems, TyCtxt}; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{DefId, LocalDefId}; use tracing::debug; /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. @@ -30,6 +30,9 @@ pub(crate) struct WrongNumberOfGenericArgs<'a, 'tcx> { /// DefId of the generic type pub(crate) def_id: DefId, + + /// DefId of the type that contains the generic + pub(crate) cx_def_id: LocalDefId, } // Provides information about the kind of arguments that were provided for @@ -94,6 +97,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset: usize, gen_args: &'a hir::GenericArgs<'a>, def_id: DefId, + cx_def_id: LocalDefId, ) -> Self { let angle_brackets = if gen_args.span_ext().is_none() { if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied } @@ -110,6 +114,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset, gen_args, def_id, + cx_def_id, } } @@ -1163,10 +1168,20 @@ impl<'a> Diagnostic<'a> for WrongNumberOfGenericArgs<'_, '_> { err.code(E0107); err.span(self.path_segment.ident.span); - self.notify(&mut err); - self.suggest(&mut err); - self.show_definition(&mut err); - self.note_synth_provided(&mut err); + let cx_span = self.tcx.def_span(self.cx_def_id); + if cx_span.in_derive_expansion() { + // Very likely this is a botched `derive` which passes the iten name straight + // through, but doesn't support type parameters. + err.span_label( + cx_span.ctxt().outer_expn_data().call_site, + "this derive macro might not support items with generic parameters", + ); + } else { + self.notify(&mut err); + self.suggest(&mut err); + self.show_definition(&mut err); + self.note_synth_provided(&mut err); + } err } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 5f14b32dcabb1..9eda20fa0d9c4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -503,6 +503,7 @@ pub(crate) fn check_generic_arg_count( has_self as usize, gen_args, def_id, + cx.item_def_id(), )); Err(reported) @@ -617,6 +618,7 @@ pub(crate) fn check_generic_arg_count( params_offset, gen_args, def_id, + cx.item_def_id(), )) .emit_err_unless_delay(all_params_are_binded || has_invalid_bound) }); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 68bb5e0a592bf..16c4549bfbc6e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1692,12 +1692,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!(": {ident}.is_some()"), - None => ".is_some()".to_string(), + Some(ident) => vec![(expr.span.shrink_to_hi(), format!(": {ident}.is_some()"))], + None if self.precedence(expr) < ExprPrecedence::Unambiguous => { + // Apply the method to the whole expression, e.g. `(*value).is_some()`. + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), ").is_some()".to_string()), + ] + } + None => vec![(expr.span.shrink_to_hi(), ".is_some()".to_string())], }; - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), + diag.multipart_suggestion( "use `Option::is_some` to test if the `Option` has a value", suggestion, Applicability::MachineApplicable, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a7ded8a82b5dc..15c60c920ce37 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -547,17 +547,7 @@ pub trait LintContext { /// retrieved from the current lint pass. Buffered or manually created ids can /// cause ICEs. fn fulfill_expectation(&self, expectation: Self::LintExpectationId) { - // We need to make sure that submitted expectation ids are correctly fulfilled suppressed - // and stored between compilation sessions. To not manually do these steps, we simply create - // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a - // normal expected lint diagnostic. - self.sess() - .dcx() - .struct_expect( - "this is a dummy diagnostic, to submit and store an expectation", - expectation.into(), - ) - .emit(); + self.sess().dcx().fulfill_expectation(expectation); } } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 820375ada47a5..3655d4594aa38 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp::min; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, DiagLocation, Diagnostic, MultiSpan}; +use rustc_errors::{Diag, DiagLocation, Diagnostic, EmissionOverride, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::{ EditionFcw, FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, @@ -416,7 +416,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( let err_level = match level { Level::Allow => { if has_future_breakage { - rustc_errors::Level::Allow + rustc_errors::Level::Warning(Some(EmissionOverride::Allowed)) } else { return; } @@ -426,13 +426,17 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( // we can't return early like in the case for `Level::Allow` because we still // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. // - // We can also not mark the lint expectation as fulfilled here right away, as it - // can still be cancelled in the decorate function. All of this means that we simply - // create a `Diag` and continue as we would for warnings. - rustc_errors::Level::Expect + // We also cannot mark the lint expectation as fulfilled here right away, because + // it can still be cancelled in the decorate function. So we create a `Diag` and + // continue as we would for `Level::Warn`. + rustc_errors::Level::Warning(Some(EmissionOverride::Expected { + lint_id: lint_id.unwrap(), + })) } - Level::ForceWarn => rustc_errors::Level::ForceWarning, - Level::Warn => rustc_errors::Level::Warning, + Level::ForceWarn => { + rustc_errors::Level::Warning(Some(EmissionOverride::Forced { lint_id })) + } + Level::Warn => rustc_errors::Level::Warning(None), Level::Deny | Level::Forbid => rustc_errors::Level::Error, }; @@ -477,12 +481,13 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( // emitted or we'll get a `must_produce_diag` ICE. // // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: - // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` - // or `Error`, then the diagnostic will be emitted regardless of CLI options. + // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `Error`, then the + // diagnostic will be emitted regardless of CLI options. // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic // will be emitted if `can_emit_warnings` is true. - let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); + let skip = + err_level == rustc_errors::Level::Warning(None) && !sess.dcx().can_emit_warnings(); let mut err: Diag<'_> = if !skip { decorate(sess.dcx(), err_level) @@ -495,7 +500,8 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( if let Some(span) = span && err.span.primary_span().is_none() { - // We can't use `err.span()` because it overwrites the labels, so we need to do it manually. + // We can't use `err.span()` because it overwrites the labels, so we need to do it + // manually. for primary in span.primary_spans() { err.span.push_primary_span(*primary); } @@ -503,9 +509,6 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a> + 'a>( err.span.push_span_diag(*label_span, label.clone()); } } - if let Some(lint_id) = lint_id { - err.lint_id(lint_id); - } if disable_suggestions { // Any suggestions made here are likely to be incorrect, so anything we diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 8a4e7740650fc..ca47b27d142a2 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -7,7 +7,7 @@ use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{InliningThreshold, OptLevel}; -use rustc_span::bug; +use rustc_span::{bug, sym}; use crate::{inline, pass_manager as pm}; @@ -64,13 +64,19 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return true; } - let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); - for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) - { - // FIXME(f16_f128): in order to avoid crashes building `core`, always inline to skip - // codegen if the function is not used. - if ty == &tcx.types.f16 || ty == &tcx.types.f128 { - return true; + let reliable_f16 = tcx.sess.config.contains(&(sym::target_has_reliable_f16, None)); + let reliable_f128 = tcx.sess.config.contains(&(sym::target_has_reliable_f128, None)); + if !reliable_f16 || !reliable_f128 { + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + for ty in + sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) + { + // FIXME(f16,f128): in order to avoid crashes building `core`, inline on targets that + // have issues to skip codegen if the function is not used. + if (!reliable_f16 && ty == &tcx.types.f16) || (!reliable_f128 && ty == &tcx.types.f128) + { + return true; + } } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bd1bdf7cc0646..ffa3a7443ecc4 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -1172,13 +1172,7 @@ impl<'tcx> DeadVisitor<'tcx> { && let (_, Some(expectation)) = self.def_lint_level_plus(node) { // Same mechanism as LintContext::fulfill_expectation. - self.tcx - .dcx() - .struct_expect( - "this is a dummy diagnostic, to submit and store an expectation", - expectation.into(), - ) - .emit(); + self.tcx.dcx().fulfill_expectation(expectation); } }; diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index d85024603ad33..ae4f7cabbe87f 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -840,7 +840,7 @@ pub(crate) struct PrivateExternCrateReexport { style = "verbose", applicability = "maybe-incorrect" )] - pub sugg: Span, + pub sugg: Option, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 938a6cb29032f..5cd45c5de5230 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1673,8 +1673,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(extern_crate_id) = pub_use_of_private_extern_crate_hack(import.summary(), decl) { let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; - let sugg = self.tcx.source_span(extern_crate_id).shrink_to_lo(); - let diagnostic = crate::diagnostics::PrivateExternCrateReexport { ident, sugg }; + let sugg = self.tcx.source_span(extern_crate_id); + let diagnostic = crate::diagnostics::PrivateExternCrateReexport { + ident, + sugg: sugg.can_be_used_for_suggestions().then(|| sugg.shrink_to_lo()), + }; return Some(BufferedEarlyLint { lint_id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), node_id: id, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 13fc598d86bfa..61b7d792d9a08 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2643,6 +2643,7 @@ options! { maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED], "save as much information as possible about the correspondence between MIR and HIR \ as source scopes (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::merge_functions` instead of this field")] merge_functions: Option = (None, parse_merge_functions, [TRACKED], "control the operation of the MergeFunctions LLVM pass, taking \ the same values as the target option of the same name"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3b60a13b8e6f2..060e614e82180 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -28,9 +28,9 @@ use rustc_span::{RealFileName, Span, Symbol}; use rustc_structures::{CrateType, Limit}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CfgAbi, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, - SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, - Target, TargetTuple, TlsModel, apple, + Arch, CfgAbi, CodeModel, DebuginfoKind, MergeFunctions, Os, PanicStrategy, RelocModel, + RelroLevel, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, + SymbolVisibility, Target, TargetTuple, TlsModel, apple, }; use crate::code_stats::CodeStats; @@ -423,6 +423,10 @@ impl EarlySession { .sanitizer .combine_with_defaults(self.target.options.default_sanitizers) } + + pub fn merge_functions(&self) -> MergeFunctions { + self.opts.unstable_opts.merge_functions.unwrap_or(self.target.merge_functions) + } } /// Some info about the backend, returned by `CodegenBackend::init` and put into the `Session`. diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 42d817c7daa31..2d7cd049eb61c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -927,8 +927,15 @@ impl ToJson for SmallDataThresholdSupport { crate::target_spec_enum! { pub enum MergeFunctions { + /// Disable function merging entirely. Disabled = "disabled", + /// Allow function merging via trampolines (i.e., functions can be implemented + /// in terms of a jump to an unrelated function with identical behavior), but + /// not via object-format-level global aliases. Trampolines = "trampolines", + /// (Default) allow function merging via either trampolines or (object format + /// level) global aliases. LLVM will normally use global aliases for merging, but + /// trampolines are also permissible. Aliases = "aliases", } diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index ccac980706571..a900ed91d720c 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -49,8 +49,27 @@ unsafe extern "Rust" { /// to the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// -/// Note: while this type is unstable, the functionality it provides can be -/// accessed through the [free functions in `alloc`](self#functions). +/// Similar to [`alloc`], [`dealloc`], and the other global allocation functions, +/// the way in which calls are forwarded to the allocator registered with +/// `#[global_allocator]` is unspecified. See their safety docs for more information. +/// +/// This allocator must be treated like an opaque allocator that only guarantees +/// the contract described in the docs of [`Allocator`], as well as the following +/// things: +/// * All instances of `Global` are [*equivalent*]. +/// * Allocations from `Global` are only invalidated by calls to de-/reallocating functions. +/// If no such call is made, then the allocation will live for the rest of the program. +/// * The global allocation functions are equivalent to the methods on `Global`, except +/// that they disallow zero-sized allocations, and implicitly ignore any returned +/// excess size. +/// +/// Note that the current implementation of `Global` does not take advantage of +/// some features of [`Allocator`], such as zero-sized allocations (which currently always +/// return a dangling pointer) and overallocating. +/// +/// This may change in the future. You must not rely on it for correctness! +/// +/// [*equivalent*]: Allocator#equivalent-allocators #[stable(feature = "allocator_api", since = "CURRENT_RUSTC_VERSION")] #[derive(Copy, Debug)] #[derive_const(Clone, Default)] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9d871d8c5745d..e149e2b4a7039 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1701,7 +1701,6 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /// # Examples /// /// ``` -/// #![feature(mem_conjure_zst)] /// use std::mem::conjure_zst; /// /// assert_eq!(unsafe { conjure_zst::<()>() }, ()); @@ -1709,8 +1708,9 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { /// ``` /// /// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited -#[unstable(feature = "mem_conjure_zst", issue = "95383")] -#[rustc_const_unstable(feature = "mem_conjure_zst", issue = "95383")] +#[stable(feature = "mem_conjure_zst", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "mem_conjure_zst", since = "CURRENT_RUSTC_VERSION")] +#[rustc_allow_const_fn_unstable(const_type_name)] // type_name() called only at run time pub const unsafe fn conjure_zst() -> T { const_assert!( T::IS_ZST, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b8ffcd53b25d8..5498982edf34a 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,8 +27,8 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(extend_one)] -#![feature(mem_conjure_zst)] #![feature(f16)] +#![cfg_attr(bootstrap, feature(mem_conjure_zst))] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index cd647eb32f515..3006fddbbe662 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2040,6 +2040,12 @@ test!(BuildStd { default: false }); +test!(AssemblyGcc { + path: "tests/assembly-gcc", + mode: CompiletestMode::Assembly, + suite: "assembly-gcc", + default: true +}); test!(AssemblyLlvm { path: "tests/assembly-llvm", mode: CompiletestMode::Assembly, diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap index 32ed54050dc3d..787686b117cd2 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap @@ -23,6 +23,9 @@ expression: test [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap index d1cdf81a14e08..879114ffc8170 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap @@ -20,6 +20,9 @@ expression: test --skip=coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap index df4b8a2ce1299..a30e3a2407937 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_map.snap @@ -23,6 +23,9 @@ expression: test --skip=coverage-map [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap index 75992df8aa616..12e0a3233f7ea 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage_run.snap @@ -23,6 +23,9 @@ expression: test --skip=coverage-run [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap index a44052fac41f9..3b07718b8aecf 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_coverage.snap @@ -20,6 +20,9 @@ expression: test --skip=tests/coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap index c0105510ae416..6db9443b8543c 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests.snap @@ -20,6 +20,9 @@ expression: test tests [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap index d1a90ca426918..ab4e9e6f7d997 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_tests_skip_coverage.snap @@ -17,6 +17,9 @@ expression: test tests --skip=coverage [Test] test::CodegenUnits targets: [aarch64-unknown-linux-gnu] - Suite(tests/codegen-units) +[Test] test::AssemblyGcc + targets: [aarch64-unknown-linux-gnu] + - Suite(tests/assembly-gcc) [Test] test::AssemblyLlvm targets: [aarch64-unknown-linux-gnu] - Suite(tests/assembly-llvm) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0593f30946320..c221a976b2698 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -886,6 +886,7 @@ impl<'a> Builder<'a> { test::MirOpt, test::CodegenLlvm, test::CodegenUnits, + test::AssemblyGcc, test::AssemblyLlvm, test::Incremental, test::Debuginfo, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index cc6ac39672950..dd1beb65e7fe0 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1943,6 +1943,7 @@ mod snapshot { [test] compiletest-mir-opt 1 [test] compiletest-codegen-llvm 1 [test] compiletest-codegen-units 1 + [test] compiletest-assembly-gcc 1 [test] compiletest-assembly-llvm 1 [test] compiletest-incremental 1 [test] compiletest-debuginfo 1 @@ -2124,6 +2125,7 @@ mod snapshot { [test] compiletest-mir-opt 2 [test] compiletest-codegen-llvm 2 [test] compiletest-codegen-units 2 + [test] compiletest-assembly-gcc 2 [test] compiletest-assembly-llvm 2 [test] compiletest-incremental 2 [test] compiletest-debuginfo 2 diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index b31850759687a..34c7901d077e1 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -7,11 +7,8 @@ use crate::core::session::Session; use crate::utils::tests::TestCtx; #[test] -fn test_ndk_compiler_c() { +fn test_ndk_compiler() { let ndk_path = PathBuf::from("/ndk"); - let target_triple = "arm-unknown-linux-android"; - let expected_triple_translated = "armv7a-unknown-linux-android"; - let expected_compiler = format!("{}21-{}", expected_triple_translated, Language::C.clang()); let host_tag = if cfg!(target_os = "macos") { "darwin-x86_64" } else if cfg!(target_os = "windows") { @@ -19,50 +16,23 @@ fn test_ndk_compiler_c() { } else { "linux-x86_64" }; - let expected_path = ndk_path - .join("toolchains") - .join("llvm") - .join("prebuilt") - .join(host_tag) - .join("bin") - .join(&expected_compiler); - let result = ndk_compiler(Language::C, target_triple, &ndk_path); - assert_eq!(result, expected_path); -} -#[test] -fn test_ndk_compiler_cpp() { - let ndk_path = PathBuf::from("/ndk"); - let target_triple = "arm-unknown-linux-android"; - let expected_triple_translated = "armv7a-unknown-linux-android"; - let expected_compiler = - format!("{}21-{}", expected_triple_translated, Language::CPlusPlus.clang()); - let host_tag = if cfg!(target_os = "macos") { - "darwin-x86_64" - } else if cfg!(target_os = "windows") { - "windows-x86_64" - } else { - "linux-x86_64" - }; - let expected_path = ndk_path - .join("toolchains") - .join("llvm") - .join("prebuilt") - .join(host_tag) - .join("bin") - .join(&expected_compiler); - let result = ndk_compiler(Language::CPlusPlus, target_triple, &ndk_path); - assert_eq!(result, expected_path); + for (language, compiler) in [ + (Language::C, "armv7a-unknown-linux-android21-clang"), + (Language::CPlusPlus, "armv7a-unknown-linux-android21-clang++"), + ] { + let expected = + ndk_path.join("toolchains/llvm/prebuilt").join(host_tag).join("bin").join(compiler); + + assert_eq!(ndk_compiler(language, "arm-unknown-linux-android", &ndk_path), expected); + } } #[test] -fn test_language_gcc() { +fn test_language_compiler_names() { assert_eq!(Language::C.gcc(), "gcc"); assert_eq!(Language::CPlusPlus.gcc(), "g++"); -} -#[test] -fn test_language_clang() { assert_eq!(Language::C.clang(), "clang"); assert_eq!(Language::CPlusPlus.clang(), "clang++"); } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4ee91d411903a..da88172c688f5 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -426,6 +426,81 @@ fn impl_self_ty(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Ty<'_> { .unwrap_or(ty.skip_binder()) } +fn transitive_reexport_path(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { + transitive_reexport_path_inner(tcx, def_id, &mut Vec::new()) +} + +/// Simplified implementation of `rustc_middle::ty::print::pretty::try_print_visible_def_path_recur`. +fn transitive_reexport_path_inner( + tcx: TyCtxt<'_>, + def_id: DefId, + callers: &mut Vec, +) -> Option> { + use rustc_hir::def_id::ModId; + use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; + + if let Some(cnum) = def_id.as_crate_root() { + return Some(vec![tcx.crate_name(cnum)]); + } + + let visible_parent_map = tcx.visible_parent_map(()); + let mut cur_def_key = tcx.def_key(def_id); + + // For a constructor, we want the name of its parent rather than . + if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: def_id.krate, + index: cur_def_key + .parent + .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), + }; + + cur_def_key = tcx.def_key(parent); + } + + let visible_parent = visible_parent_map.get(&def_id).cloned()?; + // FIXME: Should we also check for private items? + if tcx.is_doc_hidden(visible_parent) { + return None; + } + + let actual_parent = tcx.opt_parent(def_id); + let mut data = cur_def_key.disambiguated_data.data; + match data { + DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { + // Item might be re-exported several times, but filter for the one + // that's public and whose identifier isn't `_`. + let reexport = tcx + .module_children(ModId::new_unchecked(visible_parent)) + .iter() + .filter(|child| child.res.opt_def_id() == Some(def_id)) + .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore) + .map(|child| child.ident.name); + + if let Some(new_name) = reexport { + *name = new_name; + } else { + // There is no name that is public and isn't `_`, so bail. + return None; + } + } + // Re-exported `extern crate`. + DefPathData::CrateRoot => { + data = DefPathData::TypeNs(tcx.crate_name(def_id.krate)); + } + _ => {} + } + + if callers.contains(&visible_parent) { + return None; + } + callers.push(visible_parent); + let mut path = transitive_reexport_path_inner(tcx, visible_parent, callers)?; + callers.pop(); + path.push(DisambiguatedDefPathData { data, disambiguator: 0 }.as_sym(false)); + Some(path) +} + fn generate_item_def_id_path( mut def_id: DefId, original_def_id: DefId, @@ -435,10 +510,13 @@ fn generate_item_def_id_path( let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); let mut prim = None; + let mut maybe_have_impl_not_in_def_crate = false; // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. - if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { + if def_id != original_def_id + && let DefKind::Impl { of_trait } = tcx.def_kind(def_id) + { let ty = impl_self_ty(tcx, def_id); // If this is a dyn trait, we want to get the actual trait from which the method comes from. // Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we @@ -454,18 +532,35 @@ fn generate_item_def_id_path( def_id = trait_def_id; } else if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) { def_id = new_def_id; + maybe_have_impl_not_in_def_crate = !of_trait + && !original_def_id.is_local() + && !def_id.is_local() + && def_id.krate != original_def_id.krate; } else { prim = PrimitiveType::from_ty(ty); } } - let mut fqp = vec![crate_name]; - let shortty = if let Some(prim) = prim { - fqp.push(prim.as_sym()); - ItemType::Primitive + let (shortty, fqp) = if let Some(prim) = prim { + (ItemType::Primitive, vec![crate_name, prim.as_sym()]) } else { - fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); - ItemType::from_def_id(def_id, tcx) + ( + ItemType::from_def_id(def_id, tcx), + if maybe_have_impl_not_in_def_crate + // We have a method, not coming from a trait, implemented from a different crate + // where the original item is defined. So in short, the item is using + // `#[rustc_allow_incoherent_impl]` and we need to keep the non-final item path. + // Sadly if we use `item_relative_path` which uses `def_path`, it renders the final + // item path and not the intermediate one. + && let Some(fqp) = transitive_reexport_path(tcx, def_id) + { + fqp + } else { + let mut fqp = vec![crate_name]; + fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); + fqp + }, + ) }; let module_fqp = to_module_fqp(shortty, &fqp); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 8c580d3520dfd..1968f5eb73d4e 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -60,6 +60,7 @@ impl TestMode { string_enum! { #[derive(Clone, Copy, PartialEq, Debug)] pub(crate) enum TestSuite { + AssemblyGcc => "assembly-gcc", AssemblyLlvm => "assembly-llvm", CodegenLlvm => "codegen-llvm", CodegenUnits => "codegen-units", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index c728e56cf639d..0bb8a7003589c 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1604,7 +1604,8 @@ impl<'test> TestCx<'test> { // Exhaustively match all other suites. // Note that some suites never actually use this method, so the // return value for those suites is not necessarily meaningful. - TestSuite::AssemblyLlvm + TestSuite::AssemblyGcc + | TestSuite::AssemblyLlvm | TestSuite::BuildStd | TestSuite::CodegenLlvm | TestSuite::CodegenUnits diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4d25aed007416..793c7d6f0401b 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -569,7 +569,7 @@ fn report_msg<'tcx>( let tcx = machine.tcx; let level = match diag_level { DiagLevel::Error => Level::Error, - DiagLevel::Warning => Level::Warning, + DiagLevel::Warning => Level::Warning(None), DiagLevel::Note => Level::Note, }; let mut err = Diag::new(tcx.sess.dcx(), level, title); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 83a77e73cb7ea..c61b43492ce9e 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -418,7 +418,7 @@ mod tests { Some(ignore_list), ); let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); emitter.emit_diagnostic(non_fatal_diagnostic); assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0); assert_eq!(can_reset_errors.load(Ordering::Acquire), true); @@ -439,7 +439,7 @@ mod tests { None, ); let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); emitter.emit_diagnostic(non_fatal_diagnostic); assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); assert_eq!(can_reset_errors.load(Ordering::Acquire), false); @@ -467,8 +467,8 @@ mod tests { ); let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); - let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span)); - let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span)); + let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span)); + let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span)); let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); emitter.emit_diagnostic(bar_diagnostic); emitter.emit_diagnostic(foo_diagnostic); diff --git a/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs new file mode 100644 index 0000000000000..c8945ed96b3ee --- /dev/null +++ b/tests/assembly-gcc/stack-protector/stack-protector-heuristics-effect.rs @@ -0,0 +1,309 @@ +//@ revisions: all strong basic none missing +//@ assembly-output: emit-asm +//@ ignore-apple slightly different policy on stack protection of arrays +//@ ignore-msvc stack check code uses different function names +//@ ignore-nvptx64 stack protector is not supported +//@ ignore-wasm32-unknown-unknown +//@ [all] compile-flags: -Z stack-protector=all +//@ [strong] compile-flags: -Z stack-protector=strong +//@ [basic] compile-flags: -Z stack-protector=basic +//@ [none] compile-flags: -Z stack-protector=none +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled + +#![crate_type = "lib"] +#![allow(internal_features)] +#![feature(unsized_fn_params)] + +// CHECK-LABEL: "emptyfn" +#[no_mangle] +pub fn emptyfn() { + // all: __stack_chk_fail + // strong-NOT: __stack_chk_fail + // basic-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "array_char" +#[no_mangle] +pub fn array_char(f: fn(*const char)) { + let a = ['c'; 1]; + let b = ['d'; 3]; + let c = ['e'; 15]; + + f(&a as *const _); + f(&b as *const _); + f(&c as *const _); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "array_u8_1" +#[no_mangle] +pub fn array_u8_1(f: fn(*const u8)) { + let a = [0u8; 1]; + f(&a as *const _); + + // The 'strong' heuristic adds stack protection to functions with local + // array variables regardless of their size. + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "array_u8_small" +#[no_mangle] +pub fn array_u8_small(f: fn(*const u8)) { + let a = [0u8; 2]; + let b = [0u8; 7]; + f(&a as *const _); + f(&b as *const _); + + // Small arrays do not lead to stack protection by the 'basic' heuristic. + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "array_u8_large" +#[no_mangle] +pub fn array_u8_large(f: fn(*const u8)) { + let a = [0u8; 9]; + f(&a as *const _); + + // Since `a` is a byte array with size greater than 8, the basic heuristic + // will also protect this function. + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +#[derive(Copy, Clone)] +pub struct ByteSizedNewtype(u8); + +// CHECK-LABEL: "array_bytesizednewtype_9" +#[no_mangle] +pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { + let a = [ByteSizedNewtype(0); 9]; + f(&a as *const _); + + // Since `a` is a byte array in the GCC output, the basic heuristic will + // also protect this function. + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "local_var_addr_used_indirectly" +#[no_mangle] +pub fn local_var_addr_used_indirectly(f: fn(bool)) { + let a = 5; + let a_addr = &a as *const _ as usize; + f(a_addr & 0x10 == 0); + + // This function takes the address of a local variable taken. Although this + // address is never used as a way to refer to stack memory, the `strong` + // heuristic adds stack smash protection. This is also the case in C++: + // ``` + // cat << EOF | g++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk + // #include + // void f(void (*g)(bool)) { + // int32_t x; + // g((reinterpret_cast(&x) & 0x10U) == 0); + // } + // EOF + // ``` + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "local_string_addr_taken" +#[no_mangle] +pub fn local_string_addr_taken(f: fn(&String)) { + let x = String::new(); + f(&x); + + // Taking the address of the local variable `x` leads to stack smash + // protection. It does not matter that the reference is not mut. + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +pub trait SelfByRef { + fn f(&self) -> i32; +} + +impl SelfByRef for i32 { + fn f(&self) -> i32 { + return self + 1; + } +} + +// CHECK-LABEL: "local_var_addr_taken_used_locally_only" +#[no_mangle] +pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) { + let x = factory(); + let g = x.f(); + sink(g); + + // Even though the local variable conceptually has its address taken, as + // it's passed by reference to the trait function, the use of the reference + // is easily inlined. There is therefore no stack smash protection even with + // the `strong` heuristic. + + // all: __stack_chk_fail + // strong-NOT: __stack_chk_fail + // basic-NOT: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +pub struct Gigastruct { + does: u64, + not: u64, + have: u64, + array: u64, + members: u64, +} + +// CHECK-LABEL: "local_large_var_moved" +#[no_mangle] +pub fn local_large_var_moved(f: fn(Gigastruct)) { + let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 }; + f(x); + + // Even though the local variable conceptually doesn't have its address + // taken, it's so large that the "move" is implemented with a reference to a + // stack-local variable in the ABI. Consequently, this function *is* + // protected. This is also the case for rvalue-references in C++, + // regardless of struct size: + // ``` + // cat < + // #include + // void f(void (*g)(uint64_t&&)) { + // uint64_t x; + // g(std::move(x)); + // } + // EOF + // ``` + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "local_large_var_cloned" +#[no_mangle] +pub fn local_large_var_cloned(f: fn(Gigastruct)) { + f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 }); + + // A new instance of `Gigastruct` is passed to `f()`, without any apparent + // connection to this stack frame. Still, since instances of `Gigastruct` + // are sufficiently large, it is allocated in the caller stack frame and + // passed as a pointer. As such, this function is *also* protected, just + // like `local_large_var_moved`. + // + // This matches clang++ behavior, but not g++ behavior. + // + // In any case, both options are fine from a specification point of view, there + // is no "user-accessible pointer", and there is no strong reason to avoid generating + // a canary in this case, since it doesn't seem to be one of the performance-critical + // cases in which avoiding generating a canary is important, so it seems that + // rustc should keep the clang-like behavior of generating a canary here. + // + // ``` + // cat < + // #include + // struct Gigastruct { uint64_t a, b, c, d, e; }; + // void f(void (*g)(Gigastruct)) { + // g(Gigastruct{}); + // } + // EOF + // ``` + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +extern "C" { + // Difference between LLVM and GCC: LLVM will not generate stack protection + // for "external" calls to alloca, but gcc will. See the matching test for + // stack-protector-heuristics-effect under assembly-llvm. + // + // This is a difference in heuristics and therefore fine. + // + // Check that rustc_codegen_gcc matches gcc behavior. + + fn alloca(size: usize) -> *mut (); +} + +// CHECK-LABEL: "alloca_small_compile_time_constant_arg" +#[no_mangle] +pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { + f(unsafe { alloca(8) }); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "alloca_large_compile_time_constant_arg" +#[no_mangle] +pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { + f(unsafe { alloca(9) }); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// CHECK-LABEL: "alloca_dynamic_arg" +#[no_mangle] +pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { + f(unsafe { alloca(n) }); + + // all: __stack_chk_fail + // strong: __stack_chk_fail + // basic: __stack_chk_fail + // none-NOT: __stack_chk_fail + // missing-NOT: __stack_chk_fail +} + +// rustc can currently (as of 1.98) not generate variable-sized allocas, except for +// variable-sized scalable vector types, so their interaction with stack-protector +// does not need to be tested. diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs index 3728ff3adf105..56cf0035e25bf 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs @@ -317,29 +317,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // missing-NOT: __stack_chk_fail } -// The question then is: in what ways can Rust code generate array-`alloca` -// LLVM instructions? This appears to only be generated by -// rustc_codegen_ssa::traits::Builder::array_alloca() through -// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT -// this is support for the "unsized locals" unstable feature: -// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. - -// CHECK-LABEL: unsized_fn_param{{:|\[}} -#[no_mangle] -pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { - let n = if l { 1 } else { 2 }; - f(*Box::<[u8]>::from(&s[0..n])); // slice-copy with Box::from - - // Even though slices are conceptually passed by-value both into this - // function and into `f()`, this is implemented with pass-by-reference - // using a suitably constructed fat-pointer (as if the functions - // accepted &[u8]). This function therefore doesn't need dynamic array - // alloca, and is therefore not protected by the `strong` or `basic` - // heuristics. - - // all: __stack_chk_fail - // strong-NOT: __stack_chk_fail - // basic-NOT: __stack_chk_fail - // none-NOT: __stack_chk_fail - // missing-NOT: __stack_chk_fail -} +// rustc can currently (as of 1.98) not generate variable-sized allocas, except for +// variable-sized scalable vector types, so their interaction with stack-protector +// does not need to be tested. diff --git a/tests/codegen-llvm/float/f16-f128-inline.rs b/tests/codegen-llvm/float/f16-f128-inline.rs index aa2c38c209e86..592204cf000a1 100644 --- a/tests/codegen-llvm/float/f16-f128-inline.rs +++ b/tests/codegen-llvm/float/f16-f128-inline.rs @@ -1,29 +1,84 @@ -//@ revisions: default nopt -//@[nopt] compile-flags: -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes +//@ revisions: DEFAULT NOPT +//@[NOPT] compile-flags: -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes -// Ensure that functions using `f16` and `f128` are always inlined to avoid crashes -// when the backend does not support these types. +// Ensure that functions using `f16` and `f128` are always inlined when the backend does not +// support these types, to avoid crashes. #![crate_type = "lib"] #![feature(f128)] #![feature(f16)] +#![feature(cfg_target_has_reliable_f16_f128)] -pub fn f16_arg(_a: f16) { - // CHECK-NOT: f16_arg - todo!() -} +// This test does some tricky things. On `target_has_reliable_*` platforms: +// +// * `*_on_reliable` functions should always show up in codegen since they are not auto-inlined +// (the default), +// * `*_on_not_reliable` functions aren't defined at all, so `CHECK-NOT` passes. +// +// On non-`target_has_reliable_*` platforms: +// +// * `*_on_reliable` functions are dummies so they always show up in codegen. +// * `*_on_not_reliable` functions should be auto-inlined and thus not show up in codegen. +// +// `*_on_reliable` is only checked with NOPT since otherwise they may hit auto-inlining thresholds +// unrelated to the type signature. -pub fn f16_ret() -> f16 { - // CHECK-NOT: f16_ret - todo!() -} +// NOPT: f16_arg_on_reliable +// NOPT: f16_ret_on_reliable +// CHECK-NOT: f16_arg_on_not_reliable +// CHECK-NOT: f16_ret_on_not_reliable +cfg_select! { + target_has_reliable_f16 => { + pub fn f16_arg_on_reliable(_a: f16) { + todo!() + } + + pub fn f16_ret_on_reliable() -> f16 { + todo!() + } + } + _ => { + pub fn f16_arg_on_not_reliable(_a: f16) { + todo!() + } + + pub fn f16_ret_on_not_reliable() -> f16 { + todo!() + } -pub fn f128_arg(_a: f128) { - // CHECK-NOT: f128_arg - todo!() + #[unsafe(no_mangle)] + pub fn f16_arg_on_reliable() {} + #[unsafe(no_mangle)] + pub fn f16_ret_on_reliable() {} + } } -pub fn f128_ret() -> f128 { - // CHECK-NOT: f128_ret - todo!() +// NOPT: f128_arg_on_reliable +// NOPT: f128_ret_on_reliable +// CHECK-NOT: f128_arg_on_not_reliable +// CHECK-NOT: f128_ret_on_not_reliable +cfg_select! { + target_has_reliable_f128 => { + pub fn f128_arg_on_reliable(_a: f128) { + todo!() + } + + pub fn f128_ret_on_reliable() -> f128 { + todo!() + } + } + _ => { + pub fn f128_arg_on_not_reliable(_a: f128) { + todo!() + } + + pub fn f128_ret_on_not_reliable() -> f128 { + todo!() + } + + #[unsafe(no_mangle)] + pub fn f128_arg_on_reliable() {} + #[unsafe(no_mangle)] + pub fn f128_ret_on_reliable() {} + } } diff --git a/tests/rustdoc-html/inline_cross/auxiliary/glob-of-reexports-53724.rs b/tests/rustdoc-html/inline_cross/auxiliary/glob-of-reexports-53724.rs new file mode 100644 index 0000000000000..9adc1bd772064 --- /dev/null +++ b/tests/rustdoc-html/inline_cross/auxiliary/glob-of-reexports-53724.rs @@ -0,0 +1,13 @@ +#![crate_name = "inner"] + +pub mod ser { + pub trait Serialize {} + pub trait Serializer {} +} + +pub mod de { + pub trait Deserialize {} +} + +pub use ser::{Serialize, Serializer}; +pub use de::*; diff --git a/tests/rustdoc-html/inline_cross/glob-of-reexports-53724.rs b/tests/rustdoc-html/inline_cross/glob-of-reexports-53724.rs new file mode 100644 index 0000000000000..d80b4e78f96e2 --- /dev/null +++ b/tests/rustdoc-html/inline_cross/glob-of-reexports-53724.rs @@ -0,0 +1,17 @@ +// Regression test for . + +//@ aux-build:glob-of-reexports-53724.rs +//@ build-aux-docs +//@ ignore-cross-compile + +#![crate_name = "foo"] + +extern crate inner; + +//@ has foo/trait.Serialize.html +//@ has foo/trait.Serializer.html +//@ has foo/trait.Deserialize.html +//@ has foo/index.html '//a[@class="trait"]' 'Serialize' +//@ has foo/index.html '//a[@class="trait"]' 'Serializer' +//@ has foo/index.html '//a[@class="trait"]' 'Deserialize' +pub use inner::*; diff --git a/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl1.rs b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl1.rs new file mode 100644 index 0000000000000..a67fe5ab7eee3 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl1.rs @@ -0,0 +1,18 @@ +//@ aux-build: incoherent_impl2.rs +//@ build-aux-docs + +#![feature(rustc_attrs)] +#![allow(internal_features)] + +extern crate incoherent_impl2 as baz; + +pub mod error { + pub use baz::Error; +} + +impl baz::Error { + #[rustc_allow_incoherent_impl] + pub fn new() -> Self { + Self + } +} diff --git a/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl2.rs b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl2.rs new file mode 100644 index 0000000000000..d04c24de248eb --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl2.rs @@ -0,0 +1,5 @@ +#![feature(rustc_attrs)] +#![allow(internal_features)] + +#[rustc_has_incoherent_inherent_impls] +pub struct Error; diff --git a/tests/rustdoc-html/jump-to-def/incoherent_impl.rs b/tests/rustdoc-html/jump-to-def/incoherent_impl.rs new file mode 100644 index 0000000000000..d1aeea39e7aa9 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/incoherent_impl.rs @@ -0,0 +1,24 @@ +// This test ensures that we generate correct links when we target a method implemented +// with `#[rustc_allow_incoherent_impl]` in a different crate than where the type is defined. +// Regression test for . + +//@ aux-build: incoherent_impl1.rs +//@ build-aux-docs +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +extern crate incoherent_impl1 as bar; + +//@ has 'src/foo/incoherent_impl.rs.html' +//@ has - '//pre//a[@href="../../incoherent_impl1/error/struct.Error.html#method.new"]' 'new' +//@ has - '//pre//a[@href="../../incoherent_impl2/struct.Error.html"]' 'Error' + +// Now we check that the target files exist and contain the information we want. +//@ has 'incoherent_impl1/error/struct.Error.html' +//@ has - '//*[@id="method.new"]' 'pub fn new() -> Self' +//@ has 'incoherent_impl2/struct.Error.html' + +fn foo() { + let x = bar::error::Error::new(); +} diff --git a/tests/ui/consts/std/conjure_zst.rs b/tests/ui/consts/std/conjure_zst.rs index c04deae502b0f..be52a5c4014b2 100644 --- a/tests/ui/consts/std/conjure_zst.rs +++ b/tests/ui/consts/std/conjure_zst.rs @@ -1,5 +1,3 @@ -#![feature(mem_conjure_zst)] - use std::{convert::Infallible, mem}; const INVALID: Infallible = unsafe { mem::conjure_zst() }; diff --git a/tests/ui/consts/std/conjure_zst.stderr b/tests/ui/consts/std/conjure_zst.stderr index d4c9df07a393a..8e5bedf0a3c04 100644 --- a/tests/ui/consts/std/conjure_zst.stderr +++ b/tests/ui/consts/std/conjure_zst.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation panicked: aborted execution: attempted to instantiate uninhabited type `!` - --> $DIR/conjure_zst.rs:5:38 + --> $DIR/conjure_zst.rs:3:38 | LL | const INVALID: Infallible = unsafe { mem::conjure_zst() }; | ^^^^^^^^^^^^^^^^^^ evaluation of `INVALID` failed inside this call diff --git a/tests/ui/derives/auxiliary/derive-no-generics.rs b/tests/ui/derives/auxiliary/derive-no-generics.rs new file mode 100644 index 0000000000000..ea9a2e5b1c89a --- /dev/null +++ b/tests/ui/derives/auxiliary/derive-no-generics.rs @@ -0,0 +1,34 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, quote}; + +#[proc_macro_derive(A)] +pub fn derive(item: TokenStream) -> TokenStream { + let mut tokens = item.into_iter(); + let _enum = tokens.next(); + let name = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let TokenTree::Group(group) = tokens.next().unwrap() else { panic!() }; + let mut group = group.stream().into_iter(); + let variant = group.next().unwrap(); + let TokenTree::Group(args) = group.next().unwrap() else { panic!() }; + let arg = args.stream().into_iter().next().unwrap(); + let tokens = quote! { + trait X {} + impl X for $name {} + + impl $name { + fn foo(&self) { + if let Self :: $variant(val) = self { + let _: $arg = val; + } + } + } + + }; + tokens +} diff --git a/tests/ui/derives/derive-macro-unsupported-type-params.rs b/tests/ui/derives/derive-macro-unsupported-type-params.rs new file mode 100644 index 0000000000000..d80064b4eb094 --- /dev/null +++ b/tests/ui/derives/derive-macro-unsupported-type-params.rs @@ -0,0 +1,13 @@ +//@ proc-macro: derive-no-generics.rs +#![crate_type = "lib"] + +#[macro_use] +extern crate derive_no_generics; + +#[derive(A)] +enum A { + //~^ ERROR: missing generics for enum `A` + //~| ERROR: missing generics for enum `A` + Variant(T), + //~^ ERROR: cannot find type `T` in this scope +} diff --git a/tests/ui/derives/derive-macro-unsupported-type-params.stderr b/tests/ui/derives/derive-macro-unsupported-type-params.stderr new file mode 100644 index 0000000000000..e57490e79cc9f --- /dev/null +++ b/tests/ui/derives/derive-macro-unsupported-type-params.stderr @@ -0,0 +1,28 @@ +error[E0425]: cannot find type `T` in this scope + --> $DIR/derive-macro-unsupported-type-params.rs:11:13 + | +LL | Variant(T), + | ^ not found in this scope + +error[E0107]: missing generics for enum `A` + --> $DIR/derive-macro-unsupported-type-params.rs:8:6 + | +LL | #[derive(A)] + | - this derive macro might not support items with generic parameters +LL | enum A { + | ^ + +error[E0107]: missing generics for enum `A` + --> $DIR/derive-macro-unsupported-type-params.rs:8:6 + | +LL | #[derive(A)] + | - this derive macro might not support items with generic parameters +LL | enum A { + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0425. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/pub/pub-reexport-extern-std-issue-163293.rs b/tests/ui/pub/pub-reexport-extern-std-issue-163293.rs new file mode 100644 index 0000000000000..9189ca2fb41b7 --- /dev/null +++ b/tests/ui/pub/pub-reexport-extern-std-issue-163293.rs @@ -0,0 +1,7 @@ +pub use std as x; +//~^ ERROR extern crate `std` is private and cannot be re-exported +//~^^ WARN this was previously accepted by the compiler but is being phased out + +//@ edition: 2015 + +fn main() {} diff --git a/tests/ui/pub/pub-reexport-extern-std-issue-163293.stderr b/tests/ui/pub/pub-reexport-extern-std-issue-163293.stderr new file mode 100644 index 0000000000000..ee5ba1fc80aa9 --- /dev/null +++ b/tests/ui/pub/pub-reexport-extern-std-issue-163293.stderr @@ -0,0 +1,24 @@ +error[E0365]: extern crate `std` is private and cannot be re-exported + --> $DIR/pub-reexport-extern-std-issue-163293.rs:1:9 + | +LL | pub use std as x; + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127909 + = note: `#[deny(pub_use_of_private_extern_crate)]` (part of `#[deny(future_incompatible)]`) on by default + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0365`. +Future incompatibility report: Future breakage diagnostic: +error[E0365]: extern crate `std` is private and cannot be re-exported + --> $DIR/pub-reexport-extern-std-issue-163293.rs:1:9 + | +LL | pub use std as x; + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127909 + = note: `#[deny(pub_use_of_private_extern_crate)]` (part of `#[deny(future_incompatible)]`) on by default + diff --git a/tests/ui/suggestions/option-to-bool.fixed b/tests/ui/suggestions/option-to-bool.fixed new file mode 100644 index 0000000000000..b6160a1bb8f15 --- /dev/null +++ b/tests/ui/suggestions/option-to-bool.fixed @@ -0,0 +1,35 @@ +// An `is_some()` suggestion must apply to the whole expression, preserving its precedence. +//@ run-rustfix + +fn foo(x: Option) { + if true && x.is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + let reference = &x; + if (*reference).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if (&x).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if (Value + Value).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value +} + +struct Value; + +impl std::ops::Add for Value { + type Output = Option; + + fn add(self, _: Self) -> Self::Output { + Some(1) + } +} + +fn main() { + foo(Some(1)); +} diff --git a/tests/ui/suggestions/option-to-bool.rs b/tests/ui/suggestions/option-to-bool.rs index bbc5d1d71cb72..25272b96dfc28 100644 --- a/tests/ui/suggestions/option-to-bool.rs +++ b/tests/ui/suggestions/option-to-bool.rs @@ -1,7 +1,35 @@ +// An `is_some()` suggestion must apply to the whole expression, preserving its precedence. +//@ run-rustfix + fn foo(x: Option) { if true && x {} //~^ ERROR mismatched types //~| HELP use `Option::is_some` to test if the `Option` has a value + + let reference = &x; + if *reference {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if &x {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if Value + Value {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value } -fn main() {} +struct Value; + +impl std::ops::Add for Value { + type Output = Option; + + fn add(self, _: Self) -> Self::Output { + Some(1) + } +} + +fn main() { + foo(Some(1)); +} diff --git a/tests/ui/suggestions/option-to-bool.stderr b/tests/ui/suggestions/option-to-bool.stderr index ab97eae6c5c55..dd49b1b2b0ad1 100644 --- a/tests/ui/suggestions/option-to-bool.stderr +++ b/tests/ui/suggestions/option-to-bool.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:2:16 + --> $DIR/option-to-bool.rs:5:16 | LL | if true && x {} | ---- ^ expected `bool`, found `Option` @@ -13,6 +13,45 @@ help: use `Option::is_some` to test if the `Option` has a value LL | if true && x.is_some() {} | ++++++++++ -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:10:8 + | +LL | if *reference {} + | ^^^^^^^^^^ expected `bool`, found `Option` + | + = note: expected type `bool` + found enum `Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if (*reference).is_some() {} + | + +++++++++++ + +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:14:8 + | +LL | if &x {} + | ^^ expected `bool`, found `&Option` + | + = note: expected type `bool` + found reference `&Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if (&x).is_some() {} + | + +++++++++++ + +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:18:8 + | +LL | if Value + Value {} + | ^^^^^^^^^^^^^ expected `bool`, found `Option` + | + = note: expected type `bool` + found enum `Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if (Value + Value).is_some() {} + | + +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`.