This commit is contained in:
lcnr 2025-04-13 13:30:37 +02:00 committed by GitHub
commit 39a34dfd32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 1234 additions and 1546 deletions

View File

@ -156,12 +156,6 @@ borrowck_moved_due_to_usage_in_operator =
*[false] operator
}
borrowck_opaque_type_lifetime_mismatch =
opaque type used twice with different lifetimes
.label = lifetime `{$arg}` used here
.prev_lifetime_label = lifetime `{$prev}` previously used here
.note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
borrowck_partial_var_move_by_use_in_closure =
variable {$is_partial ->
[true] partially moved

View File

@ -15,7 +15,7 @@ pub use super::polonius::legacy::{
RichLocation, RustcFacts,
};
pub use super::region_infer::RegionInferenceContext;
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
use crate::BorrowCheckRootCtxt;
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
///
@ -101,6 +101,6 @@ pub fn get_body_with_borrowck_facts(
def_id: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
*do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap()
let root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
*root_cx.borrowck_root(Some(options)).1.unwrap()
}

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

@ -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};
@ -84,9 +83,6 @@ impl<'tcx> RegionErrors<'tcx> {
let guar = self.1.sess.dcx().delayed_bug(format!("{val:?}"));
self.0.push((val, guar));
}
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn into_iter(
self,
) -> impl Iterator<Item = (RegionErrorKind<'tcx>, ErrorGuaranteed)> {
@ -108,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.
@ -312,9 +296,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// 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 } => {
@ -364,30 +345,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

@ -21,9 +21,14 @@ use std::borrow::Cow;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
use std::rc::Rc;
use borrow_set::LocalsStateAtExit;
use polonius_engine::AllFacts;
use region_infer::opaque_types::DeferredOpaqueTypeError;
use root_cx::BorrowCheckRootCtxt;
use rustc_abi::FieldIdx;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::LintDiagnostic;
@ -32,6 +37,7 @@ use rustc_hir::CRATE_HIR_ID;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::{
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
@ -47,14 +53,17 @@ use rustc_mir_dataflow::impls::{
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
use tracing::{debug, instrument};
use type_check::free_region_relations::UniversalRegionRelations;
use type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults};
use crate::borrow_set::{BorrowData, BorrowSet};
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions, RustcFacts};
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
use crate::diagnostics::{
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
@ -62,8 +71,10 @@ use crate::diagnostics::{
use crate::path_utils::*;
use crate::place_ext::PlaceExt;
use crate::places_conflict::{PlaceConflictBias, places_conflict};
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::prefixes::PrefixSet;
use crate::region_infer::RegionInferenceContext;
use crate::renumber::RegionCtxt;
@ -75,7 +86,6 @@ mod constraints;
mod dataflow;
mod def_use;
mod diagnostics;
mod member_constraints;
mod nll;
mod path_utils;
mod place_ext;
@ -126,12 +136,8 @@ fn mir_borrowck(
let opaque_types = ConcreteOpaqueTypes(Default::default());
Ok(tcx.arena.alloc(opaque_types))
} else {
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
do_mir_borrowck(&mut root_cx, def, None).0;
debug_assert!(closure_requirements.is_none());
debug_assert!(used_mut_upvars.is_empty());
root_cx.finalize()
let root_cx = BorrowCheckRootCtxt::new(tcx, def);
root_cx.borrowck_root(None).0
}
}
@ -143,6 +149,8 @@ struct PropagatedBorrowCheckResults<'tcx> {
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
}
type DeferredClosureRequirements<'tcx> = Vec<(LocalDefId, ty::GenericArgsRef<'tcx>, Locations)>;
/// After we borrow check a closure, we are left with various
/// requirements that we have inferred between the free regions that
/// appear in the closure's signature or on its field types. These
@ -281,6 +289,24 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
}
}
struct BorrowckState<'tcx> {
infcx: BorrowckInferCtxt<'tcx>,
body_owned: Body<'tcx>,
promoted: IndexVec<Promoted, Body<'tcx>>,
move_data: MoveData<'tcx>,
borrow_set: BorrowSet<'tcx>,
location_table: PoloniusLocationTable,
location_map: Rc<DenseLocationMap>,
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
constraints: MirTypeckRegionConstraints<'tcx>,
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
polonius_facts: Option<AllFacts<RustcFacts>>,
polonius_context: Option<PoloniusContext>,
}
/// Perform the actual borrow checking.
///
/// Use `consumer_options: None` for the default behavior of returning
@ -289,11 +315,11 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
///
/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
#[instrument(skip(root_cx), level = "debug")]
fn do_mir_borrowck<'tcx>(
fn start_do_mir_borrowck<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
def: LocalDefId,
consumer_options: Option<ConsumerOptions>,
) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
) -> BorrowckState<'tcx> {
let tcx = root_cx.tcx;
let infcx = BorrowckInferCtxt::new(tcx, def);
let (input_body, promoted) = tcx.mir_promoted(def);
@ -304,36 +330,17 @@ fn do_mir_borrowck<'tcx>(
root_cx.set_tainted_by_errors(e);
}
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
for var_debug_info in &input_body.var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
if let Some(local) = place.as_local() {
if let Some(prev_name) = local_names[local]
&& var_debug_info.name != prev_name
{
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local,
prev_name,
var_debug_info.name
);
}
local_names[local] = Some(var_debug_info.name);
}
}
}
// Replace all regions with fresh inference variables. This
// requires first making our own copy of the MIR. This copy will
// be modified (in place) to contain non-lexical lifetimes. It
// will have a lifetime tied to the inference context.
let mut body_owned = input_body.clone();
let mut promoted = input_promoted.to_owned();
let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
let universal_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
let body = &body_owned; // no further changes
let location_table = PoloniusLocationTable::new(body);
let location_map = Rc::new(DenseLocationMap::new(body));
let move_data = MoveData::gather_moves(body, tcx, |_| true);
@ -344,6 +351,80 @@ fn do_mir_borrowck<'tcx>(
let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
|| is_polonius_legacy_enabled;
let mut polonius_facts =
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
universal_region_relations,
region_bound_pairs,
known_type_outlives_obligations,
deferred_closure_requirements,
polonius_context,
} = type_check::type_check(
root_cx,
&infcx,
&body,
&promoted,
universal_regions,
&location_table,
&borrow_set,
&mut polonius_facts,
flow_inits,
&move_data,
Rc::clone(&location_map),
);
BorrowckState {
infcx,
body_owned,
promoted,
move_data,
borrow_set,
location_table,
location_map,
universal_region_relations,
region_bound_pairs,
known_type_outlives_obligations,
constraints,
deferred_closure_requirements,
deferred_opaque_type_errors: Default::default(),
polonius_facts,
polonius_context,
}
}
fn resume_do_mir_borrowck<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
consumer_options: Option<ConsumerOptions>,
BorrowckState {
infcx,
body_owned,
promoted,
move_data,
borrow_set,
location_table,
location_map,
universal_region_relations,
region_bound_pairs: _,
known_type_outlives_obligations: _,
constraints,
deferred_closure_requirements,
deferred_opaque_type_errors,
polonius_facts,
polonius_context,
}: BorrowckState<'tcx>,
) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
assert!(!infcx.has_opaque_types_in_storage());
assert!(deferred_closure_requirements.is_empty());
let tcx = root_cx.tcx;
let body = &body_owned;
let def = body.source.def_id().expect_local();
// Compute non-lexical lifetimes.
let nll::NllOutput {
regioncx,
@ -353,39 +434,43 @@ fn do_mir_borrowck<'tcx>(
nll_errors,
polonius_diagnostics,
} = nll::compute_regions(
root_cx,
&infcx,
free_regions,
body,
&promoted,
&location_table,
flow_inits,
&move_data,
&borrow_set,
location_map,
consumer_options,
universal_region_relations,
constraints,
polonius_facts,
polonius_context,
);
if nll_errors.has_errors().is_none() && !deferred_opaque_type_errors.is_empty() {
regioncx.emit_deferred_opaque_type_errors(root_cx, &infcx, deferred_opaque_type_errors);
}
// 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);
polonius::dump_polonius_mir(
&infcx,
body,
&regioncx,
&opt_closure_req,
&borrow_set,
polonius_diagnostics.as_ref(),
);
// We also have a `#[rustc_regions]` annotation that causes us to dump
// information.
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req);
let movable_coroutine = body.coroutine.is_some()
&& tcx.coroutine_movability(def.to_def_id()) == hir::Movability::Movable;
let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, diags_buffer);
let movable_coroutine =
// The first argument is the coroutine type passed by value
if let Some(local) = body.local_decls.raw.get(1)
// Get the interior types and args which typeck computed
&& let ty::Coroutine(def_id, _) = *local.ty.kind()
&& tcx.coroutine_movability(def_id) == hir::Movability::Movable
{
true
} else {
false
};
// While promoteds should mostly be correct by construction, we need to check them for
// invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`.
for promoted_body in &promoted {
@ -403,7 +488,6 @@ fn do_mir_borrowck<'tcx>(
location_table: &location_table,
movable_coroutine,
fn_self_span_reported: Default::default(),
locals_are_invalidated_at_exit,
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
uninitialized_error_reported: Default::default(),
@ -435,6 +519,26 @@ fn do_mir_borrowck<'tcx>(
promoted_mbcx.report_move_errors();
}
let mut local_names = IndexVec::from_elem(None, &body.local_decls);
for var_debug_info in &body.var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
if let Some(local) = place.as_local() {
if let Some(prev_name) = local_names[local]
&& var_debug_info.name != prev_name
{
span_bug!(
var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local,
prev_name,
var_debug_info.name
);
}
local_names[local] = Some(var_debug_info.name);
}
}
}
let mut mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
@ -442,7 +546,6 @@ fn do_mir_borrowck<'tcx>(
move_data: &move_data,
location_table: &location_table,
movable_coroutine,
locals_are_invalidated_at_exit,
fn_self_span_reported: Default::default(),
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
@ -455,9 +558,9 @@ fn do_mir_borrowck<'tcx>(
local_names,
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output,
move_errors: Vec::new(),
diags_buffer,
polonius_output: polonius_output.as_deref(),
polonius_diagnostics: polonius_diagnostics.as_ref(),
};
@ -474,16 +577,6 @@ fn do_mir_borrowck<'tcx>(
mbcx.report_move_errors();
// If requested, dump polonius MIR.
polonius::dump_polonius_mir(
&infcx,
body,
&regioncx,
&borrow_set,
polonius_diagnostics.as_ref(),
&opt_closure_req,
);
// For each non-user used mutable variable, check if it's been assigned from
// a user-declared local. If so, then put that local into the used_mut set.
// Note that this set is expected to be small - only upvars from closures
@ -514,7 +607,6 @@ fn do_mir_borrowck<'tcx>(
};
let body_with_facts = if consumer_options.is_some() {
let output_facts = mbcx.polonius_output;
Some(Box::new(BodyWithBorrowckFacts {
body: body_owned,
promoted,
@ -522,7 +614,7 @@ fn do_mir_borrowck<'tcx>(
region_inference_context: regioncx,
location_table: polonius_input.as_ref().map(|_| location_table),
input_facts: polonius_input,
output_facts,
output_facts: polonius_output,
}))
} else {
None
@ -582,12 +674,7 @@ pub(crate) struct BorrowckInferCtxt<'tcx> {
impl<'tcx> BorrowckInferCtxt<'tcx> {
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let typing_mode = if tcx.use_typing_mode_borrowck() {
TypingMode::borrowck(tcx, def_id)
} else {
TypingMode::analysis_in_body(tcx, def_id)
};
let infcx = tcx.infer_ctxt().build(typing_mode);
let infcx = tcx.infer_ctxt().build(TypingMode::borrowck(tcx, def_id));
let param_env = tcx.param_env(def_id);
BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
}
@ -655,13 +742,6 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
location_table: &'a PoloniusLocationTable,
movable_coroutine: bool,
/// This keeps track of whether local variables are free-ed when the function
/// exits even without a `StorageDead`, which appears to be the case for
/// constants.
///
/// I'm not sure this is the right approach - @eddyb could you try and
/// figure this out?
locals_are_invalidated_at_exit: bool,
/// This field keeps track of when borrow errors are reported in the access_place function
/// so that there is no duplicate reporting. This field cannot also be used for the conflicting
/// borrow errors that is handled by the `reservation_error_reported` field as the inclusion
@ -709,12 +789,11 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
/// The counter for generating new region names.
next_region_name: RefCell<usize>,
/// Results of Polonius analysis.
polonius_output: Option<Box<PoloniusOutput>>,
diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
move_errors: Vec<MoveError<'tcx>>,
/// Results of Polonius analysis.
polonius_output: Option<&'a PoloniusOutput>,
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
}
@ -938,13 +1017,20 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
| TerminatorKind::Return
| TerminatorKind::TailCall { .. }
| TerminatorKind::CoroutineDrop => {
// Returning from the function implicitly kills storage for all locals and statics.
// Often, the storage will already have been killed by an explicit
// StorageDead, but we don't always emit those (notably on unwind paths),
// so this "extra check" serves as a kind of backup.
for i in state.borrows.iter() {
let borrow = &self.borrow_set[i];
self.check_for_invalidation_at_exit(loc, borrow, span);
match self.borrow_set.locals_state_at_exit() {
LocalsStateAtExit::AllAreInvalidated => {
// Returning from the function implicitly kills storage for all locals and statics.
// Often, the storage will already have been killed by an explicit
// StorageDead, but we don't always emit those (notably on unwind paths),
// so this "extra check" serves as a kind of backup.
for i in state.borrows.iter() {
let borrow = &self.borrow_set[i];
self.check_for_invalidation_at_exit(loc, borrow, span);
}
}
// If we do not implicitly invalidate all locals on exit,
// we check for conflicts when dropping or moving this local.
LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved: _ } => {}
}
}
@ -1716,22 +1802,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
// we'll have a memory leak) and assume that all statics have a destructor.
//
// FIXME: allow thread-locals to borrow other thread locals?
let (might_be_alive, will_be_dropped) =
if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
// Thread-locals might be dropped after the function exits
// We have to dereference the outer reference because
// borrows don't conflict behind shared references.
root_place.projection = TyCtxtConsts::DEREF_PROJECTION;
(true, true)
} else {
(false, self.locals_are_invalidated_at_exit)
};
if !will_be_dropped {
debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
return;
}
let might_be_alive = if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
// Thread-locals might be dropped after the function exits
// We have to dereference the outer reference because
// borrows don't conflict behind shared references.
root_place.projection = TyCtxtConsts::DEREF_PROJECTION;
true
} else {
false
};
let sd = if might_be_alive { Deep } else { Shallow(None) };

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

