Merge pull request #4534 from rust-lang/rustup-2025-08-21

Automatic Rustup
This commit is contained in:
Ralf Jung 2025-08-21 14:48:22 +00:00 committed by GitHub
commit 802de3ded1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
348 changed files with 2955 additions and 2029 deletions

View file

@ -907,6 +907,12 @@ impl TokenTreeCursor {
pub fn bump(&mut self) {
self.index += 1;
}
// For skipping ahead in rare circumstances.
#[inline]
pub fn bump_to_end(&mut self) {
self.index = self.stream.len();
}
}
/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that

View file

@ -1,7 +1,6 @@
use std::fmt;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph;
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
use rustc_middle::mir::{
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
@ -317,9 +316,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
loans_out_of_scope_at_location: FxIndexMap::default(),
};
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
let issuing_region = loan_data.region;
let loan_issued_at = loan_data.reserve_location;
prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
}
prec.loans_out_of_scope_at_location
@ -328,45 +326,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
/// Loans are in scope while they are live: whether they are contained within any live region.
/// In the location-insensitive analysis, a loan will be contained in a region if the issuing
/// region can reach it in the subset graph. So this is a reachability problem.
fn precompute_loans_out_of_scope(
&mut self,
loan_idx: BorrowIndex,
issuing_region: RegionVid,
loan_issued_at: Location,
) {
let sccs = self.regioncx.constraint_sccs();
let universal_regions = self.regioncx.universal_regions();
// The loop below was useful for the location-insensitive analysis but shouldn't be
// impactful in the location-sensitive case. It seems that it does, however, as without it a
// handful of tests fail. That likely means some liveness or outlives data related to choice
// regions is missing
// FIXME: investigate the impact of loans traversing applied member constraints and why some
// tests fail otherwise.
//
// We first handle the cases where the loan doesn't go out of scope, depending on the
// issuing region's successors.
for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
// Via applied member constraints
//
// The issuing region can flow into the choice regions, and they are either:
// - placeholders or free regions themselves,
// - or also transitively outlive a free region.
//
// That is to say, if there are applied member constraints here, the loan escapes the
// function and cannot go out of scope. We could early return here.
//
// For additional insurance via fuzzing and crater, we verify that the constraint's min
// choice indeed escapes the function. In the future, we could e.g. turn this check into
// a debug assert and early return as an optimization.
let scc = sccs.scc(successor);
for constraint in self.regioncx.applied_member_constraints(scc) {
if universal_regions.is_universal_region(constraint.min_choice) {
return;
}
}
}
fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
let first_block = loan_issued_at.block;
let first_bb_data = &self.body.basic_blocks[first_block];

View file

@ -13,6 +13,8 @@ use rustc_middle::mir::{self, ConstraintCategory, Location};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::Span;
use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
use crate::MirBorrowckCtxt;
@ -26,13 +28,61 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if errors.is_empty() {
return;
}
let infcx = self.infcx;
let mut guar = None;
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
None;
for error in errors {
guar = Some(match error {
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx),
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
self.infcx.dcx().emit_err(err)
infcx.dcx().emit_err(err)
}
DeferredOpaqueTypeError::UnexpectedHiddenRegion {
opaque_type_key,
hidden_type,
member_region,
} => {
let named_ty =
self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty);
let named_key = self
.regioncx
.name_regions_for_member_constraint(infcx.tcx, opaque_type_key);
let named_region =
self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region);
let diag = unexpected_hidden_region_diagnostic(
infcx,
self.mir_def_id(),
hidden_type.span,
named_ty,
named_region,
named_key,
);
if last_unexpected_hidden_region
!= Some((hidden_type.span, named_ty, named_key))
{
last_unexpected_hidden_region =
Some((hidden_type.span, named_ty, named_key));
diag.emit()
} else {
diag.delay_as_bug()
}
}
DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
span,
opaque_type_key,
} => infcx.dcx().span_err(
span,
format!(
"non-defining use of `{}` in the defining scope",
Ty::new_opaque(
infcx.tcx,
opaque_type_key.def_id.to_def_id(),
opaque_type_key.args
)
),
),
});
}
let guar = guar.unwrap();
@ -194,7 +244,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
// Find a path between the borrow region and our opaque capture.
if let Some((path, _)) =
self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
self.regioncx.find_constraint_path_between_regions(self.borrow_region, |r| {
r == opaque_region_vid
})
{

View file

@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{
self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type,
find_param_with_region, suggest_adding_lifetime_params,
};
use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
use tracing::{debug, instrument, trace};
@ -105,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).
TypeTestError { type_test: TypeTest<'tcx> },
/// An unexpected hidden region for an opaque type.
UnexpectedHiddenRegion {
/// The span for the member constraint.
span: Span,
/// The hidden type.
hidden_ty: Ty<'tcx>,
/// The opaque type.
key: ty::OpaqueTypeKey<'tcx>,
/// The unexpected region.
member_region: ty::Region<'tcx>,
},
/// Higher-ranked subtyping error.
BoundUniversalRegionError {
/// The placeholder free region.
@ -323,11 +310,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
// buffered in the `MirBorrowckCtxt`.
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
None;
for (nll_error, _) in nll_errors.into_iter() {
match nll_error {
RegionErrorKind::TypeTestError { type_test } => {
@ -378,30 +361,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
let named_ty =
self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
let named_key =
self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key);
let named_region = self
.regioncx
.name_regions_for_member_constraint(self.infcx.tcx, member_region);
let diag = unexpected_hidden_region_diagnostic(
self.infcx,
self.mir_def_id(),
span,
named_ty,
named_region,
named_key,
);
if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
self.buffer_error(diag);
last_unexpected_hidden_region = Some((span, named_ty, named_key));
} else {
diag.delay_as_bug();
}
}
RegionErrorKind::BoundUniversalRegionError {
longer_fr,
placeholder,

View file

@ -15,7 +15,6 @@ use tracing::debug;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
use crate::consumers::OutlivesConstraint;
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
use crate::ty::VarianceDiagInfo;
@ -30,7 +29,6 @@ pub(crate) struct LoweredConstraints<'tcx> {
pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
pub(crate) liveness_constraints: LivenessValues,
@ -147,9 +145,10 @@ impl scc::Annotation for RegionTracker {
/// Determines if the region variable definitions contain
/// placeholders, and compute them for later use.
fn region_definitions<'tcx>(
universal_regions: &UniversalRegions<'tcx>,
// FIXME: This is also used by opaque type handling. Move it to a separate file.
pub(super) fn region_definitions<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
let var_infos = infcx.get_region_var_infos();
// Create a RegionDefinition for each inference variable. This happens here because
@ -213,14 +212,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
) -> LoweredConstraints<'tcx> {
let universal_regions = &universal_region_relations.universal_regions;
let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
let (definitions, has_placeholders) = region_definitions(infcx, universal_regions);
let MirTypeckRegionConstraints {
placeholder_indices,
placeholder_index_to_region: _,
liveness_constraints,
mut outlives_constraints,
member_constraints,
universe_causes,
type_tests,
} = constraints;
@ -246,7 +244,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
return LoweredConstraints {
type_tests,
member_constraints,
constraint_sccs,
scc_annotations: scc_annotations.scc_to_annotation,
definitions,
@ -283,7 +280,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
constraint_sccs,
definitions,
scc_annotations,
member_constraints,
outlives_constraints: Frozen::freeze(outlives_constraints),
type_tests,
liveness_constraints,

View file

@ -78,7 +78,6 @@ mod dataflow;
mod def_use;
mod diagnostics;
mod handle_placeholders;
mod member_constraints;
mod nll;
mod path_utils;
mod place_ext;
@ -335,9 +334,10 @@ fn do_mir_borrowck<'tcx>(
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
mut constraints,
universal_region_relations,
opaque_type_values,
region_bound_pairs,
known_type_outlives_obligations,
polonius_context,
} = type_check::type_check(
root_cx,
@ -352,6 +352,17 @@ fn do_mir_borrowck<'tcx>(
Rc::clone(&location_map),
);
let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses(
root_cx,
&infcx,
&body,
&universal_region_relations,
&region_bound_pairs,
&known_type_outlives_obligations,
&location_map,
&mut constraints,
);
// Compute non-lexical lifetimes using the constraints computed
// by typechecking the MIR body.
let nll::NllOutput {
@ -375,8 +386,6 @@ fn do_mir_borrowck<'tcx>(
polonius_context,
);
let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
// Dump MIR results into a file, if that is enabled. This lets us
// write unit-tests, as well as helping with debugging.
nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);

View file

@ -1,226 +0,0 @@
use std::hash::Hash;
use std::ops::Index;
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use tracing::instrument;
/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
/// indexed by the region `R0`.
#[derive(Debug)]
pub(crate) struct MemberConstraintSet<'tcx, R>
where
R: Copy + Eq,
{
/// Stores the first "member" constraint for a given `R0`. This is an
/// index into the `constraints` vector below.
first_constraints: FxIndexMap<R, NllMemberConstraintIndex>,
/// Stores the data about each `R0 member of [R1..Rn]` constraint.
/// These are organized into a linked list, so each constraint
/// contains the index of the next constraint with the same `R0`.
constraints: IndexVec<NllMemberConstraintIndex, MemberConstraint<'tcx>>,
/// Stores the `R1..Rn` regions for *all* sets. For any given
/// constraint, we keep two indices so that we can pull out a
/// slice.
choice_regions: Vec<ty::RegionVid>,
}
/// Represents a `R0 member of [R1..Rn]` constraint
#[derive(Debug)]
pub(crate) struct MemberConstraint<'tcx> {
next_constraint: Option<NllMemberConstraintIndex>,
/// The span where the hidden type was instantiated.
pub(crate) definition_span: Span,
/// The hidden type in which `R0` appears. (Used in error reporting.)
pub(crate) hidden_ty: Ty<'tcx>,
pub(crate) key: ty::OpaqueTypeKey<'tcx>,
/// The region `R0`.
pub(crate) member_region_vid: ty::RegionVid,
/// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
start_index: usize,
/// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
end_index: usize,
}
rustc_index::newtype_index! {
#[debug_format = "MemberConstraintIndex({})"]
pub(crate) struct NllMemberConstraintIndex {}
}
impl Default for MemberConstraintSet<'_, ty::RegionVid> {
fn default() -> Self {
Self {
first_constraints: Default::default(),
constraints: Default::default(),
choice_regions: Default::default(),
}
}
}
impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
pub(crate) fn is_empty(&self) -> bool {
self.constraints.is_empty()
}
/// Pushes a member constraint into the set.
#[instrument(level = "debug", skip(self))]
pub(crate) fn add_member_constraint(
&mut self,
key: ty::OpaqueTypeKey<'tcx>,
hidden_ty: Ty<'tcx>,
definition_span: Span,
member_region_vid: ty::RegionVid,
choice_regions: &[ty::RegionVid],
) {
let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
let start_index = self.choice_regions.len();
self.choice_regions.extend(choice_regions);
let end_index = self.choice_regions.len();
let constraint_index = self.constraints.push(MemberConstraint {
next_constraint,
member_region_vid,
definition_span,
hidden_ty,
key,
start_index,
end_index,
});
self.first_constraints.insert(member_region_vid, constraint_index);
}
}
impl<'tcx, R1> MemberConstraintSet<'tcx, R1>
where
R1: Copy + Hash + Eq,
{
/// Remap the "member region" key using `map_fn`, producing a new
/// member constraint set. This is used in the NLL code to map from
/// the original `RegionVid` to an scc index. In some cases, we
/// may have multiple `R1` values mapping to the same `R2` key -- that
/// is ok, the two sets will be merged.
pub(crate) fn into_mapped<R2>(
self,
mut map_fn: impl FnMut(R1) -> R2,
) -> MemberConstraintSet<'tcx, R2>
where
R2: Copy + Hash + Eq,
{
// We can re-use most of the original data, just tweaking the
// linked list links a bit.
//
// For example if we had two keys `Ra` and `Rb` that both now
// wind up mapped to the same key `S`, we would append the
// linked list for `Ra` onto the end of the linked list for
// `Rb` (or vice versa) -- this basically just requires
// rewriting the final link from one list to point at the other
// other (see `append_list`).
let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
let mut first_constraints2 = FxIndexMap::default();
first_constraints2.reserve(first_constraints.len());
for (r1, start1) in first_constraints {
let r2 = map_fn(r1);
if let Some(&start2) = first_constraints2.get(&r2) {
append_list(&mut constraints, start1, start2);
}
first_constraints2.insert(r2, start1);
}
MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions }
}
}
impl<'tcx, R> MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> {
self.constraints.indices()
}
/// Iterate down the constraint indices associated with a given
/// peek-region. You can then use `choice_regions` and other
/// methods to access data.
pub(crate) fn indices(
&self,
member_region_vid: R,
) -> impl Iterator<Item = NllMemberConstraintIndex> {
let mut next = self.first_constraints.get(&member_region_vid).cloned();
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
if let Some(current) = next {
next = self.constraints[current].next_constraint;
Some(current)
} else {
None
}
})
}
/// Returns the "choice regions" for a given member
/// constraint. This is the `R1..Rn` from a constraint like:
///
/// ```text
/// R0 member of [R1..Rn]
/// ```
pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
&self.choice_regions[*start_index..*end_index]
}
}
impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
where
R: Copy + Eq,
{
type Output = MemberConstraint<'tcx>;
fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> {
&self.constraints[i]
}
}
/// Given a linked list starting at `source_list` and another linked
/// list starting at `target_list`, modify `target_list` so that it is
/// followed by `source_list`.
///
/// Before:
///
/// ```text
/// target_list: A -> B -> C -> (None)
/// source_list: D -> E -> F -> (None)
/// ```
///
/// After:
///
/// ```text
/// target_list: A -> B -> C -> D -> E -> F -> (None)
/// ```
fn append_list(
constraints: &mut IndexSlice<NllMemberConstraintIndex, MemberConstraint<'_>>,
target_list: NllMemberConstraintIndex,
source_list: NllMemberConstraintIndex,
) {
let mut p = target_list;
loop {
let r = &mut constraints[p];
match r.next_constraint {
Some(q) => p = q,
None => {
r.next_constraint = Some(source_list);
return;
}
}
}
}

View file

@ -1,8 +1,6 @@
use std::cell::OnceCell;
use std::collections::VecDeque;
use std::rc::Rc;
use rustc_data_structures::binary_search_util;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::scc::{self, Sccs};
@ -24,15 +22,13 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::{DUMMY_SP, Span};
use tracing::{Level, debug, enabled, instrument, trace};
use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
use crate::constraints::graph::NormalConstraintGraph;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
use crate::dataflow::BorrowIndex;
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::polonius::LiveLoans;
use crate::polonius::legacy::PoloniusOutput;
use crate::region_infer::reverse_sccs::ReverseSccGraph;
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
use crate::type_check::Locations;
use crate::type_check::free_region_relations::UniversalRegionRelations;
@ -120,20 +116,6 @@ pub struct RegionInferenceContext<'tcx> {
scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
/// `B: A`. This is used to compute the universal regions that are required
/// to outlive a given SCC.
rev_scc_graph: OnceCell<ReverseSccGraph>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// Records the member constraints that we applied to each scc.
/// This is useful for error reporting. Once constraint
/// propagation is done, this vector is sorted according to
/// `member_region_scc`.
member_constraints_applied: Vec<AppliedMemberConstraint>,
/// Map universe indexes to information on why we created it.
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
@ -150,32 +132,6 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
}
/// Each time that `apply_member_constraint` is successful, it appends
/// one of these structs to the `member_constraints_applied` field.
/// This is used in error reporting to trace out what happened.
///
/// The way that `apply_member_constraint` works is that it effectively
/// adds a new lower bound to the SCC it is analyzing: so you wind up
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Debug)]
pub(crate) struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
/// The vector if `AppliedMemberConstraint` elements is kept sorted
/// by this field.
pub(crate) member_region_scc: ConstraintSccIndex,
/// The "best option" that `apply_member_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
pub(crate) min_choice: ty::RegionVid,
/// The "member constraint index" -- we can find out details about
/// the constraint from
/// `set.member_constraints[member_constraint_index]`.
pub(crate) member_constraint_index: NllMemberConstraintIndex,
}
#[derive(Debug)]
pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
@ -268,7 +224,6 @@ enum Trace<'a, 'tcx> {
StartRegion,
FromGraph(&'a OutlivesConstraint<'tcx>),
FromStatic(RegionVid),
FromMember(RegionVid, RegionVid, Span),
NotVisited,
}
@ -363,7 +318,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
liveness_constraints,
universe_causes,
placeholder_indices,
member_constraints,
} = lowered_constraints;
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
@ -385,9 +339,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
scc_values.merge_liveness(scc, region, &liveness_constraints);
}
let member_constraints =
Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self {
definitions,
liveness_constraints,
@ -395,9 +346,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
constraint_graph,
constraint_sccs,
scc_annotations,
rev_scc_graph: OnceCell::new(),
member_constraints,
member_constraints_applied: Vec::new(),
universe_causes,
scc_values,
type_tests,
@ -550,19 +498,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_values.placeholders_contained_in(scc)
}
/// Once region solving has completed, this function will return the member constraints that
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
pub(crate) fn applied_member_constraints(
&self,
scc: ConstraintSccIndex,
) -> &[AppliedMemberConstraint] {
binary_search_util::binary_search_slice(
&self.member_constraints_applied,
|applied| applied.member_region_scc,
&scc,
)
}
/// Performs region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
@ -607,12 +542,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!(?errors_buffer);
if errors_buffer.is_empty() {
self.check_member_constraints(infcx, &mut errors_buffer);
}
debug!(?errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or_default();
if outlives_requirements.is_empty() {
@ -642,146 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
// To propagate constraints, we walk the DAG induced by the
// SCC. For each SCC, we visit its successors and compute
// SCC. For each SCC `A`, we visit its successors and compute
// their values, then we union all those values to get our
// own.
for scc in self.constraint_sccs.all_sccs() {
self.compute_value_for_scc(scc);
}
// Sort the applied member constraints so we can binary search
// through them later.
self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
}
/// Computes the value of the SCC `scc_a`, which has not yet been
/// computed, by unioning the values of its successors.
/// Assumes that all successors have been computed already
/// (which is assured by iterating over SCCs in dependency order).
#[instrument(skip(self), level = "debug")]
fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
// Walk each SCC `B` such that `A: B`...
for &scc_b in self.constraint_sccs.successors(scc_a) {
debug!(?scc_b);
self.scc_values.add_region(scc_a, scc_b);
}
// Now take member constraints into account.
let member_constraints = Rc::clone(&self.member_constraints);
for m_c_i in member_constraints.indices(scc_a) {
self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
}
debug!(value = ?self.scc_values.region_value_str(scc_a));
}
/// Invoked for each `R0 member of [R1..Rn]` constraint.
///
/// `scc` is the SCC containing R0, and `choice_regions` are the
/// `R1..Rn` regions -- they are always known to be universal
/// regions (and if that's not true, we just don't attempt to
/// enforce the constraint).
///
/// The current value of `scc` at the time the method is invoked
/// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
///
/// This function only adds the member constraints to the region graph,
/// it does not check them. They are later checked in
/// `check_member_constraints` after the region graph has been computed.
#[instrument(skip(self, member_constraint_index), level = "debug")]
fn apply_member_constraint(
&mut self,
scc: ConstraintSccIndex,
member_constraint_index: NllMemberConstraintIndex,
choice_regions: &[ty::RegionVid],
) {
// Create a mutable vector of the options. We'll try to winnow
// them down.
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
// Convert to the SCC representative: sometimes we have inference
// variables in the member constraint that wind up equated with
// universal regions. The scc representative is the minimal numbered
// one from the corresponding scc so it will be the universal region
// if one exists.
for c_r in &mut choice_regions {
let scc = self.constraint_sccs.scc(*c_r);
*c_r = self.scc_representative(scc);
}
// If the member region lives in a higher universe, we currently choose
// the most conservative option by leaving it unchanged.
if !self.max_placeholder_universe_reached(scc).is_root() {
return;
}
// The existing value for `scc` is a lower-bound. This will
// consist of some set `{P} + {LB}` of points `{P}` and
// lower-bound free regions `{LB}`. As each choice region `O`
// is a free region, it will outlive the points. But we can
// only consider the option `O` if `O: LB`.
choice_regions.retain(|&o_r| {
self.scc_values
.universal_regions_outlived_by(scc)
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
});
debug!(?choice_regions, "after lb");
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region `R0` (`UB:
// R0`). Therefore, we need only keep an option `O` if `UB: O`
// for all UB.
let universal_region_relations = &self.universal_region_relations;
for ub in self.reverse_scc_graph().upper_bounds(scc) {
debug!(?ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
debug!(?choice_regions, "after ub");
// At this point we can pick any member of `choice_regions` and would like to choose
// it to be a small as possible. To avoid potential non-determinism we will pick the
// smallest such choice.
//
// Because universal regions are only partially ordered (i.e, not every two regions are
// comparable), we will ignore any region that doesn't compare to all others when picking
// the minimum choice.
//
// For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
// `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
// `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
choice_regions.iter().all(|&r2| {
self.universal_region_relations.outlives(r1, r2)
|| self.universal_region_relations.outlives(r2, r1)
})
});
// Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
match (r1_outlives_r2, r2_outlives_r1) {
(true, true) => r1.min(r2),
(true, false) => r2,
(false, true) => r1,
(false, false) => bug!("incomparable regions in total order"),
for scc_a in self.constraint_sccs.all_sccs() {
// Walk each SCC `B` such that `A: B`...
for &scc_b in self.constraint_sccs.successors(scc_a) {
debug!(?scc_b);
self.scc_values.add_region(scc_a, scc_b);
}
}) else {
debug!("no unique minimum choice");
return;
};
// As we require `'scc: 'min_choice`, we have definitely already computed
// its `scc_values` at this point.
let min_choice_scc = self.constraint_sccs.scc(min_choice);
debug!(?min_choice, ?min_choice_scc);
if self.scc_values.add_region(scc, min_choice_scc) {
self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
min_choice,
member_constraint_index,
});
}
}
@ -1376,13 +1174,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_annotations[scc].max_nameable_universe()
}
pub(crate) fn max_placeholder_universe_reached(
&self,
scc: ConstraintSccIndex,
) -> UniverseIndex {
self.scc_annotations[scc].max_placeholder_universe_reached()
}
/// Checks the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
@ -1551,43 +1342,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
#[instrument(level = "debug", skip(self, infcx, errors_buffer))]
fn check_member_constraints(
&self,
infcx: &InferCtxt<'tcx>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
let member_constraints = Rc::clone(&self.member_constraints);
for m_c_i in member_constraints.all_indices() {
debug!(?m_c_i);
let m_c = &member_constraints[m_c_i];
let member_region_vid = m_c.member_region_vid;
debug!(
?member_region_vid,
value = ?self.region_value_str(member_region_vid),
);
let choice_regions = member_constraints.choice_regions(m_c_i);
debug!(?choice_regions);
// Did the member region wind up equal to any of the option regions?
if let Some(o) =
choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
{
debug!("evaluated as equal to {:?}", o);
continue;
}
// If not, report an error.
let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
span: m_c.definition_span,
hidden_ty: m_c.hidden_ty,
key: m_c.key,
member_region,
});
}
}
/// We have a constraint `fr1: fr2` that is not satisfied, where
/// `fr2` represents some universal region. Here, `r` is some
/// region where we know that `fr1: r` and this function has the
@ -1651,18 +1405,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// Walks the graph of constraints (where `'a: 'b` is considered
/// an edge `'a -> 'b`) to find all paths from `from_region` to
/// `to_region`. The paths are accumulated into the vector
/// `results`. The paths are stored as a series of
/// `ConstraintIndex` values -- in other words, a list of *edges*.
/// an edge `'a -> 'b`) to find a path from `from_region` to
/// `to_region`.
///
/// Returns: a series of constraints as well as the region `R`
/// that passed the target test.
#[instrument(skip(self, target_test), ret)]
pub(crate) fn find_constraint_paths_between_regions(
pub(crate) fn find_constraint_path_between_regions(
&self,
from_region: RegionVid,
target_test: impl Fn(RegionVid) -> bool,
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
self.find_constraint_path_between_regions_inner(true, from_region, &target_test).or_else(
|| self.find_constraint_path_between_regions_inner(false, from_region, &target_test),
)
}
/// The constraints we get from equating the hidden type of each use of an opaque
/// with its final concrete type may end up getting preferred over other, potentially
/// longer constraint paths.
///
/// Given that we compute the final concrete type by relying on this existing constraint
/// path, this can easily end up hiding the actual reason for why we require these regions
/// to be equal.
///
/// To handle this, we first look at the path while ignoring these constraints and then
/// retry while considering them. This is not perfect, as the `from_region` may have already
/// been partially related to its argument region, so while we rely on a member constraint
/// to get a complete path, the most relevant step of that path already existed before then.
fn find_constraint_path_between_regions_inner(
&self,
ignore_opaque_type_constraints: bool,
from_region: RegionVid,
target_test: impl Fn(RegionVid) -> bool,
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
context[from_region] = Trace::StartRegion;
@ -1677,7 +1452,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
while let Some(r) = deque.pop_front() {
debug!(
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
"find_constraint_path_between_regions: from_region={:?} r={:?} value={}",
from_region,
r,
self.region_value_str(r),
@ -1711,20 +1486,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
result.push(c);
}
Trace::FromMember(sup, sub, span) => {
let c = OutlivesConstraint {
sup,
sub,
locations: Locations::All(span),
span,
category: ConstraintCategory::OpaqueType,
variance_info: ty::VarianceDiagInfo::default(),
from_closure: false,
};
p = c.sup;
result.push(c);
}
Trace::StartRegion => {
result.reverse();
return Some((result, r));
@ -1763,23 +1524,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
// This loop can be hot.
for constraint in edges {
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
debug!("Ignoring illegal universe constraint: {constraint:?}");
continue;
match constraint.category {
ConstraintCategory::IllegalUniverse => {
debug!("Ignoring illegal universe constraint: {constraint:?}");
continue;
}
ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => {
debug!("Ignoring member constraint: {constraint:?}");
continue;
}
_ => {}
}
debug_assert_eq!(constraint.sup, r);
handle_trace(constraint.sub, Trace::FromGraph(constraint));
}
}
// Member constraints can also give rise to `'r: 'x` edges that
// were not part of the graph initially, so watch out for those.
// (But they are extremely rare; this loop is very cold.)
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
let sub = constraint.min_choice;
let p_c = &self.member_constraints[constraint.member_constraint_index];
handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
}
}
None
@ -1790,7 +1549,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
trace!(scc = ?self.constraint_sccs.scc(fr1));
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
self.find_constraint_paths_between_regions(fr1, |r| {
self.find_constraint_path_between_regions(fr1, |r| {
// First look for some `r` such that `fr1: r` and `r` is live at `location`
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
self.liveness_constraints.is_live_at(r, location)
@ -1800,9 +1559,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// `fr1: r` and `r` is a placeholder from some universe
// `fr1` cannot name. This would force `fr1` to be
// `'static`.
self.find_constraint_paths_between_regions(fr1, |r| {
self.cannot_name_placeholder(fr1, r)
})
self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(fr1, r))
})
.or_else(|| {
// If we fail to find THAT, it may be that `fr1` is a
@ -1815,9 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// must be able to name the universe of R2, because R2 will
// be at least `'empty(Universe(R2))`, and `R1` must be at
// larger than that.
self.find_constraint_paths_between_regions(fr1, |r| {
self.cannot_name_placeholder(r, fr1)
})
self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(r, fr1))
})
.map(|(_path, r)| r)
.unwrap()
@ -1873,9 +1628,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
// Find all paths
let (path, target_region) = self
.find_constraint_paths_between_regions(from_region, target_test)
.find_constraint_path_between_regions(from_region, target_test)
.or_else(|| {
self.find_constraint_paths_between_regions(from_region, |r| {
self.find_constraint_path_between_regions(from_region, |r| {
self.cannot_name_placeholder(from_region, r)
})
})
@ -2111,11 +1866,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self.constraint_sccs
}
/// Access to the region graph, built from the outlives constraints.
pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> {
self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
}
/// Returns the representative `RegionVid` for a given SCC.
/// See `RegionTracker` for how a region variable ID is chosen.
///

View file

@ -1,299 +0,0 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_macros::extension;
use rustc_middle::ty::{
self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, fold_regions,
};
use rustc_span::Span;
use rustc_trait_selection::opaque_types::{
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
};
use tracing::{debug, instrument};
use super::RegionInferenceContext;
use crate::BorrowCheckRootCtxt;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// Resolve any opaque types that were encountered while borrow checking
/// this item. This is then used to get the type in the `type_of` query.
///
/// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
/// This is lowered to give HIR something like
///
/// type f<'a>::_Return<'_x> = impl Sized + '_x;
/// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
///
/// When checking the return type record the type from the return and the
/// type used in the return value. In this case they might be `_Return<'1>`
/// and `&'2 i32` respectively.
///
/// Once we to this method, we have completed region inference and want to
/// call `infer_opaque_definition_from_instantiation` to get the inferred
/// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
/// compares lifetimes directly, so we need to map the inference variables
/// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
///
/// First we map the regions in the generic parameters `_Return<'1>` to
/// their `external_name` giving `_Return<'a>`. This step is a bit involved.
/// See the [rustc-dev-guide chapter] for more info.
///
/// Then we map all the lifetimes in the concrete type to an equal
/// universal region that occurs in the opaque type's args, in this case
/// this would result in `&'a i32`. We only consider regions in the args
/// in case there is an equal region that does not. For example, this should
/// be allowed:
/// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
///
/// This will then allow `infer_opaque_definition_from_instantiation` to
/// determine that `_Return<'_x> = &'_x i32`.
///
/// There's a slight complication around closures. Given
/// `fn f<'a: 'a>() { || {} }` the closure's type is something like
/// `f::<'a>::{{closure}}`. The region parameter from f is essentially
/// ignored by type checking so ends up being inferred to an empty region.
/// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
/// which has no `external_name` in which case we use `'{erased}` as the
/// region to pass to `infer_opaque_definition_from_instantiation`.
///
/// [rustc-dev-guide chapter]:
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
#[instrument(level = "debug", skip(self, root_cx, infcx))]
pub(crate) fn infer_opaque_types(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
for (opaque_type_key, concrete_type) in opaque_ty_decls {
debug!(?opaque_type_key, ?concrete_type);
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
let opaque_type_key =
opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
// Use the SCC representative instead of directly using `region`.
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
let scc = self.constraint_sccs.scc(region.as_var());
let vid = self.scc_representative(scc);
let named = match self.definitions[vid].origin {
// Iterate over all universal regions in a consistent order and find the
// *first* equal region. This makes sure that equal lifetimes will have
// the same name and simplifies subsequent handling.
// See [rustc-dev-guide chapter] § "Semantic lifetime equality".
NllRegionVariableOrigin::FreeRegion => self
.universal_regions()
.universal_regions_iter()
.filter(|&ur| {
// See [rustc-dev-guide chapter] § "Closure restrictions".
!matches!(
self.universal_regions().region_classification(ur),
Some(RegionClassification::External)
)
})
.find(|&ur| self.universal_region_relations.equal(vid, ur))
.map(|ur| self.definitions[ur].external_name.unwrap()),
NllRegionVariableOrigin::Placeholder(placeholder) => {
Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
}
NllRegionVariableOrigin::Existential { .. } => None,
}
.unwrap_or_else(|| {
ty::Region::new_error_with_message(
infcx.tcx,
concrete_type.span,
"opaque type with non-universal region args",
)
});
arg_regions.push((vid, named));
named
});
debug!(?opaque_type_key, ?arg_regions);
let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
arg_regions
.iter()
.find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
.map(|&(_, arg_named)| arg_named)
.unwrap_or(infcx.tcx.lifetimes.re_erased)
});
debug!(?concrete_type);
let ty = match infcx
.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type)
{
Ok(ty) => ty,
Err(err) => {
errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
continue;
}
};
// Sometimes, when the hidden type is an inference variable, it can happen that
// the hidden type becomes the opaque type itself. In this case, this was an opaque
// usage of the opaque type and we can ignore it. This check is mirrored in typeck's
// writeback.
if !infcx.next_trait_solver() {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
&& alias_ty.args == opaque_type_key.args
{
continue;
}
}
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { span: concrete_type.span, ty },
);
// Check that all opaque types have the same region parameters if they have the same
// non-region parameters. This is necessary because within the new solver we perform
// various query operations modulo regions, and thus could unsoundly select some impls
// that don't hold.
if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
infcx.tcx.erase_regions(opaque_type_key),
(opaque_type_key, concrete_type.span),
) && let Some((arg1, arg2)) = std::iter::zip(
prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
)
.find(|(arg1, arg2)| arg1 != arg2)
{
errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
LifetimeMismatchOpaqueParam {
arg: arg1,
prev: arg2,
span: prev_span,
prev_span: concrete_type.span,
},
));
}
}
errors
}
/// Map the regions in the type to named regions. This is similar to what
/// `infer_opaque_types` does, but can infer any universal region, not only
/// ones from the args for the opaque type. It also doesn't double check
/// that the regions produced are in fact equal to the named region they are
/// replaced with. This is fine because this function is only to improve the
/// region names in error messages.
///
/// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
/// lax with mapping region vids that are *shorter* than a universal region to
/// that universal region. This is useful for member region constraints since
/// we want to suggest a universal region name to capture even if it's technically
/// not equal to the error region.
pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);
// Special handling of higher-ranked regions.
if !self.max_nameable_universe(scc).is_root() {
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
// If the region contains a single placeholder then they're equal.
Some((0, placeholder)) => {
return ty::Region::new_placeholder(tcx, placeholder);
}
// Fallback: this will produce a cryptic error message.
_ => return region,
}
}
// Find something that we can name
let upper_bound = self.approx_universal_upper_bound(vid);
if let Some(universal_region) = self.definitions[upper_bound].external_name {
return universal_region;
}
// Nothing exact found, so we pick a named upper bound, if there's only one.
// If there's >1 universal region, then we probably are dealing w/ an intersection
// region which cannot be mapped back to a universal.
// FIXME: We could probably compute the LUB if there is one.
let scc = self.constraint_sccs.scc(vid);
let upper_bounds: Vec<_> = self
.reverse_scc_graph()
.upper_bounds(scc)
.filter_map(|vid| self.definitions[vid].external_name)
.filter(|r| !r.is_static())
.collect();
match &upper_bounds[..] {
[universal_region] => *universal_region,
_ => region,
}
}
_ => region,
})
}
}
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Given the fully resolved, instantiated type for an opaque
/// type, i.e., the value of an inference variable like C1 or C2
/// (*), computes the "definition type" for an opaque type
/// definition -- that is, the inferred value of `Foo1<'x>` or
/// `Foo2<'x>` that we would conceptually use in its definition:
/// ```ignore (illustrative)
/// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA
/// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
/// ```
/// Note that these values are defined in terms of a distinct set of
/// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
/// purpose of this function is to do that translation.
///
/// (*) C1 and C2 were introduced in the comments on
/// `register_member_constraints`. Read that comment for more context.
///
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
/// - `args`, the args used to instantiate this opaque type
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
#[instrument(level = "debug", skip(self))]
fn infer_opaque_definition_from_instantiation(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
check_opaque_type_parameter_valid(
self,
opaque_type_key,
instantiated_ty.span,
DefiningScopeKind::MirBorrowck,
)?;
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(
opaque_type_key,
self.tcx,
DefiningScopeKind::MirBorrowck,
)
.ty;
definition_ty.error_reported()?;
Ok(definition_ty)
}
}

View file

