handle opaque types before region inference

This commit is contained in:
lcnr 2025-08-12 11:51:46 +02:00
parent e255a9b28a
commit 8365ad17da
45 changed files with 1098 additions and 1240 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,6 @@
use std::cell::OnceCell;
use std::collections::VecDeque;
use std::rc::Rc;
use rustc_data_structures::binary_search_util;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::scc::{self, Sccs};
@ -24,15 +22,13 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::{DUMMY_SP, Span};
use tracing::{Level, debug, enabled, instrument, trace};
use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
use crate::constraints::graph::NormalConstraintGraph;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
use crate::dataflow::BorrowIndex;
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::polonius::LiveLoans;
use crate::polonius::legacy::PoloniusOutput;
use crate::region_infer::reverse_sccs::ReverseSccGraph;
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
use crate::type_check::Locations;
use crate::type_check::free_region_relations::UniversalRegionRelations;
@ -120,20 +116,6 @@ pub struct RegionInferenceContext<'tcx> {
scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
/// `B: A`. This is used to compute the universal regions that are required
/// to outlive a given SCC.
rev_scc_graph: OnceCell<ReverseSccGraph>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// Records the member constraints that we applied to each scc.
/// This is useful for error reporting. Once constraint
/// propagation is done, this vector is sorted according to
/// `member_region_scc`.
member_constraints_applied: Vec<AppliedMemberConstraint>,
/// Map universe indexes to information on why we created it.
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
@ -150,32 +132,6 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
}
/// Each time that `apply_member_constraint` is successful, it appends
/// one of these structs to the `member_constraints_applied` field.
/// This is used in error reporting to trace out what happened.
///
/// The way that `apply_member_constraint` works is that it effectively
/// adds a new lower bound to the SCC it is analyzing: so you wind up
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Debug)]
pub(crate) struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
/// The vector if `AppliedMemberConstraint` elements is kept sorted
/// by this field.
pub(crate) member_region_scc: ConstraintSccIndex,
/// The "best option" that `apply_member_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
pub(crate) min_choice: ty::RegionVid,
/// The "member constraint index" -- we can find out details about
/// the constraint from
/// `set.member_constraints[member_constraint_index]`.
pub(crate) member_constraint_index: NllMemberConstraintIndex,
}
#[derive(Debug)]
pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
@ -268,7 +224,6 @@ enum Trace<'a, 'tcx> {
StartRegion,
FromGraph(&'a OutlivesConstraint<'tcx>),
FromStatic(RegionVid),
FromMember(RegionVid, RegionVid, Span),
NotVisited,
}
@ -363,7 +318,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
liveness_constraints,
universe_causes,
placeholder_indices,
member_constraints,
} = lowered_constraints;
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
@ -385,9 +339,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
scc_values.merge_liveness(scc, region, &liveness_constraints);
}
let member_constraints =
Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self {
definitions,
liveness_constraints,
@ -395,9 +346,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
constraint_graph,
constraint_sccs,
scc_annotations,
rev_scc_graph: OnceCell::new(),
member_constraints,
member_constraints_applied: Vec::new(),
universe_causes,
scc_values,
type_tests,
@ -550,19 +498,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_values.placeholders_contained_in(scc)
}
/// Once region solving has completed, this function will return the member constraints that
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
pub(crate) fn applied_member_constraints(
&self,
scc: ConstraintSccIndex,
) -> &[AppliedMemberConstraint] {
binary_search_util::binary_search_slice(
&self.member_constraints_applied,
|applied| applied.member_region_scc,
&scc,
)
}
/// Performs region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
@ -607,12 +542,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!(?errors_buffer);
if errors_buffer.is_empty() {
self.check_member_constraints(infcx, &mut errors_buffer);
}
debug!(?errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or_default();
if outlives_requirements.is_empty() {
@ -642,146 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
// To propagate constraints, we walk the DAG induced by the
// SCC. For each SCC, we visit its successors and compute
// SCC. For each SCC `A`, we visit its successors and compute
// their values, then we union all those values to get our
// own.
for scc in self.constraint_sccs.all_sccs() {
self.compute_value_for_scc(scc);
}
// Sort the applied member constraints so we can binary search
// through them later.
self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
}
/// Computes the value of the SCC `scc_a`, which has not yet been
/// computed, by unioning the values of its successors.
/// Assumes that all successors have been computed already
/// (which is assured by iterating over SCCs in dependency order).
#[instrument(skip(self), level = "debug")]
fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
// Walk each SCC `B` such that `A: B`...
for &scc_b in self.constraint_sccs.successors(scc_a) {
debug!(?scc_b);
self.scc_values.add_region(scc_a, scc_b);
}
// Now take member constraints into account.
let member_constraints = Rc::clone(&self.member_constraints);
for m_c_i in member_constraints.indices(scc_a) {
self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
}
debug!(value = ?self.scc_values.region_value_str(scc_a));
}
/// Invoked for each `R0 member of [R1..Rn]` constraint.
///
/// `scc` is the SCC containing R0, and `choice_regions` are the
/// `R1..Rn` regions -- they are always known to be universal
/// regions (and if that's not true, we just don't attempt to
/// enforce the constraint).
///
/// The current value of `scc` at the time the method is invoked
/// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
///
/// This function only adds the member constraints to the region graph,
/// it does not check them. They are later checked in
/// `check_member_constraints` after the region graph has been computed.
#[instrument(skip(self, member_constraint_index), level = "debug")]
fn apply_member_constraint(
&mut self,
scc: ConstraintSccIndex,
member_constraint_index: NllMemberConstraintIndex,
choice_regions: &[ty::RegionVid],
) {
// Create a mutable vector of the options. We'll try to winnow
// them down.
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
// Convert to the SCC representative: sometimes we have inference
// variables in the member constraint that wind up equated with
// universal regions. The scc representative is the minimal numbered
// one from the corresponding scc so it will be the universal region
// if one exists.
for c_r in &mut choice_regions {
let scc = self.constraint_sccs.scc(*c_r);
*c_r = self.scc_representative(scc);
}
// If the member region lives in a higher universe, we currently choose
// the most conservative option by leaving it unchanged.
if !self.max_placeholder_universe_reached(scc).is_root() {
return;
}
// The existing value for `scc` is a lower-bound. This will
// consist of some set `{P} + {LB}` of points `{P}` and
// lower-bound free regions `{LB}`. As each choice region `O`
// is a free region, it will outlive the points. But we can
// only consider the option `O` if `O: LB`.
choice_regions.retain(|&o_r| {
self.scc_values
.universal_regions_outlived_by(scc)
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
});
debug!(?choice_regions, "after lb");
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region `R0` (`UB:
// R0`). Therefore, we need only keep an option `O` if `UB: O`
// for all UB.
let universal_region_relations = &self.universal_region_relations;
for ub in self.reverse_scc_graph().upper_bounds(scc) {
debug!(?ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
debug!(?choice_regions, "after ub");
// At this point we can pick any member of `choice_regions` and would like to choose
// it to be a small as possible. To avoid potential non-determinism we will pick the
// smallest such choice.
//
// Because universal regions are only partially ordered (i.e, not every two regions are
// comparable), we will ignore any region that doesn't compare to all others when picking
// the minimum choice.
//
// For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
// `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
// `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
choice_regions.iter().all(|&r2| {
self.universal_region_relations.outlives(r1, r2)
|| self.universal_region_relations.outlives(r2, r1)
})
});
// Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
match (r1_outlives_r2, r2_outlives_r1) {
(true, true) => r1.min(r2),
(true, false) => r2,
(false, true) => r1,
(false, false) => bug!("incomparable regions in total order"),
for scc_a in self.constraint_sccs.all_sccs() {
// Walk each SCC `B` such that `A: B`...
for &scc_b in self.constraint_sccs.successors(scc_a) {
debug!(?scc_b);
self.scc_values.add_region(scc_a, scc_b);
}
}) else {
debug!("no unique minimum choice");
return;
};
// As we require `'scc: 'min_choice`, we have definitely already computed
// its `scc_values` at this point.
let min_choice_scc = self.constraint_sccs.scc(min_choice);
debug!(?min_choice, ?min_choice_scc);
if self.scc_values.add_region(scc, min_choice_scc) {
self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
min_choice,
member_constraint_index,
});
}
}
@ -1376,13 +1174,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_annotations[scc].max_nameable_universe()
}
pub(crate) fn max_placeholder_universe_reached(
&self,
scc: ConstraintSccIndex,
) -> UniverseIndex {
self.scc_annotations[scc].max_placeholder_universe_reached()
}
/// Checks the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
@ -1551,43 +1342,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
#[instrument(level = "debug", skip(self, infcx, errors_buffer))]
fn check_member_constraints(
&self,
infcx: &InferCtxt<'tcx>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
let member_constraints = Rc::clone(&self.member_constraints);
for m_c_i in member_constraints.all_indices() {
debug!(?m_c_i);
let m_c = &member_constraints[m_c_i];
let member_region_vid = m_c.member_region_vid;
debug!(
?member_region_vid,
value = ?self.region_value_str(member_region_vid),
);
let choice_regions = member_constraints.choice_regions(m_c_i);
debug!(?choice_regions);
// Did the member region wind up equal to any of the option regions?
if let Some(o) =
choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
{
debug!("evaluated as equal to {:?}", o);
continue;
}
// If not, report an error.
let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
span: m_c.definition_span,
hidden_ty: m_c.hidden_ty,
key: m_c.key,
member_region,
});
}
}
/// We have a constraint `fr1: fr2` that is not satisfied, where
/// `fr2` represents some universal region. Here, `r` is some
/// region where we know that `fr1: r` and this function has the
@ -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.
///

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 `'_`
};
}
}

View file

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