@ -5,14 +5,13 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use polonius_engine::{Algorithm, Output};
use polonius_engine::{Algorithm, AllFacts, Output};
use rustc_data_structures::frozen::Frozen;
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::ResultsCursor;
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_session::config::MirIncludeSpans;
@ -20,18 +19,19 @@ use rustc_span::sym;
use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
use crate::polonius::PoloniusDiagnosticsContext;
use crate::consumers::{ConsumerOptions, RustcFacts};
use crate::diagnostics::RegionErrors;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::region_infer::RegionInferenceContext;
use crate::type_check::{self, MirTypeckResults};
use crate::type_check::MirTypeckRegionConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::universal_regions::UniversalRegions;
use crate::{
BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
polonius, renumber,
BorrowckInferCtxt, BorrowckState, ClosureOutlivesSubject, ClosureRegionRequirements, polonius,
renumber,
};
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
@ -72,56 +72,47 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
universal_regions
}
/// Computes the (non-lexical) regions from the input MIR.
pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
partial_result: &BorrowckState<'tcx>,
) -> Option<ClosureRegionRequirements<'tcx>> {
let BorrowckState {
infcx,
body_owned,
location_map,
universal_region_relations,
constraints,
..
} = partial_result;
let mut regioncx = RegionInferenceContext::new(
&infcx,
constraints.clone(),
universal_region_relations.clone(),
location_map.clone(),
);
let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, &body_owned, None);
closure_region_requirements
}
/// Computes and checks the region graph for the given constraints.
///
/// This may result in errors being reported.
pub(crate) fn compute_regions<'a, 'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
pub(crate) fn compute_regions<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
location_table: &PoloniusLocationTable,
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
location_map: Rc<DenseLocationMap>,
consumer_options: Option<ConsumerOptions>,
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
constraints: MirTypeckRegionConstraints<'tcx>,
mut polonius_facts: Option<AllFacts<RustcFacts>>,
polonius_context: Option<PoloniusContext>,
) -> NllOutput<'tcx> {
let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
|| is_polonius_legacy_enabled;
let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
|| is_polonius_legacy_enabled;
let mut polonius_facts =
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
let location_map = Rc::new(DenseLocationMap::new(body));
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
polonius_context,
} = type_check::type_check(
root_cx,
infcx,
body,
promoted,
universal_regions,
location_table,
borrow_set,
&mut polonius_facts,
flow_inits,
move_data,
Rc::clone(&location_map),
);
// Create the region inference context, taking ownership of the
// region inference data that was contained in `infcx`, and the
// base constraints generated by the type-check.
let var_infos = infcx.get_region_var_infos();
// If requested, emit legacy polonius facts.
polonius::legacy::emit_facts(
&mut polonius_facts,
@ -134,13 +125,8 @@ pub(crate) fn compute_regions<'a, 'tcx>(
&constraints,
);
let mut regioncx = RegionInferenceContext::new(
infcx,
var_infos,
constraints,
universal_region_relations,
location_map,
);
let mut regioncx =
RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map);
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
// and use them to compute loan liveness.
@ -174,12 +160,9 @@ pub(crate) fn compute_regions<'a, 'tcx>(
regioncx.solve(infcx, body, polonius_output.clone());
if let Some(guar) = nll_errors.has_errors() {
// Suppress unhelpful extra errors in `infer_opaque_types`.
infcx.set_tainted_by_errors(guar);
}
regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
NllOutput {
regioncx,
polonius_input: polonius_facts.map(Box::new),
@ -297,7 +280,6 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
) {
let tcx = infcx.tcx;
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
@ -335,13 +317,11 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
} else {
let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements");
regioncx.annotate(tcx, &mut err);
err
};
// FIXME(@lcnr): We currently don't dump the inferred hidden types here.
diagnostics_buffer.buffer_non_error(err);
err.emit();
}
fn for_each_region_constraint<'tcx>(

