mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
Merge 092d8ce411
into 65fa0ab924
This commit is contained in:
commit
39a34dfd32
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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, ®ioncx, &opt_closure_req, &borrow_set);
|
||||
polonius::dump_polonius_mir(
|
||||
&infcx,
|
||||
body,
|
||||
®ioncx,
|
||||
&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, ®ioncx, &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, ®ioncx, &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,
|
||||
®ioncx,
|
||||
&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) };
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>(
|
||||
|
@ -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() {
|
||||
|
@ -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`.
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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: ®ion_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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)"),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user