borrowck typeck children together with their parent

This commit is contained in:
lcnr 2025-03-14 16:01:09 +01:00
parent e643f59f6d
commit f05a23be5c
19 changed files with 335 additions and 217 deletions

View file

@ -15,6 +15,7 @@ pub use super::polonius::legacy::{
RichLocation, RustcFacts,
};
pub use super::region_infer::RegionInferenceContext;
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
///
@ -97,8 +98,9 @@ pub struct BodyWithBorrowckFacts<'tcx> {
/// * Polonius is highly unstable, so expect regular changes in its signature or other details.
pub fn get_body_with_borrowck_facts(
tcx: TyCtxt<'_>,
def: LocalDefId,
def_id: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
*super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
*do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap()
}

View file

@ -21,6 +21,7 @@ use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
use root_cx::BorrowCheckRootCtxt;
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
@ -45,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{
};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{Span, Symbol};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
use tracing::{debug, instrument};
@ -73,7 +74,6 @@ mod def_use;
mod diagnostics;
mod member_constraints;
mod nll;
mod opaque_types;
mod path_utils;
mod place_ext;
mod places_conflict;
@ -81,6 +81,7 @@ mod polonius;
mod prefixes;
mod region_infer;
mod renumber;
mod root_cx;
mod session_diagnostics;
mod type_check;
mod universal_regions;
@ -102,44 +103,64 @@ pub fn provide(providers: &mut Providers) {
*providers = Providers { mir_borrowck, ..*providers };
}
fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Provider for `query mir_borrowck`. Similar to `typeck`, this must
/// only be called for typeck roots which will then borrowck all
/// nested bodies as well.
fn mir_borrowck(
tcx: TyCtxt<'_>,
def: LocalDefId,
) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> {
assert!(!tcx.is_typeck_child(def.to_def_id()));
let (input_body, _) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
let input_body: &Body<'_> = &input_body.borrow();
if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
debug!("Skipping borrowck because of injected body or tainted body");
// Let's make up a borrowck result! Fun times!
let result = BorrowCheckResult {
concrete_opaque_types: FxIndexMap::default(),
closure_requirements: None,
used_mut_upvars: SmallVec::new(),
tainted_by_errors: input_body.tainted_by_errors,
};
return tcx.arena.alloc(result);
if let Some(guar) = input_body.tainted_by_errors {
debug!("Skipping borrowck because of tainted body");
Err(guar)
} else if input_body.should_skip() {
debug!("Skipping borrowck because of injected body");
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 borrowck_result = do_mir_borrowck(tcx, def, None).0;
debug!("mir_borrowck done");
tcx.arena.alloc(borrowck_result)
/// Data propagated to the typeck parent by nested items.
/// This should always be empty for the typeck root.
#[derive(Debug)]
struct PropagatedBorrowCheckResults<'tcx> {
closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
}
/// Perform the actual borrow checking.
///
/// Use `consumer_options: None` for the default behavior of returning
/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// to the given [`ConsumerOptions`].
#[instrument(skip(tcx), level = "debug")]
/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`]
/// according to the given [`ConsumerOptions`].
///
/// 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>(
tcx: TyCtxt<'tcx>,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
def: LocalDefId,
consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let tcx = root_cx.tcx;
let infcx = BorrowckInferCtxt::new(tcx, def);
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
if let Some(e) = input_body.tainted_by_errors {
infcx.set_tainted_by_errors(e);
root_cx.set_tainted_by_errors(e);
}
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
@ -185,13 +206,13 @@ fn do_mir_borrowck<'tcx>(
// Compute non-lexical lifetimes.
let nll::NllOutput {
regioncx,
concrete_opaque_types,
polonius_input,
polonius_output,
opt_closure_req,
nll_errors,
polonius_diagnostics,
} = nll::compute_regions(
root_cx,
&infcx,
free_regions,
body,
@ -210,26 +231,19 @@ fn do_mir_borrowck<'tcx>(
// We also have a `#[rustc_regions]` annotation that causes us to dump
// information.
let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
nll::dump_annotation(
&infcx,
body,
&regioncx,
&opt_closure_req,
&concrete_opaque_types,
diags_buffer,
);
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
};
// 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]); }`.
@ -240,6 +254,7 @@ fn do_mir_borrowck<'tcx>(
// this check out of `MirBorrowckCtxt`, actually doing so is far from trivial.
let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true);
let mut promoted_mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
body: promoted_body,
move_data: &move_data,
@ -280,6 +295,7 @@ fn do_mir_borrowck<'tcx>(
}
let mut mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
body,
move_data: &move_data,
@ -347,13 +363,13 @@ fn do_mir_borrowck<'tcx>(
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
mbcx.lint_unused_mut();
let tainted_by_errors = mbcx.emit_errors();
if let Some(guar) = mbcx.emit_errors() {
mbcx.root_cx.set_tainted_by_errors(guar);
}
let result = BorrowCheckResult {
concrete_opaque_types: concrete_opaque_types.into_inner(),
let result = PropagatedBorrowCheckResults {
closure_requirements: opt_closure_req,
used_mut_upvars: mbcx.used_mut_upvars,
tainted_by_errors,
};
let body_with_facts = if consumer_options.is_some() {
@ -488,6 +504,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
}
struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
root_cx: &'a mut BorrowCheckRootCtxt<'tcx>,
infcx: &'infcx BorrowckInferCtxt<'tcx>,
body: &'a Body<'tcx>,
move_data: &'a MoveData<'tcx>,
@ -1361,11 +1378,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| AggregateKind::CoroutineClosure(def_id, _)
| AggregateKind::Coroutine(def_id, _) => {
let def_id = def_id.expect_local();
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id);
let used_mut_upvars = self.root_cx.used_mut_upvars(def_id);
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
for field in used_mut_upvars {
self.propagate_closure_used_mut_upvar(&operands[*field]);
// FIXME: We're cloning the `SmallVec` here to avoid borrowing `root_cx`
// when calling `propagate_closure_used_mut_upvar`. This should ideally
// be unnecessary.
for field in used_mut_upvars.clone() {
self.propagate_closure_used_mut_upvar(&operands[field]);
}
}
AggregateKind::Adt(..)

View file

@ -25,7 +25,6 @@ use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
@ -33,13 +32,12 @@ use crate::polonius::legacy::{
use crate::region_infer::RegionInferenceContext;
use crate::type_check::{self, MirTypeckResults};
use crate::universal_regions::UniversalRegions;
use crate::{BorrowckInferCtxt, polonius, renumber};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, polonius, renumber};
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
/// closure requirements to propagate, and any generated errors.
pub(crate) struct NllOutput<'tcx> {
pub regioncx: RegionInferenceContext<'tcx>,
pub concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
pub polonius_input: Option<Box<PoloniusFacts>>,
pub polonius_output: Option<Box<PoloniusOutput>>,
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
@ -78,6 +76,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
///
/// This may result in errors being reported.
pub(crate) fn compute_regions<'a, 'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
@ -98,8 +97,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
let location_map = Rc::new(DenseLocationMap::new(body));
let mut concrete_opaque_types = ConcreteOpaqueTypes::default();
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
@ -107,6 +104,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
opaque_type_values,
polonius_context,
} = type_check::type_check(
root_cx,
infcx,
body,
promoted,
@ -117,7 +115,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
flow_inits,
move_data,
Rc::clone(&location_map),
&mut concrete_opaque_types,
);
// Create the region inference context, taking ownership of the
@ -181,11 +178,10 @@ pub(crate) fn compute_regions<'a, 'tcx>(
infcx.set_tainted_by_errors(guar);
}
regioncx.infer_opaque_types(infcx, opaque_type_values, &mut concrete_opaque_types);
regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
NllOutput {
regioncx,
concrete_opaque_types,
polonius_input: polonius_facts.map(Box::new),
polonius_output,
opt_closure_req: closure_region_requirements,
@ -301,7 +297,6 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
) {
let tcx = infcx.tcx;
@ -318,7 +313,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
// better.
let def_span = tcx.def_span(body.source.def_id());
let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
let err = if let Some(closure_region_requirements) = closure_region_requirements {
let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
regioncx.annotate(tcx, &mut err);
@ -344,9 +339,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
err
};
if !concrete_opaque_types.is_empty() {
err.note(format!("Inferred opaque type values:\n{concrete_opaque_types:#?}"));
}
// FIXME(@lcnr): We currently don't dump the inferred hidden types here.
diagnostics_buffer.buffer_non_error(err);
}