View File

@ -24,9 +24,9 @@ pub(crate) fn dump_polonius_mir<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
) {
let tcx = infcx.tcx;
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {

View File

@ -1,7 +1,6 @@
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};
@ -9,7 +8,7 @@ use rustc_errors::Diag;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_index::IndexVec;
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::bug;
use rustc_middle::mir::{
@ -23,14 +22,12 @@ 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::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::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
@ -42,7 +39,7 @@ use crate::{
mod dump_mir;
mod graphviz;
mod opaque_types;
pub(crate) mod opaque_types;
mod reverse_sccs;
pub(crate) mod values;
@ -145,7 +142,7 @@ pub struct RegionInferenceContext<'tcx> {
/// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
pub(crate) definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
/// The liveness constraints added to each region. For most
/// regions, these start out empty and steadily grow, though for
@ -166,20 +163,6 @@ pub struct RegionInferenceContext<'tcx> {
/// compute the values of each region.
constraint_sccs: ConstraintSccs,
/// 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. Computed lazily.
rev_scc_graph: Option<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>>,
@ -196,32 +179,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
@ -314,7 +271,6 @@ enum Trace<'a, 'tcx> {
StartRegion,
FromGraph(&'a OutlivesConstraint<'tcx>),
FromStatic(RegionVid),
FromMember(RegionVid, RegionVid, Span),
NotVisited,
}
@ -385,6 +341,26 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
debug!("SCC edges {:#?}", scc_node_to_edges);
}
fn create_definitions<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
) -> Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>> {
// Create a RegionDefinition for each inference variable.
let mut definitions: IndexVec<_, _> = infcx
.get_region_var_infos()
.iter()
.map(|info| RegionDefinition::new(info.universe, info.origin))
.collect();
// Add the external name for all universal regions.
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
debug!("region {variable:?} has external name {external_name:?}");
definitions[variable].external_name = Some(external_name);
}
Frozen::freeze(definitions)
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of
/// `num_region_variables` valid inference variables; the first N
@ -395,7 +371,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// of constraints produced by the MIR type check.
pub(crate) fn new(
infcx: &BorrowckInferCtxt<'tcx>,
var_infos: VarInfos,
constraints: MirTypeckRegionConstraints<'tcx>,
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
@ -406,7 +381,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
placeholder_index_to_region: _,
liveness_constraints,
mut outlives_constraints,
mut member_constraints,
universe_causes,
type_tests,
} = constraints;
@ -420,17 +394,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
// outlives bounds that we may end up checking.
outlives_constraints = Default::default();
member_constraints = Default::default();
// Also taint the entire scope.
infcx.set_tainted_by_errors(guar);
}
// Create a RegionDefinition for each inference variable.
let definitions: IndexVec<_, _> = var_infos
.iter()
.map(|info| RegionDefinition::new(info.universe, info.origin))
.collect();
let definitions = create_definitions(infcx, &universal_regions);
let constraint_sccs =
outlives_constraints.add_outlives_static(&universal_regions, &definitions);
@ -449,18 +418,12 @@ 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,
constraints,
constraint_graph,
constraint_sccs,
rev_scc_graph: None,
member_constraints,
member_constraints_applied: Vec::new(),
universe_causes,
scc_values,
type_tests,
@ -526,18 +489,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// means that the `R1: !1` constraint here will cause
/// `R1` to become `'static`.
fn init_free_and_bound_regions(&mut self) {
// Update the names (if any)
// This iterator has unstable order but we collect it all into an IndexVec
for (external_name, variable) in
self.universal_region_relations.universal_regions.named_universal_regions_iter()
{
debug!(
"init_free_and_bound_regions: region {:?} has external name {:?}",
variable, external_name
);
self.definitions[variable].external_name = Some(external_name);
}
for variable in self.definitions.indices() {
let scc = self.constraint_sccs.scc(variable);
@ -630,19 +581,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_universe(self.constraint_sccs.scc(r))
}
/// 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.
@ -654,7 +592,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
polonius_output: Option<Box<PoloniusOutput>>,
) -> (Option<ClosureRegionRequirements<'tcx>>, RegionErrors<'tcx>) {
let mir_def_id = body.source.def_id();
self.propagate_constraints();
self.scc_values.propagate_constraints(&self.constraint_sccs);
let mut errors_buffer = RegionErrors::new(infcx.tcx);
@ -687,12 +625,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() {
@ -706,168 +638,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
#[instrument(skip(self), level = "debug")]
fn propagate_constraints(&mut self) {
debug!("constraints={:#?}", {
let mut constraints: Vec<_> = self.outlives_constraints().collect();
constraints.sort_by_key(|c| (c.sup, c.sub));
constraints
.into_iter()
.map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub)))
.collect::<Vec<_>>()
});
// To propagate constraints, we walk the DAG induced by the
// SCC. For each SCC, 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],
) {
// Lazily compute the reverse graph, we'll need it later.
self.compute_reverse_scc_graph();
// 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.constraint_sccs().annotation(scc).min_universe().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.rev_scc_graph.as_ref().unwrap().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"),
}
}) 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,
});
}
}
/// Returns `true` if all the elements in the value of `scc_b` are nameable
/// in `scc_a`. Used during constraint propagation, and only once
/// the value of `scc_b` has been computed.
@ -1106,12 +876,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
#[instrument(level = "debug", skip(self))]
pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
debug!("{}", self.region_value_str(r));
// Find the smallest universal region that contains all other
// universal regions within `region`.
let mut lub = self.universal_regions().fr_fn_body;
let mut lub = self.universal_region_relations.universal_regions.fr_fn_body;
let r_scc = self.constraint_sccs.scc(r);
let static_r = self.universal_regions().fr_static;
let static_r = self.universal_region_relations.universal_regions.fr_static;
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
debug!(?ur, ?lub, ?new_lub);
@ -1123,9 +892,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Prefer the region with an `external_name` - this
// indicates that the region is early-bound, so working with
// it can produce a nicer error.
if self.region_definition(ur).external_name.is_some() {
if self.definitions[ur].external_name.is_some() {
lub = ur;
} else if self.region_definition(lub).external_name.is_some() {
} else if self.definitions[lub].external_name.is_some() {
// Leave lub unchanged
} else {
// If we get here, we don't have any reason to prefer
@ -1652,43 +1421,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!("check_bound_universal_region: all bounds satisfied");
}
#[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
@ -1812,20 +1544,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));
@ -1872,15 +1590,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
@ -2203,11 +1912,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.
///
@ -2240,7 +1944,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
impl<'tcx> RegionDefinition<'tcx> {
fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
pub(crate) fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
// Create a new region definition. Note that, for free
// regions, the `external_name` field gets updated later in
// `init_free_and_bound_regions`.

View File

@ -1,234 +1,523 @@
use rustc_data_structures::fx::FxIndexMap;
use std::rc::Rc;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_index::IndexVec;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_macros::extension;
use rustc_middle::ty::{
self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, fold_regions,
use rustc_middle::bug;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::relate::{
Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys,
};
use rustc_middle::ty::{
self, DefiningScopeKind, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid,
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, fold_regions,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
use rustc_trait_selection::opaque_types::{
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
};
use rustc_span::Span;
use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
use tracing::{debug, instrument};
use super::RegionInferenceContext;
use crate::BorrowCheckRootCtxt;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;
use super::reverse_sccs::ReverseSccGraph;
use super::values::RegionValues;
use super::{ConstraintSccs, RegionDefinition, RegionInferenceContext};
use crate::constraints::ConstraintSccIndex;
use crate::consumers::OutlivesConstraint;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::{RegionClassification, UniversalRegions};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState};
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), ret)]
pub(crate) fn infer_opaque_types(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) {
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
pub(crate) enum DeferredOpaqueTypeError<'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>,
},
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
}
for (opaque_type_key, concrete_type) in opaque_ty_decls {
debug!(?opaque_type_key, ?concrete_type);
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
pub(crate) fn handle_opaque_type_uses(&mut self, borrowck_state: &mut BorrowckState<'tcx>) {
let BorrowckState {
infcx,
constraints,
universal_region_relations,
location_map,
deferred_opaque_type_errors,
..
} = borrowck_state;
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
handle_opaque_type_uses(
self,
infcx,
constraints,
deferred_opaque_type_errors,
universal_region_relations,
Rc::clone(location_map),
)
}
}
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",
)
});
pub(crate) fn handle_opaque_type_uses<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
deferred_errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
) {
let opaque_types = infcx.take_opaque_types();
if opaque_types.is_empty() {
return;
}
arg_regions.push((vid, named));
named
});
debug!(?opaque_type_key, ?arg_regions);
let tcx = infcx.tcx;
// 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.
let mut 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 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 mut definitions: IndexVec<_, _> = infcx
.get_region_var_infos()
.iter()
.map(|info| RegionDefinition::new(info.universe, info.origin))
.collect();
let ty =
infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
// Update the names (if any)
// This iterator has unstable order but we collect it all into an IndexVec
for (external_name, variable) in
universal_region_relations.universal_regions.named_universal_regions_iter()
{
definitions[variable].external_name = Some(external_name);
}
// 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
let universal_regions = &universal_region_relations.universal_regions;
let fr_static = universal_regions.fr_static;
let constraint_sccs = &constraints.outlives_constraints.compute_sccs(fr_static, &definitions);
let rev_scc_graph = &ReverseSccGraph::compute(&constraint_sccs, universal_regions);
// 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);
}
_ => {}
}
}
scc_values.propagate_constraints(&constraint_sccs);
for entry in &mut opaque_types {
// Map all opaque types to their SCC representatives.
*entry = fold_regions(tcx, *entry, |r, _| {
let scc = constraint_sccs.scc(r.as_var());
let vid = constraint_sccs.annotation(scc).representative;
Region::new_var(tcx, vid)
})
}
// We start by looking for defining uses of the opaque. These are uses where all arguments
// of the opaque are free regions. We apply "member constraints" to its hidden region and
// map the hidden type to the definition site of the opaque.
'entry: for &(opaque_type_key, hidden_type) in &opaque_types {
// Check whether the arguments are fully universal.
//
// FIXME: We currently treat `Opaque<'a, 'a>` as a defining use and then emit an error
// as it's not fully universal. We should share this code with `check_opaque_type_parameter_valid`
// to only consider actual defining uses as defining.
let mut arg_regions = vec![(universal_regions.fr_static, tcx.lifetimes.re_static)];
for (_idx, captured_arg) in opaque_type_key.iter_captured_args(tcx) {
if let Some(region) = captured_arg.as_region() {
let vid = region.as_var();
if matches!(definitions[vid].origin, NllRegionVariableOrigin::FreeRegion)
&& !matches!(
universal_regions.region_classification(vid),
Some(RegionClassification::External)
)
{
continue;
arg_regions.push((vid, definitions[vid].external_name.unwrap()));
} else {
continue 'entry;
}
}
}
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { span: concrete_type.span, ty },
);
debug!(?opaque_type_key, ?hidden_type, "check defining use");
// 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 !ty.references_error()
&& 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 opaque_type_key = opaque_type_key.fold_captured_lifetime_args(tcx, |region| {
let vid = region.as_var();
assert!(matches!(definitions[vid].origin, NllRegionVariableOrigin::FreeRegion));
definitions[vid].external_name.unwrap()
});
let hidden_type = hidden_type.fold_with(&mut OpaqueHiddenTypeFolder {
infcx,
opaque_type_key,
hidden_type,
deferred_errors,
arg_regions: &arg_regions,
universal_region_relations,
constraint_sccs,
rev_scc_graph,
scc_values: &scc_values,
});
let ty = infcx
.infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type)
.unwrap_or_else(|err| {
deferred_errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
Ty::new_error_with_message(
tcx,
hidden_type.span,
"deferred invalid opaque type args",
)
&& 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)
{
infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
arg: arg1,
prev: arg2,
span: prev_span,
prev_span: concrete_type.span,
});
});
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { span: hidden_type.span, ty },
);
}
for &(key, hidden_type) in &opaque_types {
let Some(expected) = root_cx.get_concrete_opaque_type(key.def_id) else {
let guar =
tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope");
root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
infcx.set_tainted_by_errors(guar);
continue;
};
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 { from_forall: false },
|| crate::RegionCtxt::Existential(None),
),
_ => re,
}
});
let mut relation =
EquateRegions { infcx, span: hidden_type.span, universal_regions, constraints };
match TypeRelation::relate(&mut relation, hidden_type.ty, expected.ty) {
Ok(_) => {}
Err(_) => {
let _ = hidden_type.build_mismatch_error(&expected, tcx).map(|d| d.emit());
}
}
// normalize -> equate
//
// Do we need to normalize?
// Only to support non-universal type or const args. It feels likely that we need (and want) to do so
// This once again needs to be careful about cycles: normalizing and equating while defining/revealing
// opaque types may end up introducing new defining uses.
// How can we equate here?
// If we need to normalize the answer is just "it's TypeChecker time"
// without it we could manually walk over the types using a type relation and equate region vars
// that way.
}
}
fn to_region_vid<'tcx>(
constraints: &MirTypeckRegionConstraints<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
r: Region<'tcx>,
) -> RegionVid {
if let ty::RePlaceholder(placeholder) = r.kind() {
constraints.get_placeholder_region(placeholder).as_var()
} else {
universal_regions.to_region_vid(r)
}
}
struct OpaqueHiddenTypeFolder<'a, 'tcx> {
infcx: &'a BorrowckInferCtxt<'tcx>,
// For diagnostics.
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
deferred_errors: &'a mut Vec<DeferredOpaqueTypeError<'tcx>>,
arg_regions: &'a [(RegionVid, Region<'tcx>)],
universal_region_relations: &'a UniversalRegionRelations<'tcx>,
constraint_sccs: &'a ConstraintSccs,
rev_scc_graph: &'a ReverseSccGraph,
scc_values: &'a RegionValues<ConstraintSccIndex>,
}
impl<'tcx> OpaqueHiddenTypeFolder<'_, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn apply_member_constraint(&mut self, member_vid: RegionVid) -> Option<Region<'tcx>> {
let member = self.constraint_sccs.scc(member_vid);
if let Some((_, reg)) = self.arg_regions.iter().copied().find(|&(vid, _)| vid == member_vid)
{
debug!("member equal to arg");
return Some(reg);
}
// If the member region lives in a higher universe, we currently choose
// the most conservative option by leaving it unchanged.
if !self.constraint_sccs.annotation(member).min_universe().is_root() {
debug!("member not in root universe");
return None;
}
// 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 = self
.arg_regions
.iter()
.copied()
.filter(|&(choice_region, _)| {
self.scc_values.universal_regions_outlived_by(member).all(|lower_bound| {
self.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 self.rev_scc_graph.upper_bounds(member) {
choice_regions.retain(|&(choice_region, _)| {
self.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, _)| {
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, r1_reg), (r2, r2_reg)| {
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) => {
if r1 < r2 {
(r1, r1_reg)
} else {
(r2, r2_reg)
}
}
(true, false) => (r2, r2_reg),
(false, true) => (r1, r1_reg),
(false, false) => bug!("incomparable regions in total order"),
}
}) else {
debug!("no unique minimum choice");
return None;
};
debug!(?min_choice);
Some(min_choice)
}
fn fold_closure_args(
&mut self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> GenericArgsRef<'tcx> {
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 {
self.cx().erase_regions(arg)
} else {
arg.fold_with(self)
}
}))
}
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueHiddenTypeFolder<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
match r.kind() {
// ignore bound regions, keep visiting
ty::ReBound(_, _) => r,
_ => self.apply_member_constraint(r.as_var()).unwrap_or_else(|| {
self.deferred_errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
hidden_type: self.hidden_type,
opaque_type_key: self.opaque_type_key,
member_region: r,
});
ty::Region::new_error_with_message(
self.cx(),
self.hidden_type.span,
"opaque type with non-universal region args",
)
}),
}
}
/// Map the regions in the type to named regions. This is similar to what
/// `infer_opaque_types` does, but can infer any universal region, not only
/// ones from the args for the opaque type. It also doesn't double check
/// that the regions produced are in fact equal to the named region they are
/// replaced with. This is fine because this function is only to improve the
/// region names in error messages.
///
/// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
/// lax with mapping region vids that are *shorter* than a universal region to
/// that universal region. This is useful for member region constraints since
/// we want to suggest a universal region name to capture even if it's technically
/// not equal to the error region.
pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return ty;
}
// Special handling of higher-ranked regions.
if !self.scc_universe(scc).is_root() {
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
// If the region contains a single placeholder then they're equal.
Some((0, placeholder)) => {
return ty::Region::new_placeholder(tcx, placeholder);
}
// Fallback: this will produce a cryptic error message.
_ => return region,
}
}
// Find something that we can name
let upper_bound = self.approx_universal_upper_bound(vid);
if let Some(universal_region) = self.definitions[upper_bound].external_name {
return universal_region;
}
// Nothing exact found, so we pick a named upper bound, if there's only one.
// If there's >1 universal region, then we probably are dealing w/ an intersection
// region which cannot be mapped back to a universal.
// FIXME: We could probably compute the LUB if there is one.
let scc = self.constraint_sccs.scc(vid);
let upper_bounds: Vec<_> = self
.rev_scc_graph
.as_ref()
.unwrap()
.upper_bounds(scc)
.filter_map(|vid| self.definitions[vid].external_name)
.filter(|r| !r.is_static())
.collect();
match &upper_bounds[..] {
[universal_region] => *universal_region,
_ => region,
}
let tcx = self.cx();
match *ty.kind() {
ty::Closure(def_id, args) => {
Ty::new_closure(tcx, def_id, self.fold_closure_args(def_id, args))
}
_ => region,
})
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) =>
{
// 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).
let args =
tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map(|(&v, s)| {
if v == ty::Bivariant { tcx.erase_regions(s) } else { s.fold_with(self) }
}));
ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx)
}
_ => ty.super_fold_with(self),
}
}
}
// FUCKING SHIT NO!wegrdtfhergfsg
struct EquateRegions<'a, 'tcx> {
infcx: &'a BorrowckInferCtxt<'tcx>,
span: Span,
universal_regions: &'a UniversalRegions<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
}
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for EquateRegions<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
_variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
self.relate(a, b)
}
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
structurally_relate_tys(self, a, b)
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
if matches!(a.kind(), ty::ReBound(..)) || matches!(b.kind(), ty::ReBound(..)) {
assert_eq!(a, b);
return Ok(a);
}
let a_vid = to_region_vid(self.constraints, self.universal_regions, a);
let b_vid = to_region_vid(self.constraints, self.universal_regions, b);
let locations = Locations::All(self.span);
self.constraints.outlives_constraints.push(OutlivesConstraint {
sup: a_vid,
sub: b_vid,
locations,
span: self.span,
category: ConstraintCategory::OpaqueType,
variance_info: ty::VarianceDiagInfo::None,
from_closure: false,
});
self.constraints.outlives_constraints.push(OutlivesConstraint {
sup: b_vid,
sub: a_vid,
locations,
span: self.span,
category: ConstraintCategory::OpaqueType,
variance_info: ty::VarianceDiagInfo::None,
from_closure: false,
});
Ok(a)
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
structurally_relate_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
self.relate(a.skip_binder(), b.skip_binder())?;
Ok(a)
}
}
@ -262,19 +551,16 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Ty<'tcx> {
if let Some(e) = self.tainted_by_errors() {
return Ty::new_error(self.tcx, e);
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
if let Some(guar) = self.tainted_by_errors() {
return Err(guar.into());
}
if let Err(guar) = check_opaque_type_parameter_valid(
check_opaque_type_parameter_valid(
self,
opaque_type_key,
instantiated_ty.span,
DefiningScopeKind::MirBorrowck,
) {
return Ty::new_error(self.tcx, guar);
}
)?;
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(
@ -284,10 +570,131 @@ impl<'tcx> InferCtxt<'tcx> {
)
.ty;
if let Err(e) = definition_ty.error_reported() {
return Ty::new_error(self.tcx, e);
}
definition_ty
definition_ty.error_reported()?;
Ok(definition_ty)
}
}
impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn emit_deferred_opaque_type_errors(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
errors: Vec<DeferredOpaqueTypeError<'tcx>>,
) {
let mut prev_hidden_region_errors = FxHashMap::default();
let mut guar = None;
for error in errors {
guar = Some(match error {
DeferredOpaqueTypeError::UnexpectedHiddenRegion {
opaque_type_key,
hidden_type,
member_region,
} => self.report_unexpected_hidden_region_errors(
root_cx,
infcx,
&mut prev_hidden_region_errors,
opaque_type_key,
hidden_type,
member_region,
),
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
});
}
let guar = guar.unwrap();
root_cx.set_tainted_by_errors(guar);
infcx.set_tainted_by_errors(guar);
}
fn report_unexpected_hidden_region_errors(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
prev_errors: &mut FxHashMap<(Span, Ty<'tcx>, OpaqueTypeKey<'tcx>), ErrorGuaranteed>,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
member_region: Region<'tcx>,
) -> ErrorGuaranteed {
let tcx = infcx.tcx;
let named_ty = self.name_regions_for_member_constraint(tcx, hidden_type.ty);
let named_key = self.name_regions_for_member_constraint(tcx, opaque_type_key);
let named_region = self.name_regions_for_member_constraint(tcx, member_region);
*prev_errors.entry((hidden_type.span, named_ty, named_key)).or_insert_with(|| {
let guar = unexpected_hidden_region_diagnostic(
infcx,
root_cx.root_def_id(),
hidden_type.span,
named_ty,
named_region,
named_key,
)
.emit();
root_cx
.add_concrete_opaque_type(named_key.def_id, OpaqueHiddenType::new_error(tcx, guar));
guar
})
}
/// Map the regions in the type to named regions. This is similar to what
/// `infer_opaque_types` does, but can infer any universal region, not only
/// ones from the args for the opaque type. It also doesn't double check
/// that the regions produced are in fact equal to the named region they are
/// replaced with. This is fine because this function is only to improve the
/// region names in error messages.
///
/// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
/// lax with mapping region vids that are *shorter* than a universal region to
/// that universal region. This is useful for member region constraints since
/// we want to suggest a universal region name to capture even if it's technically
/// not equal to the error region.
fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);
// Special handling of higher-ranked regions.
if !self.constraint_sccs.annotation(scc).min_universe().is_root() {
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
// If the region contains a single placeholder then they're equal.
Some((0, placeholder)) => {
return ty::Region::new_placeholder(tcx, placeholder);
}
// Fallback: this will produce a cryptic error message.
_ => return region,
}
}
// Find something that we can name
let upper_bound = self.approx_universal_upper_bound(vid);
if let Some(universal_region) = self.definitions[upper_bound].external_name {
return universal_region;
}
// Nothing exact found, so we pick a named upper bound, if there's only one.
// If there's >1 universal region, then we probably are dealing w/ an intersection
// region which cannot be mapped back to a universal.
// FIXME: We could probably compute the LUB if there is one.
let scc = self.constraint_sccs.scc(vid);
let rev_scc_graph = &ReverseSccGraph::compute(
&self.constraint_sccs,
&self.universal_region_relations.universal_regions,
);
let upper_bounds: Vec<_> = rev_scc_graph
.upper_bounds(scc)
.filter_map(|vid| self.definitions[vid].external_name)
.filter(|r| !r.is_static())
.collect();
match &upper_bounds[..] {
[universal_region] => *universal_region,
_ => region,
}
}
_ => region,
})
}
}

View File

@ -5,8 +5,9 @@ use rustc_data_structures::graph;
use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_middle::ty::RegionVid;
use crate::RegionInferenceContext;
use super::ConstraintSccs;
use crate::constraints::ConstraintSccIndex;
use crate::universal_regions::UniversalRegions;
pub(crate) struct ReverseSccGraph {
graph: VecGraph<ConstraintSccIndex>,
@ -19,6 +20,28 @@ pub(crate) struct ReverseSccGraph {
}
impl ReverseSccGraph {
pub(super) fn compute(
constraint_sccs: &ConstraintSccs,
universal_regions: &UniversalRegions<'_>,
) -> Self {
let graph = constraint_sccs.reverse();
let mut paired_scc_regions = universal_regions
.universal_regions_iter()
.map(|region| (constraint_sccs.scc(region), region))
.collect::<Vec<_>>();
paired_scc_regions.sort();
let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect();
let mut scc_regions = FxIndexMap::default();
let mut start = 0;
for chunk in paired_scc_regions.chunk_by(|&(scc1, _), &(scc2, _)| scc1 == scc2) {
let (scc, _) = chunk[0];
scc_regions.insert(scc, start..start + chunk.len());
start += chunk.len();
}
ReverseSccGraph { graph, scc_regions, universal_regions }
}
/// Find all universal regions that are required to outlive the given SCC.
pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator<Item = RegionVid> {
let mut duplicates = FxIndexSet::default();
@ -32,31 +55,3 @@ impl ReverseSccGraph {
.filter(move |r| duplicates.insert(*r))
}
}
impl RegionInferenceContext<'_> {
/// Compute the reverse SCC-based constraint graph (lazily).
pub(super) fn compute_reverse_scc_graph(&mut self) {
if self.rev_scc_graph.is_some() {
return;
}
let graph = self.constraint_sccs.reverse();
let mut paired_scc_regions = self
.universal_regions()
.universal_regions_iter()
.map(|region| (self.constraint_sccs.scc(region), region))
.collect::<Vec<_>>();
paired_scc_regions.sort();
let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect();
let mut scc_regions = FxIndexMap::default();
let mut start = 0;
for chunk in paired_scc_regions.chunk_by(|&(scc1, _), &(scc2, _)| scc1 == scc2) {
let (scc, _) = chunk[0];
scc_regions.insert(scc, start..start + chunk.len());
start += chunk.len();
}
self.rev_scc_graph = Some(ReverseSccGraph { graph, scc_regions, universal_regions });
}
}

View File

@ -8,9 +8,11 @@ use rustc_index::interval::{IntervalSet, SparseIntervalMatrix};
use rustc_middle::mir::{BasicBlock, Location};
use rustc_middle::ty::{self, RegionVid};
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use tracing::debug;
use tracing::{debug, instrument};
use super::ConstraintSccs;
use crate::BorrowIndex;
use crate::constraints::ConstraintSccIndex;
use crate::polonius::LiveLoans;
rustc_index::newtype_index! {
@ -37,6 +39,7 @@ pub(crate) enum RegionElement {
/// Records the CFG locations where each region is live. When we initially compute liveness, we use
/// an interval matrix storing liveness ranges for each region-vid.
#[derive(Clone)]
pub(crate) struct LivenessValues {
/// The map from locations to points.
location_map: Rc<DenseLocationMap>,
@ -193,7 +196,7 @@ impl LivenessValues {
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of
/// rustc to the internal `PlaceholderIndex` values that are used in
/// NLL.
#[derive(Debug, Default)]
#[derive(Clone, Debug, Default)]
pub(crate) struct PlaceholderIndices {
indices: FxIndexSet<ty::PlaceholderRegion>,
}
@ -424,6 +427,40 @@ impl ToElementIndex for ty::PlaceholderRegion {
}
}
impl RegionValues<ConstraintSccIndex> {
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
#[instrument(skip(self, constraint_sccs), level = "debug")]
pub(super) fn propagate_constraints(&mut self, constraint_sccs: &ConstraintSccs) {
// To propagate constraints, we walk the DAG induced by the
// SCC. For each SCC, we visit its successors and compute
// their values, then we union all those values to get our
// own.
for scc in constraint_sccs.all_sccs() {
self.compute_value_for_scc(constraint_sccs, 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, constraint_sccs), level = "debug")]
fn compute_value_for_scc(
&mut self,
constraint_sccs: &ConstraintSccs,
scc_a: ConstraintSccIndex,
) {
// Walk each SCC `B` such that `A: B`...
for &scc_b in constraint_sccs.successors(scc_a) {
debug!(?scc_b);
self.add_region(scc_a, scc_b);
}
}
}
/// For debugging purposes, returns a pretty-printed string of the given points.
pub(crate) fn pretty_print_points(
location_map: &DenseLocationMap,

View File

@ -2,11 +2,17 @@ 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;
use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults};
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use crate::nll::compute_closure_requirements_modulo_opaques;
use crate::type_check::apply_closure_requirements_considering_opaques;
use crate::{
BorrowckState, ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults,
resume_do_mir_borrowck, start_do_mir_borrowck,
};
/// The shared context used by both the root as well as all its nested
/// items.
@ -14,7 +20,10 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
root_def_id: LocalDefId,
concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
partial_results: FxHashMap<LocalDefId, Option<BorrowckState<'tcx>>>,
closure_requirements_modulo_opaques:
FxHashMap<LocalDefId, Option<ClosureRegionRequirements<'tcx>>>,
final_results: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
tainted_by_errors: Option<ErrorGuaranteed>,
}
@ -24,11 +33,17 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
tcx,
root_def_id,
concrete_opaque_types: Default::default(),
nested_bodies: Default::default(),
partial_results: Default::default(),
closure_requirements_modulo_opaques: Default::default(),
final_results: Default::default(),
tainted_by_errors: None,
}
}
pub(super) fn root_def_id(&self) -> LocalDefId {
self.root_def_id
}
/// Collect all defining uses of opaque types inside of this typeck root. This
/// expects the hidden type to be mapped to the definition parameters of the opaque
/// and errors if we end up with distinct hidden types.
@ -58,44 +73,132 @@ 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);
}
fn get_or_insert_nested(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> {
fn compute_partial_results(&mut self, def_id: LocalDefId) {
debug_assert_eq!(
self.tcx.typeck_root_def_id(def_id.to_def_id()),
self.root_def_id.to_def_id()
);
if !self.nested_bodies.contains_key(&def_id) {
let result = super::do_mir_borrowck(self, def_id, None).0;
if let Some(prev) = self.nested_bodies.insert(def_id, result) {
if !self.partial_results.contains_key(&def_id) {
let result = start_do_mir_borrowck(self, def_id, None);
// We only need to store the partial result if it depends on opaque types
// or depends on a nested item which does.
let relies_on_opaques = result.infcx.has_opaque_types_in_storage()
|| !result.deferred_closure_requirements.is_empty();
let to_insert = if !relies_on_opaques {
let final_result = resume_do_mir_borrowck(self, None, result).0;
if self.final_results.insert(def_id, final_result).is_some() {
bug!("unexpected previous final result for {def_id:?}");
}
None
} else {
Some(result)
};
if self.partial_results.insert(def_id, to_insert).is_some() {
bug!("unexpected previous partial result for: {def_id:?}")
}
}
}
fn get_or_insert_final(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> {
debug_assert_eq!(
self.tcx.typeck_root_def_id(def_id.to_def_id()),
self.root_def_id.to_def_id()
);
if !self.final_results.contains_key(&def_id) {
let mut yield_do_mir_borrowck = self.partial_results.remove(&def_id).unwrap().unwrap();
self.handle_opaque_type_uses(&mut yield_do_mir_borrowck);
apply_closure_requirements_considering_opaques(self, &mut yield_do_mir_borrowck);
let result = resume_do_mir_borrowck(self, None, yield_do_mir_borrowck).0;
if let Some(prev) = self.final_results.insert(def_id, result) {
bug!("unexpected previous nested body: {prev:?}");
}
}
self.nested_bodies.get(&def_id).unwrap()
self.final_results.get(&def_id).unwrap()
}
pub(super) fn closure_requirements(
pub(crate) fn get_closure_requirements_considering_regions(
&mut self,
nested_body_def_id: LocalDefId,
def_id: LocalDefId,
) -> &Option<ClosureRegionRequirements<'tcx>> {
&self.get_or_insert_nested(nested_body_def_id).closure_requirements
&self.get_or_insert_final(def_id).closure_requirements
}
/// Get the closure requirements of the nested body `def_id`. In case
/// this nested body relies on opaques, checking that the hidden type
/// matches the final definition may introduce new region constraints
/// we aren't considering yet.
///
/// In these cases, we need to later add these requirements again, including
/// the constraints from opaque types.
pub(crate) fn get_closure_requirements_modulo_opaques(
&mut self,
def_id: LocalDefId,
) -> (&Option<ClosureRegionRequirements<'tcx>>, bool) {
self.compute_partial_results(def_id);
// In case the nested item does not use any opaque types or depend on nested items
// which do, we eagerly compute its final result to avoid duplicate work.
if let Some(final_result) = self.final_results.get(&def_id) {
(&final_result.closure_requirements, false)
} else if self.closure_requirements_modulo_opaques.contains_key(&def_id) {
(self.closure_requirements_modulo_opaques.get(&def_id).unwrap(), true)
} else {
let partial_result = self.partial_results.get(&def_id).unwrap().as_ref().unwrap();
let modulo_opaques = compute_closure_requirements_modulo_opaques(partial_result);
self.closure_requirements_modulo_opaques.insert(def_id, modulo_opaques);
(self.closure_requirements_modulo_opaques.get(&def_id).unwrap(), true)
}
}
pub(super) fn used_mut_upvars(
&mut self,
nested_body_def_id: LocalDefId,
) -> &SmallVec<[FieldIdx; 8]> {
&self.get_or_insert_nested(nested_body_def_id).used_mut_upvars
&self.get_or_insert_final(nested_body_def_id).used_mut_upvars
}
pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
if let Some(guar) = self.tainted_by_errors {
/// The actual borrowck routine. This should only be called for the typeck root,
/// not for any nested bodies.
pub(super) fn borrowck_root(
mut self,
consumer_options: Option<ConsumerOptions>,
) -> (
Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed>,
Option<Box<BodyWithBorrowckFacts<'tcx>>>,
) {
let root_def_id = self.root_def_id;
let mut yield_do_mir_borrowck =
start_do_mir_borrowck(&mut self, root_def_id, consumer_options);
self.handle_opaque_type_uses(&mut yield_do_mir_borrowck);
apply_closure_requirements_considering_opaques(&mut self, &mut yield_do_mir_borrowck);
let (PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars }, consumer_data) =
resume_do_mir_borrowck(&mut self, consumer_options, yield_do_mir_borrowck);
#[allow(rustc::potential_query_instability)]
if cfg!(debug_assertions) {
assert!(closure_requirements.is_none());
assert!(used_mut_upvars.is_empty());
assert!(self.partial_results.values().all(|entry| entry.is_none()));
}
let result = if let Some(guar) = self.tainted_by_errors {
Err(guar)
} else {
Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
}
Ok(&*self.tcx.arena.alloc(self.concrete_opaque_types))
};
(result, consumer_data)
}
}

View File

@ -1,7 +1,7 @@
use rustc_errors::MultiSpan;
use rustc_errors::codes::*;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{GenericArg, Ty};
use rustc_middle::ty::Ty;
use rustc_span::Span;
use crate::diagnostics::RegionName;
@ -294,19 +294,6 @@ pub(crate) struct MoveBorrow<'a> {
pub borrow_span: Span,
}
#[derive(Diagnostic)]
#[diag(borrowck_opaque_type_lifetime_mismatch)]
pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> {
pub arg: GenericArg<'tcx>,
pub prev: GenericArg<'tcx>,
#[primary_span]
#[label]
#[note]
pub span: Span,
#[label(borrowck_prev_lifetime_label)]
pub prev_span: Span,
}
#[derive(Subdiagnostic)]
pub(crate) enum CaptureReasonLabel<'a> {
#[label(borrowck_moved_due_to_call)]

View File

@ -21,7 +21,6 @@ use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategor
pub(crate) struct ConstraintConversion<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
tcx: TyCtxt<'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
/// Each RBP `GK: 'a` is assumed to be true. These encode
/// relationships like `T: 'a` that are added via implicit bounds
@ -34,7 +33,6 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
/// logic expecting to see (e.g.) `ReStatic`, and if we supplied
/// our special inference variable there, we would mess that up.
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations,
@ -49,7 +47,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations,
@ -59,10 +56,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
) -> Self {
Self {
infcx,
tcx: infcx.tcx,
universal_regions,
region_bound_pairs,
implicit_region_bound,
param_env,
known_type_outlives_obligations,
locations,
@ -96,7 +91,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// into a vector. These are the regions that we will be
// relating to one another.
let closure_mapping = &UniversalRegions::closure_mapping(
self.tcx,
self.infcx.tcx,
closure_args,
closure_requirements.num_external_vids,
closure_def_id,
@ -111,7 +106,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
let subject = match outlives_requirement.subject {
ClosureOutlivesSubject::Region(re) => closure_mapping[re].into(),
ClosureOutlivesSubject::Ty(subject_ty) => {
subject_ty.instantiate(self.tcx, |vid| closure_mapping[vid]).into()
subject_ty.instantiate(self.infcx.tcx, |vid| closure_mapping[vid]).into()
}
};
@ -127,14 +122,14 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
constraint_category: ConstraintCategory<'tcx>,
) {
let tcx = self.infcx.tcx;
debug!("generate: constraints at: {:#?}", self.locations);
// Extract out various useful fields we'll need below.
let ConstraintConversion {
tcx,
infcx,
universal_regions,
region_bound_pairs,
implicit_region_bound,
known_type_outlives_obligations,
..
} = *self;
@ -145,7 +140,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
break;
}
if !self.tcx.recursion_limit().value_within_limit(iteration) {
if !tcx.recursion_limit().value_within_limit(iteration) {
bug!(
"FIXME(-Znext-solver): Overflowed when processing region obligations: {outlives_predicates:#?}"
);
@ -170,10 +165,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
);
}
let implicit_region_bound =
ty::Region::new_var(tcx, universal_regions.implicit_region_bound());
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(self.span, t1, None);
TypeOutlives::new(
&mut *self,
tcx,
@ -205,7 +201,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
/// are dealt with during trait solving.
fn replace_placeholders_with_nll<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
if value.has_placeholders() {
fold_regions(self.tcx, value, |r, _| match r.kind() {
fold_regions(self.infcx.tcx, value, |r, _| match r.kind() {
ty::RePlaceholder(placeholder) => {
self.constraints.placeholder_region(self.infcx, placeholder)
}

View File

@ -17,7 +17,7 @@ use type_op::TypeOpOutput;
use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion};
use crate::universal_regions::UniversalRegions;
#[derive(Debug)]
#[derive(Clone, Debug)]
pub(crate) struct UniversalRegionRelations<'tcx> {
pub(crate) universal_regions: UniversalRegions<'tcx>,
@ -41,22 +41,20 @@ type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;
pub(crate) struct CreateResult<'tcx> {
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>,
pub(crate) known_type_outlives_obligations: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
pub(crate) region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
pub(crate) known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>,
}
pub(crate) fn create<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
universal_regions: UniversalRegions<'tcx>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> CreateResult<'tcx> {
UniversalRegionRelationsBuilder {
infcx,
param_env,
implicit_region_bound,
constraints,
universal_regions,
region_bound_pairs: Default::default(),
@ -158,13 +156,6 @@ impl UniversalRegionRelations<'_> {
self.outlives.contains(fr1, fr2)
}
/// Returns `true` if fr1 is known to equal fr2.
///
/// This will only ever be true for universally quantified regions.
pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1)
}
/// Returns a vector of free regions `x` such that `fr1: x` is
/// known to hold.
pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> {
@ -181,7 +172,6 @@ struct UniversalRegionRelationsBuilder<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
universal_regions: UniversalRegions<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
// outputs:
@ -320,7 +310,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
param_env,
&known_type_outlives_obligations,
Locations::All(span),
@ -337,8 +326,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
outlives: self.outlives.freeze(),
inverse_outlives: self.inverse_outlives.freeze(),
}),
known_type_outlives_obligations,
region_bound_pairs: self.region_bound_pairs,
known_type_outlives_obligations: Frozen::freeze(known_type_outlives_obligations),
region_bound_pairs: Frozen::freeze(self.region_bound_pairs),
normalized_inputs_and_output,
}
}

View File

@ -26,8 +26,8 @@ use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::{
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt,
TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions,
Dynamic, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex,
fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::ResultsCursor;
@ -44,7 +44,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;
@ -52,7 +51,9 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI
use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
use crate::universal_regions::{DefiningTy, UniversalRegions};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils};
use crate::{
BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState, DeferredClosureRequirements, path_utils,
};
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
@ -74,7 +75,6 @@ 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
@ -113,13 +113,11 @@ pub(crate) fn type_check<'a, 'tcx>(
move_data: &MoveData<'tcx>,
location_map: Rc<DenseLocationMap>,
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
let mut constraints = MirTypeckRegionConstraints {
placeholder_indices: PlaceholderIndices::default(),
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(),
};
@ -129,13 +127,7 @@ pub(crate) fn type_check<'a, 'tcx>(
region_bound_pairs,
normalized_inputs_and_output,
known_type_outlives_obligations,
} = free_region_relations::create(
infcx,
infcx.param_env,
implicit_region_bound,
universal_regions,
&mut constraints,
);
} = free_region_relations::create(infcx, infcx.param_env, universal_regions, &mut constraints);
let pre_obligations = infcx.take_registered_region_obligations();
assert!(
@ -158,15 +150,15 @@ pub(crate) fn type_check<'a, 'tcx>(
body,
promoted,
user_type_annotations: &body.user_type_annotations,
region_bound_pairs,
known_type_outlives_obligations,
implicit_region_bound,
region_bound_pairs: &region_bound_pairs,
known_type_outlives_obligations: &known_type_outlives_obligations,
reported_errors: Default::default(),
universal_regions: &universal_region_relations.universal_regions,
location_table,
polonius_facts,
borrow_set,
constraints: &mut constraints,
deferred_closure_requirements: Default::default(),
polonius_liveness,
};
@ -177,10 +169,10 @@ pub(crate) fn type_check<'a, 'tcx>(
liveness::generate(&mut typeck, &location_map, flow_inits, 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.
//
// FIXME: Handling opaque type uses may introduce new regions. This likely has to be moved to
// a later point.
let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| {
PoloniusContext::create_from_liveness(
liveness_context,
@ -189,10 +181,13 @@ pub(crate) fn type_check<'a, 'tcx>(
)
});
let deferred_closure_requirements = typeck.deferred_closure_requirements;
MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
region_bound_pairs,
known_type_outlives_obligations,
deferred_closure_requirements,
polonius_context,
}
}
@ -224,15 +219,15 @@ struct TypeChecker<'a, 'tcx> {
/// User type annotations are shared between the main MIR and the MIR of
/// all of the promoted items.
user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>,
region_bound_pairs: RegionBoundPairs<'tcx>,
known_type_outlives_obligations: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
implicit_region_bound: ty::Region<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &'a Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
reported_errors: FxIndexSet<(Ty<'tcx>, Span)>,
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a PoloniusLocationTable,
polonius_facts: &'a mut Option<PoloniusFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
polonius_liveness: Option<PoloniusLivenessContext>,
}
@ -242,12 +237,15 @@ 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) deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
pub(crate) polonius_context: Option<PoloniusContext>,
}
/// A collection of region constraints that must be satisfied for the
/// program to be considered well-typed.
#[derive(Clone)]
pub(crate) struct MirTypeckRegionConstraints<'tcx> {
/// Maps from a `ty::Placeholder` to the corresponding
/// `PlaceholderIndex` bit that we will use for it.
@ -274,8 +272,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>>,
@ -284,7 +280,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,
@ -300,6 +296,16 @@ impl<'tcx> MirTypeckRegionConstraints<'tcx> {
}
}
}
/// Same as `Self::placeholder_region`, except that never adds new regions but instead ICEs in case we
/// haven't already created an NLL var for this placeholders.
pub(crate) fn get_placeholder_region(
&self,
placeholder: ty::PlaceholderRegion,
) -> ty::Region<'tcx> {
let placeholder_index = self.placeholder_indices.lookup_index(placeholder);
*self.placeholder_index_to_region.get(placeholder_index).unwrap()
}
}
/// The `Locations` type summarizes *where* region constraints are
@ -377,14 +383,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 {
let features = self.tcx().features();
features.unsized_locals() || features.unsized_fn_params()
@ -422,7 +420,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.infcx,
self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.infcx.param_env,
&self.known_type_outlives_obligations,
locations,
@ -2502,12 +2499,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> {
if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
let (closure_requirements, needs_defer) =
self.root_cx.get_closure_requirements_modulo_opaques(def_id);
if needs_defer {
self.deferred_closure_requirements.push((def_id, args, locations));
}
if let Some(closure_requirements) = closure_requirements {
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.infcx.param_env,
&self.known_type_outlives_obligations,
locations,
@ -2557,6 +2559,41 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
pub(crate) fn apply_closure_requirements_considering_opaques<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
borrowck_state: &mut BorrowckState<'tcx>,
) {
let BorrowckState {
infcx,
body_owned,
universal_region_relations,
region_bound_pairs,
known_type_outlives_obligations,
constraints,
deferred_closure_requirements,
..
} = borrowck_state;
for (def_id, args, locations) in mem::take(deferred_closure_requirements).into_iter() {
if let Some(closure_requirements) =
root_cx.get_closure_requirements_considering_regions(def_id)
{
constraint_conversion::ConstraintConversion::new(
infcx,
&universal_region_relations.universal_regions,
region_bound_pairs,
infcx.param_env,
known_type_outlives_obligations,
locations,
body_owned.span, // irrelevant; will be overridden.
ConstraintCategory::Boring, // same as above.
constraints,
)
.apply_closure_requirements(closure_requirements, def_id, args);
}
}
}
trait NormalizeLocation: fmt::Debug + Copy {
fn to_locations(self) -> Locations;
}

View File

@ -1,337 +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.unpack() {
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_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
t.super_visit_with(self);
}
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

@ -39,7 +39,7 @@ use tracing::{debug, instrument};
use crate::BorrowckInferCtxt;
use crate::renumber::RegionCtxt;
#[derive(Debug)]
#[derive(Clone, Debug)]
pub(crate) struct UniversalRegions<'tcx> {
indices: UniversalRegionIndices<'tcx>,
@ -199,7 +199,7 @@ impl<'tcx> DefiningTy<'tcx> {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
struct UniversalRegionIndices<'tcx> {
/// For those regions that may appear in the parameter environment
/// ('static and early-bound regions), we maintain a map from the
@ -438,6 +438,10 @@ impl<'tcx> UniversalRegions<'tcx> {
}
}
pub(crate) fn implicit_region_bound(&self) -> RegionVid {
self.fr_fn_body
}
pub(crate) fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
self.indices.tainted_by_errors.get()
}

View File

@ -46,7 +46,7 @@
//! Frozen::freeze(new_bar)`).
/// An owned immutable value.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Frozen<T>(T);
impl<T> Frozen<T> {

View File

@ -580,15 +580,16 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
if let Err(guar) = check_opaque_type_parameter_valid(
if let Err(err) = check_opaque_type_parameter_valid(
&self.fcx,
opaque_type_key,
hidden_type.span,
DefiningScopeKind::HirTypeck,
) {
self.typeck_results
.concrete_opaque_types
.insert(opaque_type_key.def_id, ty::OpaqueHiddenType::new_error(tcx, guar));
self.typeck_results.concrete_opaque_types.insert(
opaque_type_key.def_id,
ty::OpaqueHiddenType::new_error(tcx, err.report(self.fcx)),
);
}
let hidden_type = hidden_type.remap_generic_params_to_declaration_params(

View File

@ -953,6 +953,10 @@ impl<'tcx> InferCtxt<'tcx> {
storage.var_infos.clone()
}
pub fn has_opaque_types_in_storage(&self) -> bool {
!self.inner.borrow().opaque_type_storage.opaque_types.is_empty()
}
#[instrument(level = "debug", skip(self), ret)]
pub fn take_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> {
std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types)

View File

@ -3283,11 +3283,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.sess.opts.unstable_opts.next_solver.coherence
}
#[allow(rustc::bad_opt_access)]
pub fn use_typing_mode_borrowck(self) -> bool {
self.next_trait_solver_globally() || self.sess.opts.unstable_opts.typing_mode_borrowck
}
pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
self.opt_rpitit_info(def_id).is_some()
}

View File

@ -2559,9 +2559,6 @@ written to standard error output)"),
"in diagnostics, use heuristics to shorten paths referring to items"),
tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
"select processor to schedule for (`rustc --print target-cpus` for details)"),
#[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")]
typing_mode_borrowck: bool = (false, parse_bool, [TRACKED],
"enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"),
#[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")]
ub_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
"emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"),

