Skip to content

Commit 75ee9ff

Browse files
committed
Auto merge of #145925 - lcnr:revealing-use-closures-2, r=BoxyUwU
`-Znext-solver`: support non-defining uses in closures Cleaned up version of #139587, finishing the implementation of rust-lang/types-team#129. This does not affect stable. The reasoning for why this is the case is subtle however. ## What does it do We split `do_mir_borrowck` into `borrowck_collect_region_constraints` and `borrowck_check_region_constraints`, where `borrowck_collect_region_constraints` returns an enormous `CollectRegionConstraintsResult` struct which contains all the relevant data to actually handle opaque type uses and to check the region constraints later on. `query mir_borrowck` now simply calls `BorrowCheckRootCtxt::do_mir_borrowck` which starts by iterating over all nested bodies of the current function - visiting nested bodies before their parents - and computing their `CollectRegionConstraintsResult`. After we've collected all constraints it's time to actually compute the concrete types for the opaques defined by this function. With this PR we now compute the concrete types of opaques for each body before using them to check the non-defining uses of any of them. After we've computed the concrete types by using all bodies, we use `apply_computed_concrete_opaque_types` for each body to constrain non-defining uses, before finally finishing with `borrowck_check_region_constraints`. We always visit nested bodies before their parents when doing this. ## `ClosureRegionRequirements` As we only call `borrowck_collect_region_constraints` for nested bodies before type checking the parent, we can't simply use the final `ClosureRegionRequirements` of the nested body during MIR type check. We instead track that we need to apply these requirements in `deferred_closure_requirements`. We now manually apply the final closure requirements to each body after handling opaque types. This works, except that we may need the region constraints of nested bodies to successfully define an opaque type in the parent. This is handled by using a new `fn compute_closure_requirements_modulo_opaques` which duplicates region checking - while ignoring any errors - before we've added the constraints from `apply_computed_concrete_opaque_types`. This is necessary for a lot of async tests, as pretty much the entire function is inside of an async block while the opaque type gets defined in the parent. As an performance optimization we only use `fn compute_closure_requirements_modulo_opaques` in case the nested body actually depends on any opaque types. Otherwise we eagerly call `borrowck_check_region_constraints` and apply the final closure region requirements right away. ## Impact on stable code Handling the opaque type uses in the parent function now only uses the closure requirements *modulo opaques*, while it previously also considered member constraints from nested bodies. `External` regions are never valid choice regions. Also, member constraints will never constrain a member region if it is required to be outlived by an external region, as that fails the upper-bound check. https://github.com/rust-lang/rust/blob/564ee219127b796d56f74767366fd359758b97de/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs#L90-L96 Member constraints therefore never add constraints for external regions :> r? `@BoxyUwU`
2 parents 7aef4be + b8160e9 commit 75ee9ff

File tree

19 files changed

+523
-233
lines changed

19 files changed

+523
-233
lines changed

compiler/rustc_borrowck/src/consumers.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub use super::polonius::legacy::{
1717
RichLocation, RustcFacts,
1818
};
1919
pub use super::region_infer::RegionInferenceContext;
20-
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
20+
use crate::BorrowCheckRootCtxt;
2121

2222
/// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
2323
/// its nested bodies.
@@ -127,13 +127,6 @@ pub fn get_bodies_with_borrowck_facts(
127127
) -> FxHashMap<LocalDefId, BodyWithBorrowckFacts<'_>> {
128128
let mut root_cx =
129129
BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options)));
130-
131-
// See comment in `rustc_borrowck::mir_borrowck`
132-
let nested_bodies = tcx.nested_bodies_within(root_def_id);
133-
for def_id in nested_bodies {
134-
root_cx.get_or_insert_nested(def_id);
135-
}
136-
137-
do_mir_borrowck(&mut root_cx, root_def_id);
130+
root_cx.do_mir_borrowck();
138131
root_cx.consumer.unwrap().bodies
139132
}

compiler/rustc_borrowck/src/lib.rs

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ use std::ops::{ControlFlow, Deref};
2222
use std::rc::Rc;
2323