View file

@ -1,55 +0,0 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt};
#[derive(Debug, Default)]
pub(super) struct ConcreteOpaqueTypes<'tcx> {
concrete_opaque_types: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
}
impl<'tcx> ConcreteOpaqueTypes<'tcx> {
pub(super) fn is_empty(&self) -> bool {
self.concrete_opaque_types.is_empty()
}
pub(super) fn into_inner(self) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
self.concrete_opaque_types
}
/// Insert an opaque type into the list of opaque types defined by this function
/// after mapping the hidden type to the generic parameters of the opaque type
/// definition.
pub(super) fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = self.concrete_opaque_types.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
prev.ty = Ty::new_error(tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
self.concrete_opaque_types.insert(def_id, hidden_ty);
}
}
pub(super) fn extend_from_nested_body(
&mut self,
tcx: TyCtxt<'tcx>,
nested_body: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
) {
for (&def_id, &hidden_ty) in nested_body {
self.insert(tcx, def_id, hidden_ty);
}
}
}

View file

@ -10,7 +10,7 @@ use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
use tracing::{debug, instrument};
use super::RegionInferenceContext;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::BorrowCheckRootCtxt;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;
@ -58,12 +58,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// [rustc-dev-guide chapter]:
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
#[instrument(level = "debug", skip(self, infcx), ret)]
#[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>>,
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) {
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
@ -140,11 +140,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
concrete_opaque_types.insert(
infcx.tcx,
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { ty, span: concrete_type.span },
OpaqueHiddenType { span: concrete_type.span, ty },
);
// Check that all opaque types have the same region parameters if they have the same
// non-region parameters. This is necessary because within the new solver we perform
// various query operations modulo regions, and thus could unsoundly select some impls

View file

@ -0,0 +1,102 @@
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::bug;
use rustc_middle::mir::{ClosureRegionRequirements, ConcreteOpaqueTypes};
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use smallvec::SmallVec;
use crate::PropagatedBorrowCheckResults;
/// The shared context used by both the root as well as all its nested
/// items.
pub(super) struct BorrowCheckRootCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
root_def_id: LocalDefId,
concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
tainted_by_errors: Option<ErrorGuaranteed>,
}
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
pub(super) fn new(tcx: TyCtxt<'tcx>, root_def_id: LocalDefId) -> BorrowCheckRootCtxt<'tcx> {
BorrowCheckRootCtxt {
tcx,
root_def_id,
concrete_opaque_types: Default::default(),
nested_bodies: Default::default(),
tainted_by_errors: None,
}
}
/// 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.
pub(super) fn add_concrete_opaque_type(
&mut self,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = self.concrete_opaque_types.0.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
let (Ok(e) | Err(e)) =
prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
e
});
prev.ty = Ty::new_error(self.tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
self.concrete_opaque_types.0.insert(def_id, hidden_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> {
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) {
bug!("unexpected previous nested body: {prev:?}");
}
}
self.nested_bodies.get(&def_id).unwrap()
}
pub(super) fn closure_requirements(
&mut self,
nested_body_def_id: LocalDefId,
) -> &Option<ClosureRegionRequirements<'tcx>> {
&self.get_or_insert_nested(nested_body_def_id).closure_requirements
}
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
}
pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
if let Some(guar) = self.tainted_by_errors {
Err(guar)
} else {
Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
}
}
}

View file

@ -45,7 +45,6 @@ use crate::borrow_set::BorrowSet;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
use crate::region_infer::TypeTest;
@ -53,7 +52,7 @@ 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::{BorrowckInferCtxt, path_utils};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils};
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
@ -102,6 +101,7 @@ mod relate_tys;
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
/// - `location_map` -- map between MIR `Location` and `PointIndex`
pub(crate) fn type_check<'a, 'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
@ -112,7 +112,6 @@ pub(crate) fn type_check<'a, 'tcx>(
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
move_data: &MoveData<'tcx>,
location_map: Rc<DenseLocationMap>,
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
let mut constraints = MirTypeckRegionConstraints {
@ -153,6 +152,7 @@ pub(crate) fn type_check<'a, 'tcx>(
};
let mut typeck = TypeChecker {
root_cx,
infcx,
last_span: body.span,
body,
@ -167,7 +167,6 @@ pub(crate) fn type_check<'a, 'tcx>(
polonius_facts,
borrow_set,
constraints: &mut constraints,
concrete_opaque_types,
polonius_liveness,
};
@ -215,6 +214,7 @@ enum FieldAccessError {
/// way, it accrues region constraints -- these can later be used by
/// NLL region checking.
struct TypeChecker<'a, 'tcx> {
root_cx: &'a mut BorrowCheckRootCtxt<'tcx>,
infcx: &'a BorrowckInferCtxt<'tcx>,
last_span: Span,
body: &'a Body<'tcx>,
@ -233,7 +233,6 @@ struct TypeChecker<'a, 'tcx> {
polonius_facts: &'a mut Option<PoloniusFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
concrete_opaque_types: &'a mut ConcreteOpaqueTypes<'tcx>,
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
polonius_liveness: Option<PoloniusLivenessContext>,
}
@ -2503,11 +2502,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> {
let closure_borrowck_results = tcx.mir_borrowck(def_id);
self.concrete_opaque_types
.extend_from_nested_body(tcx, &closure_borrowck_results.concrete_opaque_types);
if let Some(closure_requirements) = &closure_borrowck_results.closure_requirements {
if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.universal_regions,

View file

@ -397,8 +397,11 @@ fn best_definition_site_of_opaque<'tcx>(
return ControlFlow::Continue(());
}
if let Some(hidden_ty) =
self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id)
if let Some(hidden_ty) = self
.tcx
.mir_borrowck(item_def_id)
.ok()
.and_then(|opaque_types| opaque_types.0.get(&self.opaque_def_id))
{
ControlFlow::Break((hidden_ty.span, item_def_id))
} else {
@ -413,9 +416,6 @@ fn best_definition_site_of_opaque<'tcx>(
self.tcx
}
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
if let hir::ExprKind::Closure(closure) = ex.kind {
self.check(closure.def_id)?;
}
intravisit::walk_expr(self, ex)
}
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) -> Self::Result {

View file

@ -183,25 +183,23 @@ impl<'tcx> TaitConstraintLocator<'tcx> {
self.non_defining_use_in_defining_scope(item_def_id);
}
}
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(item_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else if let Some(&hidden_type) =
borrowck_result.concrete_opaque_types.get(&self.def_id)
{
debug!(?hidden_type, "found constraint");
self.insert_found(hidden_type);
} else if let Err(guar) = tcx
.type_of_opaque_hir_typeck(self.def_id)
.instantiate_identity()
.error_reported()
{
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else {
self.non_defining_use_in_defining_scope(item_def_id);
DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) {
Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)),
Ok(concrete_opaque_types) => {
if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) {
debug!(?hidden_type, "found constraint");
self.insert_found(hidden_type);
} else if let Err(guar) = tcx
.type_of_opaque_hir_typeck(self.def_id)
.instantiate_identity()
.error_reported()
{
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else {
self.non_defining_use_in_defining_scope(item_def_id);
}
}
}
},
}
}
}
@ -264,20 +262,20 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
Ty::new_diverging_default(tcx)
}
}
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(owner_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
Ty::new_error(tcx, guar)
} else if let Some(hidden_ty) = borrowck_result.concrete_opaque_types.get(&def_id) {
hidden_ty.ty
} else {
let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
if let Err(guar) = hir_ty.error_reported() {
Ty::new_error(tcx, guar)
DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) {
Ok(concrete_opaque_types) => {
if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) {
hidden_ty.ty
} else {
hir_ty
let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
if let Err(guar) = hir_ty.error_reported() {
Ty::new_error(tcx, guar)
} else {
hir_ty
}
}
}
}
Err(guar) => Ty::new_error(tcx, guar),
},
}
}

