Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-02-04 05:12:08 +00:00
commit cf4be3f39d
157 changed files with 1959 additions and 605 deletions

View file

@ -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);

View file

@ -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,
&regioncx,
&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:

View file

@ -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,
}
}

View file

@ -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<()> {

View file

@ -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

View file

@ -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 }
}
}

View file

@ -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);
}
}
}

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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() };
}

View file

@ -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])
}),
);
}
}
}

View file

@ -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

View file

@ -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,

View file

@ -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 =

View file

@ -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;
}

View file

@ -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())
}
}
}

View file

@ -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(

View file

@ -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)
};

View file

@ -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,
);

View file

@ -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

View file

@ -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));

View file

@ -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" }

View file

@ -268,6 +268,7 @@ fn configure_and_expand(
resolver.resolve_crate(&krate);
CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate);
krate
}

View file

@ -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

View file

@ -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,

View file

@ -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,
}

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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,)*
}

View file

@ -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(),

View file

@ -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...
//

View file

@ -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,

View file

@ -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> {

View file

@ -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,

View file

@ -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(())
}

View file

@ -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())
});
}

View file

@ -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);
});
}
}

View file

@ -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
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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.

View file

@ -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

View file

@ -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,
}
}

View file

@ -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

View file

@ -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.\

View file

@ -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");

View file

@ -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);

View file

@ -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();

View file

@ -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:

View file

@ -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,

View file

@ -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(),

View file

@ -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" => {

View file

@ -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)`.
///

View file

@ -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)`.
///

View file

@ -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(&current_value, value)` if it was full.
/// This method returns `Ok(&value)` if the cell was uninitialized
/// and `Err((&current_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.
///

View file

@ -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._

View file

@ -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(&current_value, value)` if it was full.
/// Returns `Ok(&value)` if the cell was uninitialized and
/// `Err((&current_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
///

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -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(),

View file

@ -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,

View file

@ -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, ", ")?;

View file

@ -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;
};

View file

@ -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 {

View file

@ -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),
);

View 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
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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:

View 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}

View 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}

View file

@ -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;
}

View file

@ -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)) {}

View file

@ -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 ());

View file

@ -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])

View file

@ -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

View file

@ -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 = {

View file

@ -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

View 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() {}

View 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`.

View 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() {}

View 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`.

View file

@ -1,5 +1,4 @@
//@ revisions: edition2021 edition2024
//@ ignore-wasm no panic support
//@ needs-subprocess
//@ [edition2024] edition: 2024
//@ run-pass

View 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