Merge from rustc
This commit is contained in:
commit
cf4be3f39d
157 changed files with 1959 additions and 605 deletions
|
|
@ -622,8 +622,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)
|
||||
|
|
@ -666,7 +683,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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -198,7 +199,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
polonius_output,
|
||||
opt_closure_req,
|
||||
nll_errors,
|
||||
localized_outlives_constraints,
|
||||
polonius_diagnostics,
|
||||
} = nll::compute_regions(
|
||||
&infcx,
|
||||
free_regions,
|
||||
|
|
@ -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,
|
||||
localized_outlives_constraints,
|
||||
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<MoveError<'tcx>>,
|
||||
|
||||
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
|
||||
polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
|
||||
}
|
||||
|
||||
// Check that:
|
||||
|
|
|
|||
|
|
@ -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<ClosureRegionRequirements<'tcx>>,
|
||||
pub nll_errors: RegionErrors<'tcx>,
|
||||
|
||||
/// When using `-Zpolonius=next`: the localized typeck and liveness constraints.
|
||||
pub localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
|
||||
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
|
||||
/// localized typeck and liveness constraints.
|
||||
pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
|
||||
}
|
||||
|
||||
/// 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.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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<LocalizedOutlivesConstraintSet>,
|
||||
polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
) {
|
||||
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 =
|
||||
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.localized_outlives_constraints,
|
||||
closure_region_requirements,
|
||||
&mut file,
|
||||
)?;
|
||||
|
|
@ -63,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<ClosureRegionRequirements<'tcx>>,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
|
|
|
|||
|
|
@ -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<RegionVid, PointIndex>,
|
||||
) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -32,6 +32,16 @@
|
|||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
|
||||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
|
||||
//!
|
||||
//!
|
||||
//! 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. 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;
|
||||
|
|
@ -42,8 +52,10 @@ mod typeck_constraints;
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_middle::mir::Body;
|
||||
use rustc_index::interval::SparseIntervalMatrix;
|
||||
use rustc_middle::mir::{Body, Local};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
|
|
@ -57,15 +69,40 @@ use crate::{BorrowSet, RegionInferenceContext};
|
|||
|
||||
pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
|
||||
|
||||
/// This struct holds the data needed to create the Polonius localized constraints.
|
||||
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<SparseBitMatrix<PointIndex, RegionVid>>,
|
||||
|
||||
/// 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<RegionVid, ConstraintDirection>,
|
||||
|
||||
/// 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<Local>,
|
||||
}
|
||||
|
||||
/// 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<PointIndex, RegionVid>,
|
||||
}
|
||||
|
||||
/// 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<Local>,
|
||||
}
|
||||
|
||||
/// The direction a constraint can flow into. Used to create liveness constraints according to
|
||||
|
|
@ -83,8 +120,24 @@ 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<RegionVid, PointIndex>,
|
||||
) -> 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, liveness_context }
|
||||
}
|
||||
|
||||
/// Computes live loans using the set of loans model for `-Zpolonius=next`.
|
||||
|
|
@ -95,13 +148,18 @@ 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,
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) -> LocalizedOutlivesConstraintSet {
|
||||
) -> PoloniusDiagnosticsContext {
|
||||
let PoloniusLivenessContext { live_region_variances, boring_nll_locals } =
|
||||
self.liveness_context;
|
||||
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
tcx,
|
||||
|
|
@ -112,14 +170,11 @@ 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_region_variances,
|
||||
&self.live_regions,
|
||||
&live_region_variances,
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
|
|
@ -136,6 +191,6 @@ impl PoloniusContext {
|
|||
);
|
||||
regioncx.record_live_loans(live_loans);
|
||||
|
||||
localized_outlives_constraints
|
||||
PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -70,7 +75,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 +152,11 @@ fn record_regular_live_regions<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &mut LivenessValues,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
polonius_context: &mut Option<PoloniusContext>,
|
||||
polonius_liveness: &mut Option<PoloniusLivenessContext>,
|
||||
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 +167,7 @@ struct LiveVariablesVisitor<'a, 'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &'a mut LivenessValues,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
polonius_context: &'a mut Option<PoloniusContext>,
|
||||
polonius_liveness: &'a mut Option<PoloniusLivenessContext>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
|
||||
|
|
@ -214,8 +219,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -583,8 +586,8 @@ struct TypeChecker<'a, 'tcx> {
|
|||
polonius_facts: &'a mut Option<PoloniusFacts>,
|
||||
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<PoloniusContext>,
|
||||
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
|
||||
polonius_liveness: Option<PoloniusLivenessContext>,
|
||||
}
|
||||
|
||||
/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
|
||||
|
|
|
|||
|
|
@ -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<Item = (u128, Self::BasicBlock, bool)>,
|
||||
) {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ fn generate_lto_work<B: ExtraBackendMethods>(
|
|||
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() };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -110,6 +110,20 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
else_llbb: Self::BasicBlock,
|
||||
cases: impl ExactSizeIterator<Item = (u128, Self::BasicBlock)>,
|
||||
);
|
||||
|
||||
// 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<Item = (u128, Self::BasicBlock, bool)>,
|
||||
) {
|
||||
self.switch(v, else_llbb, cases.map(|(val, bb, _)| (val, bb)))
|
||||
}
|
||||
|
||||
fn invoke(
|
||||
&mut self,
|
||||
llty: Self::Type,
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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<ty::ValTree<'tcx>, 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<mir::DestructuredConstant<'tcx>> {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<K: Idx, V, I> {
|
||||
// Entries per bucket:
|
||||
// Bucket 0: 4096 2^12
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ fn configure_and_expand(
|
|||
|
||||
resolver.resolve_crate(&krate);
|
||||
|
||||
CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate);
|
||||
krate
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<MetadataLoaderDyn>) -> CStore {
|
||||
CStore {
|
||||
metadata_loader,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CrateNum, CrateNum>;
|
||||
|
||||
/// Target modifiers - abi or exploit mitigations flags
|
||||
pub(crate) type TargetModifiers = Vec<TargetModifier>;
|
||||
|
||||
pub(crate) struct CrateMetadata {
|
||||
/// The primary crate data - binary metadata blob.
|
||||
blob: MetadataBlob,
|
||||
|
|
@ -961,6 +965,13 @@ impl CrateRoot {
|
|||
) -> impl ExactSizeIterator<Item = CrateDep> + Captures<'a> {
|
||||
self.crate_deps.decode(metadata)
|
||||
}
|
||||
|
||||
pub(crate) fn decode_target_modifiers<'a>(
|
||||
&self,
|
||||
metadata: &'a MetadataBlob,
|
||||
) -> impl ExactSizeIterator<Item = TargetModifier> + 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use rustc_middle::ty::fast_reject::{self, TreatParams};
|
|||
use rustc_middle::ty::{AssocItemContainer, SymbolName};
|
||||
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<TargetModifier> {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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<DefPathHashMapRef<'static>>,
|
||||
|
||||
source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
|
||||
target_modifiers: LazyArray<TargetModifier>,
|
||||
|
||||
compiler_builtins: bool,
|
||||
needs_allocator: bool,
|
||||
|
|
|
|||
|
|
@ -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,)*
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1439,10 +1440,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_ {
|
||||
|
|
@ -1452,7 +1453,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(),
|
||||
|
|
|
|||
|
|
@ -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<V> = DefaultCache<Self, V>;`,
|
||||
// it would be reasonable to use associated type defaults, to remove the duplication...
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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<Box<Pat<'tcx>>>,
|
||||
|
|
@ -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<Box<CanonicalUserType<'tcx>>>;
|
||||
|
||||
#[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<region::Scope>,
|
||||
}
|
||||
|
||||
#[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.
|
||||
|
|
@ -556,20 +556,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<Pat<'tcx>>,
|
||||
pub guard: Option<ExprId>,
|
||||
|
|
@ -587,7 +587,7 @@ pub enum LogicalOp {
|
|||
Or,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
#[derive(Debug, HashStable)]
|
||||
pub enum InlineAsmOperand<'tcx> {
|
||||
In {
|
||||
reg: InlineAsmRegOrRegClass,
|
||||
|
|
@ -625,13 +625,13 @@ pub enum InlineAsmOperand<'tcx> {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable, TypeVisitable)]
|
||||
#[derive(Debug, HashStable, TypeVisitable)]
|
||||
pub struct FieldPat<'tcx> {
|
||||
pub field: FieldIdx,
|
||||
pub pattern: Box<Pat<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable, TypeVisitable)]
|
||||
#[derive(Debug, HashStable, TypeVisitable)]
|
||||
pub struct Pat<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub span: Span,
|
||||
|
|
@ -739,7 +739,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
|
||||
|
|
@ -763,7 +763,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,
|
||||
|
|
|
|||
|
|
@ -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<TyCtxt<'tcx>> for Value<'tcx> {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1487,7 +1487,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) => {
|
||||
|
|
@ -1787,48 +1787,47 @@ 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(|| {
|
||||
let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| {
|
||||
bug!(
|
||||
"expected to convert valtree {:?} to raw bytes for type {:?}",
|
||||
valtree,
|
||||
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.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(());
|
||||
}
|
||||
_ => {
|
||||
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.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| {
|
||||
bug!("expected to convert valtree to raw bytes for type {:?}", t)
|
||||
});
|
||||
p!("*");
|
||||
|
|
@ -1837,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), "]");
|
||||
}
|
||||
|
|
@ -1857,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),
|
||||
": ",
|
||||
)?;
|
||||
}
|
||||
|
|
@ -1894,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.
|
||||
|
|
@ -1902,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("<ZST>"));
|
||||
} else {
|
||||
p!(write("{:?}", valtree));
|
||||
p!(write("{:?}", cv.valtree));
|
||||
}
|
||||
if print_ty {
|
||||
p!(": ", print(ty));
|
||||
p!(": ", print(cv.ty));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -476,7 +477,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1179,23 +1179,23 @@ 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) => Some(instance.def_id()),
|
||||
MonoItem::Fn(ref instance) => {
|
||||
#[cfg(llvm_enzyme)]
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<K, V> {
|
||||
cache: Sharded<FxHashMap<K, (V, DepNodeIndex)>>,
|
||||
}
|
||||
|
|
@ -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<V> {
|
||||
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<V> {
|
||||
/// 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.
|
||||
|
|
|
|||
|
|
@ -222,10 +222,10 @@ pub struct CycleError {
|
|||
pub cycle: Vec<QueryInfo>,
|
||||
}
|
||||
|
||||
/// 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, C>(tcx: Tcx, cache: &C, key: &C::Key) -> Option<C::Value>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -1224,6 +1224,7 @@ impl Default for Options {
|
|||
color: ColorConfig::Auto,
|
||||
logical_env: FxIndexMap::default(),
|
||||
verbose: false,
|
||||
target_modifiers: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2370,14 +2371,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::<OptionsTargetModifiers, String>::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,
|
||||
|
|
@ -2648,6 +2651,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
|||
color,
|
||||
logical_env,
|
||||
verbose,
|
||||
target_modifiers,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<OptionsTargetModifiers, String>,
|
||||
tmods: &mut Vec<TargetModifier>,
|
||||
) {
|
||||
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<OptionsTargetModifiers, String>,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
|
|
@ -98,6 +275,17 @@ macro_rules! top_level_options {
|
|||
})*
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
|
||||
let mut mods = Vec::<TargetModifier>::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<PrintRequest> [UNTRACKED],
|
||||
cg: CodegenOptions [SUBSTRUCT],
|
||||
cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions],
|
||||
externs: Externs [UNTRACKED],
|
||||
crate_name: Option<String> [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<OptionsTargetModifiers, String>,
|
||||
) -> $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<TargetModifier>,
|
||||
_tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
|
||||
) {
|
||||
$({
|
||||
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<O> {
|
|||
// description for option from options table
|
||||
desc: &'static str,
|
||||
is_deprecated_and_do_nothing: bool,
|
||||
tmod: Option<OptionsTargetModifiers>,
|
||||
}
|
||||
|
||||
impl<O> OptionDesc<O> {
|
||||
|
|
@ -344,6 +640,7 @@ impl<O> OptionDesc<O> {
|
|||
fn build_options<O: Default>(
|
||||
early_dcx: &EarlyDiagCtxt,
|
||||
matches: &getopts::Matches,
|
||||
target_modifiers: &mut BTreeMap<OptionsTargetModifiers, String>,
|
||||
descrs: OptionDescrs<O>,
|
||||
prefix: &str,
|
||||
outputname: &str,
|
||||
|
|
@ -357,7 +654,14 @@ fn build_options<O: Default>(
|
|||
|
||||
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<O: Default>(
|
|||
),
|
||||
}
|
||||
}
|
||||
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}`")),
|
||||
}
|
||||
|
|
@ -1614,7 +1923,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
|
||||
|
|
@ -1745,6 +2054,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<String> = (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:
|
||||
|
|
@ -1753,7 +2064,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
|
||||
|
|
@ -2098,10 +2409,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<u32> = (None, parse_opt_number, [TRACKED],
|
||||
regparm: Option<u32> = (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.\
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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::<super::RustcAbi>() {
|
||||
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| {
|
||||
|
|
@ -612,6 +625,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);
|
||||
|
|
@ -788,6 +802,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);
|
||||
|
|
|
|||
|
|
@ -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<RustcAbi, ()> {
|
||||
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,
|
||||
|
|
@ -2505,6 +2532,12 @@ pub struct TargetOptions {
|
|||
/// If not provided, LLVM will infer the float ABI from the target triple (`llvm_target`).
|
||||
pub llvm_floatabi: Option<FloatAbi>,
|
||||
|
||||
/// 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<RustcAbi>,
|
||||
|
||||
/// Whether or not RelaxElfRelocation flag will be passed to the linker
|
||||
pub relax_elf_relocations: bool,
|
||||
|
||||
|
|
@ -2664,10 +2697,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 {
|
||||
|
|
@ -2774,6 +2803,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,
|
||||
|
|
@ -3240,6 +3270,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();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -422,7 +422,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"]),
|
||||
|
|
@ -773,23 +775,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" => {
|
||||
|
|
|
|||
|
|
@ -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<I: Interner>: TypeVisitable<I> {
|
||||
pub trait TypeFoldable<I: Interner>: TypeVisitable<I> + Clone {
|
||||
/// The entry point for folding. To fold a value `t` with a folder `f`
|
||||
/// call: `t.try_fold_with(f)`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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<I: Interner>: fmt::Debug + Clone {
|
||||
pub trait TypeVisitable<I: Interner>: fmt::Debug {
|
||||
/// The entry point for visiting. To visit a value `t` with a visitor `v`
|
||||
/// call: `t.visit_with(v)`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
|||
}
|
||||
|
||||
impl<T> OnceCell<T> {
|
||||
/// 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<T> OnceCell<T> {
|
|||
|
||||
/// 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<T> OnceCell<T> {
|
|||
|
||||
/// 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<T> OnceCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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<T> OnceCell<T> {
|
|||
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<T> OnceCell<T> {
|
|||
}
|
||||
|
||||
/// 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<T> OnceCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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<T> OnceCell<T> {
|
|||
}
|
||||
|
||||
/// 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<T> OnceCell<T> {
|
|||
///
|
||||
/// let mut cell: OnceCell<u32> = 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<T> OnceCell<T> {
|
|||
|
||||
/// 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<T> OnceCell<T> {
|
|||
|
||||
/// 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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1160,9 +1160,9 @@ impl<T> (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._
|
||||
|
|
|
|||
|
|
@ -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<T, F>`]: crate::sync::LazyLock
|
||||
/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new
|
||||
|
|
@ -126,7 +129,7 @@ pub struct OnceLock<T> {
|
|||
}
|
||||
|
||||
impl<T> OnceLock<T> {
|
||||
/// 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<T> OnceLock<T> {
|
|||
|
||||
/// 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<T> OnceLock<T> {
|
|||
|
||||
/// 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> {
|
||||
|
|
@ -194,12 +198,13 @@ impl<T> OnceLock<T> {
|
|||
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
|
||||
///
|
||||
|
|
@ -228,13 +233,15 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
|
|
@ -267,8 +274,8 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
|
@ -276,7 +283,7 @@ impl<T> OnceLock<T> {
|
|||
///
|
||||
/// # 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
|
||||
|
|
@ -306,13 +313,13 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
|
|
@ -343,13 +350,13 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
|
|
@ -395,14 +402,14 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
|
|
@ -414,7 +421,7 @@ impl<T> OnceLock<T> {
|
|||
///
|
||||
/// let mut cell: OnceLock<u32> = 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());
|
||||
///
|
||||
|
|
@ -438,7 +445,7 @@ impl<T> OnceLock<T> {
|
|||
}
|
||||
|
||||
/// Consumes the `OnceLock`, returning the wrapped value. Returns
|
||||
/// `None` if the cell was empty.
|
||||
/// `None` if the cell was uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -460,7 +467,7 @@ impl<T> OnceLock<T> {
|
|||
|
||||
/// 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.
|
||||
///
|
||||
|
|
@ -526,7 +533,7 @@ impl<T> OnceLock<T> {
|
|||
|
||||
/// # Safety
|
||||
///
|
||||
/// The value must be initialized
|
||||
/// The cell must be initialized
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.is_initialized());
|
||||
|
|
@ -535,7 +542,7 @@ impl<T> OnceLock<T> {
|
|||
|
||||
/// # 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());
|
||||
|
|
@ -560,7 +567,7 @@ impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
|
|||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T> Default for OnceLock<T> {
|
||||
/// Creates a new empty cell.
|
||||
/// Creates a new uninitialized cell.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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<T: Merge>(x: &mut Option<T>, y: Option<T>, 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit fa312a343fbff01bc6cef393e326817f70719813
|
||||
Subproject commit e2fa4316c5a7c0d2499c5d6b799adcfad6ef7a45
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda
|
||||
Subproject commit f56aecc3b036dff16404b525a83b00f911b9bbea
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948
|
||||
Subproject commit 336f75835a6c0514852cc65aba9a698b699b13c8
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d
|
||||
Subproject commit 4249fb411dd27f945e2881eb0378044b94cee06f
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 054259ed1bf01cdee4309ee764c7e103f6df3de5
|
||||
Subproject commit 743766929f1e53e72fab74394ae259bbfb4a7619
|
||||
|
|
@ -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
|
||||
[`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-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
|
||||
[`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]
|
||||
|
|
|
|||
|
|
@ -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<Symbol> {
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -389,8 +389,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::<OptionsTargetModifiers, String>::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,
|
||||
|
|
|
|||
|
|
@ -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, ", ")?;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
unsafe {
|
||||
(&1_u8 as *const u8).offset_from(&2_u8); //~ERROR: not both derived from the same allocation
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
36
tests/codegen/intrinsics/cold_path2.rs
Normal file
36
tests/codegen/intrinsics/cold_path2.rs
Normal file
|
|
@ -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<bool>) {
|
||||
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}
|
||||
87
tests/codegen/intrinsics/cold_path3.rs
Normal file
87
tests/codegen/intrinsics/cold_path3.rs
Normal file
|
|
@ -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<u32>) {
|
||||
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<u32>) {
|
||||
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}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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::<impl *const u32>::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::<impl *const u32>::sub_ptr`
|
||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub const DIFFERENT_ALLOC: usize = {
|
|||
let uninit2 = std::mem::MaybeUninit::<Struct>::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::<Struct>::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 = {
|
||||
|
|
|
|||
|
|
@ -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::<impl *const u8>::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::<impl *const u8>::offset_from`
|
||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||
|
|
|
|||
9
tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs
Normal file
9
tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs
Normal file
|
|
@ -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<T = Vec<str>, 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() {}
|
||||
20
tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr
Normal file
20
tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/def-site-param-defaults-wf.rs:5:44
|
||||
|
|
||||
LL | type Alias<T = Vec<str>, 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<T = Vec<str>, 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`.
|
||||
13
tests/ui/lazy-type-alias/def-site-predicates-wf.rs
Normal file
13
tests/ui/lazy-type-alias/def-site-predicates-wf.rs
Normal file
|
|
@ -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<str>:; //~ ERROR the size for values of type `str` cannot be known at compilation time
|
||||
|
||||
type Alias1 = ()
|
||||
where
|
||||
Vec<str>: Sized; //~ ERROR the size for values of type `str` cannot be known at compilation time
|
||||
|
||||
fn main() {}
|
||||
23
tests/ui/lazy-type-alias/def-site-predicates-wf.stderr
Normal file
23
tests/ui/lazy-type-alias/def-site-predicates-wf.stderr
Normal file
|
|
@ -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<str>:;
|
||||
| ^^^^^^^^ 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<str>: 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`.
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
//@ revisions: edition2021 edition2024
|
||||
//@ ignore-wasm no panic support
|
||||
//@ needs-subprocess
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ run-pass
|
||||
|
|
|
|||
18
tests/ui/nll/get_default.legacy.stderr
Normal file
18
tests/ui/nll/get_default.legacy.stderr
Normal file
|
|
@ -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`.
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue