Merge pull request #4534 from rust-lang/rustup-2025-08-21
Automatic Rustup
This commit is contained in:
commit
802de3ded1
348 changed files with 2955 additions and 2029 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
®ion_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, ®ioncx, &opt_closure_req, &borrow_set);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
698
compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Normal file
698
compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ pub(crate) enum AttributeKind {
|
|||
Writable = 42,
|
||||
DeadOnUnwind = 43,
|
||||
DeadOnReturn = 44,
|
||||
CapturesReadOnly = 45,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 #
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
9
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
9
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
f605b57042ffeb320d7ae44490113a827139b766
|
||||
125ff8a788c5d6a66917f499abdc00051afe6886
|
||||
|
|
|
|||
23
src/tools/miri/tests/pass/prefetch.rs
Normal file
23
src/tools/miri/tests/pass/prefetch.rs
Normal 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());
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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]],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
18
tests/codegen-llvm/read-only-capture-opt.rs
Normal file
18
tests/codegen-llvm/read-only-capture-opt.rs
Normal 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
|
||||
}
|
||||
|
|
@ -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]+]]
|
||||
|
||||
|
|
|
|||
10
tests/codegen-units/item-collection/async-fn-impl.rs
Normal file
10
tests/codegen-units/item-collection/async-fn-impl.rs
Normal 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
|
||||
}
|
||||
89
tests/codegen-units/item-collection/opaque-return-impls.rs
Normal file
89
tests/codegen-units/item-collection/opaque-return-impls.rs
Normal 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>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 => (),
|
||||
|
|
|
|||
|
|
@ -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 => (),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 } )
|
||||
|
|
|
|||
18
tests/ui/async-await/async-closures/type-name.rs
Normal file
18
tests/ui/async-await/async-closures/type-name.rs
Normal 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}}");
|
||||
}
|
||||
|
|
@ -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() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue