support revealing-uses in closures

This commit is contained in:
lcnr 2025-04-09 12:24:00 +02:00
parent 1bce98ff06
commit d85f8b0222
11 changed files with 377 additions and 115 deletions

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

@ -21,10 +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;
@ -33,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,
};
@ -48,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,
@ -63,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;
@ -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);
@ -314,6 +340,7 @@ fn do_mir_borrowck<'tcx>(
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);
@ -324,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,
@ -333,18 +434,23 @@ fn do_mir_borrowck<'tcx>(
nll_errors,
polonius_diagnostics,
} = nll::compute_regions(
root_cx,
&infcx,
universal_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);

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,19 +19,19 @@ use rustc_span::sym;
use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::consumers::{ConsumerOptions, RustcFacts};
use crate::diagnostics::RegionErrors;
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::region_infer::RegionInferenceContext;
use crate::region_infer::opaque_types::handle_opaque_type_uses;
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
@ -73,55 +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, 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),
);
let (constraints, deferred_opaque_type_errors) = handle_opaque_type_uses(
root_cx,
infcx,
constraints,
&universal_region_relations,
Rc::clone(&location_map),
);
// If requested, emit legacy polonius facts.
polonius::legacy::emit_facts(
&mut polonius_facts,
@ -169,10 +160,7 @@ 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);
} else if !deferred_opaque_type_errors.is_empty() {
regioncx.emit_deferred_opaque_type_errors(root_cx, infcx, deferred_opaque_type_errors);
}
NllOutput {

View File

@ -31,7 +31,7 @@ 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};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState};
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
UnexpectedHiddenRegion {
@ -45,16 +45,39 @@ pub(crate) enum DeferredOpaqueTypeError<'tcx> {
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
}
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;
handle_opaque_type_uses(
self,
infcx,
constraints,
deferred_opaque_type_errors,
universal_region_relations,
Rc::clone(location_map),
)
}
}
pub(crate) fn handle_opaque_type_uses<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
mut constraints: MirTypeckRegionConstraints<'tcx>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
deferred_errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
location_map: Rc<DenseLocationMap>,
) -> (MirTypeckRegionConstraints<'tcx>, Vec<DeferredOpaqueTypeError<'tcx>>) {
) {
let opaque_types = infcx.take_opaque_types();
if opaque_types.is_empty() {
return (constraints, Vec::new());
return;
}
let tcx = infcx.tcx;
@ -116,8 +139,6 @@ pub(crate) fn handle_opaque_type_uses<'tcx>(
})
}
let mut deferred_errors = Vec::new();
// 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.
@ -157,7 +178,7 @@ pub(crate) fn handle_opaque_type_uses<'tcx>(
opaque_type_key,
hidden_type,
deferred_errors: &mut deferred_errors,
deferred_errors,
arg_regions: &arg_regions,
universal_region_relations,
@ -177,7 +198,6 @@ pub(crate) fn handle_opaque_type_uses<'tcx>(
)
});
// TODO this doesn't seem to help
if !tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
&& alias_ty.def_id == opaque_type_key.def_id.to_def_id()
@ -220,12 +240,8 @@ pub(crate) fn handle_opaque_type_uses<'tcx>(
}
});
let mut relation = EquateRegions {
infcx,
span: hidden_type.span,
universal_regions,
constraints: &mut constraints,
};
let mut relation =
EquateRegions { infcx, span: hidden_type.span, universal_regions, constraints };
match TypeRelation::relate(&mut relation, hidden_type.ty, expected.ty) {
Ok(_) => {}
Err(_) => {
@ -245,8 +261,6 @@ pub(crate) fn handle_opaque_type_uses<'tcx>(
// without it we could manually walk over the types using a type relation and equate region vars
// that way.
}
(constraints, deferred_errors)
}
fn to_region_vid<'tcx>(

View File

@ -39,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>,
@ -195,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>,
}

View File

@ -6,7 +6,13 @@ use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableE
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,7 +33,9 @@ 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,
}
}
@ -73,40 +84,121 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
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

@ -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,8 +41,8 @@ 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>,
}
@ -326,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

@ -51,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)*) => ({
@ -148,14 +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,
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,
};
@ -178,7 +181,15 @@ pub(crate) fn type_check<'a, 'tcx>(
)
});
MirTypeckResults { constraints, universal_region_relations, polonius_context }
let deferred_closure_requirements = typeck.deferred_closure_requirements;
MirTypeckResults {
constraints,
universal_region_relations,
region_bound_pairs,
known_type_outlives_obligations,
deferred_closure_requirements,
polonius_context,
}
}
#[track_caller]
@ -208,14 +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>>,
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>,
}
@ -225,11 +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) 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.
@ -2483,7 +2499,13 @@ 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,
@ -2537,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

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

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

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