View file

@ -955,7 +955,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
// Run unsafety check because it's responsible for stealing and
// deallocating THIR.
tcx.ensure_ok().check_unsafety(def_id);
tcx.ensure_ok().mir_borrowck(def_id)
if !tcx.is_typeck_child(def_id.to_def_id()) {
tcx.ensure_ok().mir_borrowck(def_id)
}
});
});
sess.time("MIR_effect_checking", || {

View file

@ -28,7 +28,7 @@ macro_rules! arena_types {
rustc_middle::mir::Body<'tcx>
>,
[decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>,
[decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>,
[decode] borrowck_result: rustc_middle::mir::ConcreteOpaqueTypes<'tcx>,
[] resolver: rustc_data_structures::steal::Steal<(
rustc_middle::ty::ResolverAstLowering,
std::sync::Arc<rustc_ast::Crate>,

View file

@ -10,7 +10,6 @@ use rustc_index::bit_set::BitMatrix;
use rustc_index::{Idx, IndexVec};
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_span::{Span, Symbol};
use smallvec::SmallVec;
use super::{ConstValue, SourceInfo};
use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt, fold_regions};
@ -85,16 +84,11 @@ impl Debug for CoroutineLayout<'_> {
}
}
#[derive(Debug, TyEncodable, TyDecodable, HashStable)]
pub struct BorrowCheckResult<'tcx> {
/// All the opaque types that are restricted to concrete types
/// by this function. Unlike the value in `TypeckResults`, this has
/// unerased regions.
pub concrete_opaque_types: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
pub used_mut_upvars: SmallVec<[FieldIdx; 8]>,
pub tainted_by_errors: Option<ErrorGuaranteed>,
}
/// All the opaque types that are restricted to concrete types
/// by this function. Unlike the value in `TypeckResults`, this has
/// unerased regions.
#[derive(Default, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct ConcreteOpaqueTypes<'tcx>(pub FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>);
/// The result of the `mir_const_qualif` query.
///

View file

@ -1153,11 +1153,10 @@ rustc_queries! {
return_result_from_ensure_ok
}
/// Borrow-checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> {
/// Borrow-checks the given typeck root, e.g. functions, const/static items,
/// and its children, e.g. closures, inline consts.
query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }
}
/// Gets a complete map from all types to their inherent impls.