2424
use borrow_set::LocalsStateAtExit;
25+
use polonius_engine::AllFacts;
2526
use root_cx::BorrowCheckRootCtxt;
2627
use rustc_abi::FieldIdx;
28+
use rustc_data_structures::frozen::Frozen;
2729
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2830
use rustc_data_structures::graph::dominators::Dominators;
2931
use rustc_errors::LintDiagnostic;
@@ -32,6 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
3234
use rustc_hir::def_id::LocalDefId;
3335
use rustc_index::bit_set::MixedBitSet;
3436
use rustc_index::{IndexSlice, IndexVec};
37+
use rustc_infer::infer::outlives::env::RegionBoundPairs;
3538
use rustc_infer::infer::{
3639
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
3740
};
@@ -53,23 +56,25 @@ use smallvec::SmallVec;
5356
use tracing::{debug, instrument};
5457

5558
use crate::borrow_set::{BorrowData, BorrowSet};
56-
use crate::consumers::BodyWithBorrowckFacts;
59+
use crate::consumers::{BodyWithBorrowckFacts, RustcFacts};
5760
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
5861
use crate::diagnostics::{
5962
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
6063
};
6164
use crate::path_utils::*;
6265
use crate::place_ext::PlaceExt;
6366
use crate::places_conflict::{PlaceConflictBias, places_conflict};
64-
use crate::polonius::PoloniusDiagnosticsContext;
6567
use crate::polonius::legacy::{
6668
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
6769
};
70+
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
6871
use crate::prefixes::PrefixSet;
6972
use crate::region_infer::RegionInferenceContext;
73+
use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
7074
use crate::renumber::RegionCtxt;
7175
use crate::session_diagnostics::VarNeedNotMut;
72-
use crate::type_check::MirTypeckResults;
76+
use crate::type_check::free_region_relations::UniversalRegionRelations;
77+
use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults};
7378

7479
mod borrow_set;
7580
mod borrowck_errors;
@@ -129,18 +134,7 @@ fn mir_borrowck(
129134
Ok(tcx.arena.alloc(opaque_types))
130135
} else {
131136
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
132-
// We need to manually borrowck all nested bodies from the HIR as
133-
// we do not generate MIR for dead code. Not doing so causes us to
134-
// never check closures in dead code.
135-
let nested_bodies = tcx.nested_bodies_within(def);
136-
for def_id in nested_bodies {
137-
root_cx.get_or_insert_nested(def_id);
138-
}
139-
140-
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
141-
do_mir_borrowck(&mut root_cx, def);
142-
debug_assert!(closure_requirements.is_none());
143-
debug_assert!(used_mut_upvars.is_empty());
137+
root_cx.do_mir_borrowck();
144138
root_cx.finalize()
145139
}
146140
}
@@ -153,6 +147,8 @@ struct PropagatedBorrowCheckResults<'tcx> {
153147
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
154148
}
155149

150+
type DeferredClosureRequirements<'tcx> = Vec<(LocalDefId, ty::GenericArgsRef<'tcx>, Locations)>;
151+
156152
/// After we borrow check a closure, we are left with various
157153
/// requirements that we have inferred between the free regions that
158154
/// appear in the closure's signature or on its field types. These
@@ -291,14 +287,31 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
291287
}
292288
}
293289

294-
/// Perform the actual borrow checking.
295-
///
296-
/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
297-
#[instrument(skip(root_cx), level = "debug")]
298-
fn do_mir_borrowck<'tcx>(
290+
struct CollectRegionConstraintsResult<'tcx> {
291+
infcx: BorrowckInferCtxt<'tcx>,
292+
body_owned: Body<'tcx>,
293+
promoted: IndexVec<Promoted, Body<'tcx>>,
294+
move_data: MoveData<'tcx>,
295+
borrow_set: BorrowSet<'tcx>,
296+
location_table: PoloniusLocationTable,
297+
location_map: Rc<DenseLocationMap>,
298+
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
299+
region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
300+
known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
301+
constraints: MirTypeckRegionConstraints<'tcx>,
302+
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
303+
deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
304+
polonius_facts: Option<AllFacts<RustcFacts>>,
305+
polonius_context: Option<PoloniusContext>,
306+
}
307+
308+
/// Start borrow checking by collecting the region constraints for
309+
/// the current body. This initializes the relevant data structures
310+
/// and then type checks the MIR body.
311+
fn borrowck_collect_region_constraints<'tcx>(
299312
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
300313
def: LocalDefId,
301-
) -> PropagatedBorrowCheckResults<'tcx> {
314+
) -> CollectRegionConstraintsResult<'tcx> {
302315
let tcx = root_cx.tcx;
303316
let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id());
304317
let (input_body, promoted) = tcx.mir_promoted(def);
@@ -334,10 +347,11 @@ fn do_mir_borrowck<'tcx>(
334347