View File

@ -268,8 +268,8 @@ trait_selection_oc_type_compat = type not compatible with trait
trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds
.label = opaque type defined here
trait_selection_opaque_type_non_generic_param =
expected generic {$kind} parameter, found `{$ty}`
.label = {STREQ($ty, "'static") ->
expected generic {$kind} parameter, found `{$arg}`
.label = {STREQ($arg, "'static") ->
[true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
*[other] this generic parameter must be used with a generic {$kind} parameter
}

View File

@ -1926,7 +1926,7 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture {
#[derive(Diagnostic)]
#[diag(trait_selection_opaque_type_non_generic_param, code = E0792)]
pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
pub ty: GenericArg<'tcx>,
pub arg: GenericArg<'tcx>,
pub kind: &'a str,
#[primary_span]
pub span: Span,

View File

@ -13,6 +13,49 @@ use crate::errors::NonGenericOpaqueTypeParam;
use crate::regions::OutlivesEnvironmentBuildExt;
use crate::traits::ObligationCtxt;
pub enum InvalidOpaqueTypeArgs<'tcx> {
AlreadyReported(ErrorGuaranteed),
NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span },
DuplicateParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_indices: Vec<usize>, span: Span },
}
impl From<ErrorGuaranteed> for InvalidOpaqueTypeArgs<'_> {
fn from(guar: ErrorGuaranteed) -> Self {
InvalidOpaqueTypeArgs::AlreadyReported(guar)
}
}
impl<'tcx> InvalidOpaqueTypeArgs<'tcx> {
pub fn report(self, infcx: &InferCtxt<'tcx>) -> ErrorGuaranteed {
let tcx = infcx.tcx;
match self {
InvalidOpaqueTypeArgs::AlreadyReported(guar) => guar,
InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index, span } => {
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let opaque_param = opaque_generics.param_at(param_index, tcx);
let kind = opaque_param.kind.descr();
infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
arg: opaque_type_key.args[param_index],
kind,
span,
param_span: tcx.def_span(opaque_param.def_id),
})
}
InvalidOpaqueTypeArgs::DuplicateParam { opaque_type_key, param_indices, span } => {
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let descr = opaque_generics.param_at(param_indices[0], tcx).kind.descr();
let spans: Vec<_> = param_indices
.into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
.collect();
infcx
.dcx()
.struct_span_err(span, "non-defining opaque type use in defining scope")
.with_span_note(spans, format!("{descr} used multiple times"))
.emit()
}
}
}
}
/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
///
/// [rustc-dev-guide chapter]:
@ -22,23 +65,19 @@ pub fn check_opaque_type_parameter_valid<'tcx>(
opaque_type_key: OpaqueTypeKey<'tcx>,
span: Span,
defining_scope_kind: DefiningScopeKind,
) -> Result<(), ErrorGuaranteed> {
) -> Result<(), InvalidOpaqueTypeArgs<'tcx>> {
let tcx = infcx.tcx;
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
// Avoid duplicate errors in case the opaque has already been malformed in
// HIR typeck.
if let DefiningScopeKind::MirBorrowck = defining_scope_kind {
if let Err(guar) = infcx
infcx
.tcx
.type_of_opaque_hir_typeck(opaque_type_key.def_id)
.instantiate_identity()
.error_reported()
{
return Err(guar);
}
.error_reported()?;
}
for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
@ -64,32 +103,18 @@ pub fn check_opaque_type_parameter_valid<'tcx>(
}
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
let kind = opaque_param.kind.descr();
opaque_env.param_is_error(i)?;
return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
ty: arg,
kind,
span,
param_span: tcx.def_span(opaque_param.def_id),
}));
return Err(InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index: i, span });
}
}
for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
.collect();
return Err(infcx
.dcx()
.struct_span_err(span, "non-defining opaque type use in defining scope")
.with_span_note(spans, format!("{descr} used multiple times"))
.emit());
for (_, param_indices) in seen_params {
if param_indices.len() > 1 {
return Err(InvalidOpaqueTypeArgs::DuplicateParam {
opaque_type_key,
param_indices,
span,
});
}
}