@ -0,0 +1,194 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::ty::{
self, GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitor,
};
use tracing::{debug, instrument};
use super::DefiningUse;
use super::region_ctxt::RegionCtxt;
use crate::constraints::ConstraintSccIndex;
pub(super) fn apply_member_constraints<'tcx>(
rcx: &mut RegionCtxt<'_, 'tcx>,
defining_uses: &[DefiningUse<'tcx>],
) {
// Start by collecting the member constraints of all defining uses.
//
// Applying member constraints can influence other member constraints,
// so we first collect and then apply them.
let mut member_constraints = Default::default();
for defining_use in defining_uses {
let mut visitor = CollectMemberConstraintsVisitor {
rcx,
defining_use,
member_constraints: &mut member_constraints,
};
defining_use.hidden_type.ty.visit_with(&mut visitor);
}
// Now walk over the region graph, visiting the smallest regions first and then all
// regions which have to outlive that one.
//
// Whenever we encounter a member region, we mutate the value of this SCC. This is
// as if we'd introduce new outlives constraints. However, we discard these region
// values after we've inferred the hidden types of opaques and apply the region
// constraints by simply equating the actual hidden type with the inferred one.
debug!(?member_constraints);
for scc_a in rcx.constraint_sccs.all_sccs() {
debug!(?scc_a);
// Start by adding the region values required by outlives constraints. This
// matches how we compute the final region values in `fn compute_regions`.
//
// We need to do this here to get a lower bound when applying member constraints.
// This propagates the region values added by previous member constraints.
for &scc_b in rcx.constraint_sccs.successors(scc_a) {
debug!(?scc_b);
rcx.scc_values.add_region(scc_a, scc_b);
}
for defining_use in member_constraints.get(&scc_a).into_iter().flatten() {
apply_member_constraint(rcx, scc_a, &defining_use.arg_regions);
}
}
}
#[instrument(level = "debug", skip(rcx))]
fn apply_member_constraint<'tcx>(
rcx: &mut RegionCtxt<'_, 'tcx>,
member: ConstraintSccIndex,
arg_regions: &[RegionVid],
) {
// If the member region lives in a higher universe, we currently choose
// the most conservative option by leaving it unchanged.
if !rcx.max_placeholder_universe_reached(member).is_root() {
return;
}
// The existing value of `'member` is a lower-bound. If its is already larger than
// some universal region, we cannot equate it with that region. Said differently, we
// ignore choice regions which are smaller than this member region.
let mut choice_regions = arg_regions
.iter()
.copied()
.map(|r| rcx.representative(r).rvid())
.filter(|&choice_region| {
rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| {
rcx.universal_region_relations.outlives(choice_region, lower_bound)
})
})
.collect::<Vec<_>>();
debug!(?choice_regions, "after enforcing lower-bound");
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region `R0` (`UB:
// R0`). Therefore, we need only keep an option `O` if `UB: O`
// for all UB.
//
// If we have a requirement `'upper_bound: 'member`, equating `'member`
// with some region `'choice` means we now also require `'upper_bound: 'choice`.
// Avoid choice regions for which this does not hold.
for ub in rcx.rev_scc_graph.upper_bounds(member) {
choice_regions
.retain(|&choice_region| rcx.universal_region_relations.outlives(ub, choice_region));
}
debug!(?choice_regions, "after enforcing upper-bound");
// At this point we can pick any member of `choice_regions` and would like to choose
// it to be a small as possible. To avoid potential non-determinism we will pick the
// smallest such choice.
//
// Because universal regions are only partially ordered (i.e, not every two regions are
// comparable), we will ignore any region that doesn't compare to all others when picking
// the minimum choice.
//
// For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
// `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
// `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
choice_regions.iter().all(|&r2| {
rcx.universal_region_relations.outlives(r1, r2)
|| rcx.universal_region_relations.outlives(r2, r1)
})
});
// Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
let r1_outlives_r2 = rcx.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = rcx.universal_region_relations.outlives(r2, r1);
match (r1_outlives_r2, r2_outlives_r1) {
(true, true) => r1.min(r2),
(true, false) => r2,
(false, true) => r1,
(false, false) => bug!("incomparable regions in total order"),
}
}) else {
debug!("no unique minimum choice");
return;
};
debug!(?min_choice);
// Lift the member region to be at least as large as this `min_choice` by directly
// mutating the `scc_values` as we compute it. This acts as if we've added a
// `'member: 'min_choice` while not recomputing sccs. This means different sccs
// may now actually be equal.
let min_choice_scc = rcx.constraint_sccs.scc(min_choice);
rcx.scc_values.add_region(member, min_choice_scc);
}
struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> {
rcx: &'a RegionCtxt<'a, 'tcx>,
defining_use: &'b DefiningUse<'tcx>,
member_constraints: &'a mut FxHashMap<ConstraintSccIndex, Vec<&'b DefiningUse<'tcx>>>,
}
impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.rcx.infcx.tcx
}
fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) {
let generics = self.cx().generics_of(def_id);
for arg in args.iter().skip(generics.parent_count) {
arg.visit_with(self);
}
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
fn visit_region(&mut self, r: Region<'tcx>) {
match r.kind() {
ty::ReBound(..) => return,
ty::ReVar(vid) => {
let scc = self.rcx.constraint_sccs.scc(vid);
self.member_constraints.entry(scc).or_default().push(self.defining_use);
}
_ => unreachable!(),
}
}
fn visit_ty(&mut self, ty: Ty<'tcx>) {
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return;
}
match *ty.kind() {
ty::Closure(def_id, args)
| ty::CoroutineClosure(def_id, args)
| ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args),
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
if let Some(variances) = self.cx().opt_alias_variances(kind, def_id) =>
{
// Skip lifetime parameters that are not captured, since they do
// not need member constraints registered for them; we'll erase
// them (and hopefully in the future replace them with placeholders).
for (&v, arg) in std::iter::zip(variances, args.iter()) {
if v != ty::Bivariant {
arg.visit_with(self)
}
}
}
_ => ty.super_visit_with(self),
}
}
}

View file

@ -0,0 +1,698 @@
use std::iter;
use std::rc::Rc;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries};
use rustc_infer::traits::ObligationCause;
use rustc_macros::extension;
use rustc_middle::mir::{Body, ConstraintCategory};
use rustc_middle::ty::{
self, DefiningScopeKind, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType,
OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeVisitableExt, fold_regions,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::Span;
use rustc_trait_selection::opaque_types::{
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
};
use rustc_trait_selection::solve::NoSolution;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use tracing::{debug, instrument};
use super::reverse_sccs::ReverseSccGraph;
use crate::consumers::RegionInferenceContext;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::type_check::canonical::fully_perform_op_raw;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::{RegionClassification, UniversalRegions};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt};
mod member_constraints;
mod region_ctxt;
use member_constraints::apply_member_constraints;
use region_ctxt::RegionCtxt;
/// We defer errors from [fn handle_opaque_type_uses] and only report them
/// if there are no `RegionErrors`. If there are region errors, it's likely
/// that errors here are caused by them and don't need to be handled separately.
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
UnexpectedHiddenRegion {
/// The opaque type.
opaque_type_key: OpaqueTypeKey<'tcx>,
/// The hidden type containing the member region.
hidden_type: OpaqueHiddenType<'tcx>,
/// The unexpected region.
member_region: Region<'tcx>,
},
NonDefiningUseInDefiningScope {
span: Span,
opaque_type_key: OpaqueTypeKey<'tcx>,
},
}
/// This looks at all uses of opaque types in their defining scope inside
/// of this function.
///
/// It first uses all defining uses to compute the actual concrete type of each
/// opaque type definition.
///
/// We then apply this inferred type to actually check all uses of the opaque.
pub(crate) fn handle_opaque_type_uses<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
region_bound_pairs: &RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
location_map: &Rc<DenseLocationMap>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let tcx = infcx.tcx;
let opaque_types = infcx.clone_opaque_types();
if opaque_types.is_empty() {
return Vec::new();
}
// We need to eagerly map all regions to NLL vars here, as we need to make sure we've
// introduced nll vars for all used placeholders.
//
// We need to resolve inference vars as even though we're in MIR typeck, we may still
// encounter inference variables, e.g. when checking user types.
let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
let opaque_types = opaque_types
.into_iter()
.map(|entry| {
fold_regions(tcx, infcx.resolve_vars_if_possible(entry), |r, _| {
let vid = if let ty::RePlaceholder(placeholder) = r.kind() {
constraints.placeholder_region(infcx, placeholder).as_var()
} else {
universal_region_relations.universal_regions.to_region_vid(r)
};
Region::new_var(tcx, vid)
})
})
.collect::<Vec<_>>();
debug!(?opaque_types);
let errors = compute_concrete_opaque_types(
root_cx,
infcx,
constraints,
universal_region_relations,
Rc::clone(location_map),
&opaque_types,
);
if !errors.is_empty() {
return errors;
}
let errors = apply_computed_concrete_opaque_types(
root_cx,
infcx,
body,
&universal_region_relations.universal_regions,
region_bound_pairs,
known_type_outlives_obligations,
constraints,
&opaque_types,
);
detect_opaque_types_added_while_handling_opaque_types(infcx, opaque_types_storage_num_entries);
errors
}
/// Maps an NLL var to a deterministically chosen equal universal region.
///
/// See the corresponding [rustc-dev-guide chapter] for more details. This
/// ignores changes to the region values due to member constraints. Applying
/// member constraints does not impact the result of this function.
///
/// [rustc-dev-guide chapter]: https://rustc-dev-guide.rust-lang.org/borrow_check/opaque-types-region-inference-restrictions.html
fn nll_var_to_universal_region<'tcx>(
rcx: &RegionCtxt<'_, 'tcx>,
r: RegionVid,
) -> Option<Region<'tcx>> {
// Use the SCC representative instead of directly using `region`.
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
let vid = rcx.representative(r).rvid();
match rcx.definitions[vid].origin {
// Iterate over all universal regions in a consistent order and find the
// *first* equal region. This makes sure that equal lifetimes will have
// the same name and simplifies subsequent handling.
// See [rustc-dev-guide chapter] § "Semantic lifetime equality".
NllRegionVariableOrigin::FreeRegion => rcx
.universal_regions()
.universal_regions_iter()
.filter(|&ur| {
// See [rustc-dev-guide chapter] § "Closure restrictions".
!matches!(
rcx.universal_regions().region_classification(ur),
Some(RegionClassification::External)
)
})
.find(|&ur| rcx.universal_region_relations.equal(vid, ur))
.map(|ur| rcx.definitions[ur].external_name.unwrap()),
NllRegionVariableOrigin::Placeholder(placeholder) => {
Some(ty::Region::new_placeholder(rcx.infcx.tcx, placeholder))
}
// If `r` were equal to any universal region, its SCC representative
// would have been set to a free region.
NllRegionVariableOrigin::Existential { .. } => None,
}
}
#[derive(Debug)]
struct DefiningUse<'tcx> {
/// The opaque type using non NLL vars. This uses the actual
/// free regions and placeholders. This is necessary
/// to interact with code outside of `rustc_borrowck`.
opaque_type_key: OpaqueTypeKey<'tcx>,
arg_regions: Vec<RegionVid>,
hidden_type: OpaqueHiddenType<'tcx>,
}
/// This computes the actual hidden types of the opaque types and maps them to their
/// definition sites. Outside of registering the computed concrete types this function
/// does not mutate the current borrowck state.
///
/// While it may fail to infer the hidden type and return errors, we always apply
/// the computed concrete hidden type to all opaque type uses to check whether they
/// are correct. This is necessary to support non-defining uses of opaques in their
/// defining scope.
///
/// It also means that this whole function is not really soundness critical as we
/// recheck all uses of the opaques regardless.
fn compute_concrete_opaque_types<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
// When computing the hidden type we need to track member constraints.
// We don't mutate the region graph used by `fn compute_regions` but instead
// manually track region information via a `RegionCtxt`. We discard this
// information at the end of this function.
let mut rcx = RegionCtxt::new(infcx, universal_region_relations, location_map, constraints);
// We start by checking each use of an opaque type during type check and
// check whether the generic arguments of the opaque type are fully
// universal, if so, it's a defining use.
let defining_uses = collect_defining_uses(root_cx, &mut rcx, opaque_types, &mut errors);
// We now compute and apply member constraints for all regions in the hidden
// types of each defining use. This mutates the region values of the `rcx` which
// is used when mapping the defining uses to the definition site.
apply_member_constraints(&mut rcx, &defining_uses);
// After applying member constraints, we now check whether all member regions ended
// up equal to one of their choice regions and compute the actual concrete type of
// the opaque type definition. This is stored in the `root_cx`.
compute_concrete_types_from_defining_uses(root_cx, &rcx, &defining_uses, &mut errors);
errors
}
#[instrument(level = "debug", skip_all, ret)]
fn collect_defining_uses<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
rcx: &mut RegionCtxt<'_, 'tcx>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
) -> Vec<DefiningUse<'tcx>> {
let infcx = rcx.infcx;
let mut defining_uses = vec![];
for &(opaque_type_key, hidden_type) in opaque_types {
let non_nll_opaque_type_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |r| {
nll_var_to_universal_region(&rcx, r.as_var()).unwrap_or(r)
});
if let Err(err) = check_opaque_type_parameter_valid(
infcx,
non_nll_opaque_type_key,
hidden_type.span,
DefiningScopeKind::MirBorrowck,
) {
// A non-defining use. This is a hard error on stable and gets ignored
// with `TypingMode::Borrowck`.
if infcx.tcx.use_typing_mode_borrowck() {
match err {
InvalidOpaqueTypeArgs::AlreadyReported(guar) => root_cx
.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType::new_error(infcx.tcx, guar),
),
_ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"),
}
} else {
errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
}
continue;
}
// We use the original `opaque_type_key` to compute the `arg_regions`.
let arg_regions = iter::once(rcx.universal_regions().fr_static)
.chain(
opaque_type_key
.iter_captured_args(infcx.tcx)
.filter_map(|(_, arg)| arg.as_region())
.map(Region::as_var),
)
.collect();
defining_uses.push(DefiningUse {
opaque_type_key: non_nll_opaque_type_key,
arg_regions,
hidden_type,
});
}
defining_uses
}
fn compute_concrete_types_from_defining_uses<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
rcx: &RegionCtxt<'_, 'tcx>,
defining_uses: &[DefiningUse<'tcx>],
errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
) {
let infcx = rcx.infcx;
let tcx = infcx.tcx;
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses {
// After applying member constraints, we now map all regions in the hidden type
// to the `arg_regions` of this defining use. In case a region in the hidden type
// ended up not being equal to any such region, we error.
let hidden_type =
match hidden_type.try_fold_with(&mut ToArgRegionsFolder::new(rcx, arg_regions)) {
Ok(hidden_type) => hidden_type,
Err(r) => {
errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
hidden_type,
opaque_type_key,
member_region: ty::Region::new_var(tcx, r),
});
let guar = tcx.dcx().span_delayed_bug(
hidden_type.span,
"opaque type with non-universal region args",
);
ty::OpaqueHiddenType::new_error(tcx, guar)
}
};
// Now that we mapped the member regions to their final value,
// map the arguments of the opaque type key back to the parameters
// of the opaque type definition.
let ty = infcx
.infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type)
.unwrap_or_else(|_| {
Ty::new_error_with_message(
rcx.infcx.tcx,
hidden_type.span,
"deferred invalid opaque type args",
)
});
// Sometimes, when the hidden type is an inference variable, it can happen that
// the hidden type becomes the opaque type itself. In this case, this was an opaque
// usage of the opaque type and we can ignore it. This check is mirrored in typeck's
// writeback.
if !rcx.infcx.tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
&& alias_ty.args == opaque_type_key.args
{
continue;
}
}
// Check that all opaque types have the same region parameters if they have the same
// non-region parameters. This is necessary because within the new solver we perform
// various query operations modulo regions, and thus could unsoundly select some impls
// that don't hold.
//
// FIXME(-Znext-solver): This isn't necessary after all. We can remove this check again.
if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
rcx.infcx.tcx.erase_regions(opaque_type_key),
(opaque_type_key, hidden_type.span),
) && let Some((arg1, arg2)) = std::iter::zip(
prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
)
.find(|(arg1, arg2)| arg1 != arg2)
{
errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
LifetimeMismatchOpaqueParam {
arg: arg1,
prev: arg2,
span: prev_span,
prev_span: hidden_type.span,
},
));
}
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { span: hidden_type.span, ty },
);
}
}
/// A folder to map the regions in the hidden type to their corresponding `arg_regions`.
///
/// This folder has to differentiate between member regions and other regions in the hidden
/// type. Member regions have to be equal to one of the `arg_regions` while other regions simply
/// get treated as an existential region in the opaque if they are not. Existential
/// regions are currently represented using `'erased`.
struct ToArgRegionsFolder<'a, 'tcx> {
rcx: &'a RegionCtxt<'a, 'tcx>,
// When folding closure args or bivariant alias arguments, we simply
// ignore non-member regions. However, we still need to map member
// regions to their arg region even if its in a closure argument.
//
// See tests/ui/type-alias-impl-trait/closure_wf_outlives.rs for an example.
erase_unknown_regions: bool,
arg_regions: &'a [RegionVid],
}
impl<'a, 'tcx> ToArgRegionsFolder<'a, 'tcx> {
fn new(
rcx: &'a RegionCtxt<'a, 'tcx>,
arg_regions: &'a [RegionVid],
) -> ToArgRegionsFolder<'a, 'tcx> {
ToArgRegionsFolder { rcx, erase_unknown_regions: false, arg_regions }
}
fn fold_non_member_arg(&mut self, arg: GenericArg<'tcx>) -> GenericArg<'tcx> {
let prev = self.erase_unknown_regions;
self.erase_unknown_regions = true;
let res = arg.try_fold_with(self).unwrap();
self.erase_unknown_regions = prev;
res
}
fn fold_closure_args(
&mut self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Result<GenericArgsRef<'tcx>, RegionVid> {
let generics = self.cx().generics_of(def_id);
self.cx().mk_args_from_iter(args.iter().enumerate().map(|(index, arg)| {
if index < generics.parent_count {
Ok(self.fold_non_member_arg(arg))
} else {
arg.try_fold_with(self)
}
}))
}
}
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ToArgRegionsFolder<'_, 'tcx> {
type Error = RegionVid;
fn cx(&self) -> TyCtxt<'tcx> {
self.rcx.infcx.tcx
}
fn try_fold_region(&mut self, r: Region<'tcx>) -> Result<Region<'tcx>, RegionVid> {
match r.kind() {
// ignore bound regions, keep visiting
ty::ReBound(_, _) => Ok(r),
_ => {
let r = r.as_var();
if let Some(arg_region) = self
.arg_regions
.iter()
.copied()
.find(|&arg_vid| self.rcx.eval_equal(r, arg_vid))
.and_then(|r| nll_var_to_universal_region(self.rcx, r))
{
Ok(arg_region)
} else if self.erase_unknown_regions {
Ok(self.cx().lifetimes.re_erased)
} else {
Err(r)
}
}
}
}
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, RegionVid> {
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return Ok(ty);
}
let tcx = self.cx();
Ok(match *ty.kind() {
ty::Closure(def_id, args) => {
Ty::new_closure(tcx, def_id, self.fold_closure_args(def_id, args)?)
}
ty::CoroutineClosure(def_id, args) => {
Ty::new_coroutine_closure(tcx, def_id, self.fold_closure_args(def_id, args)?)
}
ty::Coroutine(def_id, args) => {
Ty::new_coroutine(tcx, def_id, self.fold_closure_args(def_id, args)?)
}
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
if let Some(variances) = tcx.opt_alias_variances(kind, def_id) =>
{
let args = tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map(
|(&v, s)| {
if v == ty::Bivariant {
Ok(self.fold_non_member_arg(s))
} else {
s.try_fold_with(self)
}
},
))?;
ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx)
}
_ => ty.try_super_fold_with(self)?,
})
}
}
/// This function is what actually applies member constraints to the borrowck
/// state. It is also responsible to check all uses of the opaques in their
/// defining scope.
///
/// It does this by equating the hidden type of each use with the instantiated final
/// hidden type of the opaque.
fn apply_computed_concrete_opaque_types<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
region_bound_pairs: &RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
constraints: &mut MirTypeckRegionConstraints<'tcx>,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let tcx = infcx.tcx;
let mut errors = Vec::new();
for &(key, hidden_type) in opaque_types {
let Some(expected) = root_cx.get_concrete_opaque_type(key.def_id) else {
assert!(tcx.use_typing_mode_borrowck(), "non-defining use in defining scope");
errors.push(DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
span: hidden_type.span,
opaque_type_key: key,
});
let guar = tcx.dcx().span_delayed_bug(
hidden_type.span,
"non-defining use in the defining scope with no defining uses",
);
root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
continue;
};
// We erase all non-member region of the opaque and need to treat these as existentials.
let expected = ty::fold_regions(tcx, expected.instantiate(tcx, key.args), |re, _dbi| {
match re.kind() {
ty::ReErased => infcx.next_nll_region_var(
NllRegionVariableOrigin::Existential { name: None },
|| crate::RegionCtxt::Existential(None),
),
_ => re,
}
});
// We now simply equate the expected with the actual hidden type.
let locations = Locations::All(hidden_type.span);
if let Err(guar) = fully_perform_op_raw(
infcx,
body,
universal_regions,
region_bound_pairs,
known_type_outlives_obligations,
constraints,
locations,
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|ocx| {
let cause = ObligationCause::misc(
hidden_type.span,
body.source.def_id().expect_local(),
);
// We need to normalize both types in the old solver before equatingt them.
let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty);
let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty);
ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution)
},
"equating opaque types",
),
) {
root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
}
}
errors
}
/// In theory `apply_concrete_opaque_types` could introduce new uses of opaque types.
/// We do not check these new uses so this could be unsound.
///
/// We detect any new uses and simply delay a bug if they occur. If this results in
/// an ICE we can properly handle this, but we haven't encountered any such test yet.
///
/// See the related comment in `FnCtxt::detect_opaque_types_added_during_writeback`.
fn detect_opaque_types_added_while_handling_opaque_types<'tcx>(
infcx: &InferCtxt<'tcx>,
opaque_types_storage_num_entries: OpaqueTypeStorageEntries,
) {
for (key, hidden_type) in infcx
.inner
.borrow_mut()
.opaque_types()
.opaque_types_added_since(opaque_types_storage_num_entries)
{
let opaque_type_string = infcx.tcx.def_path_str(key.def_id);
let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");
infcx.dcx().span_delayed_bug(hidden_type.span, msg);
}
let _ = infcx.take_opaque_types();
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// Map the regions in the type to named regions. This is similar to what
/// `infer_opaque_types` does, but can infer any universal region, not only
/// ones from the args for the opaque type. It also doesn't double check
/// that the regions produced are in fact equal to the named region they are
/// replaced with. This is fine because this function is only to improve the
/// region names in error messages.
///
/// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
/// lax with mapping region vids that are *shorter* than a universal region to
/// that universal region. This is useful for member region constraints since
/// we want to suggest a universal region name to capture even if it's technically
/// not equal to the error region.
pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);
// Special handling of higher-ranked regions.
if !self.max_nameable_universe(scc).is_root() {
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
// If the region contains a single placeholder then they're equal.
Some((0, placeholder)) => {
return ty::Region::new_placeholder(tcx, placeholder);
}
// Fallback: this will produce a cryptic error message.
_ => return region,
}
}
// Find something that we can name
let upper_bound = self.approx_universal_upper_bound(vid);
if let Some(universal_region) = self.definitions[upper_bound].external_name {
return universal_region;
}
// Nothing exact found, so we pick a named upper bound, if there's only one.
// If there's >1 universal region, then we probably are dealing w/ an intersection
// region which cannot be mapped back to a universal.
// FIXME: We could probably compute the LUB if there is one.
let scc = self.constraint_sccs.scc(vid);
let rev_scc_graph =
ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions());
let upper_bounds: Vec<_> = rev_scc_graph
.upper_bounds(scc)
.filter_map(|vid| self.definitions[vid].external_name)
.filter(|r| !r.is_static())
.collect();
match &upper_bounds[..] {
[universal_region] => *universal_region,
_ => region,
}
}
_ => region,
})
}
}
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Given the fully resolved, instantiated type for an opaque
/// type, i.e., the value of an inference variable like C1 or C2
/// (*), computes the "definition type" for an opaque type
/// definition -- that is, the inferred value of `Foo1<'x>` or
/// `Foo2<'x>` that we would conceptually use in its definition:
/// ```ignore (illustrative)
/// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA
/// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
/// ```
/// Note that these values are defined in terms of a distinct set of
/// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
/// purpose of this function is to do that translation.
///
/// (*) C1 and C2 were introduced in the comments on
/// `register_member_constraints`. Read that comment for more context.
///
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
/// - `args`, the args used to instantiate this opaque type
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
#[instrument(level = "debug", skip(self))]
fn infer_opaque_definition_from_instantiation(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
check_opaque_type_parameter_valid(
self,
opaque_type_key,
instantiated_ty.span,
DefiningScopeKind::MirBorrowck,
)?;
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(
opaque_type_key,
self.tcx,
DefiningScopeKind::MirBorrowck,
)
.ty;
definition_ty.error_reported()?;
Ok(definition_ty)
}
}

View file

@ -0,0 +1,114 @@
use std::rc::Rc;
use rustc_data_structures::frozen::Frozen;
use rustc_index::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::ty::{RegionVid, UniverseIndex};
use rustc_mir_dataflow::points::DenseLocationMap;
use crate::BorrowckInferCtxt;
use crate::constraints::ConstraintSccIndex;
use crate::handle_placeholders::{SccAnnotations, region_definitions};
use crate::region_infer::reverse_sccs::ReverseSccGraph;
use crate::region_infer::values::RegionValues;
use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker, Representative};
use crate::type_check::MirTypeckRegionConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::universal_regions::UniversalRegions;
/// A slimmed down version of [crate::region_infer::RegionInferenceContext] used
/// only by opaque type handling.
pub(super) struct RegionCtxt<'a, 'tcx> {
pub(super) infcx: &'a BorrowckInferCtxt<'tcx>,
pub(super) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
pub(super) universal_region_relations: &'a UniversalRegionRelations<'tcx>,
pub(super) constraint_sccs: ConstraintSccs,
pub(super) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(super) rev_scc_graph: ReverseSccGraph,
pub(super) scc_values: RegionValues<ConstraintSccIndex>,
}
impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
/// Creates a new `RegionCtxt` used to compute defining opaque type uses.
///
/// This does not yet propagate region values. This is instead done lazily
/// when applying member constraints.
pub(super) fn new(
infcx: &'a BorrowckInferCtxt<'tcx>,
universal_region_relations: &'a Frozen<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
constraints: &MirTypeckRegionConstraints<'tcx>,
) -> RegionCtxt<'a, 'tcx> {
let universal_regions = &universal_region_relations.universal_regions;
let (definitions, _has_placeholders) = region_definitions(infcx, universal_regions);
let mut scc_annotations = SccAnnotations::init(&definitions);
let constraint_sccs = ConstraintSccs::new_with_annotation(
&constraints
.outlives_constraints
.graph(definitions.len())
.region_graph(&constraints.outlives_constraints, universal_regions.fr_static),
&mut scc_annotations,
);
let scc_annotations = scc_annotations.scc_to_annotation;
// Unlike the `RegionInferenceContext`, we only care about free regions
// and fully ignore liveness and placeholders.
let placeholder_indices = Default::default();
let mut scc_values =
RegionValues::new(location_map, universal_regions.len(), placeholder_indices);
for variable in definitions.indices() {
let scc = constraint_sccs.scc(variable);
match definitions[variable].origin {
NllRegionVariableOrigin::FreeRegion => {
scc_values.add_element(scc, variable);
}
_ => {}
}
}
let rev_scc_graph = ReverseSccGraph::compute(&constraint_sccs, universal_regions);
RegionCtxt {
infcx,
definitions,
universal_region_relations,
constraint_sccs,
scc_annotations,
rev_scc_graph,
scc_values,
}
}
pub(super) fn representative(&self, vid: RegionVid) -> Representative {
let scc = self.constraint_sccs.scc(vid);
self.scc_annotations[scc].representative
}
pub(crate) fn max_placeholder_universe_reached(
&self,
scc: ConstraintSccIndex,
) -> UniverseIndex {
self.scc_annotations[scc].max_placeholder_universe_reached()
}
pub(super) fn universal_regions(&self) -> &UniversalRegions<'tcx> {
&self.universal_region_relations.universal_regions
}
pub(super) fn eval_equal(&self, r1_vid: RegionVid, r2_vid: RegionVid) -> bool {
let r1 = self.constraint_sccs.scc(r1_vid);
let r2 = self.constraint_sccs.scc(r2_vid);
if r1 == r2 {
return true;
}
let universal_outlives = |sub, sup| {
self.scc_values.universal_regions_outlived_by(sub).all(|r1| {
self.scc_values
.universal_regions_outlived_by(sup)
.any(|r2| self.universal_region_relations.outlives(r2, r1))
})
};
universal_outlives(r1, r2) && universal_outlives(r2, r1)
}
}

View file

@ -5,7 +5,6 @@ use rustc_data_structures::graph;
use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_middle::ty::RegionVid;
use crate::RegionInferenceContext;
use crate::constraints::ConstraintSccIndex;
use crate::region_infer::ConstraintSccs;
use crate::universal_regions::UniversalRegions;
@ -57,12 +56,3 @@ impl ReverseSccGraph {
.filter(move |r| duplicates.insert(*r))
}
}
impl RegionInferenceContext<'_> {
/// Return the reverse graph of the region SCCs, initialising it if needed.
pub(super) fn reverse_scc_graph(&self) -> &ReverseSccGraph {
self.rev_scc_graph.get_or_init(|| {
ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())
})
}
}

View file

@ -2,7 +2,7 @@ use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::bug;
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use smallvec::SmallVec;
@ -19,7 +19,7 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> {
tainted_by_errors: Option<ErrorGuaranteed>,
/// This should be `None` during normal compilation. See [`crate::consumers`] for more
/// information on how this is used.
pub(crate) consumer: Option<BorrowckConsumer<'tcx>>,
pub consumer: Option<BorrowckConsumer<'tcx>>,
}
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
@ -67,6 +67,13 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
}
}
pub(super) fn get_concrete_opaque_type(
&mut self,
def_id: LocalDefId,
) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
self.concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
}
pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
self.tainted_by_errors = Some(guar);
}

View file

@ -2,8 +2,9 @@ use std::fmt;
use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_middle::bug;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::mir::{Body, ConstraintCategory};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast};
use rustc_span::Span;
use rustc_span::def_id::DefId;
@ -14,7 +15,69 @@ use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
use tracing::{debug, instrument};
use super::{Locations, NormalizeLocation, TypeChecker};
use crate::BorrowckInferCtxt;
use crate::diagnostics::ToUniverseInfo;
use crate::type_check::{MirTypeckRegionConstraints, constraint_conversion};
use crate::universal_regions::UniversalRegions;
#[instrument(skip(infcx, constraints, op), level = "trace")]
pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>(
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
region_bound_pairs: &RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
constraints: &mut MirTypeckRegionConstraints<'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
op: Op,
) -> Result<R, ErrorGuaranteed>
where
Op: type_op::TypeOp<'tcx, Output = R>,
Op::ErrorInfo: ToUniverseInfo<'tcx>,
{
let old_universe = infcx.universe();
let TypeOpOutput { output, constraints: query_constraints, error_info } =
op.fully_perform(infcx, locations.span(body))?;
if cfg!(debug_assertions) {
let data = infcx.take_and_reset_region_constraints();
if !data.is_empty() {
panic!("leftover region constraints: {data:#?}");
}
}
debug!(?output, ?query_constraints);
if let Some(data) = query_constraints {
constraint_conversion::ConstraintConversion::new(
infcx,
universal_regions,
region_bound_pairs,
infcx.param_env,
known_type_outlives_obligations,
locations,
locations.span(body),
category,
constraints,
)
.convert_all(data);
}
// If the query has created new universes and errors are going to be emitted, register the
// cause of these new universes for improved diagnostics.
let universe = infcx.universe();
if old_universe != universe
&& let Some(error_info) = error_info
{
let universe_info = error_info.to_universe_info(old_universe);
for u in (old_universe + 1)..=universe {
constraints.universe_causes.insert(u, universe_info.clone());
}
}
Ok(output)
}
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
/// Given some operation `op` that manipulates types, proves
@ -38,36 +101,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Op: type_op::TypeOp<'tcx, Output = R>,
Op::ErrorInfo: ToUniverseInfo<'tcx>,
{
let old_universe = self.infcx.universe();
let TypeOpOutput { output, constraints, error_info } =
op.fully_perform(self.infcx, locations.span(self.body))?;
if cfg!(debug_assertions) {
let data = self.infcx.take_and_reset_region_constraints();
if !data.is_empty() {
panic!("leftover region constraints: {data:#?}");
}
}
debug!(?output, ?constraints);
if let Some(data) = constraints {
self.push_region_constraints(locations, category, data);
}
// If the query has created new universes and errors are going to be emitted, register the
// cause of these new universes for improved diagnostics.
let universe = self.infcx.universe();
if old_universe != universe
&& let Some(error_info) = error_info
{
let universe_info = error_info.to_universe_info(old_universe);
for u in (old_universe + 1)..=universe {
self.constraints.universe_causes.insert(u, universe_info.clone());
}
}
Ok(output)
fully_perform_op_raw(
self.infcx,
self.body,
self.universal_regions,
self.region_bound_pairs,
self.known_type_outlives_obligations,
self.constraints,
locations,
category,
op,
)
}
pub(super) fn instantiate_canonical<T>(

View file

@ -26,8 +26,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::{
self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, TypeVisitableExt,
UserArgs, UserTypeAnnotationIndex, fold_regions,
GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::move_paths::MoveData;
@ -42,7 +41,6 @@ use tracing::{debug, instrument, trace};
use crate::borrow_set::BorrowSet;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
use crate::region_infer::TypeTest;
@ -67,12 +65,11 @@ macro_rules! span_mirbug {
})
}
mod canonical;
pub(crate) mod canonical;
mod constraint_conversion;
pub(crate) mod free_region_relations;
mod input_output;
pub(crate) mod liveness;
mod opaque_types;
mod relate_tys;
/// Type checks the given `mir` in the context of the inference
@ -114,7 +111,6 @@ pub(crate) fn type_check<'tcx>(
placeholder_index_to_region: IndexVec::default(),
liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)),
outlives_constraints: OutlivesConstraintSet::default(),
member_constraints: MemberConstraintSet::default(),
type_tests: Vec::default(),
universe_causes: FxIndexMap::default(),
};
@ -170,9 +166,6 @@ pub(crate) fn type_check<'tcx>(
liveness::generate(&mut typeck, &location_map, move_data);
let opaque_type_values =
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
// 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(
@ -187,7 +180,6 @@ pub(crate) fn type_check<'tcx>(
if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() {
debug!("encountered an error region; removing constraints!");
constraints.outlives_constraints = Default::default();
constraints.member_constraints = Default::default();
constraints.type_tests = Default::default();
root_cx.set_tainted_by_errors(guar);
infcx.set_tainted_by_errors(guar);
@ -196,7 +188,8 @@ pub(crate) fn type_check<'tcx>(
MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
region_bound_pairs,
known_type_outlives_obligations,
polonius_context,
}
}
@ -245,7 +238,8 @@ struct TypeChecker<'a, 'tcx> {
pub(crate) struct MirTypeckResults<'tcx> {
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
pub(crate) region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
pub(crate) known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
pub(crate) polonius_context: Option<PoloniusContext>,
}
@ -277,8 +271,6 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>,
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
pub(crate) universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
@ -287,7 +279,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
impl<'tcx> MirTypeckRegionConstraints<'tcx> {
/// Creates a `Region` for a given `PlaceholderRegion`, or returns the
/// region that corresponds to a previously created one.
fn placeholder_region(
pub(crate) fn placeholder_region(
&mut self,
infcx: &InferCtxt<'tcx>,
placeholder: ty::PlaceholderRegion,
@ -380,14 +372,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.body
}
fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid {
if let ty::RePlaceholder(placeholder) = r.kind() {
self.constraints.placeholder_region(self.infcx, placeholder).as_var()
} else {
self.universal_regions.to_region_vid(r)
}
}
fn unsized_feature_enabled(&self) -> bool {
self.tcx().features().unsized_fn_params()
}

View file

@ -1,333 +0,0 @@
use std::iter;
use rustc_data_structures::fx::FxIndexMap;
use rustc_middle::span_bug;
use rustc_middle::ty::{
self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions,
};
use tracing::{debug, trace};
use super::{MemberConstraintSet, TypeChecker};
/// Once we're done with typechecking the body, we take all the opaque types
/// defined by this function and add their 'member constraints'.
pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
typeck: &mut TypeChecker<'_, 'tcx>,
) -> FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>> {
let infcx = typeck.infcx;
// Annoying: to invoke `typeck.to_region_vid`, we need access to
// `typeck.constraints`, but we also want to be mutating
// `typeck.member_constraints`. For now, just swap out the value
// we want and replace at the end.
let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints);
let opaque_types = infcx
.take_opaque_types()
.into_iter()
.map(|(opaque_type_key, hidden_type)| {
let hidden_type = infcx.resolve_vars_if_possible(hidden_type);
register_member_constraints(
typeck,
&mut member_constraints,
opaque_type_key,
hidden_type,
);
trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
if hidden_type.has_non_region_infer() {
span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty);
}
// Convert all regions to nll vars.
let (opaque_type_key, hidden_type) =
fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| {
ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r))
});
(opaque_type_key, hidden_type)
})
.collect();
assert!(typeck.constraints.member_constraints.is_empty());
typeck.constraints.member_constraints = member_constraints;
opaque_types
}
/// Given the map `opaque_types` containing the opaque
/// `impl Trait` types whose underlying, hidden types are being
/// inferred, this method adds constraints to the regions
/// appearing in those underlying hidden types to ensure that they
/// at least do not refer to random scopes within the current
/// function. These constraints are not (quite) sufficient to
/// guarantee that the regions are actually legal values; that
/// final condition is imposed after region inference is done.
///
/// # The Problem
///
/// Let's work through an example to explain how it works. Assume
/// the current function is as follows:
///
/// ```text
/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
/// ```
///
/// Here, we have two `impl Trait` types whose values are being
/// inferred (the `impl Bar<'a>` and the `impl
/// Bar<'b>`). Conceptually, this is sugar for a setup where we
/// define underlying opaque types (`Foo1`, `Foo2`) and then, in
/// the return type of `foo`, we *reference* those definitions:
///
/// ```text
/// type Foo1<'x> = impl Bar<'x>;
/// type Foo2<'x> = impl Bar<'x>;
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
/// // ^^^^ ^^
/// // | |
/// // | args
/// // def_id
/// ```
///
/// As indicating in the comments above, each of those references
/// is (in the compiler) basically generic parameters (`args`)
/// applied to the type of a suitable `def_id` (which identifies
/// `Foo1` or `Foo2`).
///
/// Now, at this point in compilation, what we have done is to
/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
/// fresh inference variables C1 and C2. We wish to use the values
/// of these variables to infer the underlying types of `Foo1` and
/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
/// constraints like:
///
/// ```text
/// for<'a> (Foo1<'a> = C1)
/// for<'b> (Foo1<'b> = C2)
/// ```
///
/// For these equation to be satisfiable, the types `C1` and `C2`
/// can only refer to a limited set of regions. For example, `C1`
/// can only refer to `'static` and `'a`, and `C2` can only refer
/// to `'static` and `'b`. The job of this function is to impose that
/// constraint.
///
/// Up to this point, C1 and C2 are basically just random type
/// inference variables, and hence they may contain arbitrary
/// regions. In fact, it is fairly likely that they do! Consider
/// this possible definition of `foo`:
///
/// ```text
/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
/// (&*x, &*y)
/// }
/// ```
///
/// Here, the values for the concrete types of the two impl
/// traits will include inference variables:
///
/// ```text
/// &'0 i32
/// &'1 i32
/// ```
///
/// Ordinarily, the subtyping rules would ensure that these are
/// sufficiently large. But since `impl Bar<'a>` isn't a specific
/// type per se, we don't get such constraints by default. This
/// is where this function comes into play. It adds extra
/// constraints to ensure that all the regions which appear in the
/// inferred type are regions that could validly appear.
///
/// This is actually a bit of a tricky constraint in general. We
/// want to say that each variable (e.g., `'0`) can only take on
/// values that were supplied as arguments to the opaque type
/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
/// scope. We don't have a constraint quite of this kind in the current
/// region checker.
///
/// # The Solution
///
/// We generally prefer to make `<=` constraints, since they
/// integrate best into the region solver. To do that, we find the
/// "minimum" of all the arguments that appear in the args: that
/// is, some region which is less than all the others. In the case
/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
/// all). Then we apply that as a least bound to the variables
/// (e.g., `'a <= '0`).
///
/// In some cases, there is no minimum. Consider this example:
///
/// ```text
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
/// ```
///
/// Here we would report a more complex "in constraint", like `'r
/// in ['a, 'b, 'static]` (where `'r` is some region appearing in
/// the hidden type).
///
/// # Constrain regions, not the hidden concrete type
///
/// Note that generating constraints on each region `Rc` is *not*
/// the same as generating an outlives constraint on `Tc` itself.
/// For example, if we had a function like this:
///
/// ```
/// # #![feature(type_alias_impl_trait)]
/// # fn main() {}
/// # trait Foo<'a> {}
/// # impl<'a, T> Foo<'a> for (&'a u32, T) {}
/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
/// (x, y)
/// }
///
/// // Equivalent to:
/// # mod dummy { use super::*;
/// type FooReturn<'a, T> = impl Foo<'a>;
/// #[define_opaque(FooReturn)]
/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
/// (x, y)
/// }
/// # }
/// ```
///
/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
/// is an inference variable). If we generated a constraint that
/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
/// but this is not necessary, because the opaque type we
/// create will be allowed to reference `T`. So we only generate a
/// constraint that `'0: 'a`.
fn register_member_constraints<'tcx>(
typeck: &mut TypeChecker<'_, 'tcx>,
member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>,
opaque_type_key: OpaqueTypeKey<'tcx>,
OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>,
) {
let tcx = typeck.tcx();
let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty);
debug!(?hidden_ty);
let variances = tcx.variances_of(opaque_type_key.def_id);
debug!(?variances);
// For a case like `impl Foo<'a, 'b>`, we would generate a constraint
// `'r in ['a, 'b, 'static]` for each region `'r` that appears in the
// hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`).
//
// `conflict1` and `conflict2` are the two region bounds that we
// detected which were unrelated. They are used for diagnostics.
// Create the set of choice regions: each region in the hidden
// type can be equal to any of the region parameters of the
// opaque type definition.
let fr_static = typeck.universal_regions.fr_static;
let choice_regions: Vec<_> = opaque_type_key
.args
.iter()
.enumerate()
.filter(|(i, _)| variances[*i] == ty::Invariant)
.filter_map(|(_, arg)| match arg.kind() {
GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
})
.chain(iter::once(fr_static))
.collect();
// FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
// not currently sound until we have existential regions.
hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx,
op: |r| {
member_constraints.add_member_constraint(
opaque_type_key,
hidden_ty,
span,
typeck.to_region_vid(r),
&choice_regions,
)
},
});
}
/// Visitor that requires that (almost) all regions in the type visited outlive
/// `least_region`. We cannot use `push_outlives_components` because regions in
/// closure signatures are not included in their outlives components. We need to
/// ensure all regions outlive the given bound so that we don't end up with,
/// say, `ReVar` appearing in a return type and causing ICEs when other
/// functions end up with region constraints involving regions from other
/// functions.
///
/// We also cannot use `for_each_free_region` because for closures it includes
/// the regions parameters from the enclosing item.
///
/// We ignore any type parameters because impl trait values are assumed to
/// capture all the in-scope type parameters.
struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
tcx: TyCtxt<'tcx>,
op: OP,
}
impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
where
OP: FnMut(ty::Region<'tcx>),
{
fn visit_region(&mut self, r: ty::Region<'tcx>) {
match r.kind() {
// ignore bound regions, keep visiting
ty::ReBound(_, _) => {}
_ => (self.op)(r),
}
}
fn visit_ty(&mut self, ty: Ty<'tcx>) {
// We're only interested in types involving regions
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return;
}
match *ty.kind() {
ty::Closure(_, args) => {
// Skip lifetime parameters of the enclosing item(s)
for upvar in args.as_closure().upvar_tys() {
upvar.visit_with(self);
}
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
}
ty::CoroutineClosure(_, args) => {
// Skip lifetime parameters of the enclosing item(s)
for upvar in args.as_coroutine_closure().upvar_tys() {
upvar.visit_with(self);
}
args.as_coroutine_closure().signature_parts_ty().visit_with(self);
}
ty::Coroutine(_, args) => {
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.
for upvar in args.as_coroutine().upvar_tys() {
upvar.visit_with(self);
}
args.as_coroutine().return_ty().visit_with(self);
args.as_coroutine().yield_ty().visit_with(self);
args.as_coroutine().resume_ty().visit_with(self);
}
ty::Alias(kind, ty::AliasTy { def_id, args, .. })
if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) =>
{
// Skip lifetime parameters that are not captured, since they do
// not need member constraints registered for them; we'll erase
// them (and hopefully in the future replace them with placeholders).
for (v, s) in std::iter::zip(variances, args.iter()) {
if *v != ty::Bivariant {
s.visit_with(self);
}
}
}
_ => {
ty.super_visit_with(self);
}
}
}
}