View file

@ -501,7 +501,7 @@ impl_decodable_via_ref! {
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
&'tcx traits::ImplSource<'tcx, ()>,
&'tcx mir::Body<'tcx>,
&'tcx mir::BorrowCheckResult<'tcx>,
&'tcx mir::ConcreteOpaqueTypes<'tcx>,
&'tcx ty::List<ty::BoundVariableKind>,
&'tcx ty::ListWithCachedTypeInfo<ty::Clause<'tcx>>,
&'tcx ty::List<FieldIdx>,

View file

@ -498,8 +498,11 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
}
// We only need to borrowck non-synthetic MIR.
let tainted_by_errors =
if !tcx.is_synthetic_mir(def) { tcx.mir_borrowck(def).tainted_by_errors } else { None };
let tainted_by_errors = if !tcx.is_synthetic_mir(def) {
tcx.mir_borrowck(tcx.typeck_root_def_id(def.to_def_id()).expect_local()).err()
} else {
None
};
let is_fn_like = tcx.def_kind(def).is_fn_like();
if is_fn_like {
@ -795,7 +798,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def: LocalDefId) -> &IndexVec<Promoted, Body<'_
}
if !tcx.is_synthetic_mir(def) {
tcx.ensure_done().mir_borrowck(def);
tcx.ensure_done().mir_borrowck(tcx.typeck_root_def_id(def.to_def_id()).expect_local());
}
let mut promoted = tcx.mir_promoted(def).1.steal();

View file

