handle opaque types before region inference
This commit is contained in:
parent
e255a9b28a
commit
8365ad17da
45 changed files with 1098 additions and 1240 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1711,20 +1465,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));
|
||||
|
|
@ -1771,15 +1511,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
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
|
||||
|
|
@ -2111,11 +1842,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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +1,590 @@
|
|||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
|
||||
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, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
|
||||
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::RegionInferenceContext;
|
||||
use crate::BorrowCheckRootCtxt;
|
||||
use super::reverse_sccs::ReverseSccGraph;
|
||||
use crate::consumers::RegionInferenceContext;
|
||||
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
|
||||
use crate::universal_regions::RegionClassification;
|
||||
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>,
|
||||
},
|
||||
}
|
||||
|
||||
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();
|
||||
/// 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();
|
||||
}
|
||||
|
||||
for (opaque_type_key, concrete_type) in opaque_ty_decls {
|
||||
debug!(?opaque_type_key, ?concrete_type);
|
||||
// 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<_>>();
|
||||
|
||||
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
|
||||
vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
|
||||
debug!(?opaque_types);
|
||||
|
||||
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",
|
||||
)
|
||||
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),
|
||||
});
|
||||
|
||||
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;
|
||||
let guar = tcx.dcx().span_delayed_bug(
|
||||
hidden_type.span,
|
||||
"opaque type with non-universal region args",
|
||||
);
|
||||
ty::OpaqueHiddenType::new_error(tcx, guar)
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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",
|
||||
)
|
||||
});
|
||||
|
||||
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)
|
||||
// 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
|
||||
{
|
||||
errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
|
||||
LifetimeMismatchOpaqueParam {
|
||||
arg: arg1,
|
||||
prev: arg2,
|
||||
span: prev_span,
|
||||
prev_span: concrete_type.span,
|
||||
},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
errors
|
||||
// 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
|
||||
|
|
@ -231,8 +629,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// 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()
|
||||
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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 } )
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ LL | || let y = &*x;
|
|||
LL | || *x += 1;
|
||||
LL | || y
|
||||
LL | || })()
|
||||
| ||______^_- argument requires that borrow lasts for `'1`
|
||||
| ||______^_- opaque type requires that borrow lasts for `'1`
|
||||
| |_______|
|
||||
| creates a temporary value which is freed while still in use
|
||||
LL | }
|
||||
|
|
@ -102,7 +102,7 @@ LL | || let y = &*x;
|
|||
LL | || *x += 1;
|
||||
LL | || y
|
||||
LL | || })()
|
||||
| ||______^_- argument requires that borrow lasts for `'1`
|
||||
| ||______^_- opaque type requires that borrow lasts for `'1`
|
||||
| |_______|
|
||||
| creates a temporary value which is freed while still in use
|
||||
LL | }
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ impl<'a> Future for ListFut<'a> {
|
|||
}
|
||||
|
||||
async fn fut(bufs: &mut [&mut [u8]]) {
|
||||
ListFut(bufs).await
|
||||
//~^ ERROR lifetime may not live long enough
|
||||
ListFut(bufs).await
|
||||
}
|
||||
|
||||
pub struct ListFut2<'a>(&'a mut [&'a mut [u8]]);
|
||||
|
|
@ -31,8 +31,8 @@ impl<'a> Future for ListFut2<'a> {
|
|||
}
|
||||
|
||||
async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
|
||||
ListFut2(bufs).await
|
||||
//~^ ERROR lifetime may not live long enough
|
||||
ListFut2(bufs).await
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
error: lifetime may not live long enough
|
||||
--> $DIR/issue-76547.rs:20:13
|
||||
--> $DIR/issue-76547.rs:19:38
|
||||
|
|
||||
LL | async fn fut(bufs: &mut [&mut [u8]]) {
|
||||
| - - let's call the lifetime of this reference `'2`
|
||||
| |
|
||||
| let's call the lifetime of this reference `'1`
|
||||
LL | ListFut(bufs).await
|
||||
| ^^^^ this usage requires that `'1` must outlive `'2`
|
||||
LL | async fn fut(bufs: &mut [&mut [u8]]) {
|
||||
| ____________________-_____-___________^
|
||||
| | | |
|
||||
| | | let's call the lifetime of this reference `'2`
|
||||
| | let's call the lifetime of this reference `'1`
|
||||
LL | |
|
||||
LL | | ListFut(bufs).await
|
||||
LL | | }
|
||||
| |_^ opaque type requires that `'1` must outlive `'2`
|
||||
|
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
|
|
@ -14,14 +17,17 @@ LL | async fn fut<'a>(bufs: &'a mut [&'a mut [u8]]) {
|
|||
| ++++ ++ ++
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/issue-76547.rs:34:14
|
||||
--> $DIR/issue-76547.rs:33:46
|
||||
|
|
||||
LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
|
||||
| - - let's call the lifetime of this reference `'2`
|
||||
| |
|
||||
| let's call the lifetime of this reference `'1`
|
||||
LL | ListFut2(bufs).await
|
||||
| ^^^^ this usage requires that `'1` must outlive `'2`
|
||||
LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
|
||||
| _____________________-_____-__________________^
|
||||
| | | |
|
||||
| | | let's call the lifetime of this reference `'2`
|
||||
| | let's call the lifetime of this reference `'1`
|
||||
LL | |
|
||||
LL | | ListFut2(bufs).await
|
||||
LL | | }
|
||||
| |_^ opaque type requires that `'1` must outlive `'2`
|
||||
|
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ LL | async fn async_ret_impl_trait3<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trai
|
|||
LL | |
|
||||
LL | | (a, b)
|
||||
LL | | }
|
||||
| |_^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
||||
| |_^ opaque type requires that `'a` must outlive `'b`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'b`
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ struct Point {
|
|||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
fn foo () -> impl FnMut()->() {
|
||||
fn foo () -> impl FnMut() {
|
||||
let mut p = Point {x: 1, y: 2 };
|
||||
let mut c = || {
|
||||
//~^ ERROR closure may outlive the current function, but it borrows `p`
|
||||
p.x+=5;
|
||||
p.x += 5;
|
||||
println!("{:?}", p);
|
||||
//~^ ERROR `p` does not live long enough
|
||||
};
|
||||
c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
error[E0373]: closure may outlive the current function, but it borrows `p`, which is owned by the current function
|
||||
--> $DIR/borrowck-4.rs:10:17
|
||||
error[E0597]: `p` does not live long enough
|
||||
--> $DIR/borrowck-4.rs:12:25
|
||||
|
|
||||
LL | let mut c = || {
|
||||
| ^^ may outlive borrowed value `p`
|
||||
...
|
||||
LL | println!("{:?}", p);
|
||||
| - `p` is borrowed here
|
||||
|
|
||||
note: closure is returned here
|
||||
--> $DIR/borrowck-4.rs:15:5
|
||||
|
|
||||
LL | c
|
||||
| ^
|
||||
help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
LL | let mut c = move || {
|
||||
| ++++
|
||||
LL | let mut p = Point {x: 1, y: 2 };
|
||||
| ----- binding `p` declared here
|
||||
LL | let mut c = || {
|
||||
| --
|
||||
| |
|
||||
| _________________value captured here
|
||||
| |
|
||||
LL | | p.x += 5;
|
||||
LL | | println!("{:?}", p);
|
||||
| | ^ borrowed value does not live long enough
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____- assignment requires that `p` is borrowed for `'static`
|
||||
LL | c
|
||||
LL | }
|
||||
| - `p` dropped here while still borrowed
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0373`.
|
||||
For more information about this error, try `rustc --explain E0597`.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
error[E0373]: closure may outlive the current function, but it borrows `prefix`, which is owned by the current function
|
||||
--> $DIR/does-not-live-long-enough.rs:6:33
|
||||
|
|
||||
LL | fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator<Item=&'a str> {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref())
|
||||
| ^^^ ------ `prefix` is borrowed here
|
||||
| |
|
||||
| may outlive borrowed value `prefix`
|
||||
|
|
||||
note: closure is returned here
|
||||
note: function requires argument type to outlive `'a`
|
||||
--> $DIR/does-not-live-long-enough.rs:6:9
|
||||
|
|
||||
LL | self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//@ check-pass
|
||||
// FIXME(-Znext-solver): enable this test
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// Nested impl-traits can impose different member constraints on the same region variable.
|
||||
|
||||
//@ check-pass
|
||||
// FIXME(-Znext-solver): enable this test
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
trait Cap<'a> {}
|
||||
impl<T> Cap<'_> for T {}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = im
|
|||
// This should resolve.
|
||||
fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
|
||||
//~^ ERROR implementation of `Bar` is not general enough
|
||||
//~| ERROR lifetime may not live long enough
|
||||
|
||||
// This should resolve.
|
||||
fn two_htrb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Qux<'b>> {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error[E0261]: use of undeclared lifetime name `'b`
|
||||
--> $DIR/nested-rpit-hrtb.rs:56:77
|
||||
--> $DIR/nested-rpit-hrtb.rs:57:77
|
||||
|
|
||||
LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
|
||||
| ^^ undeclared lifetime
|
||||
|
|
@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz
|
|||
| ++++
|
||||
|
||||
error[E0261]: use of undeclared lifetime name `'b`
|
||||
--> $DIR/nested-rpit-hrtb.rs:64:82
|
||||
--> $DIR/nested-rpit-hrtb.rs:65:82
|
||||
|
|
||||
LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
|
||||
| ^^ undeclared lifetime
|
||||
|
|
@ -87,6 +87,12 @@ LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc
|
|||
but trait `Qux<'_>` is implemented for `()`
|
||||
= help: for that trait implementation, expected `()`, found `&'a ()`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/nested-rpit-hrtb.rs:49:93
|
||||
|
|
||||
LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
|
||||
| -- lifetime `'b` defined here ^^ opaque type requires that `'b` must outlive `'static`
|
||||
|
||||
error: implementation of `Bar` is not general enough
|
||||
--> $DIR/nested-rpit-hrtb.rs:49:93
|
||||
|
|
||||
|
|
@ -97,7 +103,7 @@ LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc =
|
|||
= note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0`
|
||||
|
||||
error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied
|
||||
--> $DIR/nested-rpit-hrtb.rs:60:64
|
||||
--> $DIR/nested-rpit-hrtb.rs:61:64
|
||||
|
|
||||
LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()`
|
||||
|
|
@ -106,7 +112,7 @@ LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b>
|
|||
but trait `Qux<'_>` is implemented for `()`
|
||||
= help: for that trait implementation, expected `()`, found `&'a ()`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0261, E0277, E0657.
|
||||
For more information about an error, try `rustc --explain E0261`.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//@ check-pass
|
||||
// FIXME(-Znext-solver): enable this test
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
// A regression test for an error in `redis` while working on #139587.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
error: non-defining use of `impl Sized + '_` in the defining scope
|
||||
--> $DIR/as-projection-term.rs:14:19
|
||||
|
|
||||
LL | fn recur<'a>() -> impl Sized + 'a {
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | prove_proj(|| recur());
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@ fn recur<'a>() -> impl Sized + 'a {
|
|||
// inference variable at this point, we unify it with `opaque<'1>` and
|
||||
// end up ignoring that defining use as the hidden type is equal to its key.
|
||||
prove_proj(|| recur());
|
||||
//[next]~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
//[next]~^ ERROR non-defining use of `impl Sized + '_` in the defining scope
|
||||
}
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
|
|||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | None::<&'_ &'_ ()>
|
||||
| ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
||||
| ^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'b`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'b`
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ mod capture_tait {
|
|||
#[define_opaque(Opq2)]
|
||||
fn test() -> Opq2 {}
|
||||
//~^ ERROR hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
|
||||
//~| ERROR expected generic lifetime parameter, found `'a`
|
||||
}
|
||||
|
||||
mod capture_tait_complex_pass {
|
||||
|
|
@ -52,7 +53,8 @@ mod capture_tait_complex_fail {
|
|||
type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
|
||||
#[define_opaque(Opq2)]
|
||||
fn test() -> Opq2 {}
|
||||
//~^ ERROR hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
|
||||
//~^ ERROR expected generic lifetime parameter, found `'a`
|
||||
//~| ERROR expected generic lifetime parameter, found `'a`
|
||||
}
|
||||
|
||||
// non-defining use because 'static is used.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,15 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {}
|
|||
| | opaque type defined here
|
||||
| hidden type `&'a ()` captures the lifetime `'a` as defined here
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:32:23
|
||||
|
|
||||
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | fn test() -> Opq2 {}
|
||||
| ^^
|
||||
|
||||
error[E0700]: hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
|
||||
--> $DIR/higher-ranked-regions-basic.rs:32:23
|
||||
|
|
||||
|
|
@ -28,7 +37,7 @@ LL | fn test() -> Opq2 {}
|
|||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:42:23
|
||||
--> $DIR/higher-ranked-regions-basic.rs:43:23
|
||||
|
|
||||
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -37,7 +46,7 @@ LL | fn test() -> Opq2 {}
|
|||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'b`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:42:23
|
||||
--> $DIR/higher-ranked-regions-basic.rs:43:23
|
||||
|
|
||||
LL | type Opq0<'a> = impl Sized;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -45,19 +54,26 @@ LL | type Opq0<'a> = impl Sized;
|
|||
LL | fn test() -> Opq2 {}
|
||||
| ^^
|
||||
|
||||
error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
|
||||
--> $DIR/higher-ranked-regions-basic.rs:54:23
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:55:23
|
||||
|
|
||||
LL | type Opq0<'a> = impl Sized;
|
||||
| ---------- opaque type defined here
|
||||
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a
|
||||
| -- hidden type `&'b ()` captures the lifetime `'b` as defined here
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | fn test() -> Opq2 {}
|
||||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:63:65
|
||||
--> $DIR/higher-ranked-regions-basic.rs:55:23
|
||||
|
|
||||
LL | type Opq0<'a> = impl Sized;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | fn test() -> Opq2 {}
|
||||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:65:65
|
||||
|
|
||||
LL | type Opq0<'a, 'b> = impl Sized;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -66,7 +82,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {}
|
|||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:72:60
|
||||
--> $DIR/higher-ranked-regions-basic.rs:74:60
|
||||
|
|
||||
LL | type Opq0<'a, 'b> = impl Sized;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -75,7 +91,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {}
|
|||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:82:23
|
||||
--> $DIR/higher-ranked-regions-basic.rs:84:23
|
||||
|
|
||||
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -84,7 +100,7 @@ LL | fn test() -> Opq2 {}
|
|||
| ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'a`
|
||||
--> $DIR/higher-ranked-regions-basic.rs:82:23
|
||||
--> $DIR/higher-ranked-regions-basic.rs:84:23
|
||||
|
|
||||
LL | type Opq0<'a, 'b> = impl Sized;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
|
|
@ -92,7 +108,7 @@ LL | type Opq0<'a, 'b> = impl Sized;
|
|||
LL | fn test() -> Opq2 {}
|
||||
| ^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0700, E0792.
|
||||
For more information about an error, try `rustc --explain E0700`.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ mod bav {
|
|||
impl Bar for i32 {}
|
||||
|
||||
fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
|
||||
val.use_self() //~ ERROR cannot return value referencing function parameter
|
||||
val.use_self() //~ ERROR `val` does not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
error[E0515]: cannot return value referencing function parameter `val`
|
||||
error[E0597]: `val` does not live long enough
|
||||
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:21:9
|
||||
|
|
||||
LL | fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
|
||||
| -- --- binding `val` declared here
|
||||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | val.use_self()
|
||||
| ---^^^^^^^^^^^
|
||||
| ^^^-----------
|
||||
| |
|
||||
| returns a value referencing data owned by the current function
|
||||
| `val` is borrowed here
|
||||
| borrowed value does not live long enough
|
||||
| opaque type requires that `val` is borrowed for `'a`
|
||||
LL | }
|
||||
| - `val` dropped here while still borrowed
|
||||
|
||||
error[E0515]: cannot return value referencing function parameter `val`
|
||||
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
|
||||
|
|
@ -70,5 +76,5 @@ LL | val.use_self()
|
|||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0515, E0521.
|
||||
Some errors have detailed explanations: E0515, E0521, E0597.
|
||||
For more information about an error, try `rustc --explain E0515`.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ LL | |
|
|||
LL | | .filter_map(|_| foo(src))
|
||||
| |_________________________________^
|
||||
|
|
||||
= note: hidden type `FilterMap<std::slice::Iter<'static, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_`
|
||||
= note: hidden type `FilterMap<std::slice::Iter<'_, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ LL | |
|
|||
LL | | current: None,
|
||||
LL | | remaining: self.0.iter(),
|
||||
LL | | }
|
||||
| |_________^ returning this value requires that `'1` must outlive `'static`
|
||||
| |_________^ opaque type requires that `'1` must outlive `'static`
|
||||
|
|
||||
help: to declare that `impl Iterator<Item = Box<(dyn Foo + 'static)>>` captures data from argument `self`, you can add an explicit `'_` lifetime bound
|
||||
|
|
||||
|
|
@ -63,7 +63,7 @@ LL | |
|
|||
LL | | current: None,
|
||||
LL | | remaining: self.0.iter(),
|
||||
LL | | }
|
||||
| |_________^ returning this value requires that `'a` must outlive `'static`
|
||||
| |_________^ opaque type requires that `'a` must outlive `'static`
|
||||
|
|
||||
help: to declare that `impl Iterator<Item = Box<(dyn Foo + 'static)>>` captures data from argument `self`, you can add an explicit `'a` lifetime bound
|
||||
|
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
|
|||
Some(a)
|
||||
} else {
|
||||
None::<Opaque<'static>>
|
||||
//~^ ERROR hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
|
||||
//~^ ERROR expected generic lifetime parameter, found `'static`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
|
||||
error[E0792]: expected generic lifetime parameter, found `'static`
|
||||
--> $DIR/different_args_considered_equal2.rs:10:9
|
||||
|
|
||||
LL | pub type Opaque<'a> = impl Sized;
|
||||
| ---------- opaque type defined here
|
||||
...
|
||||
LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
|
||||
| -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here
|
||||
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
|
||||
...
|
||||
LL | None::<Opaque<'static>>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
error[E0700]: hidden type for `Opaque<'x>` captures lifetime that does not appear in bounds
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/generic-not-strictly-equal.rs:34:5
|
||||
|
|
||||
LL | type Opaque<'a> = impl Copy + Captures<'a>;
|
||||
| ------------------------ opaque type defined here
|
||||
...
|
||||
LL | fn test<'x>(_: Opaque<'x>) {
|
||||
| -- hidden type `&'x u8` captures the lifetime `'x` as defined here
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | relate(opaque, hidden); // defining use: Opaque<'?1> := u8
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ fn test<'x>(_: Opaque<'x>) {
|
|||
|
||||
ensure_outlives::<'x>(opaque); // outlives constraint: '?1: 'x
|
||||
relate(opaque, hidden); // defining use: Opaque<'?1> := u8
|
||||
//[basic]~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
//[member_constraints]~^^ ERROR captures lifetime that does not appear in bounds
|
||||
//~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ impl Foo for () {
|
|||
type Assoc<'a, 'b> = impl Sized;
|
||||
fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
|
||||
let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
|
||||
//~^ ERROR `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
|
||||
//~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
x
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
error[E0700]: hidden type for `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/in-assoc-ty-early-bound.rs:11:60
|
||||
|
|
||||
LL | type Assoc<'a, 'b> = impl Sized;
|
||||
| ---------- opaque type defined here
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
LL | fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
|
||||
| -- hidden type `&'a ()` captures the lifetime `'a` as defined here
|
||||
LL | let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
|
||||
| ^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Foo for () {
|
|||
{
|
||||
let _ = |x: &'a ()| {
|
||||
let _: Self::Assoc<'a> = x;
|
||||
//~^ ERROR `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bound
|
||||
//~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
error[E0700]: hidden type for `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bounds
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/in-assoc-ty-early-bound2.rs:15:20
|
||||
|
|
||||
LL | type Assoc<'a> = impl Sized;
|
||||
| ---------- opaque type defined here
|
||||
LL | fn bar<'a: 'a>()
|
||||
| -- hidden type `&'a ()` captures the lifetime `'a` as defined here
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | let _: Self::Assoc<'a> = x;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
||||
For more information about this error, try `rustc --explain E0792`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue