From 6054a33bf2f9b03db0f4123aacdf4fe9a44dd20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 09:57:33 +0000 Subject: [PATCH 01/36] split polonius context into per-phase data - describe how that data flows during borrowck - prepares for recording some liveness data for diagnostics, not just for the main analysis --- .../src/polonius/liveness_constraints.rs | 24 +-------- compiler/rustc_borrowck/src/polonius/mod.rs | 53 ++++++++++++++++--- .../src/type_check/liveness/mod.rs | 14 ++--- .../src/type_check/liveness/trace.rs | 4 +- compiler/rustc_borrowck/src/type_check/mod.rs | 25 +++++---- 5 files changed, 70 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index 75ee29c9d0d5..6ab09f731c07 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -1,7 +1,6 @@ use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; -use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; @@ -9,12 +8,12 @@ use rustc_mir_dataflow::points::PointIndex; use super::{ ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, - PoloniusContext, + PoloniusLivenessContext, }; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { +impl PoloniusLivenessContext { /// Record the variance of each region contained within the given value. pub(crate) fn record_live_region_variance<'tcx>( &mut self, @@ -30,25 +29,6 @@ impl PoloniusContext { }; extractor.relate(value, value).expect("Can't have a type error relating to itself"); } - - /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we - /// need to transpose the "points where each region is live" matrix to a "live regions per point" - /// matrix. - // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of - // borrowck. - pub(crate) fn record_live_regions_per_point( - &mut self, - num_regions: usize, - points_per_live_region: &SparseIntervalMatrix, - ) { - let mut live_regions_per_point = SparseBitMatrix::new(num_regions); - for region in points_per_live_region.rows() { - for point in points_per_live_region.row(region).unwrap().iter() { - live_regions_per_point.insert(point, region); - } - } - self.live_regions = Some(live_regions_per_point); - } } /// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 502c868194af..11414a5719d8 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -32,6 +32,16 @@ //! - //! - //! +//! +//! Data flows like this: +//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the +//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext]. +//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to +//! the polonius shape. That's the main [PoloniusContext]. +//! 3) during region inference, that data and the NLL outlives constraints are used to create the +//! localized outlives constraints, as described above. +//! 4) transfer these constraints back to the main borrowck procedure: it handles computing errors +//! and diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; @@ -43,6 +53,7 @@ mod typeck_constraints; use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; +use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::Body; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; @@ -57,11 +68,21 @@ use crate::{BorrowSet, RegionInferenceContext}; pub(crate) type LiveLoans = SparseBitMatrix; -/// This struct holds the data needed to create the Polonius localized constraints. +/// This struct holds the liveness data created during MIR typeck, and which will be used later in +/// the process, to compute the polonius localized constraints. +#[derive(Default)] +pub(crate) struct PoloniusLivenessContext { + /// The expected edge direction per live region: the kind of directed edge we'll create as + /// liveness constraints depends on the variance of types with respect to each contained region. + live_region_variances: BTreeMap, +} + +/// This struct holds the data needed to create the Polonius localized constraints. Its data is +/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. pub(crate) struct PoloniusContext { /// The set of regions that are live at a given point in the CFG, used to create localized /// outlives constraints between regions that are live at connected points in the CFG. - live_regions: Option>, + live_regions: SparseBitMatrix, /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. @@ -83,8 +104,27 @@ enum ConstraintDirection { } impl PoloniusContext { - pub(crate) fn new() -> PoloniusContext { - Self { live_region_variances: BTreeMap::new(), live_regions: None } + /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we + /// need to transpose the "points where each region is live" matrix to a "live regions per point" + /// matrix. + // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of + // borrowck. + pub(crate) fn create_from_liveness( + liveness_context: PoloniusLivenessContext, + num_regions: usize, + points_per_live_region: &SparseIntervalMatrix, + ) -> PoloniusContext { + let mut live_regions_per_point = SparseBitMatrix::new(num_regions); + for region in points_per_live_region.rows() { + for point in points_per_live_region.row(region).unwrap().iter() { + live_regions_per_point.insert(point, region); + } + } + + PoloniusContext { + live_regions: live_regions_per_point, + live_region_variances: liveness_context.live_region_variances, + } } /// Computes live loans using the set of loans model for `-Zpolonius=next`. @@ -112,13 +152,10 @@ impl PoloniusContext { &mut localized_outlives_constraints, ); - let live_regions = self.live_regions.as_ref().expect( - "live regions per-point data should have been created at the end of MIR typeck", - ); create_liveness_constraints( body, regioncx.liveness_constraints(), - live_regions, + &self.live_regions, &self.live_region_variances, regioncx.universal_regions(), &mut localized_outlives_constraints, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 4e0b2a4e2968..23b8bbb38e8b 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -14,7 +14,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::PoloniusLivenessContext; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -70,7 +70,7 @@ pub(super) fn generate<'a, 'tcx>( typeck.tcx(), &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, - &mut typeck.polonius_context, + &mut typeck.polonius_liveness, body, ); } @@ -147,11 +147,11 @@ fn record_regular_live_regions<'tcx>( tcx: TyCtxt<'tcx>, liveness_constraints: &mut LivenessValues, universal_regions: &UniversalRegions<'tcx>, - polonius_context: &mut Option, + polonius_liveness: &mut Option, body: &Body<'tcx>, ) { let mut visitor = - LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context }; + LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_liveness }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -162,7 +162,7 @@ struct LiveVariablesVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, liveness_constraints: &'a mut LivenessValues, universal_regions: &'a UniversalRegions<'tcx>, - polonius_context: &'a mut Option, + polonius_liveness: &'a mut Option, } impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> { @@ -214,8 +214,8 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + if let Some(polonius_liveness) = self.polonius_liveness { + polonius_liveness.record_live_region_variance(self.tcx, self.universal_regions, value); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index c564d85616e2..62d49a62744e 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -580,8 +580,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = typeck.polonius_context { - polonius_context.record_live_region_variance( + if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() { + polonius_liveness.record_live_region_variance( typeck.infcx.tcx, typeck.universal_regions, value, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index a2ef5588f48f..45120324a0c0 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -48,8 +48,8 @@ use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; use crate::member_constraints::MemberConstraintSet; -use crate::polonius::PoloniusContext; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; +use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; use crate::renumber::RegionCtxt; @@ -148,8 +148,8 @@ pub(crate) fn type_check<'a, 'tcx>( debug!(?normalized_inputs_and_output); - let mut polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - Some(PoloniusContext::new()) + let polonius_liveness = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + Some(PoloniusLivenessContext::default()) } else { None }; @@ -168,7 +168,7 @@ pub(crate) fn type_check<'a, 'tcx>( polonius_facts, borrow_set, constraints: &mut constraints, - polonius_context: &mut polonius_context, + polonius_liveness, }; typeck.check_user_type_annotations(); @@ -185,11 +185,14 @@ pub(crate) fn type_check<'a, 'tcx>( let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); - if let Some(polonius_context) = typeck.polonius_context.as_mut() { - let num_regions = infcx.num_region_vars(); - let points_per_live_region = typeck.constraints.liveness_constraints.points(); - polonius_context.record_live_regions_per_point(num_regions, points_per_live_region); - } + // We're done with typeck, we can finalize the polonius liveness context for region inference. + let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { + PoloniusContext::create_from_liveness( + liveness_context, + infcx.num_region_vars(), + typeck.constraints.liveness_constraints.points(), + ) + }); MirTypeckResults { constraints, @@ -564,8 +567,8 @@ struct TypeChecker<'a, 'tcx> { polonius_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - /// When using `-Zpolonius=next`, the helper data used to create polonius constraints. - polonius_context: &'a mut Option, + /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. + polonius_liveness: Option, } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions From a8fc140848ccd1700fb1a403ef6d12e646b1f556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 10:37:40 +0000 Subject: [PATCH 02/36] create context for errors and diagnostics for last borrowck phase --- compiler/rustc_borrowck/src/lib.rs | 4 ++-- compiler/rustc_borrowck/src/nll.rs | 11 ++++++----- compiler/rustc_borrowck/src/polonius/dump.rs | 12 +++++++----- compiler/rustc_borrowck/src/polonius/mod.rs | 18 +++++++++++++----- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index decfab502bb5..c6caae45b7b2 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -198,7 +198,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, opt_closure_req, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } = nll::compute_regions( &infcx, free_regions, @@ -329,7 +329,7 @@ fn do_mir_borrowck<'tcx>( body, ®ioncx, &borrow_set, - localized_outlives_constraints, + polonius_diagnostics, &opt_closure_req, ); diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 35264bd1a707..1da6a596cfb5 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -27,7 +27,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors}; -use crate::polonius::LocalizedOutlivesConstraintSet; +use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; @@ -46,8 +46,9 @@ pub(crate) struct NllOutput<'tcx> { pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, - /// When using `-Zpolonius=next`: the localized typeck and liveness constraints. - pub localized_outlives_constraints: Option, + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. + /// localized typeck and liveness constraints. + pub polonius_diagnostics: Option, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -144,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. - let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| { + let polonius_diagnostics = polonius_context.as_ref().map(|polonius_context| { polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) }); @@ -188,7 +189,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( polonius_output, opt_closure_req: closure_region_requirements, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 6d32ee17f4c2..b742a12ecf94 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -12,7 +12,9 @@ use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::polonius::{ + LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext, +}; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; @@ -23,7 +25,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: Option, + polonius_diagnostics: Option, closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; @@ -35,8 +37,8 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } - let localized_outlives_constraints = localized_outlives_constraints - .expect("missing localized constraints with `-Zpolonius=next`"); + let polonius_diagnostics_context = + polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?; @@ -45,7 +47,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body, regioncx, borrow_set, - localized_outlives_constraints, + polonius_diagnostics_context.localized_outlives_constraints, closure_region_requirements, &mut file, )?; diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 11414a5719d8..82f5752f3623 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -39,9 +39,9 @@ //! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to //! the polonius shape. That's the main [PoloniusContext]. //! 3) during region inference, that data and the NLL outlives constraints are used to create the -//! localized outlives constraints, as described above. -//! 4) transfer these constraints back to the main borrowck procedure: it handles computing errors -//! and diagnostics, debugging and MIR dumping concerns. +//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext]. +//! 4) transfer this back to the main borrowck procedure: it handles computing errors and +//! diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; @@ -89,6 +89,12 @@ pub(crate) struct PoloniusContext { live_region_variances: BTreeMap, } +/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is +/// computed from the [PoloniusContext] when computing NLL regions. +pub(crate) struct PoloniusDiagnosticsContext { + localized_outlives_constraints: LocalizedOutlivesConstraintSet, +} + /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -135,13 +141,15 @@ impl PoloniusContext { /// /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan /// liveness, to be used by the loan scope and active loans computations. + /// + /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( &self, tcx: TyCtxt<'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, - ) -> LocalizedOutlivesConstraintSet { + ) -> PoloniusDiagnosticsContext { let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( tcx, @@ -173,6 +181,6 @@ impl PoloniusContext { ); regioncx.record_live_loans(live_loans); - localized_outlives_constraints + PoloniusDiagnosticsContext { localized_outlives_constraints } } } From 6ce46a82cb158c612a909b9acb16882c16fbe970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:04:55 +0000 Subject: [PATCH 03/36] record boring locals in polonius context this is used in diagnostics to focus on relevant live locals to match NLL diagnostics --- .../src/diagnostics/explain_borrow.rs | 23 +++++++++++-- compiler/rustc_borrowck/src/lib.rs | 8 ++++- compiler/rustc_borrowck/src/nll.rs | 2 +- compiler/rustc_borrowck/src/polonius/dump.rs | 8 ++--- compiler/rustc_borrowck/src/polonius/mod.rs | 34 ++++++++++++------- .../src/type_check/liveness/mod.rs | 23 ++++++++----- 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 2656e0bb6a45..eeba62bb1429 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -548,8 +548,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } + // NLL doesn't consider boring locals for liveness, and wouldn't encounter a + // `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring + // locals yet, and will encounter them when trying to explain why a borrow contains a given + // point. + // + // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we + // ensure that we don't emit live boring locals as explanations. + let is_local_boring = |local| { + if let Some(polonius_diagnostics) = self.polonius_diagnostics { + polonius_diagnostics.boring_nll_locals.contains(&local) + } else { + assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled()); + + // Boring locals are never the cause of a borrow explanation in NLLs. + false + } + }; match find_use::find(body, regioncx, tcx, region_sub, use_location) { - Some(Cause::LiveVar(local, location)) => { + Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => { let span = body.source_info(location).span; let spans = self .move_spans(Place::from(local).as_ref(), location) @@ -592,7 +609,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - None => { + Some(Cause::LiveVar(..)) | None => { + // Here, under NLL: no cause was found. Under polonius: no cause was found, or a + // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.to_error_region_vid(borrow_region_vid) { let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index c6caae45b7b2..fe904726c289 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -60,6 +60,7 @@ use crate::diagnostics::{ use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; +use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput}; use crate::prefixes::PrefixSet; use crate::region_infer::RegionInferenceContext; @@ -270,6 +271,7 @@ fn do_mir_borrowck<'tcx>( polonius_output: None, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, @@ -308,6 +310,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; // Compute and report region errors, if any. @@ -329,7 +332,7 @@ fn do_mir_borrowck<'tcx>( body, ®ioncx, &borrow_set, - polonius_diagnostics, + polonius_diagnostics.as_ref(), &opt_closure_req, ); @@ -579,6 +582,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>, move_errors: Vec>, + + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. + polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, } // Check that: diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 1da6a596cfb5..2031579dfd50 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -145,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. - let polonius_diagnostics = polonius_context.as_ref().map(|polonius_context| { + let polonius_diagnostics = polonius_context.map(|polonius_context| { polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) }); diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index b742a12ecf94..5f9fa3612b8d 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -25,7 +25,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - polonius_diagnostics: Option, + polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; @@ -37,7 +37,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } - let polonius_diagnostics_context = + let polonius_diagnostics = polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); let _: io::Result<()> = try { @@ -47,7 +47,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body, regioncx, borrow_set, - polonius_diagnostics_context.localized_outlives_constraints, + &polonius_diagnostics.localized_outlives_constraints, closure_region_requirements, &mut file, )?; @@ -65,7 +65,7 @@ fn emit_polonius_dump<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, closure_region_requirements: &Option>, out: &mut dyn io::Write, ) -> io::Result<()> { diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 82f5752f3623..142ef8ba28ef 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -52,9 +52,10 @@ mod typeck_constraints; use std::collections::BTreeMap; +use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::SparseBitMatrix; use rustc_index::interval::SparseIntervalMatrix; -use rustc_middle::mir::Body; +use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; @@ -75,24 +76,33 @@ pub(crate) struct PoloniusLivenessContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. live_region_variances: BTreeMap, + + /// The regions that outlive free regions are used to distinguish relevant live locals from + /// boring locals. A boring local is one whose type contains only such regions. Polonius + /// currently has more boring locals than NLLs so we record the latter to use in errors and + /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics. + pub(crate) boring_nll_locals: FxHashSet, } /// This struct holds the data needed to create the Polonius localized constraints. Its data is /// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. pub(crate) struct PoloniusContext { + /// The liveness data we recorded during MIR typeck. + liveness_context: PoloniusLivenessContext, + /// The set of regions that are live at a given point in the CFG, used to create localized /// outlives constraints between regions that are live at connected points in the CFG. live_regions: SparseBitMatrix, - - /// The expected edge direction per live region: the kind of directed edge we'll create as - /// liveness constraints depends on the variance of types with respect to each contained region. - live_region_variances: BTreeMap, } /// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is /// computed from the [PoloniusContext] when computing NLL regions. pub(crate) struct PoloniusDiagnosticsContext { + /// The localized outlives constraints that were computed in the main analysis. localized_outlives_constraints: LocalizedOutlivesConstraintSet, + + /// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals]. + pub(crate) boring_nll_locals: FxHashSet, } /// The direction a constraint can flow into. Used to create liveness constraints according to @@ -127,10 +137,7 @@ impl PoloniusContext { } } - PoloniusContext { - live_regions: live_regions_per_point, - live_region_variances: liveness_context.live_region_variances, - } + PoloniusContext { live_regions: live_regions_per_point, liveness_context } } /// Computes live loans using the set of loans model for `-Zpolonius=next`. @@ -144,12 +151,15 @@ impl PoloniusContext { /// /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( - &self, + self, tcx: TyCtxt<'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> PoloniusDiagnosticsContext { + let PoloniusLivenessContext { live_region_variances, boring_nll_locals } = + self.liveness_context; + let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( tcx, @@ -164,7 +174,7 @@ impl PoloniusContext { body, regioncx.liveness_constraints(), &self.live_regions, - &self.live_region_variances, + &live_region_variances, regioncx.universal_regions(), &mut localized_outlives_constraints, ); @@ -181,6 +191,6 @@ impl PoloniusContext { ); regioncx.record_live_loans(live_loans); - PoloniusDiagnosticsContext { localized_outlives_constraints } + PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 23b8bbb38e8b..dcc179030020 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -38,19 +38,24 @@ pub(super) fn generate<'a, 'tcx>( ) { debug!("liveness::generate"); + let mut free_regions = regions_that_outlive_free_regions( + typeck.infcx.num_region_vars(), + &typeck.universal_regions, + &typeck.constraints.outlives_constraints, + ); + // NLLs can avoid computing some liveness data here because its constraints are // location-insensitive, but that doesn't work in polonius: locals whose type contains a region // that outlives a free region are not necessarily live everywhere in a flow-sensitive setting, // unlike NLLs. - let free_regions = if !typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - regions_that_outlive_free_regions( - typeck.infcx.num_region_vars(), - &typeck.universal_regions, - &typeck.constraints.outlives_constraints, - ) - } else { - typeck.universal_regions.universal_regions_iter().collect() - }; + // We do record these regions in the polonius context, since they're used to differentiate + // relevant and boring locals, which is a key distinction used later in diagnostics. + if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { + let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); + typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = + boring_locals.into_iter().collect(); + free_regions = typeck.universal_regions.universal_regions_iter().collect(); + } let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); From bb1385ca8bdacd092e4110d26176dbf12092eedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:47:04 +0000 Subject: [PATCH 04/36] merge duplicate issue-46589 tests also add explicit revisions for -Zpolonius=next --- src/tools/tidy/src/issues.txt | 1 - ...ue-46589.stderr => issue-46589.nll.stderr} | 2 +- tests/ui/nll/issue-46589.rs | 13 ++++---- tests/ui/nll/polonius/issue-46589.rs | 31 ------------------- 4 files changed, 8 insertions(+), 39 deletions(-) rename tests/ui/nll/{issue-46589.stderr => issue-46589.nll.stderr} (94%) delete mode 100644 tests/ui/nll/polonius/issue-46589.rs diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 839d23fb9a78..17786ed6d2fd 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -3146,7 +3146,6 @@ ui/nll/issue-97997.rs ui/nll/issue-98170.rs ui/nll/issue-98589-closures-relate-named-regions.rs ui/nll/issue-98693.rs -ui/nll/polonius/issue-46589.rs ui/nll/relate_tys/issue-48071.rs ui/nll/ty-outlives/issue-53789-1.rs ui/nll/ty-outlives/issue-53789-2.rs diff --git a/tests/ui/nll/issue-46589.stderr b/tests/ui/nll/issue-46589.nll.stderr similarity index 94% rename from tests/ui/nll/issue-46589.stderr rename to tests/ui/nll/issue-46589.nll.stderr index abf62aaa5108..dc80c1d08dec 100644 --- a/tests/ui/nll/issue-46589.stderr +++ b/tests/ui/nll/issue-46589.nll.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `**other` as mutable more than once at a time - --> $DIR/issue-46589.rs:23:21 + --> $DIR/issue-46589.rs:24:21 | LL | *other = match (*other).get_self() { | -------- first mutable borrow occurs here diff --git a/tests/ui/nll/issue-46589.rs b/tests/ui/nll/issue-46589.rs index 417d9cab657a..10aff0d7b4ef 100644 --- a/tests/ui/nll/issue-46589.rs +++ b/tests/ui/nll/issue-46589.rs @@ -1,8 +1,9 @@ -// This tests passes in Polonius mode, so is skipped in the automated compare-mode. -// We will manually check it passes in Polonius tests, as we can't have a test here -// which conditionally passes depending on a test revision/compile-flags. - -//@ ignore-compare-mode-polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius_next polonius +//@ [polonius_next] check-pass +//@ [polonius_next] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] compile-flags: -Zpolonius struct Foo; @@ -21,7 +22,7 @@ impl Foo { *other = match (*other).get_self() { Some(s) => s, None => (*other).new_self() - //~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] + //[nll]~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] }; let c = other; diff --git a/tests/ui/nll/polonius/issue-46589.rs b/tests/ui/nll/polonius/issue-46589.rs deleted file mode 100644 index af791f7e24dd..000000000000 --- a/tests/ui/nll/polonius/issue-46589.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This test is a copy of `ui/nll/issue-46589.rs` which fails in NLL but succeeds in Polonius. -// As we can't have a test here which conditionally passes depending on a test -// revision/compile-flags. We ensure here that it passes in Polonius mode. - -//@ check-pass -//@ compile-flags: -Z polonius - -struct Foo; - -impl Foo { - fn get_self(&mut self) -> Option<&mut Self> { - Some(self) - } - - fn new_self(&mut self) -> &mut Self { - self - } - - fn trigger_bug(&mut self) { - let other = &mut (&mut *self); - - *other = match (*other).get_self() { - Some(s) => s, - None => (*other).new_self() - }; - - let c = other; - } -} - -fn main() {} From e2d16419e8832a058ba451c382ee74f8ac4578f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:54:41 +0000 Subject: [PATCH 05/36] add explicit revisions to check-pass tests --- tests/ui/nll/polonius/assignment-kills-loans.rs | 5 ++++- tests/ui/nll/polonius/call-kills-loans.rs | 5 ++++- .../ui/nll/polonius/location-insensitive-scopes-liveness.rs | 6 ++++-- tests/ui/nll/polonius/storagedead-kills-loans.rs | 5 ++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/ui/nll/polonius/assignment-kills-loans.rs b/tests/ui/nll/polonius/assignment-kills-loans.rs index 182262e5c4b9..56ea9c2f1f93 100644 --- a/tests/ui/nll/polonius/assignment-kills-loans.rs +++ b/tests/ui/nll/polonius/assignment-kills-loans.rs @@ -4,8 +4,11 @@ // facts only on simple assignments, but not projections, incorrectly causing errors to be emitted // for code accepted by NLL. They are all variations from example code in the NLL RFC. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct List { value: T, diff --git a/tests/ui/nll/polonius/call-kills-loans.rs b/tests/ui/nll/polonius/call-kills-loans.rs index c02293c9a78a..0657c42f48cd 100644 --- a/tests/ui/nll/polonius/call-kills-loans.rs +++ b/tests/ui/nll/polonius/call-kills-loans.rs @@ -4,8 +4,11 @@ // by NLL but was incorrectly rejected by Polonius because of these // missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct Thing; diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs index cb56c8f6deb4..677b7aa1df86 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs +++ b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs @@ -5,9 +5,11 @@ // than `liveness::trace`, on some specific CFGs shapes: a variable was dead during tracing but its // regions were marked live later, and live loans were not recomputed at this point. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ revisions: nll polonius -//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius // minimized from wavefc-cli-3.0.0 fn repro1() { diff --git a/tests/ui/nll/polonius/storagedead-kills-loans.rs b/tests/ui/nll/polonius/storagedead-kills-loans.rs index 89cf919bb48e..75a37e6ece57 100644 --- a/tests/ui/nll/polonius/storagedead-kills-loans.rs +++ b/tests/ui/nll/polonius/storagedead-kills-loans.rs @@ -3,8 +3,11 @@ // is correctly accepted by NLL but was incorrectly rejected by // Polonius because of these missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius use std::{io, mem}; use std::io::Read; From 18d417fdcfc372dc81244169be19c581499091ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 13:03:56 +0000 Subject: [PATCH 06/36] add explicit revisions to polonius tests The blessed expectations were recently removed because they were only checked via the compare-mode. This switches to explicit revisions to ensure it doesn't happen again. - `assignment-to-differing-field` - `polonius-smoke-test` - `subset-relations` --- ...signment-to-differing-field.legacy.stderr} | 8 +-- ...ignment-to-differing-field.polonius.stderr | 51 ++++++++++++++++ .../polonius/assignment-to-differing-field.rs | 5 +- ...derr => polonius-smoke-test.legacy.stderr} | 8 +-- .../polonius-smoke-test.polonius.stderr | 59 +++++++++++++++++++ tests/ui/nll/polonius/polonius-smoke-test.rs | 8 ++- ....stderr => subset-relations.legacy.stderr} | 2 +- .../polonius/subset-relations.polonius.stderr | 14 +++++ tests/ui/nll/polonius/subset-relations.rs | 7 ++- 9 files changed, 148 insertions(+), 14 deletions(-) rename tests/ui/nll/polonius/{assignment-to-differing-field.stderr => assignment-to-differing-field.legacy.stderr} (92%) create mode 100644 tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr rename tests/ui/nll/polonius/{polonius-smoke-test.stderr => polonius-smoke-test.legacy.stderr} (91%) create mode 100644 tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr rename tests/ui/nll/polonius/{subset-relations.stderr => subset-relations.legacy.stderr} (92%) create mode 100644 tests/ui/nll/polonius/subset-relations.polonius.stderr diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr similarity index 92% rename from tests/ui/nll/polonius/assignment-to-differing-field.stderr rename to tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr index c46d010e4f57..cf5594dbd078 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.stderr +++ b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:20:21 + --> $DIR/assignment-to-differing-field.rs:23:21 | LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here @@ -11,7 +11,7 @@ LL | return result; | ------ returning this value requires that `list.0.value` is borrowed for `'a` error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:23:26 + --> $DIR/assignment-to-differing-field.rs:26:26 | LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here @@ -23,7 +23,7 @@ LL | list.1 = n; | ---------- assignment requires that `list.0.next` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:37:21 + --> $DIR/assignment-to-differing-field.rs:40:21 | LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here @@ -35,7 +35,7 @@ LL | return result; | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:40:26 + --> $DIR/assignment-to-differing-field.rs:43:26 | LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr new file mode 100644 index 000000000000..cf5594dbd078 --- /dev/null +++ b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr @@ -0,0 +1,51 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:23:21 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:26:26 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:40:21 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut ((((list.0).0).0).0).0.value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:43:26 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | *((((list.0).0).0).0).1 = n; + | --------------------------- assignment requires that `list.0.0.0.0.0.next` is borrowed for `'a` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.rs b/tests/ui/nll/polonius/assignment-to-differing-field.rs index fb6c95695254..9701cd2bb5e2 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.rs +++ b/tests/ui/nll/polonius/assignment-to-differing-field.rs @@ -4,7 +4,10 @@ // that we do not kill too many borrows. Assignments to the `.1` // field projections should leave the borrows on `.0` intact. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct List { value: T, diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr similarity index 91% rename from tests/ui/nll/polonius/polonius-smoke-test.stderr rename to tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr index a8a8267290de..1268f6167f85 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.stderr +++ b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr @@ -1,11 +1,11 @@ error[E0515]: cannot return reference to local variable `x` - --> $DIR/polonius-smoke-test.rs:6:5 + --> $DIR/polonius-smoke-test.rs:10:5 | LL | &x | ^^ returns a reference to data owned by the current function error[E0503]: cannot use `x` because it was mutably borrowed - --> $DIR/polonius-smoke-test.rs:12:13 + --> $DIR/polonius-smoke-test.rs:16:13 | LL | let y = &mut x; | ------ `x` is borrowed here @@ -15,7 +15,7 @@ LL | let w = y; | - borrow later used here error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/polonius-smoke-test.rs:18:13 + --> $DIR/polonius-smoke-test.rs:22:13 | LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { | - - let's call the lifetime of this reference `'1` @@ -35,7 +35,7 @@ LL + let y = &mut x.clone(); | error[E0505]: cannot move out of `s` because it is borrowed - --> $DIR/polonius-smoke-test.rs:42:5 + --> $DIR/polonius-smoke-test.rs:46:5 | LL | let s = &mut 1; | - binding `s` declared here diff --git a/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr new file mode 100644 index 000000000000..1268f6167f85 --- /dev/null +++ b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr @@ -0,0 +1,59 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/polonius-smoke-test.rs:10:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/polonius-smoke-test.rs:16:13 + | +LL | let y = &mut x; + | ------ `x` is borrowed here +LL | let z = x; + | ^ use of borrowed `x` +LL | let w = y; + | - borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/polonius-smoke-test.rs:22:13 + | +LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + | - - let's call the lifetime of this reference `'1` + | | + | binding `x` declared here +LL | let y = &mut *x; + | ------- borrow of `*x` occurs here +LL | let z = x; + | ^ move out of `x` occurs here +LL | y + | - returning this value requires that `*x` is borrowed for `'1` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &mut *x; +LL + let y = &mut x.clone(); + | + +error[E0505]: cannot move out of `s` because it is borrowed + --> $DIR/polonius-smoke-test.rs:46:5 + | +LL | let s = &mut 1; + | - binding `s` declared here +LL | let r = &mut *s; + | ------- borrow of `*s` occurs here +LL | let tmp = foo(&r); +LL | s; + | ^ move out of `s` occurs here +LL | tmp; + | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &mut *s; +LL + let r = &mut s.clone(); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0503, E0505, E0515. +For more information about an error, try `rustc --explain E0503`. diff --git a/tests/ui/nll/polonius/polonius-smoke-test.rs b/tests/ui/nll/polonius/polonius-smoke-test.rs index ea5cdb263f5d..7fbb3f9b47c3 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.rs +++ b/tests/ui/nll/polonius/polonius-smoke-test.rs @@ -1,5 +1,9 @@ -// Check that Polonius borrow check works for simple cases. -//@ compile-flags: -Z polonius +// Check that Polonius works for simple cases. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy pub fn return_ref_to_local() -> &'static i32 { let x = 0; diff --git a/tests/ui/nll/polonius/subset-relations.stderr b/tests/ui/nll/polonius/subset-relations.legacy.stderr similarity index 92% rename from tests/ui/nll/polonius/subset-relations.stderr rename to tests/ui/nll/polonius/subset-relations.legacy.stderr index 9deca6449a8f..10d42ca58d9d 100644 --- a/tests/ui/nll/polonius/subset-relations.stderr +++ b/tests/ui/nll/polonius/subset-relations.legacy.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/subset-relations.rs:10:5 + --> $DIR/subset-relations.rs:13:5 | LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { | -- -- lifetime `'b` defined here diff --git a/tests/ui/nll/polonius/subset-relations.polonius.stderr b/tests/ui/nll/polonius/subset-relations.polonius.stderr new file mode 100644 index 000000000000..10d42ca58d9d --- /dev/null +++ b/tests/ui/nll/polonius/subset-relations.polonius.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/subset-relations.rs:13:5 + | +LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | y + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/polonius/subset-relations.rs b/tests/ui/nll/polonius/subset-relations.rs index 3c1af1983cfd..d47e4e05533e 100644 --- a/tests/ui/nll/polonius/subset-relations.rs +++ b/tests/ui/nll/polonius/subset-relations.rs @@ -3,7 +3,10 @@ // two free regions outlive each other, without any evidence that this // relation holds. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy // returning `y` requires that `'b: 'a`, but it's not known to be true fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { @@ -22,7 +25,7 @@ fn implied_bounds_subset<'a, 'b>(x: &'a &'b mut u32) -> &'a u32 { // `'b: 'a` is declared, and `'a: 'c` is known via implied bounds: // `'b: 'c` is therefore known to hold transitively -fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { +fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { y } From a616fa565745e2b8e8e245826f9452eb4c6d1191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 31 Jan 2025 09:10:15 +0000 Subject: [PATCH 07/36] update NLL `get_default` test for poloniuses - it still mentions AST borrowck - it tracks errors that shouldn't exist and are fixed by polonius --- tests/ui/nll/get_default.legacy.stderr | 18 ++++++++++++++++++ ...t_default.stderr => get_default.nll.stderr} | 10 +++++----- tests/ui/nll/get_default.polonius.stderr | 18 ++++++++++++++++++ tests/ui/nll/get_default.rs | 17 ++++++++++------- 4 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 tests/ui/nll/get_default.legacy.stderr rename tests/ui/nll/{get_default.stderr => get_default.nll.stderr} (87%) create mode 100644 tests/ui/nll/get_default.polonius.stderr diff --git a/tests/ui/nll/get_default.legacy.stderr b/tests/ui/nll/get_default.legacy.stderr new file mode 100644 index 000000000000..699250312dc6 --- /dev/null +++ b/tests/ui/nll/get_default.legacy.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.stderr b/tests/ui/nll/get_default.nll.stderr similarity index 87% rename from tests/ui/nll/get_default.stderr rename to tests/ui/nll/get_default.nll.stderr index af79771e7e1b..9b5976ca7170 100644 --- a/tests/ui/nll/get_default.stderr +++ b/tests/ui/nll/get_default.nll.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:21:17 + --> $DIR/get_default.rs:24:17 | LL | fn ok(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -14,7 +14,7 @@ LL | map.set(String::new()); // Ideally, this would not error. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:32:17 + --> $DIR/get_default.rs:35:17 | LL | fn err(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -22,14 +22,14 @@ LL | loop { LL | match map.get() { | --- immutable borrow occurs here LL | Some(v) => { -LL | map.set(String::new()); // Both AST and MIR error here +LL | map.set(String::new()); // We always expect an error here. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | return v; | - returning this value requires that `*map` is borrowed for `'1` error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:37:17 + --> $DIR/get_default.rs:40:17 | LL | fn err(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -40,7 +40,7 @@ LL | match map.get() { LL | return v; | - returning this value requires that `*map` is borrowed for `'1` ... -LL | map.set(String::new()); // Ideally, just AST would error here +LL | map.set(String::new()); // Ideally, this would not error. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here error: aborting due to 3 previous errors diff --git a/tests/ui/nll/get_default.polonius.stderr b/tests/ui/nll/get_default.polonius.stderr new file mode 100644 index 000000000000..699250312dc6 --- /dev/null +++ b/tests/ui/nll/get_default.polonius.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.rs b/tests/ui/nll/get_default.rs index ffac8a33da10..c3c09e9c2c55 100644 --- a/tests/ui/nll/get_default.rs +++ b/tests/ui/nll/get_default.rs @@ -1,7 +1,10 @@ // Basic test for free regions in the NLL code. This test ought to -// report an error due to a reborrowing constraint. Right now, we get -// a variety of errors from the older, AST-based machinery (notably -// borrowck), and then we get the NLL error at the end. +// report an error due to a reborrowing constraint. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct Map { } @@ -19,7 +22,7 @@ fn ok(map: &mut Map) -> &String { } None => { map.set(String::new()); // Ideally, this would not error. - //~^ ERROR borrowed as immutable + //[nll]~^ ERROR borrowed as immutable } } } @@ -29,13 +32,13 @@ fn err(map: &mut Map) -> &String { loop { match map.get() { Some(v) => { - map.set(String::new()); // Both AST and MIR error here + map.set(String::new()); // We always expect an error here. //~^ ERROR borrowed as immutable return v; } None => { - map.set(String::new()); // Ideally, just AST would error here - //~^ ERROR borrowed as immutable + map.set(String::new()); // Ideally, this would not error. + //[nll]~^ ERROR borrowed as immutable } } } From 2c778c1e4ca8053e787998c9b18bb57222719a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 31 Jan 2025 13:05:10 +0100 Subject: [PATCH 08/36] ensure sufficient stack in tail call check --- compiler/rustc_mir_build/src/check_tail_calls.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 0659e3ea314b..921205428dbd 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -1,4 +1,5 @@ use rustc_abi::ExternAbi; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Applicability; use rustc_hir::LangItem; use rustc_hir::def::DefKind; @@ -344,12 +345,14 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { - if let ExprKind::Become { value } = expr.kind { - let call = &self.thir[value]; - self.check_tail_call(call, expr); - } + ensure_sufficient_stack(|| { + if let ExprKind::Become { value } = expr.kind { + let call = &self.thir[value]; + self.check_tail_call(call, expr); + } - visit::walk_expr(self, expr); + visit::walk_expr(self, expr); + }); } } From 94562ee1eae83d6cc80bda4d57b43bcdb7a842b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 31 Jan 2025 13:05:38 +0100 Subject: [PATCH 09/36] ensure sufficient stack in unsafety check --- compiler/rustc_mir_build/src/check_unsafety.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 995bc311b7c4..3980fdfc0b16 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::mem; use std::ops::Bound; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; @@ -473,7 +474,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => { let prev_id = self.hir_context; self.hir_context = hir_id; - self.visit_expr(&self.thir[value]); + ensure_sufficient_stack(|| { + self.visit_expr(&self.thir[value]); + }); self.hir_context = prev_id; return; // don't visit the whole expression } From 5f447a1ca6516fefe33fa0351aaf2c27b2911d98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Feb 2025 14:27:04 -0800 Subject: [PATCH 10/36] Add test of fn pointer without argument name --- tests/rustdoc/fn-pointer-arg-name.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/rustdoc/fn-pointer-arg-name.rs b/tests/rustdoc/fn-pointer-arg-name.rs index 3bde6e9ecfa8..212c79dfab08 100644 --- a/tests/rustdoc/fn-pointer-arg-name.rs +++ b/tests/rustdoc/fn-pointer-arg-name.rs @@ -3,3 +3,11 @@ //@ has foo/fn.f.html //@ has - '//pre[@class="rust item-decl"]' 'pub fn f(callback: fn(len: usize, foo: u32))' pub fn f(callback: fn(len: usize, foo: u32)) {} + +//@ has foo/fn.g.html +//@ has - '//pre[@class="rust item-decl"]' 'pub fn g(_: fn(_: usize, _: u32))' +pub fn g(_: fn(usize, _: u32)) {} + +//@ has foo/fn.mixed.html +//@ has - '//pre[@class="rust item-decl"]' 'pub fn mixed(_: fn(_: usize, foo: u32))' +pub fn mixed(_: fn(usize, foo: u32)) {} From b866debf3cdbc30f71b0800684236213ebbb2d8c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Feb 2025 14:54:02 -0800 Subject: [PATCH 11/36] Omit argument names from function pointers that do not have argument names --- src/librustdoc/clean/mod.rs | 22 ++++++++++++++----- src/librustdoc/html/format.rs | 4 +++- tests/rustdoc/assoc-consts.rs | 2 +- tests/rustdoc/fn-pointer-arg-name.rs | 2 +- tests/rustdoc/inherent-projections.rs | 2 +- tests/rustdoc/macro-higher-kinded-function.rs | 8 +++---- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7853e311a040..0d4ff6e7aa57 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1104,17 +1104,29 @@ fn clean_args_from_types_and_names<'tcx>( types: &[hir::Ty<'tcx>], names: &[Ident], ) -> Arguments { + fn nonempty_name(ident: &Ident) -> Option { + if ident.name == kw::Underscore || ident.name == kw::Empty { + None + } else { + Some(ident.name) + } + } + + // If at least one argument has a name, use `_` as the name of unnamed + // arguments. Otherwise omit argument names. + let default_name = if names.iter().any(|ident| nonempty_name(ident).is_some()) { + kw::Underscore + } else { + kw::Empty + }; + Arguments { values: types .iter() .enumerate() .map(|(i, ty)| Argument { type_: clean_ty(ty, cx), - name: names - .get(i) - .map(|ident| ident.name) - .filter(|ident| !ident.is_empty()) - .unwrap_or(kw::Underscore), + name: names.get(i).and_then(nonempty_name).unwrap_or(default_name), is_const: false, }) .collect(), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 20a8dc724914..ffd457c82737 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1408,7 +1408,9 @@ impl clean::Arguments { ) -> impl Display + 'a + Captures<'tcx> { fmt::from_fn(move |f| { for (i, input) in self.values.iter().enumerate() { - write!(f, "{}: ", input.name)?; + if !input.name.is_empty() { + write!(f, "{}: ", input.name)?; + } input.type_.print(cx).fmt(f)?; if i + 1 < self.values.len() { write!(f, ", ")?; diff --git a/tests/rustdoc/assoc-consts.rs b/tests/rustdoc/assoc-consts.rs index cb8e839541c3..247b5b180a86 100644 --- a/tests/rustdoc/assoc-consts.rs +++ b/tests/rustdoc/assoc-consts.rs @@ -45,7 +45,7 @@ pub fn f(_: &(ToString + 'static)) {} impl Bar { //@ has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.F"]' \ - // "const F: fn(_: &(dyn ToString + 'static))" + // "const F: fn(&(dyn ToString + 'static))" pub const F: fn(_: &(ToString + 'static)) = f; } diff --git a/tests/rustdoc/fn-pointer-arg-name.rs b/tests/rustdoc/fn-pointer-arg-name.rs index 212c79dfab08..0ee1964511a1 100644 --- a/tests/rustdoc/fn-pointer-arg-name.rs +++ b/tests/rustdoc/fn-pointer-arg-name.rs @@ -5,7 +5,7 @@ pub fn f(callback: fn(len: usize, foo: u32)) {} //@ has foo/fn.g.html -//@ has - '//pre[@class="rust item-decl"]' 'pub fn g(_: fn(_: usize, _: u32))' +//@ has - '//pre[@class="rust item-decl"]' 'pub fn g(_: fn(usize, u32))' pub fn g(_: fn(usize, _: u32)) {} //@ has foo/fn.mixed.html diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs index 4fc769f70f55..ced4db9a490c 100644 --- a/tests/rustdoc/inherent-projections.rs +++ b/tests/rustdoc/inherent-projections.rs @@ -13,7 +13,7 @@ impl Owner { } // Make sure we handle bound vars correctly. -//@ has 'inherent_projections/fn.user.html' '//pre[@class="rust item-decl"]' "user(_: for<'a> fn(_: Carrier<'a>::Focus))" +//@ has 'inherent_projections/fn.user.html' '//pre[@class="rust item-decl"]' "user(_: for<'a> fn(Carrier<'a>::Focus))" pub fn user(_: for<'a> fn(Carrier<'a>::Focus)) {} pub struct Carrier<'a>(&'a ()); diff --git a/tests/rustdoc/macro-higher-kinded-function.rs b/tests/rustdoc/macro-higher-kinded-function.rs index f14125d13091..738ea8fb3f15 100644 --- a/tests/rustdoc/macro-higher-kinded-function.rs +++ b/tests/rustdoc/macro-higher-kinded-function.rs @@ -11,10 +11,10 @@ macro_rules! gen { } //@ has 'foo/struct.Providers.html' -//@ has - '//*[@class="rust item-decl"]//code' "pub a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8," -//@ has - '//*[@class="rust item-decl"]//code' "pub b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16," -//@ has - '//*[@id="structfield.a"]/code' "a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8" -//@ has - '//*[@id="structfield.b"]/code' "b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16" +//@ has - '//*[@class="rust item-decl"]//code' "pub a: for<'tcx> fn(TyCtxt<'tcx>, u8) -> i8," +//@ has - '//*[@class="rust item-decl"]//code' "pub b: for<'tcx> fn(TyCtxt<'tcx>, u16) -> i16," +//@ has - '//*[@id="structfield.a"]/code' "a: for<'tcx> fn(TyCtxt<'tcx>, u8) -> i8" +//@ has - '//*[@id="structfield.b"]/code' "b: for<'tcx> fn(TyCtxt<'tcx>, u16) -> i16" gen! { (a, 'tcx, [u8], [i8]) (b, 'tcx, [u16], [i16]) From 199ef412c5ab80f4b1b5316de70b8f1e721c9648 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sat, 1 Feb 2025 20:27:14 -0500 Subject: [PATCH 12/36] test compile time fixes --- compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 914f2c21fa78..ce2161a07ebf 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -403,7 +403,7 @@ fn generate_lto_work( assert!(needs_thin_lto.is_empty()); let mut module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - if cgcx.lto == Lto::Fat { + if cgcx.lto == Lto::Fat && !autodiff.is_empty() { let config = cgcx.config(ModuleKind::Regular); module = unsafe { module.autodiff(cgcx, autodiff, config).unwrap() }; } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index c985ea04278d..d0edd58def3f 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1179,23 +1179,19 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } + let mut autodiff_mono_items: Vec<_> = vec![]; let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), + MonoItem::Fn(ref instance) => { + autodiff_mono_items.push((mono_item, instance)); + Some(instance.def_id()) + } MonoItem::Static(def_id) => Some(def_id), _ => None, }) .collect(); - let autodiff_mono_items: Vec<_> = items - .iter() - .filter_map(|item| match *item { - MonoItem::Fn(ref instance) => Some((item, instance)), - _ => None, - }) - .collect(); - let autodiff_items = autodiff::find_autodiff_source_functions(tcx, &usage_map, autodiff_mono_items); let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); From e47caa2810bc35655ca082836396e544436f5b54 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 2 Feb 2025 05:13:53 -0500 Subject: [PATCH 13/36] run less code if cfg(llvm_enzyme) isn't used --- compiler/rustc_monomorphize/src/partitioning.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index d0edd58def3f..3ef061195da5 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1179,11 +1179,15 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } + #[cfg(not(llvm_enzyme))] + let autodiff_mono_items: Vec<_> = vec![]; + #[cfg(llvm_enzyme)] let mut autodiff_mono_items: Vec<_> = vec![]; let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item { MonoItem::Fn(ref instance) => { + #[cfg(llvm_enzyme)] autodiff_mono_items.push((mono_item, instance)); Some(instance.def_id()) } From a375d1d15e67d9cf3013976c8231449edfec6c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:04:09 +0800 Subject: [PATCH 14/36] compiletest: cleanup `is_rustdoc` logic --- src/tools/compiletest/src/runtest.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ca48abda5fc1..0e2da2b02ca0 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1417,9 +1417,7 @@ impl<'test> TestCx<'test> { } fn is_rustdoc(&self) -> bool { - self.config.src_base.ends_with("rustdoc-ui") - || self.config.src_base.ends_with("rustdoc-js") - || self.config.src_base.ends_with("rustdoc-json") + matches!(self.config.suite.as_str(), "rustdoc-ui" | "rustdoc-js" | "rustdoc-json") } fn get_mir_dump_dir(&self) -> PathBuf { From 788a38953f11cef41663cb912d51126cb595fef4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 2 Feb 2025 14:23:59 +0100 Subject: [PATCH 15/36] miri: improve error when offset_from preconditions are violated --- compiler/rustc_const_eval/messages.ftl | 4 +++- .../src/interpret/intrinsics.rs | 20 ++++++++++++++++++- .../ptr_offset_from_different_allocs.rs | 5 +++++ .../ptr_offset_from_different_allocs.stderr | 15 ++++++++++++++ .../ptr_offset_from_different_ints.rs | 2 +- .../ptr_offset_from_different_ints.stderr | 4 ++-- .../fail/intrinsics/ptr_offset_from_oob.rs | 7 +++++++ .../intrinsics/ptr_offset_from_oob.stderr | 15 ++++++++++++++ tests/ui/const-ptr/forbidden_slices.stderr | 4 ++-- tests/ui/consts/offset_from_ub.rs | 10 +++++----- tests/ui/consts/offset_from_ub.stderr | 14 ++++++------- 11 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.stderr create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d600d223bffd..eecc6690f515 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -280,7 +280,9 @@ const_eval_nullary_intrinsic_fail = could not evaluate nullary intrinsic const_eval_offset_from_different_allocations = - `{$name}` called on pointers into different allocations + `{$name}` called on two different pointers that are not both derived from the same allocation +const_eval_offset_from_out_of_bounds = + `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second const_eval_offset_from_test = diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 9f5f2533e085..4ca317e3a1e5 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -319,7 +319,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check that the memory between them is dereferenceable at all, starting from the // origin pointer: `dist` is `a - b`, so it is based on `b`. - self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?; + self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest) + .map_err_kind(|_| { + // This could mean they point to different allocations, or they point to the same allocation + // but not the entire range between the pointers is in-bounds. + if let Ok((a_alloc_id, ..)) = self.ptr_try_get_alloc_id(a, 0) + && let Ok((b_alloc_id, ..)) = self.ptr_try_get_alloc_id(b, 0) + && a_alloc_id == b_alloc_id + { + err_ub_custom!( + fluent::const_eval_offset_from_out_of_bounds, + name = intrinsic_name, + ) + } else { + err_ub_custom!( + fluent::const_eval_offset_from_different_allocations, + name = intrinsic_name, + ) + } + })?; // Then check that this is also dereferenceable from `a`. This ensures that they are // derived from the same allocation. self.check_ptr_access_signed( diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.rs new file mode 100644 index 000000000000..5a997ad8ec40 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.rs @@ -0,0 +1,5 @@ +fn main() { + unsafe { + (&1_u8 as *const u8).offset_from(&2_u8); //~ERROR: not both derived from the same allocation + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.stderr new file mode 100644 index 000000000000..34d7c6a8021d --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_allocs.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation + --> tests/fail/intrinsics/ptr_offset_from_different_allocs.rs:LL:CC + | +LL | (&1_u8 as *const u8).offset_from(&2_u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/ptr_offset_from_different_allocs.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs index 0acda559d3a5..0d34e711ca79 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.rs @@ -15,6 +15,6 @@ fn main() { let _ = p1.byte_offset_from(p1); // UB because different pointers. - let _ = p1.byte_offset_from(p2); //~ERROR: no provenance + let _ = p1.byte_offset_from(p2); //~ERROR: not both derived from the same allocation } } diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr index 7ef66390fcda..897945d6d5da 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation --> tests/fail/intrinsics/ptr_offset_from_different_ints.rs:LL:CC | LL | let _ = p1.byte_offset_from(p2); - | ^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs new file mode 100644 index 000000000000..06f6b7a01174 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs @@ -0,0 +1,7 @@ +fn main() { + let mem = [0u8; 1]; + let ptr = mem.as_ptr(); + unsafe { + ptr.wrapping_add(4).offset_from(ptr); //~ERROR: the memory range between them is not in-bounds of an allocation + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr new file mode 100644 index 000000000000..67df633bef5b --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation + --> tests/fail/intrinsics/ptr_offset_from_oob.rs:LL:CC + | +LL | ptr.wrapping_add(4).offset_from(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/ptr_offset_from_oob.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index fad078ad2b2b..2e0c04dcf1ea 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -190,7 +190,7 @@ LL | from_ptr_range(ptr..ptr.add(1)) error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from_unsigned` called on pointers into different allocations + = note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::sub_ptr` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -205,7 +205,7 @@ LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).ad error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from_unsigned` called on pointers into different allocations + = note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::sub_ptr` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index 0232b03a813e..883569006050 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -17,7 +17,7 @@ pub const DIFFERENT_ALLOC: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| pointers into different allocations + //~| not both derived from the same allocation offset as usize }; @@ -37,7 +37,7 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| dangling pointer + //~| not both derived from the same allocation }; const OUT_OF_BOUNDS_1: isize = { @@ -46,7 +46,7 @@ const OUT_OF_BOUNDS_1: isize = { let end_ptr = (start_ptr).wrapping_add(length); // First ptr is out of bounds unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed - //~| expected a pointer to 10 bytes of memory + //~| the memory range between them is not in-bounds of an allocation }; const OUT_OF_BOUNDS_2: isize = { @@ -55,7 +55,7 @@ const OUT_OF_BOUNDS_2: isize = { let end_ptr = (start_ptr).wrapping_add(length); // Second ptr is out of bounds unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| expected a pointer to the end of 10 bytes of memory + //~| the memory range between them is not in-bounds of an allocation }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { @@ -64,7 +64,7 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } //~ERROR evaluation of constant value failed - //~| pointers into different allocations + //~| not both derived from the same allocation }; pub const TOO_FAR_APART1: isize = { diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index ac4597ff0119..1379365cc25c 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -2,12 +2,12 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:19:27 | LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on pointers into different allocations + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from` called on pointers into different allocations + = note: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -27,25 +27,25 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:39:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got 0x8[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:48:14 | LL | unsafe { ptr_offset_from(end_ptr, start_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got ALLOC0 which is only $BYTES bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:57:14 | LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC1+0xa which does not have enough space to the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:66:14 | LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:73:14 @@ -80,7 +80,7 @@ LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got a null pointer + = note: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL From 4d0cb4213b14457db5eb3ad21b1a65d5a91dd1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:16:48 +0800 Subject: [PATCH 16/36] compiletest: remove useless path join in `rustdoc_json` runtest logic --- src/tools/compiletest/src/runtest/rustdoc_json.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs index 31fdb0a5d13b..bf7eb2e109a4 100644 --- a/src/tools/compiletest/src/runtest/rustdoc_json.rs +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -16,13 +16,12 @@ impl TestCx<'_> { self.fatal_proc_rec("rustdoc failed!", &proc_res); } - let root = self.config.find_rust_src_root().unwrap(); let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); json_out.set_extension("json"); let res = self.run_command_to_procres( Command::new(self.config.jsondocck_path.as_ref().unwrap()) .arg("--doc-dir") - .arg(root.join(&out_dir)) + .arg(&out_dir) .arg("--template") .arg(&self.testpaths.file), ); From 05c88a31e7d421b311710571b80add5c0959a6d3 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Mon, 18 Nov 2024 04:07:29 +0700 Subject: [PATCH 17/36] Target modifiers (special marked options) are recorded in metainfo and compared to be equal in different crates --- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_metadata/messages.ftl | 10 + compiler/rustc_metadata/src/creader.rs | 97 ++++- compiler/rustc_metadata/src/errors.rs | 25 ++ compiler/rustc_metadata/src/rmeta/decoder.rs | 15 + compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +- compiler/rustc_metadata/src/rmeta/mod.rs | 5 +- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_session/src/config.rs | 8 +- compiler/rustc_session/src/lib.rs | 3 + compiler/rustc_session/src/options.rs | 337 +++++++++++++++++- src/librustdoc/config.rs | 9 +- .../auxiliary/default_reg_struct_return.rs | 20 ++ .../auxiliary/wrong_regparm.rs | 20 ++ .../auxiliary/wrong_regparm_and_ret.rs | 20 ++ .../defaults_check.error.stderr | 13 + tests/ui/target_modifiers/defaults_check.rs | 27 ++ ...incompatible_regparm.allow_no_value.stderr | 2 + ...ncompatible_regparm.error_generated.stderr | 13 + .../target_modifiers/incompatible_regparm.rs | 23 ++ tests/ui/target_modifiers/two_flags.rs | 23 ++ .../two_flags.unknown_allowed.stderr | 8 + 22 files changed, 666 insertions(+), 24 deletions(-) create mode 100644 tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs create mode 100644 tests/ui/target_modifiers/auxiliary/wrong_regparm.rs create mode 100644 tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs create mode 100644 tests/ui/target_modifiers/defaults_check.error.stderr create mode 100644 tests/ui/target_modifiers/defaults_check.rs create mode 100644 tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr create mode 100644 tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr create mode 100644 tests/ui/target_modifiers/incompatible_regparm.rs create mode 100644 tests/ui/target_modifiers/two_flags.rs create mode 100644 tests/ui/target_modifiers/two_flags.unknown_allowed.stderr diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 241bc35857a4..f9cb5f217a59 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -269,6 +269,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); + CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); krate } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d2b5ae531859..6fc84b06647f 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -113,6 +113,14 @@ metadata_incompatible_rustc = found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info} .help = please recompile that crate using this compiler ({$rustc_version}) (consider running `cargo clean` first) +metadata_incompatible_target_modifiers = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + +metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error +metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}` + metadata_incompatible_wasm_link = `wasm_import_module` is incompatible with other arguments in `#[link]` attributes @@ -284,6 +292,8 @@ metadata_unknown_link_kind = metadata_unknown_link_modifier = unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed +metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$flag_name}`, requested by `-Cunsafe-allow-abi-mismatch={$flag_name}` + metadata_unsupported_abi = ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index a6cd0ecafd0d..6bad83127900 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -23,7 +23,10 @@ use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; -use rustc_session::config::{self, CrateType, ExternLocation}; +use rustc_session::config::{ + self, CrateType, ExtendedTargetModifierInfo, ExternLocation, OptionsTargetModifiers, + TargetModifier, +}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; @@ -35,7 +38,9 @@ use tracing::{debug, info, trace}; use crate::errors; use crate::locator::{CrateError, CrateLocator, CratePaths}; -use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; +use crate::rmeta::{ + CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers, +}; /// The backend's way to give the crate store access to the metadata in a library. /// Note that it returns the raw metadata bytes stored in the library file, whether @@ -296,6 +301,94 @@ impl CStore { } } + fn report_target_modifiers_extended( + tcx: TyCtxt<'_>, + krate: &Crate, + mods: &TargetModifiers, + dep_mods: &TargetModifiers, + data: &CrateMetadata, + ) { + let span = krate.spans.inner_span.shrink_to_lo(); + let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; + let name = tcx.crate_name(LOCAL_CRATE); + let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); + let report_diff = |prefix: &String, + opt_name: &String, + flag_local_value: &String, + flag_extern_value: &String| { + if allowed_flag_mismatches.contains(&opt_name) { + return; + } + tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { + span, + extern_crate: data.name(), + local_crate: name, + flag_name: opt_name.clone(), + flag_name_prefixed: format!("-{}{}", prefix, opt_name), + flag_local_value: flag_local_value.to_string(), + flag_extern_value: flag_extern_value.to_string(), + }); + }; + let mut it1 = mods.iter().map(tmod_extender); + let mut it2 = dep_mods.iter().map(tmod_extender); + let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; + let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; + let no_val = "*".to_string(); + loop { + left_name_val = left_name_val.or_else(|| it1.next()); + right_name_val = right_name_val.or_else(|| it2.next()); + match (&left_name_val, &right_name_val) { + (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { + cmp::Ordering::Equal => { + if l.0.tech_value != r.0.tech_value { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name); + } + left_name_val = None; + right_name_val = None; + } + cmp::Ordering::Greater => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + cmp::Ordering::Less => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + }, + (Some(l), None) => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + (None, Some(r)) => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + (None, None) => break, + } + } + } + + pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) { + for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch { + if !OptionsTargetModifiers::is_target_modifier(flag_name) { + tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed { + span: krate.spans.inner_span.shrink_to_lo(), + flag_name: flag_name.clone(), + }); + } + } + let mods = tcx.sess.opts.gather_target_modifiers(); + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + let dep_mods = data.target_modifiers(); + if mods != dep_mods { + Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data); + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index cefc6498f68f..a77f9bc623b5 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -739,3 +739,28 @@ pub(crate) struct WasmCAbi { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiers { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub flag_local_value: String, + pub flag_extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_unknown_target_modifier_unsafe_allowed)] +pub struct UnknownTargetModifierUnsafeAllowed { + #[primary_span] + pub span: Span, + pub flag_name: String, +} diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e02c4871f35b..2ae4a6a6066d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -29,6 +29,7 @@ use rustc_middle::{bug, implement_ty_decoder}; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::{Decodable, Decoder}; use rustc_session::Session; +use rustc_session::config::TargetModifier; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_span::hygiene::HygieneDecodeContext; use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; @@ -73,6 +74,9 @@ impl MetadataBlob { /// own crate numbers. pub(crate) type CrateNumMap = IndexVec; +/// Target modifiers - abi or exploit mitigations flags +pub(crate) type TargetModifiers = Vec; + pub(crate) struct CrateMetadata { /// The primary crate data - binary metadata blob. blob: MetadataBlob, @@ -961,6 +965,13 @@ impl CrateRoot { ) -> impl ExactSizeIterator + Captures<'a> { self.crate_deps.decode(metadata) } + + pub(crate) fn decode_target_modifiers<'a>( + &self, + metadata: &'a MetadataBlob, + ) -> impl ExactSizeIterator + Captures<'a> { + self.target_modifiers.decode(metadata) + } } impl<'a> CrateMetadataRef<'a> { @@ -1883,6 +1894,10 @@ impl CrateMetadata { self.dependencies.push(cnum); } + pub(crate) fn target_modifiers(&self) -> TargetModifiers { + self.root.decode_target_modifiers(&self.blob).collect() + } + pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool { let update = Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c538ab99fb54..161388a290b0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::{AssocItemContainer, SymbolName}; use rustc_middle::util::common::to_readable_str; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; -use rustc_session::config::{CrateType, OptLevel}; +use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, @@ -692,6 +692,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode source_map. This needs to be done last, because encoding `Span`s tells us which // `SourceFiles` we actually need to encode. let source_map = stat!("source-map", || self.encode_source_map()); + let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); let root = stat!("final", || { let attrs = tcx.hir().krate_attrs(); @@ -735,6 +736,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { native_libraries, foreign_modules, source_map, + target_modifiers, traits, impls, incoherent_impls, @@ -2009,6 +2011,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(deps.iter().map(|(_, dep)| dep)) } + fn encode_target_modifiers(&mut self) -> LazyArray { + empty_proc_macro!(self); + let tcx = self.tcx; + self.lazy_array(tcx.sess.opts.gather_target_modifiers()) + } + fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> { empty_proc_macro!(self); let tcx = self.tcx; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4f9cdc9a474b..fd0f9bd05b31 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::num::NonZero; -pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers}; use decoder::{DecodeContext, Metadata}; use def_path_hash_map::DefPathHashMapRef; use encoder::EncodeContext; @@ -32,7 +32,7 @@ use rustc_middle::ty::{ use rustc_middle::util::Providers; use rustc_middle::{mir, trivially_parameterized_over_tcx}; use rustc_serialize::opaque::FileEncoder; -use rustc_session::config::SymbolManglingVersion; +use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; @@ -282,6 +282,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, source_map: LazyTable>>, + target_modifiers: LazyArray, compiler_builtins: bool, needs_allocator: bool, diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 6b6c6f3c72fe..8eaf0a58f708 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -101,6 +101,7 @@ trivially_parameterized_over_tcx! { rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, rustc_session::cstore::NativeLib, + rustc_session::config::TargetModifier, rustc_span::ExpnData, rustc_span::ExpnHash, rustc_span::ExpnId, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 97bd2670aa64..a301a0458018 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1191,6 +1191,7 @@ impl Default for Options { color: ColorConfig::Auto, logical_env: FxIndexMap::default(), verbose: false, + target_modifiers: BTreeMap::default(), } } } @@ -2337,14 +2338,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let crate_types = parse_crate_types_from_list(unparsed_crate_types) .unwrap_or_else(|e| early_dcx.early_fatal(e)); - let mut unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + + let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); check_error_format_stability(early_dcx, &unstable_opts, error_format); let output_types = parse_output_types(early_dcx, &unstable_opts, matches); - let mut cg = CodegenOptions::build(early_dcx, matches); + let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( early_dcx, &output_types, @@ -2615,6 +2618,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M color, logical_env, verbose, + target_modifiers, } } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index dcf86d1a4089..924350b8cbcd 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -4,6 +4,9 @@ #![feature(let_chains)] #![feature(map_many_mut)] #![feature(rustc_attrs)] +// To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums +// with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). +#![recursion_limit = "256"] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e5..b804a5881ccd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -10,6 +10,7 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl}; use rustc_feature::UnstableFeatures; +use rustc_macros::{Decodable, Encodable}; use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ @@ -59,18 +60,194 @@ macro_rules! hash_substruct { }; } +/// Extended target modifier info. +/// For example, when external target modifier is '-Zregparm=2': +/// Target modifier enum value + user value ('2') from external crate +/// is converted into description: prefix ('Z'), name ('regparm'), tech value ('Some(2)'). +pub struct ExtendedTargetModifierInfo { + /// Flag prefix (usually, 'C' for codegen flags or 'Z' for unstable flags) + pub prefix: String, + /// Flag name + pub name: String, + /// Flag parsed technical value + pub tech_value: String, +} + +/// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value) +/// which alter the ABI or effectiveness of exploit mitigations. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] +pub struct TargetModifier { + /// Option enum value + pub opt: OptionsTargetModifiers, + /// User-provided option value (before parsing) + pub value_name: String, +} + +impl TargetModifier { + pub fn extend(&self) -> ExtendedTargetModifierInfo { + self.opt.reparse(&self.value_name) + } +} + +fn tmod_push_impl( + opt: OptionsTargetModifiers, + tmod_vals: &BTreeMap, + tmods: &mut Vec, +) { + tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() }) +} + +macro_rules! tmod_push { + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => { + tmod_push_impl( + OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), + $tmod_vals, + $mods, + ); + }; +} + +macro_rules! gather_tmods { + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [SUBSTRUCT], [TARGET_MODIFIER]) => { + compile_error!("SUBSTRUCT can't be target modifier"); + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [UNTRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [SUBSTRUCT], []) => { + $opt_expr.gather_target_modifiers($mods, $tmod_vals); + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [UNTRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED_NO_CRATE_HASH], []) => {{}}; +} + +macro_rules! gather_tmods_top_level { + ($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT $substruct_enum:ident]) => { + $opt_expr.gather_target_modifiers($mods, $tmod_vals); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident TARGET_MODIFIER]) => { + compile_error!("Top level option can't be target modifier"); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident]) => {}; +} + +/// Macro for generating OptionsTargetsModifiers top-level enum with impl. +/// Will generate something like: +/// ```rust,ignore (illustrative) +/// pub enum OptionsTargetModifiers { +/// CodegenOptions(CodegenOptionsTargetModifiers), +/// UnstableOptions(UnstableOptionsTargetModifiers), +/// } +/// impl OptionsTargetModifiers { +/// pub fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo { +/// match self { +/// Self::CodegenOptions(v) => v.reparse(user_value), +/// Self::UnstableOptions(v) => v.reparse(user_value), +/// } +/// } +/// pub fn is_target_modifier(flag_name: &str) -> bool { +/// CodegenOptionsTargetModifiers::is_target_modifier(flag_name) || +/// UnstableOptionsTargetModifiers::is_target_modifier(flag_name) +/// } +/// } +/// ``` +macro_rules! top_level_tmod_enum { + ($( {$($optinfo:tt)*} ),* $(,)*) => { + top_level_tmod_enum! { @parse {}, (user_value){}; $($($optinfo)*|)* } + }; + // Termination + ( + @parse + {$($variant:tt($substruct_enum:tt))*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum OptionsTargetModifiers { + $($variant($substruct_enum)),* + } + impl OptionsTargetModifiers { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + $($substruct_enum::is_target_modifier(flag_name))||* + } + } + }; + // Adding SUBSTRUCT option group into $eout + ( + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + [SUBSTRUCT $substruct_enum:ident $variant:ident] | + $($tail:tt)* + ) => { + top_level_tmod_enum! { + @parse + { + $($eout)* + $variant($substruct_enum) + }, + ($puser_value){ + $($pout)* + Self::$variant(v) => v.reparse($puser_value), + }; + $($tail)* + } + }; + // Skipping non-target-modifier and non-substruct + ( + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + [$non_substruct:ident] | + $($tail:tt)* + ) => { + top_level_tmod_enum! { + @parse + { + $($eout)* + }, + ($puser_value){ + $($pout)* + }; + $($tail)* + } + }; +} + macro_rules! top_level_options { ( $( #[$top_level_attr:meta] )* pub struct Options { $( $( #[$attr:meta] )* - $opt:ident : $t:ty [$dep_tracking_marker:ident], + $opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident $variant:ident )?], )* } ) => ( + top_level_tmod_enum!( {$([$dep_tracking_marker $($tmod $variant),*])|*} ); + #[derive(Clone)] $( #[$top_level_attr] )* pub struct Options { $( $( #[$attr] )* pub $opt: $t - ),* + ),*, + pub target_modifiers: BTreeMap, } impl Options { @@ -98,6 +275,17 @@ macro_rules! top_level_options { })* hasher.finish() } + + pub fn gather_target_modifiers(&self) -> Vec { + let mut mods = Vec::::new(); + $({ + gather_tmods_top_level!($opt, + &self.$opt, &mut mods, &self.target_modifiers, + [$dep_tracking_marker $($tmod),*]); + })* + mods.sort_by(|a, b| a.opt.cmp(&b.opt)); + mods + } } ); } @@ -165,9 +353,9 @@ top_level_options!( #[rustc_lint_opt_deny_field_access("should only be used via `Config::hash_untracked_state`")] untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH], - unstable_opts: UnstableOptions [SUBSTRUCT], + unstable_opts: UnstableOptions [SUBSTRUCT UnstableOptionsTargetModifiers UnstableOptions], prints: Vec [UNTRACKED], - cg: CodegenOptions [SUBSTRUCT], + cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions], externs: Externs [UNTRACKED], crate_name: Option [TRACKED], /// Indicates how the compiler should treat unstable features. @@ -226,6 +414,98 @@ top_level_options!( } ); +macro_rules! tmod_enum_opt { + ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, $v:ident) => { + Some(OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt)) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, ) => { + None + }; +} + +macro_rules! tmod_enum { + ($tmod_enum_name:ident, $prefix:expr, $( {$($optinfo:tt)*} ),* $(,)*) => { + tmod_enum! { $tmod_enum_name, $prefix, @parse {}, (user_value){}; $($($optinfo)*|)* } + }; + // Termination + ( + $tmod_enum_name:ident, $prefix:expr, + @parse + {$($eout:tt)*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum $tmod_enum_name { + $($eout),* + } + impl $tmod_enum_name { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + match flag_name.replace('-', "_").as_str() { + $(stringify!($eout) => true,)* + _ => false, + } + } + } + }; + // Adding target-modifier option into $eout + ( + $tmod_enum_name:ident, $prefix:expr, + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + $opt:ident, $parse:ident, $t:ty, [TARGET_MODIFIER] | + $($tail:tt)* + ) => { + tmod_enum! { + $tmod_enum_name, $prefix, + @parse + { + $($eout)* + $opt + }, + ($puser_value){ + $($pout)* + Self::$opt => { + let mut parsed : $t = Default::default(); + parse::$parse(&mut parsed, Some($puser_value)); + ExtendedTargetModifierInfo { + prefix: $prefix.to_string(), + name: stringify!($opt).to_string().replace('_', "-"), + tech_value: format!("{:?}", parsed), + } + }, + }; + $($tail)* + } + }; + // Skipping non-target-modifier + ( + $tmod_enum_name:ident, $prefix:expr, + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + $opt:ident, $parse:ident, $t:ty, [] | + $($tail:tt)* + ) => { + tmod_enum! { + $tmod_enum_name, $prefix, + @parse + { + $($eout)* + }, + ($puser_value){ + $($pout)* + }; + $($tail)* + } + }; +} + /// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this /// macro is to define an interface that can be programmatically used by the option parser /// to initialize the struct without hardcoding field names all over the place. @@ -235,11 +515,11 @@ top_level_options!( /// generated code to parse an option into its respective field in the struct. There are a few /// hand-written parsers for parsing specific types of values in this module. macro_rules! options { - ($struct_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, + ($struct_name:ident, $tmod_enum_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, $($( #[$attr:meta] )* $opt:ident : $t:ty = ( $init:expr, $parse:ident, - [$dep_tracking_marker:ident], + [$dep_tracking_marker:ident $( $tmod:ident )?], $desc:expr $(, deprecated_do_nothing: $dnn:literal )?) ),* ,) => @@ -248,6 +528,8 @@ macro_rules! options { #[rustc_lint_opt_ty] pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } + tmod_enum!( $tmod_enum_name, $prefix, {$($opt, $parse, $t, [$($tmod),*])|*} ); + impl Default for $struct_name { fn default() -> $struct_name { $struct_name { $($opt: $init),* } @@ -258,8 +540,9 @@ macro_rules! options { pub fn build( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, ) -> $struct_name { - build_options(early_dcx, matches, $stat, $prefix, $outputname) + build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname) } fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 { @@ -279,11 +562,23 @@ macro_rules! options { ); hasher.finish() } + + pub fn gather_target_modifiers( + &self, + _mods: &mut Vec, + _tmod_vals: &BTreeMap, + ) { + $({ + gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals, + [$dep_tracking_marker], [$($tmod),*]); + })* + } } pub const $stat: OptionDescrs<$struct_name> = &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, - type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )? } ),* ]; + type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?, + tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; mod $optmod { $( @@ -328,6 +623,7 @@ pub struct OptionDesc { // description for option from options table desc: &'static str, is_deprecated_and_do_nothing: bool, + tmod: Option, } impl OptionDesc { @@ -344,6 +640,7 @@ impl OptionDesc { fn build_options( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, descrs: OptionDescrs, prefix: &str, outputname: &str, @@ -357,7 +654,14 @@ fn build_options( let option_to_lookup = key.replace('-', "_"); match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { - Some(OptionDesc { name: _, setter, type_desc, desc, is_deprecated_and_do_nothing }) => { + Some(OptionDesc { + name: _, + setter, + type_desc, + desc, + is_deprecated_and_do_nothing, + tmod, + }) => { if *is_deprecated_and_do_nothing { // deprecation works for prefixed options only assert!(!prefix.is_empty()); @@ -377,6 +681,11 @@ fn build_options( ), } } + if let Some(tmod) = *tmod + && let Some(value) = value + { + target_modifiers.insert(tmod, value.to_string()); + } } None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")), } @@ -1581,7 +1890,7 @@ pub mod parse { } options! { - CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", + CodegenOptions, CodegenOptionsTargetModifiers, CG_OPTIONS, cgopts, "C", "codegen", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -1712,6 +2021,8 @@ options! { target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ This feature is unsafe."), + unsafe_allow_abi_mismatch: Vec = (Vec::new(), parse_comma_list, [UNTRACKED], + "Allow incompatible target modifiers in dependency crates (comma separated list)"), // tidy-alphabetical-end // If you add a new option, please update: @@ -1720,7 +2031,7 @@ options! { } options! { - UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -2052,10 +2363,10 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), - reg_struct_return: bool = (false, parse_bool, [TRACKED], + reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "On x86-32 targets, it overrides the default ABI to return small structs in registers. It is UNSOUND to link together crates that use different values for this flag!"), - regparm: Option = (None, parse_opt_number, [TRACKED], + regparm: Option = (None, parse_opt_number, [TRACKED TARGET_MODIFIER], "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ in registers EAX, EDX, and ECX instead of on the stack for\ \"C\", \"cdecl\", and \"stdcall\" fn.\ diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 80bc6cebd2aa..7437f2045c59 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, - UnstableOptions, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, - parse_externs, parse_target_triple, + OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options, + parse_crate_types_from_list, parse_externs, parse_target_triple, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -387,8 +387,9 @@ impl Options { config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); - let codegen_options = CodegenOptions::build(early_dcx, matches); - let unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + let codegen_options = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); + let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let remap_path_prefix = match parse_remap_path_prefix(matches) { Ok(prefix_mappings) => prefix_mappings, diff --git a/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs new file mode 100644 index 000000000000..355e7c56e946 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs new file mode 100644 index 000000000000..2e16f1ee747c --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs new file mode 100644 index 000000000000..39c6be9d5892 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Zreg-struct-return=true -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr new file mode 100644 index 000000000000..c545dd710690 --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` + --> $DIR/defaults_check.rs:20:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return` + = help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/defaults_check.rs b/tests/ui/target_modifiers/defaults_check.rs new file mode 100644 index 000000000000..b8f4848d3a42 --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.rs @@ -0,0 +1,27 @@ +// Tests that default unspecified target modifier value in dependency crate is ok linked +// with the same value, explicitly specified +//@ aux-crate:default_reg_struct_return=default_reg_struct_return.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +//@ revisions:error ok ok_explicit +//@[ok] compile-flags: +//@[ok_explicit] compile-flags: -Zreg-struct-return=false +//@[error] compile-flags: -Zreg-struct-return=true + +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@[ok] build-pass +//@[ok_explicit] build-pass + +#![crate_type = "rlib"] +//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + default_reg_struct_return::somefun(); +} diff --git a/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr new file mode 100644 index 000000000000..8597332825b5 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr @@ -0,0 +1,2 @@ +error: codegen option `unsafe-allow-abi-mismatch` requires a comma-separated list of strings (C unsafe-allow-abi-mismatch=) + diff --git a/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr new file mode 100644 index 000000000000..692fc7a4e3fa --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` + --> $DIR/incompatible_regparm.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zregparm` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm` + = help: set `-Zregparm=2` in this crate or `-Zregparm=1` in `wrong_regparm` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/incompatible_regparm.rs b/tests/ui/target_modifiers/incompatible_regparm.rs new file mode 100644 index 000000000000..e866c5aa891b --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm=wrong_regparm.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=1 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:error_generated allow_regparm_mismatch allow_no_value + +//@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm +//@[allow_regparm_mismatch] build-pass +//@[allow_no_value] compile-flags: -Cunsafe-allow-abi-mismatch + +#![crate_type = "rlib"] +//[error_generated]~^ ERROR mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.rs b/tests/ui/target_modifiers/two_flags.rs new file mode 100644 index 000000000000..ca17117a267b --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm_and_ret=wrong_regparm_and_ret.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:two_allowed unknown_allowed + +//@[two_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=regparm,reg-struct-return +//@[two_allowed] build-pass +//@[unknown_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=unknown_flag -Zregparm=2 -Zreg-struct-return=true + +#![crate_type = "rlib"] +//[unknown_allowed]~^ ERROR unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm_and_ret::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr new file mode 100644 index 000000000000..c8040c6e389b --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr @@ -0,0 +1,8 @@ +error: unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` + --> $DIR/two_flags.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + +error: aborting due to 1 previous error + From 17e4aec4492c23c27b17e3f58e6992aa9a559928 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Feb 2025 09:02:34 +1100 Subject: [PATCH 18/36] `TypeVisitable` doesn't require `Clone`. `TypeFoldable` does, because it involves the production of new values. But `TypeVisitable` only involves the visiting of values. --- compiler/rustc_type_ir/src/fold.rs | 2 +- compiler/rustc_type_ir/src/visit.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d337a1a8ad9b..711e42ff0558 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -73,7 +73,7 @@ type Never = std::convert::Infallible; /// which means in practice almost every foldable type needs to also be /// visitable. (However, there are some types that are visitable without being /// foldable.) -pub trait TypeFoldable: TypeVisitable { +pub trait TypeFoldable: TypeVisitable + Clone { /// The entry point for folding. To fold a value `t` with a folder `f` /// call: `t.try_fold_with(f)`. /// diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 3213638afb25..0a0749421705 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -59,7 +59,7 @@ use crate::{self as ty, Interner, TypeFlags}; /// /// To implement this conveniently, use the derive macro located in /// `rustc_macros`. -pub trait TypeVisitable: fmt::Debug + Clone { +pub trait TypeVisitable: fmt::Debug { /// The entry point for visiting. To visit a value `t` with a visitor `v` /// call: `t.visit_with(v)`. /// From f0b6d660c9c13ab7b19da9e12aeb4dcab45e544b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Feb 2025 08:59:17 +1100 Subject: [PATCH 19/36] Derive `Clone` on fewer THIR types. Some of these were never necessary, and some were facilitated by the previous commit. --- compiler/rustc_middle/src/thir.rs | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 86014c34b458..89df9bab53a5 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -18,7 +18,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::{IndexVec, newtype_index}; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable}; +use rustc_macros::{HashStable, TypeVisitable}; use rustc_middle::middle::region; use rustc_middle::mir::interpret::AllocId; use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; @@ -53,7 +53,7 @@ macro_rules! thir_with_elements { /// A container for a THIR body. /// /// This can be indexed directly by any THIR index (e.g. [`ExprId`]). - #[derive(Debug, HashStable, Clone)] + #[derive(Debug, HashStable)] pub struct Thir<'tcx> { $( pub $field_name: $field_ty, @@ -98,14 +98,14 @@ thir_with_elements! { params: ParamId => Param<'tcx> => "p{}", } -#[derive(Debug, HashStable, Clone)] +#[derive(Debug, HashStable)] pub enum BodyTy<'tcx> { Const(Ty<'tcx>), Fn(FnSig<'tcx>), } /// Description of a type-checked function parameter. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Param<'tcx> { /// The pattern that appears in the parameter list, or None for implicit parameters. pub pat: Option>>, @@ -125,7 +125,7 @@ pub enum LintLevel { Explicit(HirId), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Block { /// Whether the block itself has a label. Used by `label: {}` /// and `try` blocks. @@ -145,7 +145,7 @@ pub struct Block { type UserTy<'tcx> = Option>>; -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct AdtExpr<'tcx> { /// The ADT we're constructing. pub adt_def: AdtDef<'tcx>, @@ -162,7 +162,7 @@ pub struct AdtExpr<'tcx> { pub base: AdtExprBase<'tcx>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum AdtExprBase<'tcx> { /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. None, @@ -175,7 +175,7 @@ pub enum AdtExprBase<'tcx> { DefaultFields(Box<[Ty<'tcx>]>), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct ClosureExpr<'tcx> { pub closure_id: LocalDefId, pub args: UpvarArgs<'tcx>, @@ -184,7 +184,7 @@ pub struct ClosureExpr<'tcx> { pub fake_reads: Vec<(ExprId, FakeReadCause, HirId)>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct InlineAsmExpr<'tcx> { pub asm_macro: AsmMacro, pub template: &'tcx [InlineAsmTemplatePiece], @@ -202,12 +202,12 @@ pub enum BlockSafety { ExplicitUnsafe(HirId), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Stmt<'tcx> { pub kind: StmtKind<'tcx>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum StmtKind<'tcx> { /// An expression with a trailing semicolon. Expr { @@ -247,11 +247,11 @@ pub enum StmtKind<'tcx> { }, } -#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, HashStable)] pub struct LocalVarId(pub HirId); /// A THIR expression. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Expr<'tcx> { /// kind of expression pub kind: ExprKind<'tcx>, @@ -278,7 +278,7 @@ pub struct TempLifetime { pub backwards_incompatible: Option, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { /// `Scope`s are used to explicitly mark destruction scopes, /// and to track the `HirId` of the expressions within the scope. @@ -543,20 +543,20 @@ pub enum ExprKind<'tcx> { /// Represents the association of a field identifier and an expression. /// /// This is used in struct constructors. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct FieldExpr { pub name: FieldIdx, pub expr: ExprId, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct FruInfo<'tcx> { pub base: ExprId, pub field_types: Box<[Ty<'tcx>]>, } /// A `match` arm. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Arm<'tcx> { pub pattern: Box>, pub guard: Option, @@ -574,7 +574,7 @@ pub enum LogicalOp { Or, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -612,13 +612,13 @@ pub enum InlineAsmOperand<'tcx> { }, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, pub pattern: Box>, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct Pat<'tcx> { pub ty: Ty<'tcx>, pub span: Span, @@ -726,7 +726,7 @@ impl<'tcx> Pat<'tcx> { } } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct Ascription<'tcx> { pub annotation: CanonicalUserTypeAnnotation<'tcx>, /// Variance to use when relating the `user_ty` to the **type of the value being @@ -750,7 +750,7 @@ pub struct Ascription<'tcx> { pub variance: ty::Variance, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub enum PatKind<'tcx> { /// A wildcard pattern: `_`. Wild, From c371363650a580f9908f985af94e065e8beed0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 2 Feb 2025 12:27:27 +0100 Subject: [PATCH 20/36] LTA: Check where-clauses for well-formedness at the def site --- .../rustc_hir_analysis/src/check/wfcheck.rs | 24 ++++++++++--------- .../def-site-param-defaults-wf.rs | 9 +++++++ .../def-site-param-defaults-wf.stderr | 20 ++++++++++++++++ .../lazy-type-alias/def-site-predicates-wf.rs | 13 ++++++++++ .../def-site-predicates-wf.stderr | 23 ++++++++++++++++++ 5 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs create mode 100644 tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr create mode 100644 tests/ui/lazy-type-alias/def-site-predicates-wf.rs create mode 100644 tests/ui/lazy-type-alias/def-site-predicates-wf.stderr diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c9837ca3716c..121d23cd83d5 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -328,16 +328,20 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() hir::ItemKind::TraitAlias(..) => check_trait(tcx, item), // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => Ok(()), - hir::ItemKind::TyAlias(hir_ty, hir_generics) => { - if tcx.type_alias_is_lazy(item.owner_id) { - // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. - // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. - let res = check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); - check_variances_for_type_defn(tcx, item, hir_generics); - res - } else { + hir::ItemKind::TyAlias(hir_ty, hir_generics) if tcx.type_alias_is_lazy(item.owner_id) => { + let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + let ty = tcx.type_of(def_id).instantiate_identity(); + let item_ty = wfcx.normalize(hir_ty.span, Some(WellFormedLoc::Ty(def_id)), ty); + wfcx.register_wf_obligation( + hir_ty.span, + Some(WellFormedLoc::Ty(def_id)), + item_ty.into(), + ); + check_where_clauses(wfcx, item.span, def_id); Ok(()) - } + }); + check_variances_for_type_defn(tcx, item, hir_generics); + res } _ => Ok(()), }; @@ -1276,7 +1280,6 @@ fn check_item_fn( enum UnsizedHandling { Forbid, - Allow, AllowIfForeignTail, } @@ -1294,7 +1297,6 @@ fn check_item_type( let forbid_unsized = match unsized_handling { UnsizedHandling::Forbid => true, - UnsizedHandling::Allow => false, UnsizedHandling::AllowIfForeignTail => { let tail = tcx.struct_tail_for_codegen(item_ty, wfcx.infcx.typing_env(wfcx.param_env)); diff --git a/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs new file mode 100644 index 000000000000..90bb0a294f81 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs @@ -0,0 +1,9 @@ +//! Ensure that we check generic parameter defaults for well-formedness at the definition site. +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias, const N: usize = {0 - 1}> = T; +//~^ ERROR evaluation of constant value failed +//~| ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr new file mode 100644 index 000000000000..da0021ccaf4d --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/def-site-param-defaults-wf.rs:5:44 + | +LL | type Alias, const N: usize = {0 - 1}> = T; + | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-param-defaults-wf.rs:5:16 + | +LL | type Alias, const N: usize = {0 - 1}> = T; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/lazy-type-alias/def-site-predicates-wf.rs b/tests/ui/lazy-type-alias/def-site-predicates-wf.rs new file mode 100644 index 000000000000..5d9235347cb8 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-predicates-wf.rs @@ -0,0 +1,13 @@ +//! Ensure that we check the predicates at the definition site for well-formedness. +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias0 = () +where + Vec:; //~ ERROR the size for values of type `str` cannot be known at compilation time + +type Alias1 = () +where + Vec: Sized; //~ ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr b/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr new file mode 100644 index 000000000000..b44e702c35cb --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr @@ -0,0 +1,23 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-predicates-wf.rs:7:5 + | +LL | Vec:; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-predicates-wf.rs:11:15 + | +LL | Vec: Sized; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From baa1cdde2976dbdbe76b194c7032c388da444135 Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Fri, 31 Jan 2025 07:10:15 -0800 Subject: [PATCH 21/36] Docs for f16 and f128: correct a typo and add details --- library/core/src/primitive_docs.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index c5f029363e58..bbf5939fe1b0 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1160,9 +1160,9 @@ impl (T,) {} /// /// Note that most common platforms will not support `f16` in hardware without enabling extra target /// features, with the notable exception of Apple Silicon (also known as M1, M2, etc.) processors. -/// Hardware support on x86-64 requires the avx512fp16 feature, while RISC-V requires Zhf. -/// Usually the fallback implementation will be to use `f32` hardware if it exists, and convert -/// between `f16` and `f32` when performing math. +/// Hardware support on x86/x86-64 requires the avx512fp16 or avx10.1 features, while RISC-V requires +/// Zfh, and Arm/AArch64 requires FEAT_FP16. Usually the fallback implementation will be to use `f32` +/// hardware if it exists, and convert between `f16` and `f32` when performing math. /// /// *[See also the `std::f16::consts` module](crate::f16::consts).* /// @@ -1344,10 +1344,10 @@ mod prim_f64 {} /// quad-precision values][wikipedia] for more information. /// /// Note that no platforms have hardware support for `f128` without enabling target specific features, -/// as for all instruction set architectures `f128` is considered an optional feature. -/// Only Power ISA ("PowerPC") and RISC-V specify it, and only certain microarchitectures -/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, -/// so it will always be a software implementation significantly slower than `f64`. +/// as for all instruction set architectures `f128` is considered an optional feature. Only Power ISA +/// ("PowerPC") and RISC-V (via the Q extension) specify it, and only certain microarchitectures +/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, so it will always +/// be a software implementation significantly slower than `f64`. /// /// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On /// x86 in particular, these functions do link but their results are always incorrect._ From f88f0a8c0ea9cea48c6aba7381d5460619035e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 1 Feb 2025 22:47:08 +0100 Subject: [PATCH 22/36] Remove a footgun-y feature / relic of the past from the compiletest DSL --- src/tools/compiletest/src/header.rs | 8 ---- tests/assembly/small_data_threshold.rs | 60 +++++++++++++------------- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 8c96554738e6..452a2e9a9d51 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -882,14 +882,6 @@ fn iter_header( } let ln = ln.trim(); - // Assume that any directives will be found before the first module or function. This - // doesn't seem to be an optimization with a warm page cache. Maybe with a cold one. - // FIXME(jieyouxu): this will cause `//@` directives in the rest of the test file to - // not be checked. - if ln.starts_with("fn") || ln.starts_with("mod") { - return; - } - let Some(directive_line) = line_directive(line_number, comment, ln) else { continue; }; diff --git a/tests/assembly/small_data_threshold.rs b/tests/assembly/small_data_threshold.rs index d3ba144600e8..bed515915b85 100644 --- a/tests/assembly/small_data_threshold.rs +++ b/tests/assembly/small_data_threshold.rs @@ -58,35 +58,35 @@ static mut Z: u64 = 0; // Currently, only MIPS and RISCV successfully put any objects in the small data // sections so the U/V/W/X tests are skipped on Hexagon and M68K -//@ RISCV: .section .sdata -//@ RISCV-NOT: .section -//@ RISCV: U: -//@ RISCV: .section .sbss -//@ RISCV-NOT: .section -//@ RISCV: V: -//@ RISCV: .section .sdata -//@ RISCV-NOT: .section -//@ RISCV: W: -//@ RISCV: .section .sbss -//@ RISCV-NOT: .section -//@ RISCV: X: +// RISCV: .section .sdata +// RISCV-NOT: .section +// RISCV: U: +// RISCV: .section .sbss +// RISCV-NOT: .section +// RISCV: V: +// RISCV: .section .sdata +// RISCV-NOT: .section +// RISCV: W: +// RISCV: .section .sbss +// RISCV-NOT: .section +// RISCV: X: -//@ MIPS: .section .sdata -//@ MIPS-NOT: .section -//@ MIPS: U: -//@ MIPS: .section .sbss -//@ MIPS-NOT: .section -//@ MIPS: V: -//@ MIPS: .section .sdata -//@ MIPS-NOT: .section -//@ MIPS: W: -//@ MIPS: .section .sbss -//@ MIPS-NOT: .section -//@ MIPS: X: +// MIPS: .section .sdata +// MIPS-NOT: .section +// MIPS: U: +// MIPS: .section .sbss +// MIPS-NOT: .section +// MIPS: V: +// MIPS: .section .sdata +// MIPS-NOT: .section +// MIPS: W: +// MIPS: .section .sbss +// MIPS-NOT: .section +// MIPS: X: -//@ CHECK: .section .data.Y, -//@ CHECK-NOT: .section -//@ CHECK: Y: -//@ CHECK: .section .bss.Z, -//@ CHECK-NOT: .section -//@ CHECK: Z: +// CHECK: .section .data.Y, +// CHECK-NOT: .section +// CHECK: Y: +// CHECK: .section .bss.Z, +// CHECK-NOT: .section +// CHECK: Z: From e661514bda5a06d33f5b872a1a88bb72b47b064f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 31 Jan 2025 16:52:39 +1100 Subject: [PATCH 23/36] Remove hook calling via `TyCtxtAt`. All hooks receive a `TyCtxtAt` argument. Currently hooks can be called through `TyCtxtAt` or `TyCtxt`. In the latter case, a `TyCtxtAt` is constructed with a dummy span and passed to the hook. However, in practice hooks are never called through `TyCtxtAt`, and always receive a dummy span. (I confirmed this via code inspection, and double-checked it by temporarily making the `TyCtxtAt` code path panic and running all the tests.) This commit removes all the `TyCtxtAt` machinery for hooks. All hooks now receive `TyCtxt` instead of `TyCtxtAt`. There are two existing hooks that use `TyCtxtAt::span`: `const_caller_location_provider` and `try_destructure_mir_constant_for_user_output`. For both hooks the span is always a dummy span, probably unintentionally. This dummy span use is now explicit. If a non-dummy span is needed for these two hooks it would be easy to add it as an extra argument because hooks are less constrained than queries. --- .../rustc_const_eval/src/const_eval/mod.rs | 9 +++++---- .../src/util/caller_location.rs | 9 ++++----- compiler/rustc_incremental/src/lib.rs | 2 +- .../src/rmeta/decoder/cstore_impl.rs | 6 +++--- compiler/rustc_middle/src/hooks/mod.rs | 20 +++---------------- .../rustc_mir_transform/src/coverage/query.rs | 4 +--- compiler/rustc_monomorphize/src/collector.rs | 4 ++-- compiler/rustc_query_impl/src/lib.rs | 5 ++--- 8 files changed, 21 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 34f795bda759..c0438fb3ff81 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,7 +1,7 @@ // Not in interpret to make sure we do not use private implementation details use rustc_abi::VariantIdx; -use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::query::Key; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -35,16 +35,17 @@ pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeC #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( - tcx: TyCtxtAt<'tcx>, + tcx: TyCtxt<'tcx>, val: mir::ConstValue<'tcx>, ty: Ty<'tcx>, ) -> Option> { let typing_env = ty::TypingEnv::fully_monomorphized(); - let (ecx, op) = mk_eval_cx_for_const_val(tcx, typing_env, val, ty)?; + // FIXME: use a proper span here? + let (ecx, op) = mk_eval_cx_for_const_val(tcx.at(rustc_span::DUMMY_SP), typing_env, val, ty)?; // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match ty.kind() { - ty::Array(_, len) => (len.try_to_target_usize(tcx.tcx)? as usize, None, op), + ty::Array(_, len) => (len.try_to_target_usize(tcx)? as usize, None, op), ty::Adt(def, _) if def.variants().is_empty() => { return None; } diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 6dd9447cf5a5..e926040e9ba1 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,7 +1,6 @@ use rustc_hir::LangItem; -use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; use tracing::trace; @@ -48,15 +47,15 @@ fn alloc_caller_location<'tcx>( } pub(crate) fn const_caller_location_provider( - tcx: TyCtxtAt<'_>, + tcx: TyCtxt<'_>, file: Symbol, line: u32, col: u32, ) -> mir::ConstValue<'_> { trace!("const_caller_location: {}:{}:{}", file, line, col); let mut ecx = mk_eval_cx_to_read_const_val( - tcx.tcx, - tcx.span, + tcx, + rustc_span::DUMMY_SP, // FIXME: use a proper span here? ty::TypingEnv::fully_monomorphized(), CanAccessMutGlobal::No, ); diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 6dab6468870a..563ed7614c60 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -24,7 +24,7 @@ use rustc_middle::util::Providers; #[allow(missing_docs)] pub fn provide(providers: &mut Providers) { providers.hooks.save_dep_graph = - |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx.tcx)); + |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx)); } rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index da07ad8f6c07..d727602cbd53 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -689,7 +689,7 @@ fn provide_cstore_hooks(providers: &mut Providers) { providers.hooks.def_path_hash_to_def_id_extern = |tcx, hash, stable_crate_id| { // If this is a DefPathHash from an upstream crate, let the CrateStore map // it to a DefId. - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); let cnum = *tcx .untracked() .stable_crate_ids @@ -702,11 +702,11 @@ fn provide_cstore_hooks(providers: &mut Providers) { }; providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); cstore.get_crate_data(cnum).expn_hash_to_expn_id(tcx.sess, index_guess, hash) }; providers.hooks.import_source_files = |tcx, cnum| { - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); let cdata = cstore.get_crate_data(cnum); for file_index in 0..cdata.root.source_map.size() { cdata.imported_source_file(file_index as u32, tcx.sess); diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 2be242364c11..276a02b4e0fe 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -6,11 +6,9 @@ use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_session::StableCrateId; use rustc_span::def_id::{CrateNum, LocalDefId}; -use rustc_span::{DUMMY_SP, ExpnHash, ExpnId}; -use tracing::instrument; +use rustc_span::{ExpnHash, ExpnId}; use crate::mir; -use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; macro_rules! declare_hooks { @@ -22,26 +20,14 @@ macro_rules! declare_hooks { #[inline(always)] pub fn $name(self, $($arg: $K,)*) -> $V { - self.at(DUMMY_SP).$name($($arg,)*) - } - )* - } - - impl<'tcx> TyCtxtAt<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - #[instrument(level = "debug", skip(self), ret)] - pub fn $name(self, $($arg: $K,)*) -> $V - { - (self.tcx.hooks.$name)(self, $($arg,)*) + (self.hooks.$name)(self, $($arg,)*) } )* } pub struct Providers { $(pub $name: for<'tcx> fn( - TyCtxtAt<'tcx>, + TyCtxt<'tcx>, $($arg: $K,)* ) -> $V,)* } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 5e7b46182dcf..a849ed4c3e27 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -6,7 +6,6 @@ use rustc_middle::mir::coverage::{ FunctionCoverageInfo, MappingKind, Op, }; use rustc_middle::mir::{Body, Statement, StatementKind}; -use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::def_id::LocalDefId; @@ -15,8 +14,7 @@ use tracing::trace; /// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { - providers.hooks.is_eligible_for_coverage = - |TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id); + providers.hooks.is_eligible_for_coverage = is_eligible_for_coverage; providers.queries.coverage_attr_on = coverage_attr_on; providers.queries.coverage_ids_info = coverage_ids_info; } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 246faed50e36..745f020301f3 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -953,7 +953,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { +fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -976,7 +976,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - return true; } - if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(*tcx).is_some() { + if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d2bb0b3f2771..bbc83cca99d8 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -224,7 +224,6 @@ pub fn query_system<'a>( rustc_middle::rustc_query_append! { define_queries! } pub fn provide(providers: &mut rustc_middle::util::Providers) { - providers.hooks.alloc_self_profile_query_strings = - |tcx| alloc_self_profile_query_strings(tcx.tcx); - providers.hooks.query_key_hash_verify_all = |tcx| query_key_hash_verify_all(tcx.tcx); + providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings; + providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all; } From 8c5e6a20d00a8b19a297b8b6eaccfab31a0dde65 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Mon, 3 Feb 2025 06:53:58 +0000 Subject: [PATCH 24/36] override default config profile on tarballs This is the same logic used in the Python script https://github.com/rust-lang/rust/blob/613bdd49978298648ed05ace086bd1ecad54b44a/src/bootstrap/bootstrap.py#L1273-L1274 Signed-off-by: onur-ozkan --- src/bootstrap/src/core/config/config.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 98490118f7d6..781d0e602c33 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -700,7 +700,7 @@ trait Merge { impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { build, install, llvm, rust, dist, target, profile: _, change_id }: Self, + TomlConfig { build, install, llvm, rust, dist, target, profile, change_id }: Self, replace: ReplaceOpt, ) { fn do_merge(x: &mut Option, y: Option, replace: ReplaceOpt) { @@ -712,7 +712,10 @@ impl Merge for TomlConfig { } } } + self.change_id.inner.merge(change_id.inner, replace); + self.profile.merge(profile, replace); + do_merge(&mut self.build, build, replace); do_merge(&mut self.install, install, replace); do_merge(&mut self.llvm, llvm, replace); @@ -1505,6 +1508,10 @@ impl Config { build.cargo = build.cargo.take().or(std::env::var_os("CARGO").map(|p| p.into())); } + if GitInfo::new(false, &config.src).is_from_tarball() && toml.profile.is_none() { + toml.profile = Some("dist".into()); + } + if let Some(include) = &toml.profile { // Allows creating alias for profile names, allowing // profiles to be renamed while maintaining back compatibility From d4dce76e19be44e7496f2184c98e9725bd8d0588 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:26:26 +0100 Subject: [PATCH 25/36] ci: move dist-x86_64-msvc to windows 2025 --- src/ci/github-actions/jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 2bbe37565808..58a9eb696665 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -558,7 +558,7 @@ auto: SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-8c + <<: *job-windows-25-8c - name: dist-i686-msvc env: From 623d6e8ca423e37daf435136da32697e3cdd605b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 3 Feb 2025 21:31:06 +1100 Subject: [PATCH 26/36] Notes on types/traits used for in-memory query caching When the word "cache" appears in the context of the query system, it often isn't obvious whether that is referring to the in-memory query cache or the on-disk incremental cache. For these types, we can assure the reader that they are for in-memory caching. --- .../rustc_data_structures/src/vec_cache.rs | 13 ++++++++++++ compiler/rustc_middle/src/query/keys.rs | 6 ++++++ .../rustc_query_system/src/query/caches.rs | 20 ++++++++++++++++++- .../rustc_query_system/src/query/plumbing.rs | 8 ++++---- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index eb251b587c80..2ff60ab7f36f 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -206,6 +206,19 @@ impl SlotIndex { } } +/// In-memory cache for queries whose keys are densely-numbered IDs +/// (e.g `CrateNum`, `LocalDefId`), and can therefore be used as indices +/// into a dense vector of cached values. +/// +/// (As of [#124780] the underlying storage is not an actual `Vec`, but rather +/// a series of increasingly-large buckets, for improved performance when the +/// parallel frontend is using multiple threads.) +/// +/// Each entry in the cache stores the query's return value (`V`), and also +/// an associated index (`I`), which in practice is a `DepNodeIndex` used for +/// query dependency tracking. +/// +/// [#124780]: https://github.com/rust-lang/rust/pull/124780 pub struct VecCache { // Entries per bucket: // Bucket 0: 4096 2^12 diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 949d8303385c..1489d57aba64 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -20,6 +20,12 @@ pub struct LocalCrate; /// The `Key` trait controls what types can legally be used as the key /// for a query. pub trait Key: Sized { + /// The type of in-memory cache to use for queries with this key type. + /// + /// In practice the cache type must implement [`QueryCache`], though that + /// constraint is not enforced here. + /// + /// [`QueryCache`]: rustc_query_system::query::QueryCache // N.B. Most of the keys down below have `type Cache = DefaultCache;`, // it would be reasonable to use associated type defaults, to remove the duplication... // diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index e6f3d97742d3..e11123dff26a 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -11,18 +11,30 @@ use rustc_span::def_id::{DefId, DefIndex}; use crate::dep_graph::DepNodeIndex; +/// Trait for types that serve as an in-memory cache for query results, +/// for a given key (argument) type and value (return) type. +/// +/// Types implementing this trait are associated with actual key/value types +/// by the `Cache` associated type of the `rustc_middle::query::Key` trait. pub trait QueryCache: Sized { type Key: Hash + Eq + Copy + Debug; type Value: Copy; - /// Checks if the query is already computed and in the cache. + /// Returns the cached value (and other information) associated with the + /// given key, if it is present in the cache. fn lookup(&self, key: &Self::Key) -> Option<(Self::Value, DepNodeIndex)>; + /// Adds a key/value entry to this cache. + /// + /// Called by some part of the query system, after having obtained the + /// value by executing the query or loading a cached value from disk. fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); } +/// In-memory cache for queries whose keys aren't suitable for any of the +/// more specialized kinds of cache. Backed by a sharded hashmap. pub struct DefaultCache { cache: Sharded>, } @@ -67,6 +79,8 @@ where } } +/// In-memory cache for queries whose key type only has one value (e.g. `()`). +/// The cache therefore only needs to store one query return value. pub struct SingleCache { cache: OnceLock<(V, DepNodeIndex)>, } @@ -101,6 +115,10 @@ where } } +/// In-memory cache for queries whose key is a [`DefId`]. +/// +/// Selects between one of two internal caches, depending on whether the key +/// is a local ID or foreign-crate ID. pub struct DefIdCache { /// Stores the local DefIds in a dense map. Local queries are much more often dense, so this is /// a win over hashing query keys at marginal memory cost (~5% at most) compared to FxHashMap. diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 6fb5e37d2d06..18aae8c00b24 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -222,10 +222,10 @@ pub struct CycleError { pub cycle: Vec, } -/// Checks if the query is already computed and in the cache. -/// It returns the shard index and a lock guard to the shard, -/// which will be used if the query is not in the cache and we need -/// to compute it. +/// Checks whether there is already a value for this key in the in-memory +/// query cache, returning that value if present. +/// +/// (Also performs some associated bookkeeping, if a value was found.) #[inline(always)] pub fn try_get_cached(tcx: Tcx, cache: &C, key: &C::Key) -> Option where From f8b01b3d19a362f2b2416bf14773c54aa5f07324 Mon Sep 17 00:00:00 2001 From: Pyrode Date: Mon, 3 Feb 2025 17:48:39 +0530 Subject: [PATCH 27/36] OnceCell & OnceLock docs: Using (un)initialized consistently --- library/core/src/cell/once.rs | 53 +++++++++++++------------ library/std/src/sync/once_lock.rs | 65 +++++++++++++++++-------------- 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 6a85791916a6..c6c96571d33c 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -8,6 +8,9 @@ use crate::{fmt, mem}; /// only immutable references can be obtained unless one has a mutable reference to the cell /// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// +/// A `OnceCell` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// /// [`RefCell`]: crate::cell::RefCell @@ -35,7 +38,7 @@ pub struct OnceCell { } impl OnceCell { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -46,7 +49,7 @@ impl OnceCell { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -56,19 +59,19 @@ impl OnceCell { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut().as_mut() } - /// Sets the contents of the cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// # Errors /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. + /// This method returns `Ok(())` if the cell was uninitialized + /// and `Err(value)` if it was already initialized. /// /// # Examples /// @@ -92,13 +95,13 @@ impl OnceCell { } } - /// Sets the contents of the cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was + /// uninitialized, then returns a reference to it. /// /// # Errors /// - /// This method returns `Ok(&value)` if the cell was empty and - /// `Err(¤t_value, value)` if it was full. + /// This method returns `Ok(&value)` if the cell was uninitialized + /// and `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -130,12 +133,12 @@ impl OnceCell { Ok(slot.insert(value)) } - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. + /// Gets the contents of the cell, initializing it to `f()` + /// if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -164,11 +167,11 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, - /// initializing it with `f` if the cell was empty. + /// initializing it to `f()` if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -199,13 +202,13 @@ impl OnceCell { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -239,12 +242,12 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -256,7 +259,7 @@ impl OnceCell { /// /// let mut cell: OnceCell = OnceCell::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -295,7 +298,7 @@ impl OnceCell { /// Consumes the cell, returning the wrapped value. /// - /// Returns `None` if the cell was empty. + /// Returns `None` if the cell was uninitialized. /// /// # Examples /// @@ -321,7 +324,7 @@ impl OnceCell { /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceCell` is uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 49f2dafd8fd9..f2945f489640 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,6 +13,9 @@ use crate::sync::Once; /// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock /// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// +/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -126,7 +129,7 @@ pub struct OnceLock { } impl OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -141,8 +144,8 @@ impl OnceLock { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -156,7 +159,8 @@ impl OnceLock { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. This method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -196,12 +200,13 @@ impl OnceLock { unsafe { self.get_unchecked() } } - /// Sets the contents of this cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. /// - /// Returns `Ok(())` if the cell's value was set by this call. + /// Returns `Ok(())` if the cell was uninitialized and + /// `Err(value)` if the cell was already initialized. /// /// # Examples /// @@ -230,13 +235,15 @@ impl OnceLock { } } - /// Sets the contents of this cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was uninitialized, + /// then returns a reference to it. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `try_insert` returns, though not necessarily the + /// one provided. /// - /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// Returns `Ok(&value)` if the cell was uninitialized and + /// `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -269,8 +276,8 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. + /// Gets the contents of the cell, initializing it to `f()` if the cell + /// was uninitialized. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -278,7 +285,7 @@ impl OnceLock { /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. The @@ -308,13 +315,13 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. + /// it to `f()` if the cell was uninitialized. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -345,13 +352,13 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. @@ -397,14 +404,14 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// # Examples @@ -416,7 +423,7 @@ impl OnceLock { /// /// let mut cell: OnceLock = OnceLock::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -440,7 +447,7 @@ impl OnceLock { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was empty. + /// `None` if the cell was uninitialized. /// /// # Examples /// @@ -462,7 +469,7 @@ impl OnceLock { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -528,7 +535,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -537,7 +544,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -562,7 +569,7 @@ impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] impl Default for OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. /// /// # Example /// From 6e0dfc813cf4035994743c3d355f004e27cbd144 Mon Sep 17 00:00:00 2001 From: FedericoBruzzone Date: Sun, 2 Feb 2025 11:46:21 +0100 Subject: [PATCH 28/36] Refactor using the type-level constant value `ty::Value` Signed-off-by: FedericoBruzzone --- .../src/const_eval/valtrees.rs | 42 +++++++------- compiler/rustc_const_eval/src/lib.rs | 10 +--- compiler/rustc_middle/src/mir/pretty.rs | 11 ++-- compiler/rustc_middle/src/ty/print/pretty.rs | 56 ++++++++++--------- .../rustc_middle/src/ty/structural_impls.rs | 2 +- 5 files changed, 62 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 4d625f76aba2..c35910a706b2 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -273,59 +273,63 @@ pub(crate) fn eval_to_valtree<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. // FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function -// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately. #[instrument(skip(tcx), level = "debug", ret)] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, - valtree: ty::ValTree<'tcx>, + cv: ty::Value<'tcx>, ) -> mir::ConstValue<'tcx> { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s // (those for constants with type bool, int, uint, float or char). // For all other types we create an `MPlace` and fill that by walking // the `ValTree` and using `place_projection` and `place_field` to // create inner `MPlace`s which are filled recursively. - // FIXME Does this need an example? - match *ty.kind() { + // FIXME: Does this need an example? + match *cv.ty.kind() { ty::FnDef(..) => { - assert!(valtree.unwrap_branch().is_empty()); + assert!(cv.valtree.unwrap_branch().is_empty()); mir::ConstValue::ZeroSized } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { - match valtree { + match cv.valtree { ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), ty::ValTree::Branch(_) => bug!( "ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf" ), } } - ty::Pat(ty, _) => valtree_to_const_value(tcx, typing_env, ty, valtree), + ty::Pat(ty, _) => { + let cv = ty::Value { valtree: cv.valtree, ty }; + valtree_to_const_value(tcx, typing_env, cv) + } ty::Ref(_, inner_ty, _) => { let mut ecx = mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); - let imm = valtree_to_ref(&mut ecx, valtree, inner_ty); - let imm = - ImmTy::from_immediate(imm, tcx.layout_of(typing_env.as_query_input(ty)).unwrap()); + let imm = valtree_to_ref(&mut ecx, cv.valtree, inner_ty); + let imm = ImmTy::from_immediate( + imm, + tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap(), + ); op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) } ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { - let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); + let layout = tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap(); if layout.is_zst() { // Fast path to avoid some allocations. return mir::ConstValue::ZeroSized; } if layout.backend_repr.is_scalar() - && (matches!(ty.kind(), ty::Tuple(_)) - || matches!(ty.kind(), ty::Adt(def, _) if def.is_struct())) + && (matches!(cv.ty.kind(), ty::Tuple(_)) + || matches!(cv.ty.kind(), ty::Adt(def, _) if def.is_struct())) { // A Scalar tuple/struct; we can avoid creating an allocation. - let branches = valtree.unwrap_branch(); + let branches = cv.valtree.unwrap_branch(); // Find the non-ZST field. (There can be aligned ZST!) for (i, &inner_valtree) in branches.iter().enumerate() { let field = layout.field(&LayoutCx::new(tcx, typing_env), i); if !field.is_zst() { - return valtree_to_const_value(tcx, typing_env, field.ty, inner_valtree); + let cv = ty::Value { valtree: inner_valtree, ty: field.ty }; + return valtree_to_const_value(tcx, typing_env, cv); } } bug!("could not find non-ZST field during in {layout:#?}"); @@ -335,9 +339,9 @@ pub fn valtree_to_const_value<'tcx>( mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); // Need to create a place for this valtree. - let place = create_valtree_place(&mut ecx, layout, valtree); + let place = create_valtree_place(&mut ecx, layout, cv.valtree); - valtree_into_mplace(&mut ecx, &place, valtree); + valtree_into_mplace(&mut ecx, &place, cv.valtree); dump_place(&ecx, &place); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); @@ -362,7 +366,7 @@ pub fn valtree_to_const_value<'tcx>( | ty::Slice(_) | ty::Dynamic(..) | ty::UnsafeBinder(_) => { - bug!("no ValTree should have been created for type {:?}", ty.kind()) + bug!("no ValTree should have been created for type {:?}", cv.ty.kind()) } } } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index ecf9745b7794..b44d2a01d57c 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -46,14 +46,8 @@ pub fn provide(providers: &mut Providers) { }; providers.hooks.try_destructure_mir_constant_for_user_output = const_eval::try_destructure_mir_constant_for_user_output; - providers.valtree_to_const_val = |tcx, cv| { - const_eval::valtree_to_const_value( - tcx, - ty::TypingEnv::fully_monomorphized(), - cv.ty, - cv.valtree, - ) - }; + providers.valtree_to_const_val = + |tcx, cv| const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), cv); providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a318bacb866d..b5e491ae3e10 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -12,6 +12,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use tracing::trace; +use ty::print::PrettyPrinter; use super::graphviz::write_mir_fn_graphviz; use crate::mir::interpret::ConstAllocation; @@ -1429,10 +1430,10 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { }) }; - // FIXME: call pretty_print_const_valtree? - let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { - ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"), - ty::ValTree::Branch(_) => "Branch(..)".to_string(), + let fmt_valtree = |cv: &ty::Value<'tcx>| { + let mut cx = FmtPrinter::new(self.tcx, Namespace::ValueNS); + cx.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap(); + cx.into_buffer() }; let val = match const_ { @@ -1442,7 +1443,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) } ty::ConstKind::Value(cv) => { - format!("ty::Valtree({})", fmt_valtree(&cv.valtree)) + format!("ty::Valtree({})", fmt_valtree(&cv)) } // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`. ty::ConstKind::Error(_) => "Error".to_string(), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 018fcc66aeed..10d350fbd5cf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1485,7 +1485,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), ty::ConstKind::Value(cv) => { - return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty); + return self.pretty_print_const_valtree(cv, print_ty); } ty::ConstKind::Bound(debruijn, bound_var) => { @@ -1785,48 +1785,49 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately. fn pretty_print_const_valtree( &mut self, - valtree: ty::ValTree<'tcx>, - ty: Ty<'tcx>, + cv: ty::Value<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { define_scoped_cx!(self); if self.should_print_verbose() { - p!(write("ValTree({:?}: ", valtree), print(ty), ")"); + p!(write("ValTree({:?}: ", cv.valtree), print(cv.ty), ")"); return Ok(()); } let u8_type = self.tcx().types.u8; - match (valtree, ty.kind()) { + match (cv.valtree, cv.ty.kind()) { (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { ty::Slice(t) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { - bug!( - "expected to convert valtree {:?} to raw bytes for type {:?}", - valtree, - t - ) - }); + let bytes = + cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { + bug!( + "expected to convert valtree {:?} to raw bytes for type {:?}", + cv.valtree, + t + ) + }); return self.pretty_print_byte_str(bytes); } ty::Str => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { - bug!("expected to convert valtree to raw bytes for type {:?}", ty) - }); + let bytes = + cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { + bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty) + }); p!(write("{:?}", String::from_utf8_lossy(bytes))); return Ok(()); } _ => { + let cv = ty::Value { valtree: cv.valtree, ty: *inner_ty }; p!("&"); - p!(pretty_print_const_valtree(valtree, *inner_ty, print_ty)); + p!(pretty_print_const_valtree(cv, print_ty)); return Ok(()); } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { + let bytes = cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); @@ -1835,10 +1836,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // Aggregates, printed as array/tuple/struct/variant construction syntax. (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { - let contents = - self.tcx().destructure_const(ty::Const::new_value(self.tcx(), valtree, ty)); + let contents = self.tcx().destructure_const(ty::Const::new_value( + self.tcx(), + cv.valtree, + cv.ty, + )); let fields = contents.fields.iter().copied(); - match *ty.kind() { + match *cv.ty.kind() { ty::Array(..) => { p!("[", comma_sep(fields), "]"); } @@ -1855,7 +1859,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { write!(this, "unreachable()")?; Ok(()) }, - |this| this.print_type(ty), + |this| this.print_type(cv.ty), ": ", )?; } @@ -1892,7 +1896,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return self.pretty_print_const_scalar_int(leaf, *inner_ty, print_ty); } (ty::ValTree::Leaf(leaf), _) => { - return self.pretty_print_const_scalar_int(leaf, ty, print_ty); + return self.pretty_print_const_scalar_int(leaf, cv.ty, print_ty); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading // their fields instead of just dumping the memory. @@ -1900,13 +1904,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // fallback - if valtree == ty::ValTree::zst() { + if cv.valtree == ty::ValTree::zst() { p!(write("")); } else { - p!(write("{:?}", valtree)); + p!(write("{:?}", cv.valtree)); } if print_ty { - p!(": ", print(ty)); + p!(": ", print(cv.ty)); } Ok(()) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 9e9de4fb0644..03b26b445380 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -170,7 +170,7 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> { bug!("we checked that this is a valtree") }; let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(cv.valtree, cv.ty, /*print_ty*/ true)?; + cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?; f.write_str(&cx.into_buffer()) }); } From 190a8a65dbc520aad88154c0e9d187fdf5880f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Mon, 3 Feb 2025 18:50:25 +0800 Subject: [PATCH 29/36] tests: remove redundant `//@ ignore-{wasm,wasm32,emscripten}` --- tests/ui/lifetimes/tail-expr-lock-poisoning.rs | 1 - tests/ui/panics/issue-47429-short-backtraces.rs | 2 -- tests/ui/panics/issue-47429-short-backtraces.run.stderr | 2 +- tests/ui/panics/runtime-switch.rs | 1 - tests/ui/panics/runtime-switch.run.stderr | 2 +- tests/ui/panics/short-ice-remove-middle-frames-2.rs | 1 - tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr | 2 +- tests/ui/panics/short-ice-remove-middle-frames.rs | 1 - tests/ui/panics/short-ice-remove-middle-frames.run.stderr | 2 +- tests/ui/test-attrs/test-panic-abort-disabled.rs | 2 -- tests/ui/test-attrs/test-panic-abort-nocapture.rs | 2 -- tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr | 4 ++-- tests/ui/test-attrs/test-panic-abort.rs | 2 -- tests/ui/test-attrs/test-panic-abort.run.stdout | 2 +- 14 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs index d7c7b2e24143..1c838a9e5e19 100644 --- a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs +++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs @@ -1,5 +1,4 @@ //@ revisions: edition2021 edition2024 -//@ ignore-wasm no panic support //@ needs-subprocess //@ [edition2024] edition: 2024 //@ run-pass diff --git a/tests/ui/panics/issue-47429-short-backtraces.rs b/tests/ui/panics/issue-47429-short-backtraces.rs index 4a73ebe8712d..378ade67bfd0 100644 --- a/tests/ui/panics/issue-47429-short-backtraces.rs +++ b/tests/ui/panics/issue-47429-short-backtraces.rs @@ -17,8 +17,6 @@ //@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-wasm no panic support -//@ ignore-emscripten no panic support //@ ignore-fuchsia Backtraces not symbolized //@ needs-subprocess diff --git a/tests/ui/panics/issue-47429-short-backtraces.run.stderr b/tests/ui/panics/issue-47429-short-backtraces.run.stderr index c6e2d13fb5da..32dc6592271d 100644 --- a/tests/ui/panics/issue-47429-short-backtraces.run.stderr +++ b/tests/ui/panics/issue-47429-short-backtraces.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:26:5: +thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:24:5: explicit panic stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/runtime-switch.rs b/tests/ui/panics/runtime-switch.rs index 9f0be8c7ddfa..7d5b41693407 100644 --- a/tests/ui/panics/runtime-switch.rs +++ b/tests/ui/panics/runtime-switch.rs @@ -18,7 +18,6 @@ //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename //@ ignore-wasm no backtrace support -//@ ignore-emscripten no panic support //@ ignore-fuchsia Backtrace not symbolized //@ needs-subprocess diff --git a/tests/ui/panics/runtime-switch.run.stderr b/tests/ui/panics/runtime-switch.run.stderr index 458a0ee534a9..70ed127af864 100644 --- a/tests/ui/panics/runtime-switch.run.stderr +++ b/tests/ui/panics/runtime-switch.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/runtime-switch.rs:29:5: +thread 'main' panicked at $DIR/runtime-switch.rs:28:5: explicit panic stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.rs b/tests/ui/panics/short-ice-remove-middle-frames-2.rs index 48f60b141706..660530f0eada 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.rs @@ -5,7 +5,6 @@ //@ needs-unwind //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-emscripten no panic //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr index 1fddcca69510..664d51e185dd 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:63:5: +thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:62:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames.rs b/tests/ui/panics/short-ice-remove-middle-frames.rs index 216c51277995..41fb6e9950e6 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames.rs @@ -5,7 +5,6 @@ //@ needs-unwind //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-emscripten no panic //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. diff --git a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr index 630b09d8d1ec..e966462331fc 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/short-ice-remove-middle-frames.rs:59:5: +thread 'main' panicked at $DIR/short-ice-remove-middle-frames.rs:58:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/test-attrs/test-panic-abort-disabled.rs b/tests/ui/test-attrs/test-panic-abort-disabled.rs index 05dd9395c2b0..e83be65f9250 100644 --- a/tests/ui/test-attrs/test-panic-abort-disabled.rs +++ b/tests/ui/test-attrs/test-panic-abort-disabled.rs @@ -4,8 +4,6 @@ //@ run-flags: --test-threads=1 //@ needs-unwind -//@ ignore-wasm no panic support -//@ ignore-emscripten no panic support //@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.rs b/tests/ui/test-attrs/test-panic-abort-nocapture.rs index f7e15dbdbc30..6a1025ea087c 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.rs +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.rs @@ -7,8 +7,6 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ ignore-android #120567 -//@ ignore-wasm no panic support -//@ ignore-emscripten no panic support //@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr index 256811711703..b9f267838b19 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr @@ -1,11 +1,11 @@ -thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:34:5: +thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:32:5: assertion `left == right` failed left: 2 right: 4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:28:5: +thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:26:5: assertion `left == right` failed left: 2 right: 4 diff --git a/tests/ui/test-attrs/test-panic-abort.rs b/tests/ui/test-attrs/test-panic-abort.rs index 951cf54346b4..6c9b641fb6fb 100644 --- a/tests/ui/test-attrs/test-panic-abort.rs +++ b/tests/ui/test-attrs/test-panic-abort.rs @@ -7,8 +7,6 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ ignore-android #120567 -//@ ignore-wasm no panic support -//@ ignore-emscripten no panic support //@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort.run.stdout b/tests/ui/test-attrs/test-panic-abort.run.stdout index 844808e86370..0faa7f0dfcec 100644 --- a/tests/ui/test-attrs/test-panic-abort.run.stdout +++ b/tests/ui/test-attrs/test-panic-abort.run.stdout @@ -18,7 +18,7 @@ testing123 ---- it_fails stderr ---- testing321 -thread 'main' panicked at $DIR/test-panic-abort.rs:39:5: +thread 'main' panicked at $DIR/test-panic-abort.rs:37:5: assertion `left == right` failed left: 2 right: 5 From f6c1c5718a6ad0c5a929dc36f597477350fa79ef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 18:13:58 +0100 Subject: [PATCH 30/36] platform-support docs: fix x87 errata footnotes --- src/doc/rustc/src/platform-support.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 8227dfa043e3..d43b890e0d08 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -309,13 +309,13 @@ target | std | host | notes [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] -[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium +[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium [^x86_32-floats-x87] [`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] -`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] -[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] +`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku with SSE [^x86_32-floats-return-ABI] +[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd w/o SSE [^x86_32-floats-x87] [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] -[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD [^x86_32-floats-return-ABI] -[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS +[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD with SSE2 [^x86_32-floats-return-ABI] +[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS w/o SSE [^x86_32-floats-x87] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] From f755f4cd1a2450da62b53f98f662d445f1f048b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 18:18:13 +0100 Subject: [PATCH 31/36] add rustc_abi to control ABI decisions LLVM does not have flags for, and use it for x86 softfloat --- compiler/rustc_target/src/spec/json.rs | 15 ++++++ compiler/rustc_target/src/spec/mod.rs | 49 +++++++++++++++-- .../src/spec/targets/i686_unknown_uefi.rs | 3 +- .../src/spec/targets/x86_64_unknown_none.rs | 5 +- .../src/spec/targets/x86_64_unknown_uefi.rs | 3 +- compiler/rustc_target/src/target_features.rs | 52 +++++++++++++------ ...dden-hardfloat-target-feature-attribute.rs | 1 + ...-hardfloat-target-feature-attribute.stderr | 2 +- .../forbidden-hardfloat-target-feature-cfg.rs | 14 ----- ...oat-target-feature-flag-disable-implied.rs | 3 ++ ...dfloat-target-feature-flag-disable-neon.rs | 1 + ...n-hardfloat-target-feature-flag-disable.rs | 2 + ...forbidden-hardfloat-target-feature-flag.rs | 12 +++++ ...idden-hardfloat-target-feature-flag.stderr | 11 ++++ .../forbidden-target-feature-attribute.rs | 7 +-- .../forbidden-target-feature-attribute.stderr | 8 +-- .../forbidden-target-feature-cfg.rs | 9 ++-- .../forbidden-target-feature-flag-disable.rs | 8 +-- ...rbidden-target-feature-flag-disable.stderr | 2 +- .../forbidden-target-feature-flag.rs | 8 +-- .../forbidden-target-feature-flag.stderr | 2 +- 21 files changed, 159 insertions(+), 58 deletions(-) delete mode 100644 tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs create mode 100644 tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs create mode 100644 tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 9cdc0801b1f0..1ec22bbd73b8 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -128,6 +128,19 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, RustcAbi) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(rustc_abi) => base.$key_name = Some(rustc_abi), + _ => return Some(Err(format!( + "'{s}' is not a valid value for rustc-abi. \ + Use 'x86-softfloat' or leave the field unset." + ))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, RelocModel) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -611,6 +624,7 @@ impl Target { key!(llvm_mcount_intrinsic, optional); key!(llvm_abiname); key!(llvm_floatabi, FloatAbi)?; + key!(rustc_abi, RustcAbi)?; key!(relax_elf_relocations, bool); key!(llvm_args, list); key!(use_ctors_section, bool); @@ -786,6 +800,7 @@ impl ToJson for Target { target_option_val!(llvm_mcount_intrinsic); target_option_val!(llvm_abiname); target_option_val!(llvm_floatabi); + target_option_val!(rustc_abi); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); target_option_val!(use_ctors_section); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bcd2aff54bb1..b029eae4b7d0 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1114,6 +1114,33 @@ impl ToJson for FloatAbi { } } +/// The Rustc-specific variant of the ABI used for this target. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RustcAbi { + /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. + X86Softfloat, +} + +impl FromStr for RustcAbi { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "x86-softfloat" => RustcAbi::X86Softfloat, + _ => return Err(()), + }) + } +} + +impl ToJson for RustcAbi { + fn to_json(&self) -> Json { + match *self { + RustcAbi::X86Softfloat => "x86-softfloat", + } + .to_json() + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TlsModel { GeneralDynamic, @@ -2502,6 +2529,12 @@ pub struct TargetOptions { /// If not provided, LLVM will infer the float ABI from the target triple (`llvm_target`). pub llvm_floatabi: Option, + /// Picks a specific ABI for this target. This is *not* just for "Rust" ABI functions, + /// it can also affect "C" ABI functions; the point is that this flag is interpreted by + /// rustc and not forwarded to LLVM. + /// So far, this is only used on x86. + pub rustc_abi: Option, + /// Whether or not RelaxElfRelocation flag will be passed to the linker pub relax_elf_relocations: bool, @@ -2661,10 +2694,6 @@ impl TargetOptions { .collect(); } } - - pub(crate) fn has_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature)) - } } impl Default for TargetOptions { @@ -2770,6 +2799,7 @@ impl Default for TargetOptions { llvm_mcount_intrinsic: None, llvm_abiname: "".into(), llvm_floatabi: None, + rustc_abi: None, relax_elf_relocations: false, llvm_args: cvs![], use_ctors_section: false, @@ -3236,6 +3266,17 @@ impl Target { _ => {} } + // Check consistency of Rust ABI declaration. + if let Some(rust_abi) = self.rustc_abi { + match rust_abi { + RustcAbi::X86Softfloat => check_matches!( + &*self.arch, + "x86" | "x86_64", + "`x86-softfloat` ABI is only valid for x86 targets" + ), + } + } + // Check that the given target-features string makes some basic sense. if !self.features.is_empty() { let mut features_enabled = FxHashSet::default(); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs index 39d3a5a46330..c1ed565f0fe6 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs @@ -5,7 +5,7 @@ // The cdecl ABI is used. It differs from the stdcall or fastcall ABI. // "i686-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{Target, base}; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); // Use -GNU here, because of the reason below: // Background and Problem: diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs index cfefb1de9938..e14a36735894 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs @@ -5,8 +5,8 @@ // features. use crate::spec::{ - Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, SanitizerSet, StackProbeType, - Target, TargetOptions, + Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetOptions, }; pub(crate) fn target() -> Target { @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { relro_level: RelroLevel::Full, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), + rustc_abi: Some(RustcAbi::X86Softfloat), features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float".into(), supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, disable_redzone: true, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs index a11a79ff41a8..8fd92e8123b9 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs @@ -6,7 +6,7 @@ // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. use crate::abi::call::Conv; -use crate::spec::{Target, base}; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -26,6 +26,7 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); Target { llvm_target: "x86_64-unknown-windows".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 3bc4b92987f1..9bcf2b7e3cf5 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::{Symbol, sym}; -use crate::spec::{FloatAbi, Target}; +use crate::spec::{FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -417,7 +417,9 @@ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), - ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), + // This cannot actually be toggled, the ABI always fixes it, so it'd make little sense to + // stabilize. It must be in this list for the ABI check to be able to use it. + ("soft-float", Stability::Unstable(sym::x87_target_feature), &[]), ("sse", Stable, &[]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), @@ -768,23 +770,41 @@ impl Target { // questions "which ABI is used". match &*self.arch { "x86" => { - // We support 2 ABIs, hardfloat (default) and softfloat. - // x86 has no sane ABI indicator so we have to use the target feature. - if self.has_feature("soft-float") { - NOTHING - } else { - // Hardfloat ABI. x87 must be enabled. - FeatureConstraints { required: &["x87"], incompatible: &[] } + // We use our own ABI indicator here; LLVM does not have anything native. + // Every case should require or forbid `soft-float`! + match self.rustc_abi { + None => { + // Default hardfloat ABI. + // x87 must be enabled, soft-float must be disabled. + FeatureConstraints { required: &["x87"], incompatible: &["soft-float"] } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } } } "x86_64" => { - // We support 2 ABIs, hardfloat (default) and softfloat. - // x86 has no sane ABI indicator so we have to use the target feature. - if self.has_feature("soft-float") { - NOTHING - } else { - // Hardfloat ABI. x87 and SSE2 must be enabled. - FeatureConstraints { required: &["x87", "sse2"], incompatible: &[] } + // We use our own ABI indicator here; LLVM does not have anything native. + // Every case should require or forbid `soft-float`! + match self.rustc_abi { + None => { + // Default hardfloat ABI. On x86-64, this always includes SSE2. + FeatureConstraints { + required: &["x87", "sse2"], + incompatible: &["soft-float"], + } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } } } "arm" => { diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs index dab01179c0b3..215e64979f73 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs @@ -1,3 +1,4 @@ +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. //@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib //@ needs-llvm-components: riscv #![feature(no_core, lang_items, riscv_target_feature)] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr index 9df56d86729c..bfe767e5ffb0 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr @@ -1,5 +1,5 @@ error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI - --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:9:18 + --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:10:18 | LL | #[target_feature(enable = "d")] | ^^^^^^^^^^^^ diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs deleted file mode 100644 index 8755791c1c04..000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ check-pass -#![feature(no_core, lang_items)] -#![no_core] -#![allow(unexpected_cfgs)] - -#[lang = "sized"] -pub trait Sized {} - -// The compile_error macro does not exist, so if the `cfg` evaluates to `true` this -// complains about the missing macro rather than showing the error... but that's good enough. -#[cfg(not(target_feature = "x87"))] -compile_error!("the x87 feature *should* be exposed in `cfg`"); diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs index 3d09217327ab..81f138b175f2 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs @@ -1,8 +1,11 @@ +//! Ensure that if disabling a target feature implies disabling an ABI-required target feature, +//! we complain. //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib //@ needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-sse // For now this is just a warning. //@ build-pass +//@error-pattern: must be enabled to ensure that the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs index 95c366bb3f5b..7242bcc85bfd 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs @@ -3,6 +3,7 @@ //@ compile-flags: -Ctarget-feature=-neon // For now this is just a warning. //@ build-pass +//@error-pattern: must be enabled to ensure that the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs index fd8023664dad..7eebcf05dc0f 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs @@ -1,8 +1,10 @@ +//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib //@ needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-x87 // For now this is just a warning. //@ build-pass +//@error-pattern: must be enabled to ensure that the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs new file mode 100644 index 000000000000..f277a309cd69 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs @@ -0,0 +1,12 @@ +//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=+soft-float +// For now this is just a warning. +//@ build-pass +//@error-pattern: must be disabled to ensure that the ABI +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr new file mode 100644 index 000000000000..e49672f33b9a --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr @@ -0,0 +1,11 @@ +warning: target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 + +warning: unstable feature specified for `-Ctarget-feature`: `soft-float` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-target-feature-attribute.rs index 2ba5f2215c7b..6bb6f8aaffb6 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.rs @@ -1,11 +1,12 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 +//! Ensure "forbidden" target features cannot be enabled via `#[target_feature]`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} -#[target_feature(enable = "soft-float")] +#[target_feature(enable = "forced-atomics")] //~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr index f3d54cc19d3e..f8ea0c0e793f 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr @@ -1,8 +1,8 @@ -error: target feature `soft-float` cannot be enabled with `#[target_feature]`: unsound because it changes float ABI - --> $DIR/forbidden-target-feature-attribute.rs:9:18 +error: target feature `forced-atomics` cannot be enabled with `#[target_feature]`: unsound because it changes the ABI of atomic operations + --> $DIR/forbidden-target-feature-attribute.rs:10:18 | -LL | #[target_feature(enable = "soft-float")] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "forced-atomics")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/forbidden-target-feature-cfg.rs b/tests/ui/target-feature/forbidden-target-feature-cfg.rs index 1f001e9f8ff8..e848ffde018e 100644 --- a/tests/ui/target-feature/forbidden-target-feature-cfg.rs +++ b/tests/ui/target-feature/forbidden-target-feature-cfg.rs @@ -1,5 +1,6 @@ -//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib -//@ needs-llvm-components: x86 +//! Ensure "forbidden" target features are not exposed via `cfg`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv //@ check-pass #![feature(no_core, lang_items)] #![no_core] @@ -10,5 +11,5 @@ pub trait Sized {} // The compile_error macro does not exist, so if the `cfg` evaluates to `true` this // complains about the missing macro rather than showing the error... but that's good enough. -#[cfg(target_feature = "soft-float")] -compile_error!("the soft-float feature should not be exposed in `cfg`"); +#[cfg(target_feature = "forced-atomics")] +compile_error!("the forced-atomics feature should not be exposed in `cfg`"); diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs index b09c53bd46af..cf85c5212289 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs @@ -1,8 +1,10 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-soft-float +//! Ensure "forbidden" target features cannot be disabled via `-Ctarget-feature`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +//@ compile-flags: -Ctarget-feature=-forced-atomics // For now this is just a warning. //@ build-pass +//@error-pattern: unsound because it changes the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr index 797cd4be5c27..171ed0de6aaf 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be disabled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `forced-atomics` cannot be disabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations | = note: 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 #116344 diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.rs b/tests/ui/target-feature/forbidden-target-feature-flag.rs index 0f688fde7f43..245841eb0395 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag.rs @@ -1,8 +1,10 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=+soft-float +//! Ensure "forbidden" target features cannot be enabled via `-Ctarget-feature`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +//@ compile-flags: -Ctarget-feature=+forced-atomics // For now this is just a warning. //@ build-pass +//@error-pattern: unsound because it changes the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-target-feature-flag.stderr index 075666fbf668..f8490f066d1d 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be enabled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `forced-atomics` cannot be enabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations | = note: 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 #116344 From 8596ce141c9ba714f39024b19afce63ca2184e67 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 1 Feb 2025 19:06:22 +0100 Subject: [PATCH 32/36] patch RfL job --- src/ci/docker/scripts/rfl-build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 8776e0f0be90..3eb85ab215e0 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,7 +2,7 @@ set -euo pipefail -LINUX_VERSION=v6.13-rc1 +LINUX_VERSION=50e57739141b41f731ab31f8380821c7969f9dc4 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt @@ -28,7 +28,7 @@ rm -rf linux || true # Download Linux at a specific commit mkdir -p linux git -C linux init -git -C linux remote add origin https://github.com/Rust-for-Linux/linux.git +git -C linux remote add origin https://github.com/Darksonn/linux.git git -C linux fetch --depth 1 origin ${LINUX_VERSION} git -C linux checkout FETCH_HEAD From e9b234917922670a5840a1e7174d8aeff1ef035c Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:22:15 -0500 Subject: [PATCH 33/36] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/book b/src/doc/book index fa312a343fbf..e2fa4316c5a7 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit fa312a343fbff01bc6cef393e326817f70719813 +Subproject commit e2fa4316c5a7c0d2499c5d6b799adcfad6ef7a45 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 4ed5a1a4a2a7..f56aecc3b036 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda +Subproject commit f56aecc3b036dff16404b525a83b00f911b9bbea diff --git a/src/doc/nomicon b/src/doc/nomicon index bc2298865544..336f75835a6c 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948 +Subproject commit 336f75835a6c0514852cc65aba9a698b699b13c8 diff --git a/src/doc/reference b/src/doc/reference index 93b921c7d321..4249fb411dd2 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d +Subproject commit 4249fb411dd27f945e2881eb0378044b94cee06f diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 054259ed1bf0..743766929f1e 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 054259ed1bf01cdee4309ee764c7e103f6df3de5 +Subproject commit 743766929f1e53e72fab74394ae259bbfb4a7619 From 00c61a81a0c6685d3d5470d5111eeb0159911836 Mon Sep 17 00:00:00 2001 From: FedericoBruzzone Date: Mon, 3 Feb 2025 18:33:27 +0100 Subject: [PATCH 34/36] Move `try_to_raw_bytes` from `ty::Valtree` to `ty::Value` Signed-off-by: FedericoBruzzone --- .../rustc_middle/src/ty/consts/valtree.rs | 47 +++++++++---------- compiler/rustc_middle/src/ty/print/pretty.rs | 24 +++++----- compiler/rustc_symbol_mangling/src/v0.rs | 3 +- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index d914b7576dc4..5905076c4d08 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -78,30 +78,6 @@ impl<'tcx> ValTree<'tcx> { Self::Branch(_) => None, } } - - /// Get the values inside the ValTree as a slice of bytes. This only works for - /// constants with types &str, &[u8], or [u8; _]. - pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { - match ty.kind() { - ty::Ref(_, inner_ty, _) => match inner_ty.kind() { - // `&str` can be interpreted as raw bytes - ty::Str => {} - // `&[u8]` can be interpreted as raw bytes - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} - // other `&_` can't be interpreted as raw bytes - _ => return None, - }, - // `[u8; N]` can be interpreted as raw bytes - ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} - // Otherwise, type cannot be interpreted as raw bytes - _ => return None, - } - - Some( - tcx.arena - .alloc_from_iter(self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8())), - ) - } } /// A type-level constant value. @@ -143,6 +119,29 @@ impl<'tcx> Value<'tcx> { } self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx)) } + + /// Get the values inside the ValTree as a slice of bytes. This only works for + /// constants with types &str, &[u8], or [u8; _]. + pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> { + match self.ty.kind() { + ty::Ref(_, inner_ty, _) => match inner_ty.kind() { + // `&str` can be interpreted as raw bytes + ty::Str => {} + // `&[u8]` can be interpreted as raw bytes + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // other `&_` can't be interpreted as raw bytes + _ => return None, + }, + // `[u8; N]` can be interpreted as raw bytes + ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // Otherwise, type cannot be interpreted as raw bytes + _ => return None, + } + + Some(tcx.arena.alloc_from_iter( + self.valtree.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8()), + )) + } } impl<'tcx> rustc_type_ir::inherent::ValueConst> for Value<'tcx> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 10d350fbd5cf..58e01ab97d07 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1801,21 +1801,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { match (cv.valtree, cv.ty.kind()) { (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { ty::Slice(t) if *t == u8_type => { - let bytes = - cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { - bug!( - "expected to convert valtree {:?} to raw bytes for type {:?}", - cv.valtree, - t - ) - }); + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { + bug!( + "expected to convert valtree {:?} to raw bytes for type {:?}", + cv.valtree, + t + ) + }); return self.pretty_print_byte_str(bytes); } ty::Str => { - let bytes = - cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { - bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty) - }); + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { + bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty) + }); p!(write("{:?}", String::from_utf8_lossy(bytes))); return Ok(()); } @@ -1827,7 +1825,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = cv.valtree.try_to_raw_bytes(self.tcx(), cv.ty).unwrap_or_else(|| { + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 7b040a8b2c41..0ac6f17b97bd 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -649,7 +649,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // HACK(jaic1): hide the `str` type behind a reference // for the following transformation from valtree to raw bytes let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, ct_ty); - let slice = valtree.try_to_raw_bytes(tcx, ref_ty).unwrap_or_else(|| { + let cv = ty::Value { ty: ref_ty, valtree }; + let slice = cv.try_to_raw_bytes(tcx).unwrap_or_else(|| { bug!("expected to get raw bytes from valtree {:?} for type {:}", valtree, ct_ty) }); let s = std::str::from_utf8(slice).expect("non utf8 str from MIR interpreter"); From 56725e842a481a83aa1884df235abe86cc53e60e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Feb 2025 16:56:07 +0100 Subject: [PATCH 35/36] platform-support: document CPU baseline for x86-32 targets --- src/doc/rustc/src/platform-support.md | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d43b890e0d08..3b6abdd84832 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,9 +34,9 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] -`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI] +`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) `x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+) `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+) @@ -162,14 +162,14 @@ target | std | notes [`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat -`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87] -`i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87] -`i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] -[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] -[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD [^x86_32-floats-return-ABI] -`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI +`i586-pc-windows-msvc` | * | 32-bit Windows (original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2, glibc 2.17, original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] +[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([PentiumPro with SSE](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] +[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? (Pentium 4, softfloat) | 32-bit UEFI [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -307,15 +307,15 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) -[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] -[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] -[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium [^x86_32-floats-x87] -[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] -`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku with SSE [^x86_32-floats-return-ABI] -[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd w/o SSE [^x86_32-floats-x87] -[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] -[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD with SSE2 [^x86_32-floats-return-ABI] -[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS w/o SSE [^x86_32-floats-x87] +[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI] +[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS (Pentium 4) [^x86_32-floats-return-ABI] +[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86 (original Pentium) [^x86_32-floats-x87] +[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+, Penryn) [^x86_32-floats-return-ABI] +`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd (PentiumPro) [^x86_32-floats-x87] +[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS (PentiumPro) [^x86_32-floats-x87] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] From 7bb5f4dd78a7a45729ab503805ba1b3065cb0de9 Mon Sep 17 00:00:00 2001 From: Jiri Bobek Date: Wed, 4 Dec 2024 12:30:42 +0100 Subject: [PATCH 36/36] improve cold_path() --- compiler/rustc_codegen_llvm/src/builder.rs | 48 +++++++++- compiler/rustc_codegen_ssa/src/mir/block.rs | 33 +++++-- compiler/rustc_codegen_ssa/src/mir/mod.rs | 27 ++++-- .../rustc_codegen_ssa/src/traits/builder.rs | 14 +++ tests/codegen/intrinsics/cold_path2.rs | 36 ++++++++ tests/codegen/intrinsics/cold_path3.rs | 87 +++++++++++++++++++ 6 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 tests/codegen/intrinsics/cold_path2.rs create mode 100644 tests/codegen/intrinsics/cold_path3.rs diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 2d007416263f..b63c8f8caef5 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -4,7 +4,7 @@ use std::{iter, ptr}; pub(crate) mod autodiff; -use libc::{c_char, c_uint}; +use libc::{c_char, c_uint, size_t}; use rustc_abi as abi; use rustc_abi::{Align, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; @@ -32,7 +32,7 @@ use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common::Funclet; use crate::context::{CodegenCx, SimpleCx}; -use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}; +use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, Metadata, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -333,6 +333,50 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn switch_with_weights( + &mut self, + v: Self::Value, + else_llbb: Self::BasicBlock, + else_is_cold: bool, + cases: impl ExactSizeIterator, + ) { + if self.cx.sess().opts.optimize == rustc_session::config::OptLevel::No { + self.switch(v, else_llbb, cases.map(|(val, dest, _)| (val, dest))); + return; + } + + let id_str = "branch_weights"; + let id = unsafe { + llvm::LLVMMDStringInContext2(self.cx.llcx, id_str.as_ptr().cast(), id_str.len()) + }; + + // For switch instructions with 2 targets, the `llvm.expect` intrinsic is used. + // This function handles switch instructions with more than 2 targets and it needs to + // emit branch weights metadata instead of using the intrinsic. + // The values 1 and 2000 are the same as the values used by the `llvm.expect` intrinsic. + let cold_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(1)) }; + let hot_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(2000)) }; + let weight = + |is_cold: bool| -> &Metadata { if is_cold { cold_weight } else { hot_weight } }; + + let mut md: SmallVec<[&Metadata; 16]> = SmallVec::with_capacity(cases.len() + 2); + md.push(id); + md.push(weight(else_is_cold)); + + let switch = + unsafe { llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, cases.len() as c_uint) }; + for (on_val, dest, is_cold) in cases { + let on_val = self.const_uint_big(self.val_ty(v), on_val); + unsafe { llvm::LLVMAddCase(switch, on_val, dest) } + md.push(weight(is_cold)); + } + + unsafe { + let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t); + self.cx.set_metadata(switch, llvm::MD_prof, md_node); + } + } + fn invoke( &mut self, llty: &'ll Type, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4be363ca9a2b..66008a476505 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -429,11 +429,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cmp = bx.icmp(IntPredicate::IntEQ, discr_value, llval); bx.cond_br(cmp, ll1, ll2); } else { - bx.switch( - discr_value, - helper.llbb_with_cleanup(self, targets.otherwise()), - target_iter.map(|(value, target)| (value, helper.llbb_with_cleanup(self, target))), - ); + let otherwise = targets.otherwise(); + let otherwise_cold = self.cold_blocks[otherwise]; + let otherwise_unreachable = self.mir[otherwise].is_empty_unreachable(); + let cold_count = targets.iter().filter(|(_, target)| self.cold_blocks[*target]).count(); + let none_cold = cold_count == 0; + let all_cold = cold_count == targets.iter().len(); + if (none_cold && (!otherwise_cold || otherwise_unreachable)) + || (all_cold && (otherwise_cold || otherwise_unreachable)) + { + // All targets have the same weight, + // or `otherwise` is unreachable and it's the only target with a different weight. + bx.switch( + discr_value, + helper.llbb_with_cleanup(self, targets.otherwise()), + target_iter + .map(|(value, target)| (value, helper.llbb_with_cleanup(self, target))), + ); + } else { + // Targets have different weights + bx.switch_with_weights( + discr_value, + helper.llbb_with_cleanup(self, targets.otherwise()), + otherwise_cold, + target_iter.map(|(value, target)| { + (value, helper.llbb_with_cleanup(self, target), self.cold_blocks[target]) + }), + ); + } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 3a896071bc6b..ba28720afecd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -502,14 +502,25 @@ fn find_cold_blocks<'tcx>( for (bb, bb_data) in traversal::postorder(mir) { let terminator = bb_data.terminator(); - // If a BB ends with a call to a cold function, mark it as cold. - if let mir::TerminatorKind::Call { ref func, .. } = terminator.kind - && let ty::FnDef(def_id, ..) = *func.ty(local_decls, tcx).kind() - && let attrs = tcx.codegen_fn_attrs(def_id) - && attrs.flags.contains(CodegenFnAttrFlags::COLD) - { - cold_blocks[bb] = true; - continue; + match terminator.kind { + // If a BB ends with a call to a cold function, mark it as cold. + mir::TerminatorKind::Call { ref func, .. } + | mir::TerminatorKind::TailCall { ref func, .. } + if let ty::FnDef(def_id, ..) = *func.ty(local_decls, tcx).kind() + && let attrs = tcx.codegen_fn_attrs(def_id) + && attrs.flags.contains(CodegenFnAttrFlags::COLD) => + { + cold_blocks[bb] = true; + continue; + } + + // If a BB ends with an `unreachable`, also mark it as cold. + mir::TerminatorKind::Unreachable => { + cold_blocks[bb] = true; + continue; + } + + _ => {} } // If all successors of a BB are cold and there's at least one of them, mark this BB as cold diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index bbf87a599426..cbe0f2e8059a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -110,6 +110,20 @@ pub trait BuilderMethods<'a, 'tcx>: else_llbb: Self::BasicBlock, cases: impl ExactSizeIterator, ); + + // This is like `switch()`, but every case has a bool flag indicating whether it's cold. + // + // Default implementation throws away the cold flags and calls `switch()`. + fn switch_with_weights( + &mut self, + v: Self::Value, + else_llbb: Self::BasicBlock, + _else_is_cold: bool, + cases: impl ExactSizeIterator, + ) { + self.switch(v, else_llbb, cases.map(|(val, bb, _)| (val, bb))) + } + fn invoke( &mut self, llty: Self::Type, diff --git a/tests/codegen/intrinsics/cold_path2.rs b/tests/codegen/intrinsics/cold_path2.rs new file mode 100644 index 000000000000..1e7e0478f4fd --- /dev/null +++ b/tests/codegen/intrinsics/cold_path2.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -O +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::cold_path; + +#[inline(never)] +#[no_mangle] +pub fn path_a() { + println!("path a"); +} + +#[inline(never)] +#[no_mangle] +pub fn path_b() { + println!("path b"); +} + +#[no_mangle] +pub fn test(x: Option) { + if let Some(_) = x { + path_a(); + } else { + cold_path(); + path_b(); + } + + // CHECK-LABEL: @test( + // CHECK: br i1 %1, label %bb2, label %bb1, !prof ![[NUM:[0-9]+]] + // CHECK: bb1: + // CHECK: path_a + // CHECK: bb2: + // CHECK: path_b +} + +// CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 1, i32 2000} diff --git a/tests/codegen/intrinsics/cold_path3.rs b/tests/codegen/intrinsics/cold_path3.rs new file mode 100644 index 000000000000..bf3347de665d --- /dev/null +++ b/tests/codegen/intrinsics/cold_path3.rs @@ -0,0 +1,87 @@ +//@ compile-flags: -O +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::cold_path; + +#[inline(never)] +#[no_mangle] +pub fn path_a() { + println!("path a"); +} + +#[inline(never)] +#[no_mangle] +pub fn path_b() { + println!("path b"); +} + +#[inline(never)] +#[no_mangle] +pub fn path_c() { + println!("path c"); +} + +#[inline(never)] +#[no_mangle] +pub fn path_d() { + println!("path d"); +} + +#[no_mangle] +pub fn test(x: Option) { + match x { + Some(0) => path_a(), + Some(1) => { + cold_path(); + path_b() + } + Some(2) => path_c(), + Some(3) => { + cold_path(); + path_d() + } + _ => path_a(), + } + + // CHECK-LABEL: @test( + // CHECK: switch i32 %1, label %bb1 [ + // CHECK: i32 0, label %bb6 + // CHECK: i32 1, label %bb5 + // CHECK: i32 2, label %bb4 + // CHECK: i32 3, label %bb3 + // CHECK: ], !prof ![[NUM1:[0-9]+]] +} + +#[no_mangle] +pub fn test2(x: Option) { + match x { + Some(10) => path_a(), + Some(11) => { + cold_path(); + path_b() + } + Some(12) => { + unsafe { core::intrinsics::unreachable() }; + path_c() + } + Some(13) => { + cold_path(); + path_d() + } + _ => { + cold_path(); + path_a() + } + } + + // CHECK-LABEL: @test2( + // CHECK: switch i32 %1, label %bb1 [ + // CHECK: i32 10, label %bb5 + // CHECK: i32 11, label %bb4 + // CHECK: i32 13, label %bb3 + // CHECK: ], !prof ![[NUM2:[0-9]+]] +} + +// CHECK: ![[NUM1]] = !{!"branch_weights", i32 2000, i32 2000, i32 1, i32 2000, i32 1} +// CHECK: ![[NUM2]] = !{!"branch_weights", i32 1, i32 2000, i32 1, i32 1}