diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 33b80c4b03d6f..660f4c50c6b5d 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -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 diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 1f087b092346e..ea83e82c339e9 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -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() } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 7511a55b03ae5..7c508ff0902bb 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -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]; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3bec07afa0fe0..0e481012e7290 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -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, 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, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3e075d420a3a9..dc0808e2c6159 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -20,10 +20,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 +36,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 +52,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, 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 +70,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 +85,6 @@ mod constraints; mod dataflow; mod def_use; mod diagnostics; -mod member_constraints; mod nll; mod path_utils; mod place_ext; @@ -126,20 +135,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); - // We need to manually borrowck all nested bodies from the HIR as - // we do not generate MIR for dead code. Not doing so causes us to - // never check closures in dead code. - let nested_bodies = tcx.nested_bodies_within(def); - for def_id in nested_bodies { - root_cx.get_or_insert_nested(def_id); - } - - 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 } } @@ -151,6 +148,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 @@ -289,6 +288,24 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { } } +struct BorrowckState<'tcx> { + infcx: BorrowckInferCtxt<'tcx>, + body_owned: Body<'tcx>, + promoted: IndexVec>, + move_data: MoveData<'tcx>, + borrow_set: BorrowSet<'tcx>, + location_table: PoloniusLocationTable, + location_map: Rc, + universal_region_relations: Frozen>, + region_bound_pairs: Frozen>, + known_type_outlives_obligations: Frozen>>, + constraints: MirTypeckRegionConstraints<'tcx>, + deferred_closure_requirements: DeferredClosureRequirements<'tcx>, + deferred_opaque_type_errors: Vec>, + polonius_facts: Option>, + polonius_context: Option, +} + /// Perform the actual borrow checking. /// /// Use `consumer_options: None` for the default behavior of returning @@ -297,11 +314,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, -) -> (PropagatedBorrowCheckResults<'tcx>, Option>>) { +) -> BorrowckState<'tcx> { let tcx = root_cx.tcx; let infcx = BorrowckInferCtxt::new(tcx, def); let (input_body, promoted) = tcx.mir_promoted(def); @@ -322,6 +339,7 @@ fn do_mir_borrowck<'tcx>( let body = &body_owned; // no further changes let location_table = PoloniusLocationTable::new(body); + let location_map = Rc::new(DenseLocationMap::new(body)); let move_data = MoveData::gather_moves(body, tcx, |_| true); @@ -332,6 +350,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, + 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>>) { + 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, @@ -341,18 +433,23 @@ fn do_mir_borrowck<'tcx>( nll_errors, polonius_diagnostics, } = nll::compute_regions( - root_cx, &infcx, - universal_regions, body, - &promoted, &location_table, - flow_inits, &move_data, &borrow_set, + location_map, consumer_options, + universal_region_relations, + constraints, + polonius_facts, + polonius_context, ); + if nll_errors.has_errors().is_none() && !deferred_opaque_type_errors.is_empty() { + regioncx.emit_deferred_opaque_type_errors(root_cx, &infcx, deferred_opaque_type_errors); + } + // Dump MIR results into a file, if that is enabled. This lets us // write unit-tests, as well as helping with debugging. nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set); @@ -578,12 +675,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 } } diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs deleted file mode 100644 index bdd0f6fe11e0f..0000000000000 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ /dev/null @@ -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, - - /// 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>, - - /// 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, -} - -/// Represents a `R0 member of [R1..Rn]` constraint -#[derive(Debug)] -pub(crate) struct MemberConstraint<'tcx> { - next_constraint: Option, - - /// 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( - 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 { - 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 { - let mut next = self.first_constraints.get(&member_region_vid).cloned(); - std::iter::from_fn(move || -> Option { - 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 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>, - 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; - } - } - } -} diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index fe899bb054fa9..74a993d7d3610 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -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::consumers::{ConsumerOptions, RustcFacts}; use crate::diagnostics::RegionErrors; -use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; +use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; use crate::region_infer::RegionInferenceContext; -use crate::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,51 +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> { + 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>, location_table: &PoloniusLocationTable, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, + location_map: Rc, consumer_options: Option, + universal_region_relations: Frozen>, + constraints: MirTypeckRegionConstraints<'tcx>, + mut polonius_facts: Option>, + polonius_context: Option, ) -> 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), - ); - // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( &mut polonius_facts, @@ -164,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), diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b4ff3d66f3d5b..23a67e2b42d27 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -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}; @@ -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; @@ -195,20 +192,6 @@ pub struct RegionInferenceContext<'tcx> { scc_annotations: IndexVec, - /// 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, - - /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. - member_constraints: Rc>, - - /// 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, - /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap>, @@ -225,32 +208,6 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Frozen>, } -/// 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 @@ -343,7 +300,6 @@ enum Trace<'a, 'tcx> { StartRegion, FromGraph(&'a OutlivesConstraint<'tcx>), FromStatic(RegionVid), - FromMember(RegionVid, RegionVid, Span), NotVisited, } @@ -453,7 +409,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, - mut member_constraints, universe_causes, type_tests, } = constraints; @@ -467,7 +422,6 @@ 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); @@ -492,9 +446,6 @@ 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, @@ -502,9 +453,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_graph, constraint_sccs, scc_annotations, - rev_scc_graph: None, - member_constraints, - member_constraints_applied: Vec::new(), universe_causes, scc_values, type_tests, @@ -662,19 +610,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. @@ -719,12 +654,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() { @@ -743,160 +672,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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::>() - }); - + pub(super) fn propagate_constraints(&mut self) { // 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 = 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.scc_universe(scc).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"), + for scc_a in self.constraint_sccs.all_sccs() { + debug!(?scc_a); + // 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); } - }) 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, - }); } } @@ -1138,7 +925,6 @@ 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; @@ -1677,43 +1463,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - #[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 @@ -1837,20 +1586,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)); @@ -1897,15 +1632,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 @@ -2232,11 +1958,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. /// @@ -2269,7 +1990,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`. diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 25cbd579ea1cd..f44ff1fe5672c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,234 +1,659 @@ -use rustc_data_structures::fx::FxIndexMap; +use std::iter; +use std::rc::Rc; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_index::IndexVec; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; +use rustc_infer::traits::ObligationCause; use rustc_macros::extension; +use rustc_middle::bug; +use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{ - self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, fold_regions, + self, DefiningScopeKind, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, + Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, 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 rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use tracing::{debug, debug_span, 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, RegionTracker}; +use crate::constraints::ConstraintSccIndex; +use crate::type_check::canonical::fully_perform_op_raw; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::type_check::{Locations, MirTypeckRegionConstraints}; +use crate::universal_regions::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`. +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>), +} + +impl<'tcx> BorrowCheckRootCtxt<'tcx> { + pub(crate) fn handle_opaque_type_uses(&mut self, borrowck_state: &mut BorrowckState<'tcx>) { + let BorrowckState { + infcx, + body_owned, + universal_region_relations, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + location_map, + deferred_opaque_type_errors, + .. + } = borrowck_state; + + handle_opaque_type_uses( + self, + infcx, + body_owned, + universal_region_relations, + region_bound_pairs, + known_type_outlives_obligations, + location_map, + constraints, + deferred_opaque_type_errors, + ) + } +} + +struct RegionCtxt<'a, 'tcx> { + infcx: &'a BorrowckInferCtxt<'tcx>, + definitions: IndexVec>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + constraint_sccs: ConstraintSccs, + annotations: IndexVec, + rev_scc_graph: ReverseSccGraph, + scc_values: RegionValues, +} + +impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { + /// Creates a new `RegionCtxt` used to compute defining opaque type uses. /// - /// [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, OpaqueHiddenType<'tcx>>, - ) { - let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = - FxIndexMap::default(); - - for (opaque_type_key, concrete_type) in opaque_ty_decls { - debug!(?opaque_type_key, ?concrete_type); - - let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> = - vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)]; - - 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", - ) - }); + /// This does not yet propagate region values. This is instead done lazily + /// when applying member constraints. + fn new( + infcx: &'a BorrowckInferCtxt<'tcx>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + location_map: Rc, + constraints: &MirTypeckRegionConstraints<'tcx>, + ) -> RegionCtxt<'a, 'tcx> { + let mut definitions: IndexVec<_, _> = infcx + .get_region_var_infos() + .iter() + .map(|info| RegionDefinition::new(info.universe, info.origin)) + .collect(); - arg_regions.push((vid, named)); - named - }); - debug!(?opaque_type_key, ?arg_regions); - - 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 ty = - infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type); - - // 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 - { - continue; + for (external_name, variable) in + universal_region_relations.universal_regions.named_universal_regions_iter() + { + definitions[variable].external_name = Some(external_name); + } + + let universal_regions = &universal_region_relations.universal_regions; + let fr_static = universal_regions.fr_static; + let (constraint_sccs, annotations) = + 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); } + _ => {} } + } - root_cx.add_concrete_opaque_type( - opaque_type_key.def_id, - OpaqueHiddenType { span: concrete_type.span, ty }, - ); - - // Check that all opaque types have the same region parameters if they have the same - // non-region parameters. This is necessary because within the new solver we perform - // various query operations modulo regions, and thus could unsoundly select some impls - // 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 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, - }); - } + RegionCtxt { + infcx, + definitions, + universal_region_relations, + constraint_sccs, + annotations, + rev_scc_graph, + scc_values, } } - /// 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(&self, tcx: TyCtxt<'tcx>, ty: T) -> T - where - T: TypeFoldable>, + fn representative(&self, vid: RegionVid) -> RegionVid { + let scc = self.constraint_sccs.scc(vid); + self.annotations[scc].representative + } + + fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + fn eval_equal(&self, r1_vid: RegionVid, r2_vid: RegionVid) -> bool { + let r1 = self.constraint_sccs.scc(r1_vid); + let r2 = self.constraint_sccs.scc(r2_vid); + + if r1 == r2 { + return true; + } + + let universal_outlives = |sub, sup| { + self.scc_values.universal_regions_outlived_by(sub).all(|r1| { + self.scc_values + .universal_regions_outlived_by(sup) + .any(|r2| self.universal_region_relations.outlives(r2, r1)) + }) + }; + universal_outlives(r1, r2) && universal_outlives(r2, r1) + } +} + +pub(crate) fn handle_opaque_type_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + location_map: &Rc, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, +) { + let tcx = infcx.tcx; + let opaque_types = infcx.clone_opaque_types(); + if opaque_types.is_empty() { + return; + } + + let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); + // 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::>(); + + debug!(?opaque_types); + + collect_defining_uses( + root_cx, + infcx, + constraints, + deferred_errors, + universal_region_relations, + location_map, + &mut opaque_types, + ); + + apply_defining_uses( + root_cx, + infcx, + body, + &universal_region_relations.universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + &opaque_types, + ); + + if let Some((key, hidden_type)) = infcx + .inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(opaque_types_storage_num_entries) + .next() { - fold_regions(tcx, ty, |region, _| match region.kind() { + let opaque_type_string = tcx.def_path_str(key.def_id); + let msg = format!("unexpected cyclic definition of `{opaque_type_string}`"); + infcx.dcx().span_delayed_bug(hidden_type.span, msg); + } + + let _ = infcx.take_opaque_types(); +} + +#[derive(Debug)] +struct DefiningUse<'tcx> { + opaque_type_key: OpaqueTypeKey<'tcx>, + arg_regions: Vec, + hidden_type: OpaqueHiddenType<'tcx>, +} + +fn collect_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + location_map: &Rc, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) { + let tcx = infcx.tcx; + let mut rcx = + RegionCtxt::new(infcx, universal_region_relations, Rc::clone(location_map), constraints); + + let mut defining_uses = vec![]; + for &(opaque_type_key, hidden_type) in opaque_types { + let non_nll_opaque_type_key = + opaque_type_key.fold_captured_lifetime_args(rcx.infcx.tcx, |r| { + let vid = rcx.representative(r.as_var()); + rcx.definitions[vid].external_name.unwrap_or(r) + }); + if let Err(_) = check_opaque_type_parameter_valid( + infcx, + non_nll_opaque_type_key, + hidden_type.span, + DefiningScopeKind::MirBorrowck, + ) { + debug!(?non_nll_opaque_type_key, "not a defining use"); + continue; + } + + let arg_regions = iter::once(rcx.universal_regions().fr_static) + .chain( + opaque_type_key + .iter_captured_args(tcx) + .filter_map(|(_, arg)| arg.as_region()) + .map(Region::as_var), + ) + .collect(); + defining_uses.push(DefiningUse { opaque_type_key, arg_regions, hidden_type }); + } + + debug!(?defining_uses); + + apply_member_constraints(&mut rcx, &defining_uses); + map_defining_uses_to_definition_site(root_cx, &rcx, &defining_uses, deferred_errors); +} + +fn apply_member_constraints<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], +) { + // Start by collecting the member constraints of all defining uses. + // + // Applying member constraints can influence other member constraints, + // so we first collect and then apply them. + let mut member_constraints = Default::default(); + for defining_use in defining_uses { + let mut visitor = CollectMemberConstraintsVisitor { + rcx, + defining_use, + member_constraints: &mut member_constraints, + }; + defining_use.hidden_type.ty.visit_with(&mut visitor); + } + + debug!(?member_constraints); + for scc_a in rcx.constraint_sccs.all_sccs() { + debug!(?scc_a); + for &scc_b in rcx.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + rcx.scc_values.add_region(scc_a, scc_b); + } + + for defining_use in member_constraints.get(&scc_a).into_iter().flatten() { + apply_member_constraint(rcx, scc_a, &defining_use.arg_regions); + } + } +} + +#[instrument(level = "debug", skip(rcx))] +fn apply_member_constraint<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + member: ConstraintSccIndex, + arg_regions: &[RegionVid], +) { + // 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 = arg_regions + .iter() + .copied() + .map(|r| rcx.representative(r)) + .filter(|&choice_region| { + rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| { + rcx.universal_region_relations.outlives(choice_region, lower_bound) + }) + }) + .collect::>(); + 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 rcx.rev_scc_graph.upper_bounds(member) { + choice_regions + .retain(|&choice_region| rcx.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| { + rcx.universal_region_relations.outlives(r1, r2) + || rcx.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 = rcx.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = rcx.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; + }; + + debug!(?min_choice); + let min_choice_scc = rcx.constraint_sccs.scc(min_choice); + rcx.scc_values.add_region(member, min_choice_scc); +} + +struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + defining_use: &'b DefiningUse<'tcx>, + member_constraints: &'a mut FxHashMap>>, +} +impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { + let generics = self.cx().generics_of(def_id); + for arg in args.iter().skip(generics.parent_count) { + arg.visit_with(self); + } + } +} +impl<'tcx> TypeVisitor> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + // Invoked for each member region in the hidden type. If possible, we try to lift + // it to one of the `arg_regions` from the `opaque_type_key`. + // + // If there's a unique minimum choice, we emit a `'member: 'min_choice` constraint. + // Note that we do not require the two regions to be equal... TODO examples + fn visit_region(&mut self, r: Region<'tcx>) { + match r.kind() { + ty::ReBound(..) => return, ty::ReVar(vid) => { - let scc = self.constraint_sccs.scc(vid); + let scc = self.rcx.constraint_sccs.scc(vid); + self.member_constraints.entry(scc).or_default().push(self.defining_use); + } + _ => unreachable!(), + } + } - // 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); - } + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } - // Fallback: this will produce a cryptic error message. - _ => return region, + match *ty.kind() { + ty::Closure(def_id, args) + | ty::CoroutineClosure(def_id, args) + | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args), + + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = self.cx().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, arg) in std::iter::zip(variances, args.iter()) { + if v != ty::Bivariant { + arg.visit_with(self) } } + } - // 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; - } + _ => ty.super_visit_with(self), + } + } +} - // 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, - } +fn map_defining_uses_to_definition_site<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + rcx: &RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], + deferred_errors: &mut Vec>, +) { + for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses { + let opaque_type_key = opaque_type_key.fold_captured_lifetime_args(rcx.infcx.tcx, |r| { + let vid = rcx.representative(r.as_var()); + rcx.definitions[vid].external_name.unwrap() + }); + let _span = debug_span!( + "map_defining_uses_to_definition_site", + ?opaque_type_key, + ?arg_regions, + ?hidden_type + ); + // TODO: explain what's going on here + let mut visitor = + HiddenTypeMeh { rcx, arg_regions, opaque_type_key, hidden_type, deferred_errors }; + let hidden_type = hidden_type.fold_with(&mut visitor); + let ty = rcx + .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( + rcx.infcx.tcx, + hidden_type.span, + "deferred invalid opaque type args", + ) + }); + root_cx.add_concrete_opaque_type( + opaque_type_key.def_id, + OpaqueHiddenType { span: hidden_type.span, ty }, + ); + } +} +struct HiddenTypeMeh<'a, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + arg_regions: &'a [RegionVid], + + opaque_type_key: OpaqueTypeKey<'tcx>, + hidden_type: OpaqueHiddenType<'tcx>, + deferred_errors: &'a mut Vec>, +} + +impl<'tcx> HiddenTypeMeh<'_, 'tcx> { + 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 { + // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // causing type mismatches. + fold_regions(self.cx(), arg, |_, _| self.cx().lifetimes.re_erased) + } else { + arg.fold_with(self) } - _ => region, - }) + })) + } +} +impl<'tcx> TypeFolder> for HiddenTypeMeh<'_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r.kind() { + // ignore bound regions, keep visiting + ty::ReBound(_, _) => r, + _ => self + .arg_regions + .iter() + .copied() + .find(|&arg_vid| self.rcx.eval_equal(r.as_var(), arg_vid)) + .map(|r| { + let vid = self.rcx.representative(r); + self.rcx.definitions[vid].external_name.unwrap() + }) + .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", + ) + }), + } + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return ty; + } + + 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)) + } + + 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 { + // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // causing type mismatches. + fold_regions(self.cx(), s, |_, _| self.cx().lifetimes.re_erased) + } else { + s.fold_with(self) + } + })); + ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx) + } + + _ => ty.super_fold_with(self), + } + } +} + +fn apply_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) { + let tcx = infcx.tcx; + 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 locations = Locations::All(hidden_type.span); + if let Err(guar) = fully_perform_op_raw( + infcx, + body, + universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + locations, + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |ocx| { + let cause = ObligationCause::misc( + hidden_type.span, + body.source.def_id().expect_local(), + ); + let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty); + let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty); + ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution) + }, + "equating opaque types", + ), + ) { + root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + } } } @@ -262,19 +687,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, InvalidOpaqueTypeArgs<'tcx>> { + if let Some(guar) = self.tainted_by_errors() { + return Err(guar.into()); } - - if let Err(err) = 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, err.report(self)); - } + )?; let definition_ty = instantiated_ty .remap_generic_params_to_declaration_params( @@ -284,10 +706,131 @@ impl<'tcx> InferCtxt<'tcx> { ) .ty; - if let Err(e) = definition_ty.error_reported() { - return Ty::new_error(self.tcx, e); + 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>, + ) { + 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(&self, tcx: TyCtxt<'tcx>, ty: T) -> T + where + T: TypeFoldable>, + { + 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.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; + } - definition_ty + // 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, + }) } } diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 8e04791461b26..e8da85eccef16 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -5,7 +5,6 @@ use rustc_data_structures::graph; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_middle::ty::RegionVid; -use crate::RegionInferenceContext; use crate::constraints::ConstraintSccIndex; use crate::region_infer::ConstraintSccs; use crate::universal_regions::UniversalRegions; @@ -57,15 +56,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; - } - - self.rev_scc_graph = - Some(ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())); - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index f1427218cdb02..f856e3b49d72a 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -37,6 +37,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, @@ -193,7 +194,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, } diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 66b526fa02a50..db82f5fd52938 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -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>, + partial_results: FxHashMap>>, + closure_requirements_modulo_opaques: + FxHashMap>>, + final_results: FxHashMap>, tainted_by_errors: Option, } @@ -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,47 +73,144 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } } + pub(super) fn get_concrete_opaque_type( + &mut self, + def_id: LocalDefId, + ) -> Option>> { + 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); } - pub(super) 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.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.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.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> { - &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>, 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, + ) -> ( + Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed>, + Option>>, + ) { + 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); + + // We need to manually borrowck all nested bodies from the HIR as + // we do not generate MIR for dead code. Not doing so causes us to + // never check closures in dead code. + let nested_bodies = self.tcx.nested_bodies_within(root_def_id); + for def_id in nested_bodies { + if !self.final_results.contains_key(&def_id) { + self.compute_partial_results(def_id); + let _ = self.get_or_insert_final(def_id); + } + } + + #[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) } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 5143b2fa20598..652822fa7c892 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -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)] diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b3fa786a5177c..906a07ee33bb3 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -2,8 +2,9 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_middle::bug; -use rustc_middle::mir::ConstraintCategory; +use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -13,8 +14,70 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; use tracing::{debug, instrument}; -use super::{Locations, NormalizeLocation, TypeChecker}; +use super::{Locations, MirTypeckRegionConstraints, NormalizeLocation, TypeChecker}; +use crate::BorrowckInferCtxt; use crate::diagnostics::ToUniverseInfo; +use crate::type_check::constraint_conversion; +use crate::universal_regions::UniversalRegions; + +#[instrument(skip(infcx, constraints, op), level = "trace")] +pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>( + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + op: Op, +) -> Result +where + Op: type_op::TypeOp<'tcx, Output = R>, + Op::ErrorInfo: ToUniverseInfo<'tcx>, +{ + let old_universe = infcx.universe(); + + let TypeOpOutput { output, constraints: query_constraints, error_info } = + op.fully_perform(infcx, locations.span(body))?; + if cfg!(debug_assertions) { + let data = infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + panic!("leftover region constraints: {data:#?}"); + } + } + + debug!(?output, ?query_constraints); + + if let Some(data) = query_constraints { + constraint_conversion::ConstraintConversion::new( + infcx, + universal_regions, + region_bound_pairs, + infcx.param_env, + known_type_outlives_obligations, + locations, + locations.span(body), + category, + constraints, + ) + .convert_all(data); + } + + // If the query has created new universes and errors are going to be emitted, register the + // cause of these new universes for improved diagnostics. + let universe = infcx.universe(); + if old_universe != universe + && let Some(error_info) = error_info + { + let universe_info = error_info.to_universe_info(old_universe); + for u in (old_universe + 1)..=universe { + constraints.universe_causes.insert(u, universe_info.clone()); + } + } + + Ok(output) +} impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Given some operation `op` that manipulates types, proves @@ -27,7 +90,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// **Any `rustc_infer::infer` operations that might generate region /// constraints should occur within this method so that those /// constraints can be properly localized!** - #[instrument(skip(self, op), level = "trace")] pub(super) fn fully_perform_op( &mut self, locations: Locations, @@ -38,36 +100,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Op: type_op::TypeOp<'tcx, Output = R>, Op::ErrorInfo: ToUniverseInfo<'tcx>, { - let old_universe = self.infcx.universe(); - - let TypeOpOutput { output, constraints, error_info } = - op.fully_perform(self.infcx, locations.span(self.body))?; - if cfg!(debug_assertions) { - let data = self.infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - panic!("leftover region constraints: {data:#?}"); - } - } - - debug!(?output, ?constraints); - - if let Some(data) = constraints { - self.push_region_constraints(locations, category, data); - } - - // If the query has created new universes and errors are going to be emitted, register the - // cause of these new universes for improved diagnostics. - let universe = self.infcx.universe(); - if old_universe != universe - && let Some(error_info) = error_info - { - let universe_info = error_info.to_universe_info(old_universe); - for u in (old_universe + 1)..=universe { - self.constraints.universe_causes.insert(u, universe_info.clone()); - } - } - - Ok(output) + fully_perform_op_raw( + self.infcx, + self.body, + self.universal_regions, + self.region_bound_pairs, + self.known_type_outlives_obligations, + self.constraints, + locations, + category, + op, + ) } pub(super) fn instantiate_canonical( diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 92732aba29ba3..c28cd8456f532 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -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>, @@ -156,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 { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c51225712093..9d143e14ef74a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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)*) => ({ @@ -69,12 +70,11 @@ macro_rules! span_mirbug { }) } -mod canonical; +pub(crate) mod canonical; 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 @@ -118,7 +118,6 @@ pub(crate) fn type_check<'a, 'tcx>( 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(), }; @@ -159,6 +158,7 @@ pub(crate) fn type_check<'a, 'tcx>( polonius_facts, borrow_set, constraints: &mut constraints, + deferred_closure_requirements: Default::default(), polonius_liveness, }; @@ -169,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, @@ -181,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,6 +227,7 @@ struct TypeChecker<'a, 'tcx> { polonius_facts: &'a mut Option, 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, } @@ -233,12 +237,15 @@ struct TypeChecker<'a, 'tcx> { pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, + pub(crate) region_bound_pairs: Frozen>, + pub(crate) known_type_outlives_obligations: Frozen>>, + pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>, pub(crate) polonius_context: Option, } /// 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. @@ -265,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>, pub(crate) type_tests: Vec>, @@ -275,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, @@ -368,14 +373,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() @@ -2502,7 +2499,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: GenericArgsRef<'tcx>, locations: Locations, ) -> ty::InstantiatedPredicates<'tcx> { - if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) { + let (closure_requirements, needs_defer) = + self.root_cx.get_closure_requirements_modulo_opaques(def_id); + if needs_defer { + self.deferred_closure_requirements.push((def_id, args, locations)); + } + + if let Some(closure_requirements) = closure_requirements { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, @@ -2556,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; } diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs deleted file mode 100644 index 341c50c37f6db..0000000000000 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ /dev/null @@ -1,333 +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, 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> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - 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); - } - } - } -} diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index c11e14d214c42..43081c2d96825 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -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 diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs index 73190574667f3..5678686ab991b 100644 --- a/compiler/rustc_data_structures/src/frozen.rs +++ b/compiler/rustc_data_structures/src/frozen.rs @@ -46,7 +46,7 @@ //! Frozen::freeze(new_bar)`). /// An owned immutable value. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Frozen(T); impl Frozen { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 60187abd55897..a37700191a005 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -227,6 +227,10 @@ fn typeck_with_inspect<'tcx>( fcx.select_obligations_where_possible(|_| {}); + if fcx.next_trait_solver() { + fcx.handle_opaque_type_uses_next(); + } + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); // This must be the last thing before `report_ambiguity_errors`. diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index e0224f8c6e1b4..a4b83a5efec21 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -1,5 +1,123 @@ -use super::FnCtxt; -impl<'tcx> FnCtxt<'_, 'tcx> { +use rustc_middle::ty::{ + DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, Ty, TypeVisitableExt, +}; +use rustc_trait_selection::opaque_types::{ + InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, +}; +use tracing::{debug, instrument}; + +use crate::FnCtxt; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + #[instrument(level = "debug", skip(self))] + pub(super) fn handle_opaque_type_uses_next(&mut self) { + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + // + // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped + // at the end of typeck. Ideally we can feed some query here to no longer define + // new opaque uses but instead always reveal by using the definitions inferred here. + let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types(); + let num_entries = self.inner.borrow_mut().opaque_types().num_entries(); + let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries)); + debug_assert_eq!(prev, None); + for entry in &mut opaque_types { + *entry = self.resolve_vars_if_possible(*entry); + } + debug!(?opaque_types); + + self.collect_defining_uses(&opaque_types); + self.apply_defining_uses(&opaque_types); + } + + fn collect_defining_uses( + &mut self, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + ) { + let tcx = self.tcx; + let typeck_results = &mut *self.typeck_results.borrow_mut(); + for &(opaque_type_key, hidden_type) in opaque_types { + match check_opaque_type_parameter_valid( + &self, + opaque_type_key, + hidden_type.span, + DefiningScopeKind::HirTypeck, + ) { + Ok(()) => {} + Err(InvalidOpaqueTypeArgs::AlreadyReported(guar)) => { + typeck_results + .concrete_opaque_types + .insert(opaque_type_key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + } + // Not a defining use, ignore and treat as revealing use instead. + Err( + InvalidOpaqueTypeArgs::NotAParam { .. } + | InvalidOpaqueTypeArgs::DuplicateParam { .. }, + ) => continue, + } + + // We ignore uses of the opaque if they have any inference variables + // as this can frequently happen with recursive calls. + // + // See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`. + if hidden_type.ty.has_non_region_infer() { + continue; + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + tcx, + DefiningScopeKind::HirTypeck, + ); + + if let Some(prev) = + typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type) + { + let entry = + typeck_results.concrete_opaque_types.get_mut(&opaque_type_key.def_id).unwrap(); + if prev.ty != hidden_type.ty { + if let Some(guar) = typeck_results.tainted_by_errors { + entry.ty = Ty::new_error(tcx, guar); + } else { + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit()); + entry.ty = Ty::new_error(tcx, guar); + } + } + + // Pick a better span if there is one. + // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. + entry.span = prev.span.substitute_dummy(hidden_type.span); + } + } + + // FIXME(-Znext-solver): Check that all opaques have been defined hre. + } + + fn apply_defining_uses( + &mut self, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + ) { + let tcx = self.tcx; + for &(key, hidden_type) in opaque_types { + let Some(&expected) = + self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id) + else { + let guar = + tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope"); + self.typeck_results + .borrow_mut() + .concrete_opaque_types + .insert(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + self.set_tainted_by_errors(guar); + continue; + }; + + let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); + self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + } + } + /// We may in theory add further uses of an opaque after cloning the opaque /// types storage during writeback when computing the defining uses. /// diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 9be041f75d769..96090e4b408ab 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -9,17 +9,21 @@ //! which creates a new `TypeckResults` which doesn't contain any inference variables. use std::mem; +use std::ops::ControlFlow; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_infer::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; use rustc_middle::ty::{ - self, DefiningScopeKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, fold_regions, + self, DefiningScopeKind, OpaqueHiddenType, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + fold_regions, }; use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; @@ -530,26 +534,34 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_opaque_types_next(&mut self) { + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + for (&def_id, &hidden_type) in &fcx_typeck_results.concrete_opaque_types { + assert!(!hidden_type.has_infer()); + self.typeck_results.concrete_opaque_types.insert(def_id, hidden_type); + } + } + #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { + if self.fcx.next_trait_solver() { + return self.visit_opaque_types_next(); + } + let tcx = self.tcx(); - // We clone the opaques instead of stealing them here as they are still used for - // normalization in the next generation trait solver. - let opaque_types = self.fcx.infcx.clone_opaque_types(); + let opaque_types = self.fcx.infcx.take_opaque_types(); let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries(); let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries)); debug_assert_eq!(prev, None); for (opaque_type_key, hidden_type) in opaque_types { let hidden_type = self.resolve(hidden_type, &hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span); - - if !self.fcx.next_trait_solver() { - if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; - } + if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; } if let Err(err) = check_opaque_type_parameter_valid( @@ -595,6 +607,30 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { entry.span = prev.span.substitute_dummy(hidden_type.span); } } + + let recursive_opaques: Vec<_> = self + .typeck_results + .concrete_opaque_types + .iter() + .filter(|&(&def_id, hidden_ty)| { + hidden_ty + .ty + .visit_with(&mut HasRecursiveOpaque { + def_id, + seen: Default::default(), + opaques: &self.typeck_results.concrete_opaque_types, + tcx, + }) + .is_break() + }) + .map(|(def_id, hidden_ty)| (*def_id, hidden_ty.span)) + .collect(); + for (def_id, span) in recursive_opaques { + let guar = self.fcx.dcx().span_err(span, "cannot resolve opaque type"); + self.typeck_results + .concrete_opaque_types + .insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) }); + } } fn visit_field_id(&mut self, hir_id: HirId) { @@ -959,3 +995,34 @@ impl<'tcx> TypeFolder> for EagerlyNormalizeConsts<'tcx> { self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct) } } + +struct HasRecursiveOpaque<'a, 'tcx> { + def_id: LocalDefId, + seen: FxHashSet, + opaques: &'a FxIndexMap>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeVisitor> for HasRecursiveOpaque<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Opaque, alias_ty) = *t.kind() + && let Some(def_id) = alias_ty.def_id.as_local() + { + if self.def_id == def_id { + return ControlFlow::Break(()); + } + + if self.seen.insert(def_id) + && let Some(hidden_ty) = self.opaques.get(&def_id) + { + ty::EarlyBinder::bind(hidden_ty.ty) + .instantiate(self.tcx, alias_ty.args) + .visit_with(self)?; + } + } + + t.super_visit_with(self) + } +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b408d76010d7b..810cc5e8db446 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -944,6 +944,10 @@ impl<'tcx> InferCtxt<'tcx> { storage.var_infos.clone() } + pub fn has_opaque_types_in_storage(&self) -> bool { + !self.inner.borrow().opaque_type_storage.is_empty() + } + #[instrument(level = "debug", skip(self), ret)] pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> { self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0759fa3da428a..e835a9c943a50 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3340,11 +3340,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() } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2d69a1c2b553e..3d72257a87c06 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -883,12 +883,12 @@ impl<'tcx> OpaqueHiddenType<'tcx> { // // We erase regions when doing this during HIR typeck. let this = match defining_scope_kind { - DefiningScopeKind::HirTypeck => tcx.erase_regions(self), + DefiningScopeKind::HirTypeck => fold_regions(tcx, self, |_, _| tcx.lifetimes.re_erased), DefiningScopeKind::MirBorrowck => self, }; let result = this.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)); if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) { - assert_eq!(result.ty, tcx.erase_regions(result.ty)); + assert_eq!(result.ty, fold_regions(tcx, result.ty, |_, _| tcx.lifetimes.re_erased)); } result } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index fc5dad9a3edf5..d31a82b68cb88 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; -use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; @@ -1075,29 +1074,6 @@ where self.add_goals(GoalSource::AliasWellFormed, goals); } - // Do something for each opaque/hidden pair defined with `def_id` in the - // current inference context. - pub(super) fn probe_existing_opaque_ty( - &mut self, - key: ty::OpaqueTypeKey, - ) -> Option<(ty::OpaqueTypeKey, I::Ty)> { - // We shouldn't have any duplicate entries when using - // this function during `TypingMode::Analysis`. - let duplicate_entries = self.delegate.clone_duplicate_opaque_types(); - assert!(duplicate_entries.is_empty(), "unexpected duplicates: {duplicate_entries:?}"); - let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter( - |(candidate_key, _)| { - candidate_key.def_id == key.def_id - && DeepRejectCtxt::relate_rigid_rigid(self.cx()) - .args_may_unify(candidate_key.args, key.args) - }, - ); - let first = matching.next(); - let second = matching.next(); - assert_eq!(second, None); - first - } - // Try to evaluate a const, or return `None` if the const is too generic. // This doesn't mean the const isn't evaluatable, though, and should be treated // as an ambiguity rather than no-solution. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index df3ad1e468bb8..66a8ea3e839c7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -1,13 +1,12 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the current `TypingMode`. -use rustc_index::bit_set::GrowableBitSet; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::GoalSource; -use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions}; +use rustc_type_ir::{self as ty, GenericArgKind, Interner, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect}; +use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; impl EvalCtxt<'_, D> where @@ -49,54 +48,29 @@ where return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - // FIXME: This may have issues when the args contain aliases... - match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) { - Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { - return self.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); - } - Err(_) => { - return Err(NoSolution); - } - Ok(()) => {} - } - // Prefer opaques registered already. - let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args }; - // FIXME: This also unifies the previous hidden type with the expected. - // - // If that fails, we insert `expected` as a new hidden type instead of - // eagerly emitting an error. - let existing = self.probe_existing_opaque_ty(opaque_type_key); - if let Some((candidate_key, candidate_ty)) = existing { - return self - .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup { - result: *result, - }) - .enter(|ecx| { - for (a, b) in std::iter::zip( - candidate_key.args.iter(), - opaque_type_key.args.iter(), - ) { - ecx.eq(goal.param_env, a, b)?; - } - ecx.eq(goal.param_env, candidate_ty, expected)?; - ecx.add_item_bounds_for_hidden_type( - def_id.into(), - candidate_key.args, - goal.param_env, - candidate_ty, - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); + // We structurally normalize the args so that we're able to detect defining uses + // later on. It also reduces the amount of duplicate definitions in the + // `opaque_type_storage`. + let normalized_args = + cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() { + GenericArgKind::Lifetime(lt) => Ok(lt.into()), + GenericArgKind::Type(ty) => { + self.structurally_normalize_ty(goal.param_env, ty).map(Into::into) + } + GenericArgKind::Const(ct) => { + self.structurally_normalize_const(goal.param_env, ct).map(Into::into) + } + }))?; + + let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args }; + if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected) + { + self.eq(goal.param_env, expected, prev)?; } - // Otherwise, define a new opaque type - let prev = self.register_hidden_type_in_storage(opaque_type_key, expected); - assert_eq!(prev, None); self.add_item_bounds_for_hidden_type( def_id.into(), - opaque_ty.args, + normalized_args, goal.param_env, expected, ); @@ -168,44 +142,3 @@ where } } } - -/// Checks whether each generic argument is simply a unique generic placeholder. -/// -/// FIXME: Interner argument is needed to constrain the `I` parameter. -fn uses_unique_placeholders_ignoring_regions( - _cx: I, - args: I::GenericArgs, -) -> Result<(), NotUniqueParam> { - let mut seen = GrowableBitSet::default(); - for arg in args.iter() { - match arg.kind() { - // Ignore regions, since we can't resolve those in a canonicalized - // query in the trait solver. - ty::GenericArgKind::Lifetime(_) => {} - ty::GenericArgKind::Type(t) => match t.kind() { - ty::Placeholder(p) => { - if !seen.insert(p.var()) { - return Err(NotUniqueParam::DuplicateParam(t.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(t.into())), - }, - ty::GenericArgKind::Const(c) => match c.kind() { - ty::ConstKind::Placeholder(p) => { - if !seen.insert(p.var()) { - return Err(NotUniqueParam::DuplicateParam(c.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(c.into())), - }, - } - } - - Ok(()) -} - -// FIXME: This should check for dupes and non-params first, then infer vars. -enum NotUniqueParam { - DuplicateParam(I::GenericArg), - NotParam(I::GenericArg), -} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b95ebfbe89f24..703f02dabff93 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2570,9 +2570,6 @@ written to standard error output)"), "in diagnostics, use heuristics to shorten paths referring to items"), tune_cpu: Option = (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 = (None, parse_opt_bool, [TRACKED], "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), diff --git a/tests/ui/impl-trait/issue-100075-2.stderr b/tests/ui/impl-trait/issue-100075-2.stderr index b3b6967750705..4eb75a4d4fed2 100644 --- a/tests/ui/impl-trait/issue-100075-2.stderr +++ b/tests/ui/impl-trait/issue-100075-2.stderr @@ -1,3 +1,9 @@ +error: cannot resolve opaque type + --> $DIR/issue-100075-2.rs:1:23 + | +LL | fn opaque(t: T) -> impl Sized { + | ^^^^^^^^^^ + warning: function cannot return without recursing --> $DIR/issue-100075-2.rs:1:1 | @@ -10,15 +16,5 @@ LL | opaque(Some(t)) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0720]: cannot resolve opaque type - --> $DIR/issue-100075-2.rs:1:23 - | -LL | fn opaque(t: T) -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | opaque(Some(t)) - | --------------- returning here with type `impl Sized` - error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/issue-100075.stderr b/tests/ui/impl-trait/issue-100075.stderr index 7596348923693..404918783d399 100644 --- a/tests/ui/impl-trait/issue-100075.stderr +++ b/tests/ui/impl-trait/issue-100075.stderr @@ -1,12 +1,8 @@ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/issue-100075.rs:13:37 | LL | fn _g(t: &'static T) -> &'static impl Marker { - | ^^^^^^^^^^^ recursive opaque type -... -LL | return _g(t); - | ----- returning here with type `&impl Marker` + | ^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/issue-87450.stderr b/tests/ui/impl-trait/issue-87450.stderr index 9567e09651d6c..5699d5edc7cfd 100644 --- a/tests/ui/impl-trait/issue-87450.stderr +++ b/tests/ui/impl-trait/issue-87450.stderr @@ -1,3 +1,9 @@ +error: cannot resolve opaque type + --> $DIR/issue-87450.rs:5:13 + | +LL | fn foo() -> impl Fn() { + | ^^^^^^^^^ + warning: function cannot return without recursing --> $DIR/issue-87450.rs:5:1 | @@ -10,18 +16,5 @@ LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0720]: cannot resolve opaque type - --> $DIR/issue-87450.rs:5:13 - | -LL | fn foo() -> impl Fn() { - | ^^^^^^^^^ recursive opaque type -... -LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) - | ----------------------------------------------- returning here with type `impl Fn()` -... -LL | fn wrap(f: impl Fn()) -> impl Fn() { - | --------- returning this opaque type `impl Fn()` - error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/no-anonymize-regions.rs b/tests/ui/impl-trait/no-anonymize-regions.rs new file mode 100644 index 0000000000000..42529c5adc060 --- /dev/null +++ b/tests/ui/impl-trait/no-anonymize-regions.rs @@ -0,0 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// A regression test for an error in `redis` while working on #139587. +// +// We check for structural equality when adding defining uses of opaques. +// In this test one defining use had anonymized regions while the other +// one did not, causing an error. +struct W(T); +fn constrain R, T, R>(f: F) -> R { + loop {} +} +fn foo<'a>(x: for<'b> fn(&'b ())) -> impl Sized + use<'a> { + let mut r = constrain(foo::<'_>); + r = W(x); + r +} +fn main() {} diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2d2731e4368f5..63eb581b8f493 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -1,113 +1,68 @@ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:6:22 | LL | fn option(i: i32) -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | if i < 0 { None } else { Some((option(i - 1), i)) } - | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>` - | | - | returning here with type `Option<(impl Sized, i32)>` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:11:15 | LL | fn tuple() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (tuple(),) - | ---------- returning here with type `(impl Sized,)` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:16:15 | LL | fn array() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | [array()] - | --------- returning here with type `[impl Sized; 1]` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:21:13 | LL | fn ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | &ptr() as *const _ - | ------------------ returning here with type `*const impl Sized` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:26:16 | LL | fn fn_ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | fn_ptr as fn() -> _ - | ------------------- returning here with type `fn() -> impl Sized` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:31:25 | -LL | fn closure_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}` +LL | fn closure_capture() -> impl Sized { + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:39:29 | -LL | fn closure_ref_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | &x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}` +LL | fn closure_ref_capture() -> impl Sized { + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:47:21 | LL | fn closure_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || closure_sig() - | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:52:23 | LL | fn coroutine_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || coroutine_sig() - | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}` + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:57:27 | -LL | fn coroutine_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | yield; -LL | | x; - | | - coroutine captures itself here -LL | | } - | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:62:5: 62:12}` +LL | fn coroutine_capture() -> impl Sized { + | ^^^^^^^^^^ -error[E0720]: cannot resolve opaque type +error: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:68:35 | LL | fn substs_change() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (substs_change::<&T>(),) - | ------------------------ returning here with type `(impl Sized,)` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:78:26 diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs b/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs index 4633ad6823024..7ba6addbc542f 100644 --- a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs +++ b/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs @@ -8,7 +8,9 @@ impl Cap<'_> for T {} // Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: // - '?15 member ['static, 'a, 'b] // from outer impl-trait // - '?15 member ['static, 'a] // from inner impl-trait -// To satisfy both we can only choose 'a. +// To satisfy both we can only choose 'a. Concretely, the applying the first member constraint +// requires ?15 to outlive at least 'b while the second requires ?15 to outlive 'a. As +// 'a outlives 'b we end up with 'a as the final member region. fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> where 's: 'a, diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs new file mode 100644 index 0000000000000..c8e0edc906bf5 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Proj<'a> { + type Assoc; +} + +impl<'a, 'b, F: FnOnce() -> &'b ()> Proj<'a> for F { + type Assoc = (); +} + +fn is_proj Proj<'a>>(f: F) {} + +fn define<'a>() -> impl Sized + use<'a> { + // This defines the RPIT to `&'unconstrained_b ()`, an inference + // variable which is in a higher universe as gets created inside + // of the binder of `F: for<'a> Proj<'a>`. This previously caused + // us to not apply member constraints. + // + // This was unnecessary. It is totally acceptable for member regions + // to be able to name placeholders from higher universes, as long as + // they don't actually do so. + is_proj(define::<'a>); + &() +} + +fn main() {} + + diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs new file mode 100644 index 0000000000000..a35d0ddd5cfa1 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs @@ -0,0 +1,31 @@ +//@ check-pass + +// Unlike `non-root-universe-existential-1.rs` this previously +// compiled. We didn't attempt to define the hidden type of +// `impl Iterator` when projecting through it. We now do. Further +// minimizing this is challenging. + +struct Type(Vec); +enum TypeTreeValueIter<'a, T> { + Once(T), + Ref(&'a ()), +} + +impl<'a, T> Iterator for TypeTreeValueIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + loop {} + } +} + +fn item>(x: I) -> ::Item { + loop {} +} + +fn get_type_tree_values<'a>(ty: &'a Type) -> impl Iterator { + let _: &'a Type = item(std::iter::once(ty).map(get_type_tree_values)); + TypeTreeValueIter::<'a, &'a Type>::Once(ty) +} + +fn main() {} diff --git a/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs b/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs new file mode 100644 index 0000000000000..5eb86a934c5b1 --- /dev/null +++ b/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs @@ -0,0 +1,31 @@ +//@ check-pass + +// We've got `'0 member ['a, 'b, 'static]` and `'1 member ['a, 'b, 'static]`. +// +// As '0 gets outlived by 'a - its "upper bound" - the only applicable choice +// region is 'a. +// +// '1 has to outlive 'b so the only applicabel choice regions are 'b and 'static. +// Considering this member constraint by itself would choose 'b as it is the +// smaller of the two regions. +// +// However, this is only the case when ignoring the member constraint on '0. +// After applying this constraint and requiring '0 to outlive 'a. As '1 outlives +// '0, the region 'b is no longer an applicable choice region for '1 as 'b does +// does not outlive 'a. We would therefore choose 'static. +// +// This means applying member constraints is order dependent. We handle this by +// first applying member constraints for regions 'x and then consider the resulting +// constraints when applying member constraints for regions 'y with 'y: 'x. +fn with_constraints<'r0, 'r1, 'a, 'b>() -> *mut (&'r0 (), &'r1 ()) +where + 'r1: 'r0, + 'a: 'r0, + 'r1: 'b, +{ + loop {} +} +fn foo<'a, 'b>() -> impl Sized + use<'a, 'b> { + with_constraints::<'_, '_, 'a, 'b>() +} +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs index b79926eebda3b..8ca646a9a2ddf 100644 --- a/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs +++ b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs @@ -3,6 +3,9 @@ // of the async block. This caused borrowck of the recursive // call to ICE. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ edition: 2021 //@ check-pass async fn test() { diff --git a/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs new file mode 100644 index 0000000000000..5e7e9738616cf --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// The recursive call to `foo` results in the opaque type use `opaque = ?unconstrained`. +// This needs to be supported and treated as a revealing use. + +fn foo(b: bool) -> impl Sized { + if b { + foo::(b); + } + 1u16 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr index d820df472f95b..11e1398ed51aa 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr @@ -1,9 +1,8 @@ -error[E0720]: cannot resolve opaque type - --> $DIR/infinite-cycle-involving-weak.rs:3:10 +error: cannot resolve opaque type + --> $DIR/infinite-cycle-involving-weak.rs:7:13 | -LL | type T = impl Copy; - | ^^^^^^^^^ cannot resolve opaque type +LL | fn foo() -> T { + | ^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr index 59ff991761247..4d66dd2c7b7b7 100644 --- a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr @@ -1,14 +1,8 @@ -error: concrete type differs from previous defining opaque type use +error: cannot resolve opaque type --> $DIR/recursive-fn-tait.rs:15:6 | LL | ) -> Diff { - | ^^^^ expected `{closure@$DIR/recursive-fn-tait.rs:8:5: 8:16}`, got `{closure@$DIR/recursive-fn-tait.rs:17:5: 17:20}` - | -note: previous use here - --> $DIR/recursive-fn-tait.rs:7:18 - | -LL | pub fn lift() -> Diff { - | ^^^^ + | ^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr index e527b5bc7f8f8..b63a8a1ff4c8c 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr @@ -1,13 +1,7 @@ -error: concrete type differs from previous defining opaque type use +error: cannot resolve opaque type --> $DIR/recursive-tait-conflicting-defn-2.rs:15:13 | LL | fn bad() -> Op { - | ^^ expected `&&str`, got `impl std::fmt::Display` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn-2.rs:7:13 - | -LL | fn foo() -> Op { | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr index 256f13b622179..78e2265aa285a 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr @@ -1,14 +1,8 @@ -error: concrete type differs from previous defining opaque type use +error: cannot resolve opaque type --> $DIR/recursive-tait-conflicting-defn.rs:25:22 | LL | fn make_option2() -> Option { - | ^^^^^^^^^^^^^^^^ expected `A`, got `B` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn.rs:20:18 - | -LL | pub fn test() -> TestImpl { - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error