335348
// Run the MIR type-checker.
336349
let MirTypeckResults {
337-
mut constraints,
350+
constraints,
338351
universal_region_relations,
339352
region_bound_pairs,
340353
known_type_outlives_obligations,
354+
deferred_closure_requirements,
341355
polonius_context,
342356
} = type_check::type_check(
343357
root_cx,
@@ -352,16 +366,53 @@ fn do_mir_borrowck<'tcx>(
352366
Rc::clone(&location_map),
353367
);
354368

355-
let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses(
356-
root_cx,
357-
&infcx,
358-
&body,
359-
&universal_region_relations,
360-
&region_bound_pairs,
361-
&known_type_outlives_obligations,
362-
&location_map,
363-
&mut constraints,
364-
);
369+
CollectRegionConstraintsResult {
370+
infcx,
371+
body_owned,
372+
promoted,
373+
move_data,
374+
borrow_set,
375+
location_table,
376+
location_map,
377+
universal_region_relations,
378+
region_bound_pairs,
379+
known_type_outlives_obligations,
380+
constraints,
381+
deferred_closure_requirements,
382+
deferred_opaque_type_errors: Default::default(),
383+
polonius_facts,
384+
polonius_context,
385+
}
386+
}
387+
388+
/// Using the region constraints computed by [borrowck_collect_region_constraints]
389+
/// and the additional constraints from [BorrowCheckRootCtxt::handle_opaque_type_uses],
390+
/// compute the region graph and actually check for any borrowck errors.
391+
fn borrowck_check_region_constraints<'tcx>(
392+
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
393+
CollectRegionConstraintsResult {
394+
infcx,
395+
body_owned,
396+
promoted,
397+
move_data,
398+
borrow_set,
399+
location_table,
400+
location_map,
401+
universal_region_relations,
402+
region_bound_pairs: _,
403+
known_type_outlives_obligations: _,
404+
constraints,
405+
deferred_closure_requirements,
406+
deferred_opaque_type_errors,
407+
polonius_facts,
408+
polonius_context,
409+
}: CollectRegionConstraintsResult<'tcx>,
410+
) -> PropagatedBorrowCheckResults<'tcx> {
411+
assert!(!infcx.has_opaque_types_in_storage());
412+
assert!(deferred_closure_requirements.is_empty());
413+
let tcx = root_cx.tcx;
414+
let body = &body_owned;
415+
let def = body.source.def_id().expect_local();
365416

366417
// Compute non-lexical lifetimes using the constraints computed
367418
// by typechecking the MIR body.
@@ -481,7 +532,7 @@ fn do_mir_borrowck<'tcx>(
481532

482533
// Compute and report region errors, if any.
483534
if nll_errors.is_empty() {
484-
mbcx.report_opaque_type_errors(opaque_type_errors);
535+
mbcx.report_opaque_type_errors(deferred_opaque_type_errors);
485536
} else {
486537
mbcx.report_region_errors(nll_errors);
487538
}

compiler/rustc_borrowck/src/nll.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,38 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
7575
universal_regions
7676
}
7777

78+
/// Computes the closure requirements given the current inference state.
79+
///
80+
/// This is intended to be used by before [BorrowCheckRootCtxt::handle_opaque_type_uses]
81+
/// because applying member constraints may rely on closure requirements.
82+
/// This is frequently the case of async functions where pretty much everything
83+
/// happens inside of the inner async block but the opaque only gets constrained
84+
/// in the parent function.
85+
pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
86+
infcx: &BorrowckInferCtxt<'tcx>,
87+
body: &Body<'tcx>,
88+
location_map: Rc<DenseLocationMap>,
89+
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
90+
constraints: &MirTypeckRegionConstraints<'tcx>,
91+
) -> Option<ClosureRegionRequirements<'tcx>> {
92+
// FIXME(#146079): we shouldn't have to clone all this stuff here.
93+
// Computing the region graph should take at least some of it by reference/`Rc`.
94+
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
95+
constraints.clone(),
96+
&universal_region_relations,
97+
infcx,
98+
);
99+
let mut regioncx = RegionInferenceContext::new(
100+
&infcx,
101+
lowered_constraints,
102+
universal_region_relations.clone(),
103+
location_map,
104+
);
105+
106+
let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None);
107+
closure_region_requirements
108+
}
109+
78110
/// Computes the (non-lexical) regions from the input MIR.
79111
///
80112
/// This may result in errors being reported.

0 commit comments

Comments
 (0)