View file

@ -42,12 +42,13 @@ trait ArgAttributesExt {
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
(ArgAttribute::NoCapture, llvm::AttributeKind::NoCapture),
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
(ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly),
];
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
@ -83,6 +84,10 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
}
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
if regular.contains(attr) {
// captures(address, read_provenance) is only available since LLVM 21.
if attr == ArgAttribute::CapturesReadOnly && llvm_util::get_version() < (21, 0, 0) {
continue;
}
attrs.push(llattr.create_attr(cx.llcx));
}
}

View file

@ -420,6 +420,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
{
to_add.push(create_alloc_family_attr(cx.llcx));
if let Some(zv) =
cx.tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant)
&& let Some(name) = zv.value_str()
{
to_add.push(llvm::CreateAttrStringValue(
cx.llcx,
"alloc-variant-zeroed",
&mangle_internal_symbol(cx.tcx, name.as_str()),
));
}
// apply to argument place instead of function
let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);

View file

@ -330,10 +330,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
_ => bug!(),
};
let ptr = args[0].immediate();
let locality = fn_args.const_at(1).to_value().valtree.unwrap_leaf().to_u32() as i32;
self.call_intrinsic(
"llvm.prefetch",
&[self.val_ty(ptr)],
&[ptr, self.const_i32(rw), args[1].immediate(), self.const_i32(cache_type)],
&[
ptr,
self.const_i32(rw),
self.const_i32(locality),
self.const_i32(cache_type),
],
)
}
sym::carrying_mul_add => {

View file

@ -251,6 +251,7 @@ pub(crate) enum AttributeKind {
Writable = 42,
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
}
/// LLVMIntPredicate

View file

@ -1,7 +1,7 @@
use std::fmt::Write;
use rustc_data_structures::intern::Interned;
use rustc_hir::def_id::CrateNum;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::definitions::DisambiguatedDefPathData;
use rustc_middle::bug;
use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer};
@ -132,6 +132,35 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> {
Ok(())
}
}
fn print_coroutine_with_kind(
&mut self,
def_id: DefId,
parent_args: &'tcx [GenericArg<'tcx>],
kind: Ty<'tcx>,
) -> Result<(), PrintError> {
self.print_def_path(def_id, parent_args)?;
let ty::Coroutine(_, args) = self.tcx.type_of(def_id).instantiate_identity().kind() else {
// Could be `ty::Error`.
return Ok(());
};
let default_kind = args.as_coroutine().kind_ty();
match kind.to_opt_closure_kind() {
_ if kind == default_kind => {
// No need to mark the closure if it's the deduced coroutine kind.
}
Some(ty::ClosureKind::Fn) | None => {
// Should never happen. Just don't mark anything rather than panicking.
}
Some(ty::ClosureKind::FnMut) => self.path.push_str("::{{call_mut}}"),
Some(ty::ClosureKind::FnOnce) => self.path.push_str("::{{call_once}}"),
}
Ok(())
}
}
impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> {

View file

@ -996,6 +996,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No,
),
rustc_attr!(
rustc_allocator_zeroed_variant, Normal, template!(NameValueStr: "function"), ErrorPreceding,
EncodeCrossCrate::Yes,
),
gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator),

View file

@ -136,6 +136,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::round_ties_even_f64
| sym::round_ties_even_f128
| sym::autodiff
| sym::prefetch_read_data
| sym::prefetch_write_data
| sym::prefetch_read_instruction
| sym::prefetch_write_instruction
| sym::const_eval_select => hir::Safety::Safe,
_ => hir::Safety::Unsafe,
};
@ -218,7 +222,7 @@ pub(crate) fn check_intrinsic_type(
| sym::prefetch_write_data
| sym::prefetch_read_instruction
| sym::prefetch_write_instruction => {
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], tcx.types.unit)
(1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.unit)
}
sym::needs_drop => (1, 0, vec![], tcx.types.bool),

View file

@ -24,7 +24,7 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
use rustc_session::{LintStoreMarker, Session};
use rustc_session::{DynLintStore, Session};
use rustc_span::edit_distance::find_best_match_for_names;
use rustc_span::{Ident, Span, Symbol, sym};
use tracing::debug;
@ -62,7 +62,13 @@ pub struct LintStore {
lint_groups: FxIndexMap<&'static str, LintGroup>,
}
impl LintStoreMarker for LintStore {}
impl DynLintStore for LintStore {
fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
rustc_session::LintGroup { name, lints, is_externally_loaded }
}))
}
}
/// The target of the `by_name` map, which accounts for renaming/deprecation.
#[derive(Debug)]

View file

@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind {
Writable = 42,
DeadOnUnwind = 43,
DeadOnReturn = 44,
CapturesReadOnly = 45,
};
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
@ -376,6 +377,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
#else
report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later");
#endif
case LLVMRustAttributeKind::CapturesReadOnly:
report_fatal_error("Should be handled separately");
}
report_fatal_error("bad LLVMRustAttributeKind");
}
@ -430,6 +433,11 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
if (RustAttr == LLVMRustAttributeKind::NoCapture) {
return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
}
if (RustAttr == LLVMRustAttributeKind::CapturesReadOnly) {
return wrap(Attribute::getWithCaptureInfo(
*unwrap(C), CaptureInfo(CaptureComponents::Address |
CaptureComponents::ReadProvenance)));
}
#endif
return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
}

View file

@ -211,11 +211,28 @@ impl LintExpectation {
}
fn explain_lint_level_source(
sess: &Session,
lint: &'static Lint,
level: Level,
src: LintLevelSource,
err: &mut Diag<'_, ()>,
) {
// Find the name of the lint group that contains the given lint.
// Assumes the lint only belongs to one group.
let lint_group_name = |lint| {
let lint_groups_iter = sess.lint_groups_iter();
let lint_id = LintId::of(lint);
lint_groups_iter
.filter(|lint_group| !lint_group.is_externally_loaded)
.find(|lint_group| {
lint_group
.lints
.iter()
.find(|lint_group_lint| **lint_group_lint == lint_id)
.is_some()
})
.map(|lint_group| lint_group.name)
};
let name = lint.name_lower();
if let Level::Allow = level {
// Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
@ -224,7 +241,15 @@ fn explain_lint_level_source(
}
match src {
LintLevelSource::Default => {
err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name));
let level_str = level.as_str();
match lint_group_name(lint) {
Some(group_name) => {
err.note_once(format!("`#[{level_str}({name})]` (part of `#[{level_str}({group_name})]`) on by default"));
}
None => {
err.note_once(format!("`#[{level_str}({name})]` on by default"));
}
}
}
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
let flag = orig_level.to_cmd_flag();
@ -427,7 +452,7 @@ pub fn lint_level(
decorate(&mut err);
}
explain_lint_level_source(lint, level, src, &mut err);
explain_lint_level_source(sess, lint, level, src, &mut err);
err.emit()
}
lint_level_impl(sess, lint, level, span, Box::new(decorate))

View file

@ -124,6 +124,15 @@ pub trait Printer<'tcx>: Sized {
trait_ref: Option<ty::TraitRef<'tcx>>,
) -> Result<(), PrintError>;
fn print_coroutine_with_kind(
&mut self,
def_id: DefId,
parent_args: &'tcx [GenericArg<'tcx>],
kind: Ty<'tcx>,
) -> Result<(), PrintError> {
self.print_path_with_generic_args(|p| p.print_def_path(def_id, parent_args), &[kind.into()])
}
// Defaults (should not be overridden):
#[instrument(skip(self), level = "debug")]
@ -162,9 +171,10 @@ pub trait Printer<'tcx>: Sized {
)) = self.tcx().coroutine_kind(def_id)
&& args.len() > parent_args.len()
{
return self.print_path_with_generic_args(
|p| p.print_def_path(def_id, parent_args),
&args[..parent_args.len() + 1][..1],
return self.print_coroutine_with_kind(
def_id,
parent_args,
args[parent_args.len()].expect_ty(),
);
} else {
// Closures' own generics are only captures, don't print them.

View file

@ -1535,7 +1535,20 @@ impl<'v> RootCollector<'_, 'v> {
fn process_nested_body(&mut self, def_id: LocalDefId) {
match self.tcx.def_kind(def_id) {
DefKind::Closure => {
if self.strategy == MonoItemCollectionStrategy::Eager
// for 'pub async fn foo(..)' also trying to monomorphize foo::{closure}
let is_pub_fn_coroutine =
match *self.tcx.type_of(def_id).instantiate_identity().kind() {
ty::Coroutine(cor_id, _args) => {
let tcx = self.tcx;
let parent_id = tcx.parent(cor_id);
tcx.def_kind(parent_id) == DefKind::Fn
&& tcx.asyncness(parent_id).is_async()
&& tcx.visibility(parent_id).is_public()
}
ty::Closure(..) | ty::CoroutineClosure(..) => false,
_ => unreachable!(),
};
if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine)
&& !self
.tcx
.generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id()))

View file

@ -1388,15 +1388,26 @@ impl<'a> Parser<'a> {
// matching `CloseDelim` we are *after* the delimited sequence,
// i.e. at depth `d - 1`.
let target_depth = self.token_cursor.stack.len() - 1;
loop {
// Advance one token at a time, so `TokenCursor::next()`
// can capture these tokens if necessary.
if let Capturing::No = self.capture_state.capturing {
// We are not capturing tokens, so skip to the end of the
// delimited sequence. This is a perf win when dealing with
// declarative macros that pass large `tt` fragments through
// multiple rules, as seen in the uom-0.37.0 crate.
self.token_cursor.curr.bump_to_end();
self.bump();
if self.token_cursor.stack.len() == target_depth {
debug_assert!(self.token.kind.close_delim().is_some());
break;
debug_assert_eq!(self.token_cursor.stack.len(), target_depth);
} else {
loop {
// Advance one token at a time, so `TokenCursor::next()`
// can capture these tokens if necessary.
self.bump();
if self.token_cursor.stack.len() == target_depth {
break;
}
}
}
debug_assert!(self.token.kind.close_delim().is_some());
// Consume close delimiter
self.bump();

View file

@ -44,6 +44,7 @@ use crate::config::{
SwitchWithOptPath,
};
use crate::filesearch::FileSearch;
use crate::lint::LintId;
use crate::parse::{ParseSess, add_feature_diagnostics};
use crate::search_paths::SearchPath;
use crate::{errors, filesearch, lint};
@ -139,7 +140,10 @@ pub struct CompilerIO {
pub temps_dir: Option<PathBuf>,
}
pub trait LintStoreMarker: Any + DynSync + DynSend {}
pub trait DynLintStore: Any + DynSync + DynSend {
/// Provides a way to access lint groups without depending on `rustc_lint`
fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = LintGroup> + '_>;
}
/// Represents the data associated with a compilation
/// session for a single crate.
@ -164,7 +168,7 @@ pub struct Session {
pub code_stats: CodeStats,
/// This only ever stores a `LintStore` but we don't want a dependency on that type here.
pub lint_store: Option<Arc<dyn LintStoreMarker>>,
pub lint_store: Option<Arc<dyn DynLintStore>>,
/// Cap lint level specified by a driver specifically.
pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
@ -240,6 +244,12 @@ impl CodegenUnits {
}
}
pub struct LintGroup {
pub name: &'static str,
pub lints: Vec<LintId>,
pub is_externally_loaded: bool,
}
impl Session {
pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
self.miri_unleashed_features.lock().push((span, feature_gate));
@ -596,6 +606,13 @@ impl Session {
(&*self.target.staticlib_prefix, &*self.target.staticlib_suffix)
}
}
pub fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = LintGroup> + '_> {
match self.lint_store {
Some(ref lint_store) => lint_store.lint_groups_iter(),
None => Box::new(std::iter::empty()),
}
}
}
// JUSTIFICATION: defn of the suggested wrapper fns

View file

@ -1837,6 +1837,7 @@ symbols! {
rustc_align,
rustc_allocator,
rustc_allocator_zeroed,
rustc_allocator_zeroed_variant,
rustc_allow_const_fn_unstable,
rustc_allow_incoherent_impl,
rustc_allowed_through_unstable_modules,

View file

@ -119,6 +119,7 @@ mod attr_impl {
const ReadOnly = 1 << 4;
const InReg = 1 << 5;
const NoUndef = 1 << 6;
const CapturesReadOnly = 1 << 7;
}
}
rustc_data_structures::external_bitflags_debug! { ArgAttribute }

View file

@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
llvm_target,
metadata: TargetMetadata {
description: Some("x86_64 Apple macOS (10.12+, Sierra+)".into()),
tier: Some(1),
tier: Some(2),
host_tools: Some(true),
std: Some(true),
},

View file

@ -13,6 +13,7 @@ use crate::errors::NonGenericOpaqueTypeParam;
use crate::regions::OutlivesEnvironmentBuildExt;
use crate::traits::ObligationCtxt;
#[derive(Debug)]
pub enum InvalidOpaqueTypeArgs<'tcx> {
AlreadyReported(ErrorGuaranteed),
NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span },

View file

@ -356,6 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>(
if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
attrs.set(ArgAttribute::ReadOnly);
attrs.set(ArgAttribute::CapturesReadOnly);
}
}
}

View file

@ -336,9 +336,9 @@ dependencies = [
name = "std_detect"
version = "0.1.5"
dependencies = [
"alloc",
"core",
"libc",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]

View file

@ -59,4 +59,3 @@ rustflags = ["-Cpanic=abort"]
rustc-std-workspace-core = { path = 'rustc-std-workspace-core' }
rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' }
rustc-std-workspace-std = { path = 'rustc-std-workspace-std' }
compiler_builtins = { path = "compiler-builtins/compiler-builtins" }

View file

@ -17,6 +17,7 @@ unsafe extern "Rust" {
#[rustc_allocator]
#[rustc_nounwind]
#[rustc_std_internal_symbol]
#[rustc_allocator_zeroed_variant = "__rust_alloc_zeroed"]
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
#[rustc_deallocator]
#[rustc_nounwind]

View file

@ -1554,6 +1554,9 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T {
///
/// Returns the first argument if the comparison determines them to be equal.
///
/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is
/// always passed as the first argument and `v2` as the second.
///
/// # Examples
///
/// ```
@ -1574,7 +1577,7 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T {
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
if compare(&v2, &v1).is_lt() { v2 } else { v1 }
if compare(&v1, &v2).is_le() { v1 } else { v2 }
}
/// Returns the element that gives the minimum value from the specified function.
@ -1646,6 +1649,9 @@ pub fn max<T: Ord>(v1: T, v2: T) -> T {
///
/// Returns the second argument if the comparison determines them to be equal.
///
/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is
/// always passed as the first argument and `v2` as the second.
///
/// # Examples
///
/// ```
@ -1666,7 +1672,7 @@ pub fn max<T: Ord>(v1: T, v2: T) -> T {
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
if compare(&v2, &v1).is_lt() { v1 } else { v2 }
if compare(&v1, &v2).is_gt() { v1 } else { v2 }
}
/// Returns the element that gives the maximum value from the specified function.
@ -1745,6 +1751,9 @@ where
///
/// Returns `[v1, v2]` if the comparison determines them to be equal.
///
/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is
/// always passed as the first argument and `v2` as the second.
///
/// # Examples
///
/// ```
@ -1769,7 +1778,7 @@ pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2]
where
F: FnOnce(&T, &T) -> Ordering,
{
if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] }
if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] }
}
/// Returns minimum and maximum values with respect to the specified key function.

View file

@ -261,53 +261,72 @@ pub unsafe fn atomic_fence<const ORD: AtomicOrdering>();
pub unsafe fn atomic_singlethreadfence<const ORD: AtomicOrdering>();
/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_read_data<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_read_data<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}
/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_write_data<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_write_data<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}
/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_read_instruction<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_read_instruction<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}
/// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction
/// if supported; otherwise, it is a no-op.
/// for the given address if supported; otherwise, it is a no-op.
/// Prefetches have no effect on the behavior of the program but can change its performance
/// characteristics.
///
/// The `locality` argument must be a constant integer and is a temporal locality specifier
/// ranging from (0) - no locality, to (3) - extremely local keep in cache.
/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality,
/// to (3) - extremely local keep in cache.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn prefetch_write_instruction<T>(data: *const T, locality: i32);
#[miri::intrinsic_fallback_is_spec]
pub const fn prefetch_write_instruction<T, const LOCALITY: i32>(data: *const T) {
// This operation is a no-op, unless it is overridden by the backend.
let _ = data;
}
/// Executes a breakpoint trap, for inspection by a debugger.
///

View file

@ -209,6 +209,48 @@ macro_rules! int_impl {
self & self.wrapping_neg()
}
/// Returns the index of the highest bit set to one in `self`, or `None`
/// if `self` is `0`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")]
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")]
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")]
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")]
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn highest_one(self) -> Option<u32> {
(self as $UnsignedT).highest_one()
}
/// Returns the index of the lowest bit set to one in `self`, or `None`
/// if `self` is `0`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")]
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")]
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")]
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")]
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn lowest_one(self) -> Option<u32> {
(self as $UnsignedT).lowest_one()
}
/// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size.
///
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

View file

@ -681,6 +681,54 @@ macro_rules! nonzero_integer {
unsafe { NonZero::new_unchecked(n) }
}
/// Returns the index of the highest bit set to one in `self`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
/// # use core::num::NonZero;
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.highest_one(), 0);")]
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.highest_one(), 4);")]
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.highest_one(), 4);")]
/// # Some(())
/// # }
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn highest_one(self) -> u32 {
Self::BITS - 1 - self.leading_zeros()
}
/// Returns the index of the lowest bit set to one in `self`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
/// # use core::num::NonZero;
/// # fn main() { test().unwrap(); }
/// # fn test() -> Option<()> {
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.lowest_one(), 0);")]
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.lowest_one(), 4);")]
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.lowest_one(), 0);")]
/// # Some(())
/// # }
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn lowest_one(self) -> u32 {
self.trailing_zeros()
}
/// Returns the number of ones in the binary representation of `self`.
///
/// # Examples

View file

@ -261,6 +261,54 @@ macro_rules! uint_impl {
self & self.wrapping_neg()
}
/// Returns the index of the highest bit set to one in `self`, or `None`
/// if `self` is `0`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")]
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")]
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")]
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")]
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn highest_one(self) -> Option<u32> {
match NonZero::new(self) {
Some(v) => Some(v.highest_one()),
None => None,
}
}
/// Returns the index of the lowest bit set to one in `self`, or `None`
/// if `self` is `0`.
///
/// # Examples
///
/// ```
/// #![feature(int_lowest_highest_one)]
///
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")]
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")]
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")]
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")]
/// ```
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn lowest_one(self) -> Option<u32> {
match NonZero::new(self) {
Some(v) => Some(v.lowest_one()),
None => None,
}
}
/// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size.
///
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

View file

@ -54,6 +54,7 @@
#![feature(generic_assert_internals)]
#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(int_lowest_highest_one)]
#![feature(int_roundings)]
#![feature(ip)]
#![feature(is_ascii_octdigit)]

View file

@ -462,3 +462,111 @@ fn test_nonzero_fmt() {
assert_eq!(i, nz);
}
#[test]
fn test_nonzero_highest_one() {
macro_rules! nonzero_int_impl {
($($T:ty),+) => {
$(
{
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!(NonZero::<$T>::new(1 << i).unwrap().highest_one(), i);
if i > <$T>::BITS {
// Set lowest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().highest_one(),
<$T>::BITS - i - 2,
);
}
// Set highest bits.
assert_eq!(
NonZero::<$T>::new(-1 << i).unwrap().highest_one(),
<$T>::BITS - 1,
);
}
}
)+
};
}
macro_rules! nonzero_uint_impl {
($($T:ty),+) => {
$(
{
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!(NonZero::<$T>::new(1 << i).unwrap().highest_one(), i);
// Set lowest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().highest_one(),
<$T>::BITS - i - 1,
);
// Set highest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX << i).unwrap().highest_one(),
<$T>::BITS - 1,
);
}
}
)+
};
}
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
}
#[test]
fn test_nonzero_lowest_one() {
macro_rules! nonzero_int_impl {
($($T:ty),+) => {
$(
{
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!(NonZero::<$T>::new(1 << i).unwrap().lowest_one(), i);
if i > <$T>::BITS {
// Set lowest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().lowest_one(),
0,
);
}
// Set highest bits.
assert_eq!(
NonZero::<$T>::new(-1 << i).unwrap().lowest_one(),
i,
);
}
}
)+
};
}
macro_rules! nonzero_uint_impl {
($($T:ty),+) => {
$(
{
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!(NonZero::<$T>::new(1 << i).unwrap().lowest_one(), i);
// Set lowest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().lowest_one(),
0,
);
// Set highest bits.
assert_eq!(
NonZero::<$T>::new(<$T>::MAX << i).unwrap().lowest_one(),
i,
);
}
}
)+
};
}
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
}

View file

@ -227,6 +227,46 @@ macro_rules! int_module {
}
}
#[test]
fn test_highest_one() {
const ZERO: $T = 0;
const ONE: $T = 1;
const MINUS_ONE: $T = -1;
assert_eq!(ZERO.highest_one(), None);
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!((ONE << i).highest_one(), Some(i));
if i != <$T>::BITS - 1 {
// Set lowest bits.
assert_eq!((<$T>::MAX >> i).highest_one(), Some(<$T>::BITS - i - 2));
}
// Set highest bits.
assert_eq!((MINUS_ONE << i).highest_one(), Some(<$T>::BITS - 1));
}
}
#[test]
fn test_lowest_one() {
const ZERO: $T = 0;
const ONE: $T = 1;
const MINUS_ONE: $T = -1;
assert_eq!(ZERO.lowest_one(), None);
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!((ONE << i).lowest_one(), Some(i));
if i != <$T>::BITS - 1 {
// Set lowest bits.
assert_eq!((<$T>::MAX >> i).lowest_one(), Some(0));
}
// Set highest bits.
assert_eq!((MINUS_ONE << i).lowest_one(), Some(i));
}
}
#[test]
fn test_from_str() {
fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> {

View file

@ -184,6 +184,40 @@ macro_rules! uint_module {
}
}
#[test]
fn test_highest_one() {
const ZERO: $T = 0;
const ONE: $T = 1;
assert_eq!(ZERO.highest_one(), None);
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!((ONE << i).highest_one(), Some(i));
// Set lowest bits.
assert_eq!((<$T>::MAX >> i).highest_one(), Some(<$T>::BITS - i - 1));
// Set highest bits.
assert_eq!((<$T>::MAX << i).highest_one(), Some(<$T>::BITS - 1));
}
}
#[test]
fn test_lowest_one() {
const ZERO: $T = 0;
const ONE: $T = 1;
assert_eq!(ZERO.lowest_one(), None);
for i in 0..<$T>::BITS {
// Set single bit.
assert_eq!((ONE << i).lowest_one(), Some(i));
// Set lowest bits.
assert_eq!((<$T>::MAX >> i).lowest_one(), Some(0));
// Set highest bits.
assert_eq!((<$T>::MAX << i).lowest_one(), Some(i));
}
}
fn from_str<T: core::str::FromStr>(t: &str) -> Option<T> {
core::str::FromStr::from_str(t).ok()
}

View file

@ -2461,7 +2461,7 @@ pub trait BufRead: Read {
/// delimiter or EOF is found.
///
/// If successful, this function will return the total number of bytes read,
/// including the delimiter byte.
/// including the delimiter byte if found.
///
/// This is useful for efficiently skipping data such as NUL-terminated strings
/// in binary file formats without buffering.
@ -2489,7 +2489,7 @@ pub trait BufRead: Read {
/// ```
/// use std::io::{self, BufRead};
///
/// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0");
/// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!");
///
/// // read name
/// let mut name = Vec::new();
@ -2509,6 +2509,11 @@ pub trait BufRead: Read {
/// .expect("reading from cursor won't fail");
/// assert_eq!(num_bytes, 11);
/// assert_eq!(animal, b"Crustacean\0");
///
/// // reach EOF
/// let num_bytes = cursor.skip_until(b'\0')
/// .expect("reading from cursor won't fail");
/// assert_eq!(num_bytes, 1);
/// ```
#[stable(feature = "bufread_skip_until", since = "1.83.0")]
fn skip_until(&mut self, byte: u8) -> Result<usize> {

View file

@ -21,8 +21,8 @@ is-it-maintained-open-issues = { repository = "rust-lang/stdarch" }
maintenance = { status = "experimental" }
[dependencies]
core = { path = "../core" }
alloc = { path = "../alloc" }
core = { version = "1.0.0", package = 'rustc-std-workspace-core' }
alloc = { version = "1.0.0", package = 'rustc-std-workspace-alloc' }
[target.'cfg(not(windows))'.dependencies]
libc = { version = "0.2.0", optional = true, default-features = false }

View file

@ -37,90 +37,121 @@ features! {
///
/// # Unprivileged Specification
///
/// The supported ratified RISC-V instruction sets are as follows:
/// The supported ratified RISC-V instruction sets are as follows (OS
/// columns denote runtime feature detection support with or without the
/// minimum supported version):
///
/// * RV32E: `"rv32e"`
/// * RV32I: `"rv32i"`
/// * RV64I: `"rv64i"`
/// * A: `"a"`
/// * Zaamo: `"zaamo"`
/// * Zalrsc: `"zalrsc"`
/// * B: `"b"`
/// * Zba: `"zba"`
/// * Zbb: `"zbb"`
/// * Zbs: `"zbs"`
/// * C: `"c"`
/// * Zca: `"zca"`
/// * Zcd: `"zcd"` (if D is enabled)
/// * Zcf: `"zcf"` (if F is enabled on RV32)
/// * D: `"d"`
/// * F: `"f"`
/// * M: `"m"`
/// * Q: `"q"`
/// * V: `"v"`
/// * Zve32x: `"zve32x"`
/// * Zve32f: `"zve32f"`
/// * Zve64x: `"zve64x"`
/// * Zve64f: `"zve64f"`
/// * Zve64d: `"zve64d"`
/// * Zicbom: `"zicbom"`
/// * Zicboz: `"zicboz"`
/// * Zicntr: `"zicntr"`
/// * Zicond: `"zicond"`
/// * Zicsr: `"zicsr"`
/// * Zifencei: `"zifencei"`
/// * Zihintntl: `"zihintntl"`
/// * Zihintpause: `"zihintpause"`
/// * Zihpm: `"zihpm"`
/// * Zimop: `"zimop"`
/// * Zabha: `"zabha"`
/// * Zacas: `"zacas"`
/// * Zawrs: `"zawrs"`
/// * Zfa: `"zfa"`
/// * Zfbfmin: `"zfbfmin"`
/// * Zfh: `"zfh"`
/// * Zfhmin: `"zfhmin"`
/// * Zfinx: `"zfinx"`
/// * Zdinx: `"zdinx"`
/// * Zhinx: `"zhinx"`
/// * Zhinxmin: `"zhinxmin"`
/// * Zcb: `"zcb"`
/// * Zcmop: `"zcmop"`
/// * Zbc: `"zbc"`
/// * Zbkb: `"zbkb"`
/// * Zbkc: `"zbkc"`
/// * Zbkx: `"zbkx"`
/// * Zk: `"zk"`
/// * Zkn: `"zkn"`
/// * Zknd: `"zknd"`
/// * Zkne: `"zkne"`
/// * Zknh: `"zknh"`
/// * Zkr: `"zkr"`
/// * Zks: `"zks"`
/// * Zksed: `"zksed"`
/// * Zksh: `"zksh"`
/// * Zkt: `"zkt"`
/// * Zvbb: `"zvbb"`
/// * Zvbc: `"zvbc"`
/// * Zvfbfmin: `"zvfbfmin"`
/// * Zvfbfwma: `"zvfbfwma"`
/// * Zvfh: `"zvfh"`
/// * Zvfhmin: `"zvfhmin"`
/// * Zvkb: `"zvkb"`
/// * Zvkg: `"zvkg"`
/// * Zvkn: `"zvkn"`
/// * Zvkned: `"zvkned"`
/// * Zvknha: `"zvknha"`
/// * Zvknhb: `"zvknhb"`
/// * Zvknc: `"zvknc"`
/// * Zvkng: `"zvkng"`
/// * Zvks: `"zvks"`
/// * Zvksed: `"zvksed"`
/// * Zvksh: `"zvksh"`
/// * Zvksc: `"zvksc"`
/// * Zvksg: `"zvksg"`
/// * Zvkt: `"zvkt"`
/// * Ztso: `"ztso"`
/// | Literal | Base | Linux |
/// |:---------- |:------- |:---------- |
/// | `"rv32e"` | RV32E | No |
/// | `"rv32i"` | RV32I | Yes [^ima] |
/// | `"rv64i"` | RV64I | Yes [^ima] |
///
/// | Literal | Extension | Linux |
/// |:--------------- |:----------- |:------------------- |
/// | `"a"` | A | Yes [^ima] |
/// | `"b"` | B | 6.5 |
/// | `"c"` | C | Yes |
/// | `"d"` | D | Yes |
/// | `"f"` | F | Yes |
/// | `"m"` | M | Yes [^ima] |
/// | `"q"` | Q | No |
/// | `"v"` | V | 6.5 |
/// | `"zaamo"` | Zaamo | 6.15 [^ima] [^dep] |
/// | `"zabha"` | Zabha | 6.16 |
/// | `"zacas"` | Zacas | 6.8 |
/// | `"zalrsc"` | Zalrsc | 6.15 [^ima] [^dep] |
/// | `"zawrs"` | Zawrs | 6.11 |
/// | `"zba"` | Zba | 6.5 |
/// | `"zbb"` | Zbb | 6.5 |
/// | `"zbc"` | Zbc | 6.8 |
/// | `"zbkb"` | Zbkb | 6.8 |
/// | `"zbkc"` | Zbkc | 6.8 |
/// | `"zbkx"` | Zbkx | 6.8 |
/// | `"zbs"` | Zbs | 6.5 |
/// | `"zca"` | Zca | 6.11 [^dep] |
/// | `"zcb"` | Zcb | 6.11 |
/// | `"zcd"` | Zcd | 6.11 [^dep] |
/// | `"zcf"` | Zcf | 6.11 [^dep] |
/// | `"zcmop"` | Zcmop | 6.11 |
/// | `"zdinx"` | Zdinx | No |
/// | `"zfa"` | Zfa | 6.8 |
/// | `"zfbfmin"` | Zfbfmin | 6.15 |
/// | `"zfh"` | Zfh | 6.8 |
/// | `"zfhmin"` | Zfhmin | 6.8 |
/// | `"zfinx"` | Zfinx | No |
/// | `"zhinx"` | Zhinx | No |
/// | `"zhinxmin"` | Zhinxmin | No |
/// | `"zicbom"` | Zicbom | 6.15 |
/// | `"zicboz"` | Zicboz | 6.7 |
/// | `"zicntr"` | Zicntr | 6.15 [^ima] [^cntr] |
/// | `"zicond"` | Zicond | 6.8 |
/// | `"zicsr"` | Zicsr | No [^ima] [^dep] |
/// | `"zifencei"` | Zifencei | No [^ima] |
/// | `"zihintntl"` | Zihintntl | 6.8 |
/// | `"zihintpause"` | Zihintpause | 6.10 |
/// | `"zihpm"` | Zihpm | 6.15 [^cntr] |
/// | `"zimop"` | Zimop | 6.11 |
/// | `"zk"` | Zk | No [^zkr] |
/// | `"zkn"` | Zkn | 6.8 |
/// | `"zknd"` | Zknd | 6.8 |
/// | `"zkne"` | Zkne | 6.8 |
/// | `"zknh"` | Zknh | 6.8 |
/// | `"zkr"` | Zkr | No [^zkr] |
/// | `"zks"` | Zks | 6.8 |
/// | `"zksed"` | Zksed | 6.8 |
/// | `"zksh"` | Zksh | 6.8 |
/// | `"zkt"` | Zkt | 6.8 |
/// | `"ztso"` | Ztso | 6.8 |
/// | `"zvbb"` | Zvbb | 6.8 |
/// | `"zvbc"` | Zvbc | 6.8 |
/// | `"zve32f"` | Zve32f | 6.11 [^dep] |
/// | `"zve32x"` | Zve32x | 6.11 [^dep] |
/// | `"zve64d"` | Zve64d | 6.11 [^dep] |
/// | `"zve64f"` | Zve64f | 6.11 [^dep] |
/// | `"zve64x"` | Zve64x | 6.11 [^dep] |
/// | `"zvfbfmin"` | Zvfbfmin | 6.15 |
/// | `"zvfbfwma"` | Zvfbfwma | 6.15 |
/// | `"zvfh"` | Zvfh | 6.8 |
/// | `"zvfhmin"` | Zvfhmin | 6.8 |
/// | `"zvkb"` | Zvkb | 6.8 |
/// | `"zvkg"` | Zvkg | 6.8 |
/// | `"zvkn"` | Zvkn | 6.8 |
/// | `"zvknc"` | Zvknc | 6.8 |
/// | `"zvkned"` | Zvkned | 6.8 |
/// | `"zvkng"` | Zvkng | 6.8 |
/// | `"zvknha"` | Zvknha | 6.8 |
/// | `"zvknhb"` | Zvknhb | 6.8 |
/// | `"zvks"` | Zvks | 6.8 |
/// | `"zvksc"` | Zvksc | 6.8 |
/// | `"zvksed"` | Zvksed | 6.8 |
/// | `"zvksg"` | Zvksg | 6.8 |
/// | `"zvksh"` | Zvksh | 6.8 |
/// | `"zvkt"` | Zvkt | 6.8 |
///
/// [^ima]: Or enabled when the IMA base behavior is detected on the Linux
/// kernel version 6.4 or later (for bases, the only matching one -- either
/// `"rv32i"` or `"rv64i"` -- is enabled).
///
/// [^cntr]: Even if this extension is available, it does not necessarily
/// mean all performance counters are accessible.
/// For example, accesses to all performance counters except `time`
/// (wall-clock) are blocked by default on the Linux kernel
/// version 6.6 or later.
/// Also beware that, even if performance counters like `cycle` and
/// `instret` are accessible, their value can be unreliable (e.g. returning
/// the constant value) under certain circumstances.
///
/// [^dep]: Or enabled as a dependency of another extension (a superset)
/// even if runtime detection of this feature itself is not supported (as
/// long as the runtime detection of the superset is supported).
///
/// [^zkr]: Linux does not report existence of this extension even if
/// supported by the hardware mainly because the `seed` CSR on the Zkr
/// extension (which provides hardware-based randomness) is normally
/// inaccessible from the user mode.
/// For the Zk extension features except this CSR, check existence of both
/// `"zkn"` and `"zkt"` features instead.
///
/// There's also bases and extensions marked as standard instruction set,
/// but they are in frozen or draft state. These instruction sets are also

View file

@ -233,6 +233,12 @@ features! {
/// AMX-TF32 (TensorFloat32 Operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_transpose: "amx-transpose";
/// AMX-TRANSPOSE (Matrix Transpose Operations)
@FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf";
/// APX-F (Advanced Performance Extensions - Foundation)
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1";
/// AVX10.1
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_2: "avx10.2";
/// AVX10.2
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] f16c: "f16c";
/// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma";

View file

@ -137,6 +137,32 @@ pub(crate) fn detect_features() -> cache::Initializer {
enable(ebx, 2, Feature::widekl);
}
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
// On intel CPUs with popcnt, lzcnt implements the
// "missing part" of ABM, so we map both to the same
// internal feature.
//
// The `is_x86_feature_detected!("lzcnt")` macro then
// internally maps to Feature::abm.
enable(extended_proc_info_ecx, 5, Feature::lzcnt);
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
// number(Family 18h).
//
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
// family 17h.
//
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
// Related Hygon kernel patch can be found on
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
// These features are available on AMD arch CPUs:
enable(extended_proc_info_ecx, 6, Feature::sse4a);
enable(extended_proc_info_ecx, 21, Feature::tbm);
enable(extended_proc_info_ecx, 11, Feature::xop);
}
// `XSAVE` and `AVX` support:
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
if cpu_xsave {
@ -161,6 +187,7 @@ pub(crate) fn detect_features() -> cache::Initializer {
// * AVX -> `XCR0.AVX[2]`
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
// * AMX -> `XCR0.AMX[18:17]`
// * APX -> `XCR0.APX[19]`
//
// by setting the corresponding bits of `XCR0` to `1`.
//
@ -173,6 +200,8 @@ pub(crate) fn detect_features() -> cache::Initializer {
let os_avx512_support = xcr0 & 0xe0 == 0xe0;
// Test `XCR0.AMX[18:17]` with the mask `0b110_0000_0000_0000_0000 == 0x60000`
let os_amx_support = xcr0 & 0x60000 == 0x60000;
// Test `XCR0.APX[19]` with the mask `0b1000_0000_0000_0000_0000 == 0x80000`
let os_apx_support = xcr0 & 0x80000 == 0x80000;
// Only if the OS and the CPU support saving/restoring the AVX
// registers we enable `xsave` support:
@ -262,34 +291,21 @@ pub(crate) fn detect_features() -> cache::Initializer {
enable(amx_feature_flags_eax, 8, Feature::amx_movrs);
}
}
if os_apx_support {
enable(extended_features_edx_leaf_1, 21, Feature::apxf);
}
let avx10_1 = enable(extended_features_edx_leaf_1, 19, Feature::avx10_1);
if avx10_1 {
let CpuidResult { ebx, .. } = unsafe { __cpuid(0x24) };
let avx10_version = ebx & 0xff;
if avx10_version >= 2 {
value.set(Feature::avx10_2 as u32);
}
}
}
}
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
// On intel CPUs with popcnt, lzcnt implements the
// "missing part" of ABM, so we map both to the same
// internal feature.
//
// The `is_x86_feature_detected!("lzcnt")` macro then
// internally maps to Feature::abm.
enable(extended_proc_info_ecx, 5, Feature::lzcnt);
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
// number(Family 18h).
//
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
// family 17h.
//
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
// Related Hygon kernel patch can be found on
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
// These features are available on AMD arch CPUs:
enable(extended_proc_info_ecx, 6, Feature::sse4a);
enable(extended_proc_info_ecx, 21, Feature::tbm);
enable(extended_proc_info_ecx, 11, Feature::xop);
}
}
// Unfortunately, some Skylake chips erroneously report support for BMI1 and

View file

@ -1048,22 +1048,18 @@ impl Step for Rustc {
// If we are building a stage3+ compiler, and full bootstrap is disabled, and we have a
// previous rustc available, we will uplift a compiler from a previous stage.
// We do not allow cross-compilation uplifting here, because there it can be quite tricky
// to figure out which stage actually built the rustc that should be uplifted.
if build_compiler.stage >= 2
&& !builder.config.full_bootstrap
&& (target == builder.host_target || builder.hosts.contains(&target))
&& target == builder.host_target
{
// Here we need to determine the **build compiler** that built the stage that we will
// be uplifting. We cannot uplift stage 1, as it has a different ABI than stage 2+,
// so we always uplift the stage2 compiler (compiled with stage 1).
let uplift_build_compiler = builder.compiler(1, build_compiler.host);
let msg = if uplift_build_compiler.host == target {
format!("Uplifting rustc (stage2 -> stage{stage})")
} else {
format!(
"Uplifting rustc (stage2:{} -> stage{stage}:{target})",
uplift_build_compiler.host
)
};
let msg = format!("Uplifting rustc from stage2 to stage{stage})");
builder.info(&msg);
// Here the compiler that built the rlibs (`uplift_build_compiler`) can be different

View file

@ -672,6 +672,83 @@ mod snapshot {
");
}
#[test]
fn build_compiler_stage_3() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("build")
.path("compiler")
.stage(3)
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 2 <host> -> rustc 3 <host>
");
}
#[test]
fn build_compiler_stage_3_cross() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("build")
.path("compiler")
.hosts(&[TEST_TRIPLE_1])
.stage(3)
.render_steps(), @r"
[build] llvm <host>
[build] llvm <target1>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 1 <host> -> std 1 <target1>
[build] rustc 2 <host> -> std 2 <target1>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 2 <host> -> rustc 3 <target1>
");
}
#[test]
fn build_compiler_stage_3_full_bootstrap() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("build")
.path("compiler")
.stage(3)
.args(&["--set", "build.full-bootstrap=true"])
.render_steps(), @r"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 2 <host> -> rustc 3 <host>
");
}
#[test]
fn build_compiler_stage_3_cross_full_bootstrap() {
let ctx = TestCtx::new();
insta::assert_snapshot!(
ctx.config("build")
.path("compiler")
.stage(3)
.hosts(&[TEST_TRIPLE_1])
.args(&["--set", "build.full-bootstrap=true"])
.render_steps(), @r"
[build] llvm <host>
[build] llvm <target1>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 1 <host> -> rustc 2 <host>
[build] rustc 2 <host> -> std 2 <target1>
[build] rustc 2 <host> -> std 2 <host>
[build] rustc 2 <host> -> rustc 3 <target1>
");
}
#[test]
fn build_compiler_codegen_backend() {
let ctx = TestCtx::new();

View file

@ -23,10 +23,6 @@ runners:
<<: *base-job
- &job-macos
os: macos-13
<<: *base-job
- &job-macos-m1
os: macos-14
<<: *base-job
@ -68,17 +64,6 @@ runners:
<<: *base-job
envs:
env-x86_64-apple-tests: &env-x86_64-apple-tests
SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact
RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc
# Ensure that host tooling is tested on our minimum supported macOS version.
MACOSX_DEPLOYMENT_TARGET: 10.12
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
SELECT_XCODE: /Applications/Xcode_15.2.app
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
production:
&production
DEPLOY_BUCKET: rust-lang-ci2
@ -455,8 +440,19 @@ auto:
- name: dist-x86_64-apple
env:
SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin
RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1
SCRIPT: >-
./x.py dist bootstrap
--include-default-paths
--host=x86_64-apple-darwin
--target=x86_64-apple-darwin
RUST_CONFIGURE_ARGS: >-
--enable-full-tools
--enable-sanitizers
--enable-profiler
--disable-docs
--set rust.jemalloc
--set rust.lto=thin
--set rust.codegen-units=1
# Ensure that host tooling is built to support our minimum support macOS version.
MACOSX_DEPLOYMENT_TARGET: 10.12
MACOSX_STD_DEPLOYMENT_TARGET: 10.12
@ -482,17 +478,6 @@ auto:
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
<<: *job-macos-m1
- name: x86_64-apple-1
env:
<<: *env-x86_64-apple-tests
<<: *job-macos
- name: x86_64-apple-2
env:
SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc
<<: *env-x86_64-apple-tests
<<: *job-macos
- name: dist-aarch64-apple
@ -517,7 +502,7 @@ auto:
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
CODEGEN_BACKENDS: llvm,cranelift
<<: *job-macos-m1
<<: *job-macos
- name: aarch64-apple
env:
@ -537,7 +522,7 @@ auto:
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
<<: *job-macos-m1
<<: *job-macos
######################
# Windows Builders #

View file

@ -36,7 +36,6 @@ target | notes
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1+, glibc 2.17+)
[`i686-pc-windows-msvc`](platform-support/windows-msvc.md) | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
`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`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+)
[`x86_64-pc-windows-msvc`](platform-support/windows-msvc.md) | 64-bit MSVC (Windows 10+, Windows Server 2016+)
`x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+)
@ -106,6 +105,7 @@ target | notes
[`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20+, glibc 2.29)
[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.3)
[`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2+, glibc 2.17)
[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+)
[`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | 64-bit x86 MinGW (Windows 10+), LLVM ABI
[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD
[`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos

View file

@ -4,9 +4,12 @@ Apple macOS targets.
**Tier: 1**
- `x86_64-apple-darwin`: macOS on 64-bit x86.
- `aarch64-apple-darwin`: macOS on ARM64 (M1-family or later Apple Silicon CPUs).
**Tier: 2**
- `x86_64-apple-darwin`: macOS on 64-bit x86.
## Target maintainers
[@thomcc](https://github.com/thomcc)

View file

@ -514,4 +514,13 @@ declare namespace rustdoc {
options?: string[],
default: string | boolean,
}
/**
* Single element in the data-locs field of a scraped example.
* First field is the start and end char index,
* other fields seem to be unused.
*
* Generated by `render_call_locations` in `render/mod.rs`.
*/
type ScrapedLoc = [[number, number], string, string]
}

View file

@ -1,7 +1,4 @@
/* global addClass, hasClass, removeClass, onEachLazy */
// Eventually fix this.
// @ts-nocheck
/* global addClass, hasClass, removeClass, onEachLazy, nonnull */
"use strict";
@ -14,8 +11,16 @@
const DEFAULT_MAX_LINES = 5;
const HIDDEN_MAX_LINES = 10;
// Scroll code block to the given code location
/**
* Scroll code block to the given code location
* @param {HTMLElement} elt
* @param {[number, number]} loc
* @param {boolean} isHidden
*/
function scrollToLoc(elt, loc, isHidden) {
/** @type {HTMLElement[]} */
// blocked on https://github.com/microsoft/TypeScript/issues/29037
// @ts-expect-error
const lines = elt.querySelectorAll("[data-nosnippet]");
let scrollOffset;
@ -35,10 +40,15 @@
scrollOffset = offsetMid - halfHeight;
}
lines[0].parentElement.scrollTo(0, scrollOffset);
elt.querySelector(".rust").scrollTo(0, scrollOffset);
nonnull(lines[0].parentElement).scrollTo(0, scrollOffset);
nonnull(elt.querySelector(".rust")).scrollTo(0, scrollOffset);
}
/**
* @param {HTMLElement} parent
* @param {string} className
* @param {string} content
*/
function createScrapeButton(parent, className, content) {
const button = document.createElement("button");
button.className = className;
@ -50,20 +60,24 @@
window.updateScrapedExample = (example, buttonHolder) => {
let locIndex = 0;
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
const link = example.querySelector(".scraped-example-title a");
/** @type {HTMLAnchorElement} */
const link = nonnull(example.querySelector(".scraped-example-title a"));
let expandButton = null;
if (!example.classList.contains("expanded")) {
expandButton = createScrapeButton(buttonHolder, "expand", "Show all");
}
const isHidden = example.parentElement.classList.contains("more-scraped-examples");
const isHidden = nonnull(example.parentElement).classList.contains("more-scraped-examples");
// @ts-expect-error
const locs = example.locs;
if (locs.length > 1) {
const next = createScrapeButton(buttonHolder, "next", "Next usage");
const prev = createScrapeButton(buttonHolder, "prev", "Previous usage");
// Toggle through list of examples in a given file
/** @type {function(function(): void): void} */
const onChangeLoc = changeIndex => {
removeClass(highlights[locIndex], "focus");
changeIndex();
@ -106,10 +120,19 @@
}
};
/**
* Initialize the `locs` field
*
* @param {HTMLElement & {locs?: rustdoc.ScrapedLoc[]}} example
* @param {boolean} isHidden
*/
function setupLoc(example, isHidden) {
example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
const locs_str = nonnull(example.attributes.getNamedItem("data-locs")).textContent;
const locs =
JSON.parse(nonnull(nonnull(locs_str)));
example.locs = locs;
// Start with the first example in view
scrollToLoc(example, example.locs[0][0], isHidden);
scrollToLoc(example, locs[0][0], isHidden);
}
const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");

View file

@ -205,7 +205,7 @@ static TARGETS: &[&str] = &[
///
/// The order here matters, more specific entries should be first.
static DOCS_FALLBACK: &[(&str, &str)] = &[
("-apple-", "x86_64-apple-darwin"),
("-apple-", "aarch64-apple-darwin"),
("aarch64", "aarch64-unknown-linux-gnu"),
("arm-", "aarch64-unknown-linux-gnu"),
("", "x86_64-unknown-linux-gnu"),

View file

@ -330,7 +330,7 @@ LL | if X.is_some() {
|
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>
= note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
= note: `#[deny(static_mut_refs)]` on by default
= note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default
error: aborting due to 36 previous errors

View file

@ -429,9 +429,9 @@ dependencies = [
[[package]]
name = "rustc-build-sysroot"
version = "0.5.9"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb13874a0e55baf4ac3d49d38206aecb31a55b75d6c4d04fd850b53942c8cc8"
checksum = "dd41ead66a69880951b2f7df3139db401d44451b4da123344d27eaa791b89c95"
dependencies = [
"anyhow",
"rustc_version",

View file

@ -18,7 +18,7 @@ directories = "6"
rustc_version = "0.4"
serde_json = "1.0.40"
cargo_metadata = "0.21"
rustc-build-sysroot = "0.5.8"
rustc-build-sysroot = "0.5.10"
# Enable some feature flags that dev-dependencies need but dependencies
# do not. This makes `./miri install` after `./miri build` faster.

View file

@ -1 +1 @@
f605b57042ffeb320d7ae44490113a827139b766
125ff8a788c5d6a66917f499abdc00051afe6886

View file

@ -0,0 +1,23 @@
#![feature(core_intrinsics)]
// Test that these intrinsics work. Their behavior should be a no-op.
fn main() {
static X: [u8; 8] = [0; 8];
::std::intrinsics::prefetch_read_data::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_read_data::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_read_data::<_, 3>(X.as_ptr());
::std::intrinsics::prefetch_write_data::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_write_data::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_write_data::<_, 3>(X.as_ptr());
::std::intrinsics::prefetch_read_instruction::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_read_instruction::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_read_instruction::<_, 3>(X.as_ptr());
::std::intrinsics::prefetch_write_instruction::<_, 1>(::std::ptr::null::<u8>());
::std::intrinsics::prefetch_write_instruction::<_, 2>(::std::ptr::dangling::<u8>());
::std::intrinsics::prefetch_write_instruction::<_, 3>(X.as_ptr());
}

View file

@ -405,6 +405,24 @@ async function runChecks(testFile, doSearch, parseQuery) {
return res;
}
function mostRecentMatch(staticFiles, regex) {
const matchingEntries = fs.readdirSync(staticFiles)
.filter(f => f.match(regex))
.map(f => {
const stats = fs.statSync(path.join(staticFiles, f));
return {
path: f,
time: stats.mtimeMs,
};
});
if (matchingEntries.length === 0) {
throw "No static file matching regex";
}
// We sort entries in descending order.
matchingEntries.sort((a, b) => b.time - a.time);
return matchingEntries[0].path;
}
/**
* Load searchNNN.js and search-indexNNN.js.
*
@ -417,9 +435,9 @@ async function runChecks(testFile, doSearch, parseQuery) {
*/
async function loadSearchJS(doc_folder, resource_suffix) {
const staticFiles = path.join(doc_folder, "static.files");
const stringdexJs = fs.readdirSync(staticFiles).find(f => f.match(/stringdex.*\.js$/));
const stringdexJs = mostRecentMatch(staticFiles, /stringdex.*\.js$/);
const stringdexModule = require(path.join(staticFiles, stringdexJs));
const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/));
const searchJs = mostRecentMatch(staticFiles, /search-[0-9a-f]{8}.*\.js$/);
const searchModule = require(path.join(staticFiles, searchJs));
globalThis.nonnull = (x, msg) => {
if (x === null) {

View file

@ -18,11 +18,11 @@ pub async fn async_fn_test() {
pub async fn foo() {}
// NONMSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
// MSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]],
// MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
// NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test",
// CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]],
// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE]],

View file

@ -80,7 +80,7 @@ pub fn option_nonzero_int(x: Option<NonZero<u64>>) -> Option<NonZero<u64>> {
x
}
// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn readonly_borrow(_: &i32) {}
@ -91,12 +91,12 @@ pub fn readonly_borrow_ret() -> &'static i32 {
loop {}
}
// CHECK: @static_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
// CHECK: @static_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
// static borrow may be captured
#[no_mangle]
pub fn static_borrow(_: &'static i32) {}
// CHECK: @named_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
// CHECK: @named_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
// borrow with named lifetime may be captured
#[no_mangle]
pub fn named_borrow<'r>(_: &'r i32) {}
@ -129,7 +129,7 @@ pub fn mutable_borrow_ret() -> &'static mut i32 {
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>.
pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1)
// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1)
// But `&NotUnpin` behaves perfectly normal.
#[no_mangle]
pub fn notunpin_borrow(_: &NotUnpin) {}
@ -138,12 +138,12 @@ pub fn notunpin_borrow(_: &NotUnpin) {}
#[no_mangle]
pub fn indirect_struct(_: S) {}
// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4 dereferenceable(32) %_1)
// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(32) %_1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn borrowed_struct(_: &S) {}
// CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %_x)
// CHECK: @option_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable_or_null(4) %_x)
#[no_mangle]
pub fn option_borrow(_x: Option<&i32>) {}
@ -185,7 +185,7 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
// With a custom allocator, it should *not* have `noalias`. (See
// <https://github.com/rust-lang/miri/issues/3341> for why.) The second argument is the allocator,
// which is a reference here that still carries `noalias` as usual.
// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1 %x.1)
// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %x.1)
#[no_mangle]
pub fn _box_custom(x: Box<i32, &std::alloc::Global>) {
drop(x)
@ -208,7 +208,7 @@ pub fn struct_return() -> S {
#[no_mangle]
pub fn helper(_: usize) {}
// CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @slice(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {}
@ -227,7 +227,7 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {}
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {}
// CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// CHECK: @str(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {}
@ -259,7 +259,7 @@ pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + U
x
}
// CHECK: { ptr, [[USIZE]] } @return_slice(ptr noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1)
// CHECK: { ptr, [[USIZE]] } @return_slice(ptr noalias noundef nonnull readonly align 2{{( captures\(address, read_provenance\))?}} %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x

View file

@ -9,56 +9,48 @@ use std::intrinsics::{
#[no_mangle]
pub fn check_prefetch_read_data(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1)
prefetch_read_data(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1)
prefetch_read_data(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1)
prefetch_read_data(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1)
prefetch_read_data(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1)
prefetch_read_data::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1)
prefetch_read_data::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1)
prefetch_read_data::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1)
prefetch_read_data::<_, 3>(data.as_ptr());
}
#[no_mangle]
pub fn check_prefetch_write_data(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1)
prefetch_write_data(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1)
prefetch_write_data(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1)
prefetch_write_data(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1)
prefetch_write_data(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1)
prefetch_write_data::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1)
prefetch_write_data::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1)
prefetch_write_data::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1)
prefetch_write_data::<_, 3>(data.as_ptr());
}
#[no_mangle]
pub fn check_prefetch_read_instruction(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0)
prefetch_read_instruction(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0)
prefetch_read_instruction(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0)
prefetch_read_instruction(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0)
prefetch_read_instruction(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0)
prefetch_read_instruction::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0)
prefetch_read_instruction::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0)
prefetch_read_instruction::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0)
prefetch_read_instruction::<_, 3>(data.as_ptr());
}
#[no_mangle]
pub fn check_prefetch_write_instruction(data: &[i8]) {
unsafe {
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0)
prefetch_write_instruction(data.as_ptr(), 0);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0)
prefetch_write_instruction(data.as_ptr(), 1);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0)
prefetch_write_instruction(data.as_ptr(), 2);
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0)
prefetch_write_instruction(data.as_ptr(), 3);
}
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0)
prefetch_write_instruction::<_, 0>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0)
prefetch_write_instruction::<_, 1>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0)
prefetch_write_instruction::<_, 2>(data.as_ptr());
// CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0)
prefetch_write_instruction::<_, 3>(data.as_ptr());
}

View file

@ -67,7 +67,7 @@ pub fn enum2_value(x: Enum2) -> Enum2 {
x
}
// CHECK: noundef [[USIZE]] @takes_slice(ptr noalias noundef nonnull readonly align 4 %x.0, [[USIZE]] noundef %x.1)
// CHECK: noundef [[USIZE]] @takes_slice(ptr {{.*}} %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn takes_slice(x: &[i32]) -> usize {
x.len()

View file

@ -0,0 +1,18 @@
//@ compile-flags: -C opt-level=3 -Z mir-opt-level=0
//@ min-llvm-version: 21
#![crate_type = "lib"]
unsafe extern "C" {
safe fn do_something(p: &i32);
}
#[unsafe(no_mangle)]
pub fn test() -> i32 {
// CHECK-LABEL: @test(
// CHECK: ret i32 0
let i = 0;
do_something(&i);
do_something(&i);
i
}

View file

@ -1,4 +1,6 @@
//@ revisions: normal llvm21
//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
//@ [llvm21] min-llvm-version: 21
//@ only-x86_64
#![crate_type = "lib"]
@ -176,6 +178,24 @@ pub fn vec_option_i32(n: usize) -> Vec<Option<i32>> {
vec![None; n]
}
// LLVM21-LABEL: @vec_array
#[cfg(llvm21)]
#[no_mangle]
pub fn vec_array(n: usize) -> Vec<[u32; 1_000_000]> {
// LLVM21-NOT: call {{.*}}alloc::vec::from_elem
// LLVM21-NOT: call {{.*}}reserve
// LLVM21-NOT: call {{.*}}__rust_alloc(
// LLVM21: call {{.*}}__rust_alloc_zeroed(
// LLVM21-NOT: call {{.*}}alloc::vec::from_elem
// LLVM21-NOT: call {{.*}}reserve
// LLVM21-NOT: call {{.*}}__rust_alloc(
// LLVM21: ret void
vec![[0; 1_000_000]; 3]
}
// Ensure that __rust_alloc_zeroed gets the right attributes for LLVM to optimize it away.
// CHECK: declare noalias noundef ptr @{{.*}}__rust_alloc_zeroed(i64 noundef, i64 allocalign noundef) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]

View file

@ -0,0 +1,10 @@
//@ edition: 2024
// When pub async fn is monomorphized, its implementation coroutine is also monomorphized
//@ compile-flags: --crate-type=lib
//~ MONO_ITEM fn async_fn @@
//~ MONO_ITEM fn async_fn::{closure#0} @@
#[unsafe(no_mangle)]
pub async fn async_fn(x: u64) -> bool {
true
}

View file

@ -0,0 +1,89 @@
//@ only-x86_64-unknown-linux-gnu
//@ compile-flags: -C panic=abort -Zinline-mir=no -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes
//@ no-prefer-dynamic
//@ edition:2024
#![crate_type = "lib"]
trait TestTrait {
fn test_func(&self);
}
struct TestStruct {}
impl TestTrait for TestStruct {
fn test_func(&self) {
println!("TestStruct::test_func");
}
}
#[inline(never)]
pub fn foo() -> impl TestTrait {
TestStruct {}
}
//~ MONO_ITEM fn foo
//~ MONO_ITEM fn <TestStruct as TestTrait>::test_func
trait TestTrait2 {
fn test_func2(&self);
}
struct TestStruct2 {}
impl TestTrait2 for TestStruct2 {
fn test_func2(&self) {
println!("TestStruct2::test_func2");
}
}
#[inline(never)]
pub fn foo2() -> Box<dyn TestTrait2> {
Box::new(TestStruct2 {})
}
//~ MONO_ITEM fn <TestStruct2 as TestTrait2>::test_func2
//~ MONO_ITEM fn alloc::alloc::exchange_malloc
//~ MONO_ITEM fn foo2
//~ MONO_ITEM fn std::alloc::Global::alloc_impl
//~ MONO_ITEM fn std::boxed::Box::<TestStruct2>::new
//~ MONO_ITEM fn std::alloc::Layout::from_size_align_unchecked::precondition_check
//~ MONO_ITEM fn std::ptr::NonNull::<T>::new_unchecked::precondition_check
struct Counter {
count: usize,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 { Some(self.count) } else { None }
}
}
#[inline(never)]
pub fn foo3() -> Box<dyn Iterator<Item = usize>> {
Box::new(Counter::new())
}
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::advance_by
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::next
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::nth
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::size_hint
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::try_fold::<std::num::NonZero<usize>, {closure@<Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}}, std::option::Option<std::num::NonZero<usize>>>
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::FromResidual<std::option::Option<std::convert::Infallible>>>::from_residual
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::branch
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::from_output
//~ MONO_ITEM fn foo3
//~ MONO_ITEM fn std::boxed::Box::<Counter>::new
//~ MONO_ITEM fn Counter::new
//~ MONO_ITEM fn core::fmt::rt::<impl std::fmt::Arguments<'_>>::new_const::<1>

View file

@ -103,21 +103,21 @@ Number of file 0 mappings: 3
Highest counter ID seen: (none)
Function name: async::g
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 16]
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 12]
Number of files: 1
- file 0 => $DIR/async.rs
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 22)
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 18)
Highest counter ID seen: c0
Function name: async::g::{closure#0} (unused)
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 17, 00, 18, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 13, 00, 14, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Number of files: 1
- file 0 => $DIR/async.rs
Number of expressions: 0
Number of file 0 mappings: 12
- Code(Zero) at (prev + 27, 23) to (start + 0, 24)
- Code(Zero) at (prev + 27, 19) to (start + 0, 20)
- Code(Zero) at (prev + 1, 11) to (start + 0, 12)
- Code(Zero) at (prev + 1, 9) to (start + 0, 10)
- Code(Zero) at (prev + 0, 14) to (start + 0, 23)

View file

@ -24,8 +24,8 @@
LL| |
LL| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
LL| |
LL| 1|pub async fn g(x: u8) {
^0
LL| 1|async fn g(x: u8) {
^0
LL| 0| match x {
LL| 0| y if e().await == y => (),
LL| 0| y if f().await == y => (),

View file

@ -24,7 +24,7 @@ async fn f() -> u8 { 1 }
async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
pub async fn g(x: u8) {
async fn g(x: u8) {
match x {
y if e().await == y => (),
y if f().await == y => (),

View file

@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
},
mode: Direct(
ArgAttributes {
regular: NoAlias | NonNull | ReadOnly | NoUndef,
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
arg_ext: None,
pointee_size: Size(2 bytes),
pointee_align: Some(

View file

@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
},
mode: Direct(
ArgAttributes {
regular: NoAlias | NonNull | ReadOnly | NoUndef,
regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly,
arg_ext: None,
pointee_size: Size(2 bytes),
pointee_align: Some(

View file

@ -165,7 +165,7 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: use `extern "C"` instead
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "cdecl" is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1

View file

@ -147,7 +147,7 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: use `extern "C"` instead
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "cdecl" is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1

View file

@ -159,7 +159,7 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: use `extern "C"` instead
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "cdecl" is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1

View file

@ -159,7 +159,7 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: use `extern "C"` instead
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "cdecl" is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1

View file

@ -141,7 +141,7 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: use `extern "C"` instead
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "cdecl" is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1

View file

@ -109,7 +109,7 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
= help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
= note: `#[warn(unsupported_calling_conventions)]` on by default
= note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default
warning: "stdcall" is not a supported ABI for the current target
--> $DIR/unsupported.rs:87:1

View file

@ -4,7 +4,7 @@ warning: trait `Bar` is never used
LL | trait Bar: Foo {
| ^^^
|
= note: `#[warn(dead_code)]` on by default
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -6,7 +6,7 @@ LL | trait Tr2<'a> { fn tr2(self) -> &'a Self; }
| |
| method in this trait
|
= note: `#[warn(dead_code)]` on by default
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -4,7 +4,7 @@ warning: trait `IntoIteratorX` is never used
LL | trait IntoIteratorX {
| ^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -7,7 +7,7 @@ LL | trait IntoIterator {
LL | fn into_iter(self) -> Self::Iter;
| ^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -7,7 +7,7 @@ LL | trait Int
LL | fn dummy(&self) { }
| ^^^^^
|
= note: `#[warn(dead_code)]` on by default
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -6,11 +6,7 @@ LL | Box::new(async { x } )
| |
| may outlive borrowed value `x`
|
note: async block is returned here
--> $DIR/async-borrowck-escaping-block-error.rs:6:5
|
LL | Box::new(async { x } )
| ^^^^^^^^^^^^^^^^^^^^^^
= note: async blocks are not executed immediately and must either take a reference or ownership of outside variables they use
help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
LL | Box::new(async move { x } )

View file

@ -0,0 +1,18 @@
//@ run-pass
//@ edition: 2024
fn once<F: FnOnce() -> T, T>(f: F) -> T {
f()
}
fn main() {
let closure = async || {};
// Name of future when called normally.
let name = std::any::type_name_of_val(&closure());
assert_eq!(name, "type_name::main::{{closure}}::{{closure}}");
// Name of future when closure is called via its FnOnce shim.
let name = std::any::type_name_of_val(&once(closure));
assert_eq!(name, "type_name::main::{{closure}}::{{closure}}::{{call_once}}");
}

View file

@ -1,7 +1,9 @@
//@ compile-flags: -Z print-type-sizes --crate-type lib
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
//@ needs-deterministic-layouts
//@ edition:2021
//@ build-pass
//@ ignore-pass
//@ only-x86_64
async fn wait() {}

View file

@ -48,6 +48,39 @@ print-type-size variant `Returned`: 1024 bytes
print-type-size upvar `.arg`: 1024 bytes
print-type-size variant `Panicked`: 1024 bytes
print-type-size upvar `.arg`: 1024 bytes
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
print-type-size field `.waker`: 8 bytes
print-type-size field `.local_waker`: 8 bytes
print-type-size field `.ext`: 16 bytes
print-type-size field `._marker`: 0 bytes
print-type-size field `._marker2`: 0 bytes
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
print-type-size field `.filename`: 16 bytes
print-type-size field `.line`: 4 bytes
print-type-size field `.col`: 4 bytes
print-type-size field `._filename`: 0 bytes
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
print-type-size variant `Some`: 16 bytes
print-type-size field `.0`: 16 bytes
print-type-size variant `None`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
print-type-size field `.0`: 16 bytes
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 16 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
print-type-size field `._vtable_ptr`: 8 bytes
print-type-size field `._phantom`: 0 bytes
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
print-type-size field `.value`: 1 bytes
print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@ -70,3 +103,7 @@ print-type-size discriminant: 1 bytes
print-type-size variant `Unresumed`: 0 bytes
print-type-size variant `Returned`: 0 bytes
print-type-size variant `Panicked`: 0 bytes
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

View file

@ -1,7 +1,9 @@
//@ compile-flags: -Z print-type-sizes --crate-type=lib
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib
//@ needs-deterministic-layouts
//@ edition: 2021
//@ build-pass
//@ ignore-pass
//@ only-x86_64
pub async fn test() {
let _ = a([0u8; 1024]).await;

View file

@ -58,3 +58,45 @@ print-type-size variant `Returned`: 1024 bytes
print-type-size upvar `.t`: 1024 bytes
print-type-size variant `Panicked`: 1024 bytes
print-type-size upvar `.t`: 1024 bytes
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
print-type-size field `.waker`: 8 bytes
print-type-size field `.local_waker`: 8 bytes
print-type-size field `.ext`: 16 bytes
print-type-size field `._marker`: 0 bytes
print-type-size field `._marker2`: 0 bytes
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
print-type-size field `.filename`: 16 bytes
print-type-size field `.line`: 4 bytes
print-type-size field `.col`: 4 bytes
print-type-size field `._filename`: 0 bytes
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
print-type-size variant `Some`: 16 bytes
print-type-size field `.0`: 16 bytes
print-type-size variant `None`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
print-type-size field `.0`: 16 bytes
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 16 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
print-type-size field `._vtable_ptr`: 8 bytes
print-type-size field `._phantom`: 0 bytes
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
print-type-size field `.pointer`: 8 bytes
print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
print-type-size discriminant: 1 bytes
print-type-size variant `Ready`: 0 bytes
print-type-size field `.0`: 0 bytes
print-type-size variant `Pending`: 0 bytes
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

View file

@ -135,7 +135,7 @@ LL | #![doc = in_root!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
error: cannot find macro `in_mod_escape` in the current scope when looking from the crate root
--> $DIR/key-value-expansion-scope.rs:4:10
@ -199,7 +199,7 @@ LL | #![doc = in_root!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: cannot find macro `in_mod_escape` in the current scope when looking from the crate root
@ -211,7 +211,7 @@ LL | #![doc = in_mod_escape!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: cannot find macro `in_mod` in the current scope when looking from module `macros_stay`
@ -223,7 +223,7 @@ LL | #[doc = in_mod!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: cannot find macro `in_mod` in the current scope when looking from module `macros_stay`
@ -235,7 +235,7 @@ LL | #![doc = in_mod!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: cannot find macro `in_mod_escape` in the current scope when looking from module `macros_escape`
@ -247,7 +247,7 @@ LL | #[doc = in_mod_escape!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: cannot find macro `in_mod_escape` in the current scope when looking from module `macros_escape`
@ -259,5 +259,5 @@ LL | #![doc = in_mod_escape!()]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #124535 <https://github.com/rust-lang/rust/issues/124535>
= help: import `macro_rules` with `use` to make it callable above its definition
= note: `#[deny(out_of_scope_macro_calls)]` on by default
= note: `#[deny(out_of_scope_macro_calls)]` (part of `#[deny(future_incompatible)]`) on by default

View file

@ -14,7 +14,7 @@ LL | #![inline = ""]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
error: aborting due to 2 previous errors
@ -27,5 +27,5 @@ LL | #![inline = ""]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default

View file

@ -256,7 +256,7 @@ LL | #[doc]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
--> $DIR/malformed-attrs.rs:76:1
@ -766,7 +766,7 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
LL | #[diagnostic::do_not_recommend()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(malformed_diagnostic_attributes)]` on by default
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: missing options for `on_unimplemented` attribute
--> $DIR/malformed-attrs.rs:138:1
@ -836,7 +836,7 @@ LL | #[doc]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
@ -848,7 +848,7 @@ LL | #[doc]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
@ -860,7 +860,7 @@ LL | #[link]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
@ -871,7 +871,7 @@ LL | #[inline = 5]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]`
@ -882,7 +882,7 @@ LL | #[ignore()]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default
Future breakage diagnostic:
error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]`
@ -893,5 +893,5 @@ LL | #[ignore = 1]
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
= note: `#[deny(ill_formed_attribute_input)]` on by default
= note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default

Some files were not shown because too many files have changed in this diff Show more