@ -0,0 +1,24 @@
//@ edition:2021
struct AnyOption<T>(T);
impl<T> AnyOption<T> {
const NONE: Option<T> = None;
}
// This is an unfortunate side-effect of borrowchecking nested items
// together with their parent. Evaluating the `AnyOption::<_>::NONE`
// pattern for exhaustiveness checking relies on the layout of the
// async block. This layout relies on `optimized_mir` of the nested
// item which is now borrowck'd together with its parent. As
// borrowck of the parent requires us to have already lowered the match,
// this is a query cycle.
fn uwu() {}
fn defines() {
match Some(async {}) {
AnyOption::<_>::NONE => {}
//~^ ERROR cycle detected when building THIR for `defines`
_ => {}
}
}
fn main() {}

View file

@ -0,0 +1,64 @@
error[E0391]: cycle detected when building THIR for `defines`
--> $DIR/non-structural-match-types-cycle-err.rs:19:9
|
LL | AnyOption::<_>::NONE => {}
| ^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires evaluating type-level constant...
--> $DIR/non-structural-match-types-cycle-err.rs:5:5
|
LL | const NONE: Option<T> = None;
| ^^^^^^^^^^^^^^^^^^^^^
note: ...which requires const-evaluating + checking `<impl at $DIR/non-structural-match-types-cycle-err.rs:4:1: 4:21>::NONE`...
--> $DIR/non-structural-match-types-cycle-err.rs:5:5
|
LL | const NONE: Option<T> = None;
| ^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`...
= note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`...
note: ...which requires optimizing MIR for `defines::{closure#0}`...
--> $DIR/non-structural-match-types-cycle-err.rs:18:16
|
LL | match Some(async {}) {
| ^^^^^
note: ...which requires elaborating drops for `defines::{closure#0}`...
--> $DIR/non-structural-match-types-cycle-err.rs:18:16
|
LL | match Some(async {}) {
| ^^^^^
note: ...which requires borrow-checking `defines`...
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `defines`...
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
note: ...which requires checking if `defines` contains FFI-unwind calls...
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
note: ...which requires building MIR for `defines`...
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
note: ...which requires match-checking `defines`...
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
= note: ...which again requires building THIR for `defines`, completing the cycle
note: cycle used when unsafety-checking `defines`
--> $DIR/non-structural-match-types-cycle-err.rs:17:1
|
LL | fn defines() {
| ^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0391`.

View file

@ -1,6 +1,4 @@
//@ edition:2021
#![feature(const_async_blocks)]
struct AnyOption<T>(T);
impl<T> AnyOption<T> {
const NONE: Option<T> = None;
@ -19,11 +17,5 @@ fn defines() {
//~^ ERROR constant of non-structural type
_ => {}
}
match Some(async {}) {
AnyOption::<_>::NONE => {}
//~^ ERROR constant of non-structural type
_ => {}
}
}
fn main() {}

View file

@ -1,5 +1,5 @@
error: constant of non-structural type `Option<fn() {uwu}>` in a pattern
--> $DIR/non-structural-match-types.rs:12:9
--> $DIR/non-structural-match-types.rs:10:9
|
LL | impl<T> AnyOption<T> {
| --------------------
@ -11,8 +11,8 @@ LL | AnyOption::<_>::NONE => {}
|
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
error: constant of non-structural type `Option<{closure@$DIR/non-structural-match-types.rs:17:16: 17:18}>` in a pattern
--> $DIR/non-structural-match-types.rs:18:9
error: constant of non-structural type `Option<{closure@$DIR/non-structural-match-types.rs:15:16: 15:18}>` in a pattern
--> $DIR/non-structural-match-types.rs:16:9
|
LL | impl<T> AnyOption<T> {
| --------------------
@ -24,19 +24,5 @@ LL | AnyOption::<_>::NONE => {}
|
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
error: constant of non-structural type `Option<{async block@$DIR/non-structural-match-types.rs:23:16: 23:21}>` in a pattern
--> $DIR/non-structural-match-types.rs:24:9
|
LL | impl<T> AnyOption<T> {
| --------------------
LL | const NONE: Option<T> = None;
| --------------------- constant defined here
...
LL | AnyOption::<_>::NONE => {}
| ^^^^^^^^^^^^^^^^^^^^ constant of non-structural type
|
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
= note: `ResumeTy` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors